          decryptedSessionKeyPackets.push(pkeskPacketCopy);
                } catch (err) {
                  // `decrypt` can still throw some non-security-sensitive errors
                  util.printDebugError(err);
                  exception = err;
                }
              }));

            } else {
              try {
                await pkeskPacket.decrypt(decryptionKeyPacket);
                if (!algos.includes(enums.write(enums.symmetric, pkeskPacket.sessionKeyAlgorithm))) {
                  throw new Error('A non-preferred symmetric algorithm was used.');
                }
                decryptedSessionKeyPackets.push(pkeskPacket);
              } catch (err) {
                util.printDebugError(err);
                exception = err;
              }
            }
          }));
        }));
        cancel(pkeskPacket.encrypted); // Don't keep copy of encrypted data in memory.
        pkeskPacket.encrypted = null;
      }));
    } else {
      throw new Error('No key or password specified.');
    }

    if (decryptedSessionKeyPackets.length > 0) {
      // Return only unique session keys
      if (decryptedSessionKeyPackets.length > 1) {
        const seen = new Set();
        decryptedSessionKeyPackets = decryptedSessionKeyPackets.filter(item => {
          const k = item.sessionKeyAlgorithm + util.uint8ArrayToString(item.sessionKey);
          if (seen.has(k)) {
            return false;
          }
          seen.add(k);
          return true;
        });
      }

      return decryptedSessionKeyPackets.map(packet => ({
        data: packet.sessionKey,
        algorithm: enums.read(enums.symmetric, packet.sessionKeyAlgorithm)
      }));
    }
    throw exception || new Error('Session key decryption failed.');
  }

  /**
   * Get literal data that is the body of the message
   * @returns {(Uint8Array|null)} Literal body of the message as Uint8Array.
   */
  getLiteralData() {
    const msg = this.unwrapCompressed();
    const literal = msg.packets.findPacket(enums.packet.literalData);
    return (literal && literal.getBytes()) || null;
  }

  /**
   * Get filename from literal data packet
   * @returns {(String|null)} Filename of literal data packet as string.
   */
  getFilename() {
    const msg = this.unwrapCompressed();
    const literal = msg.packets.findPacket(enums.packet.literalData);
    return (literal && literal.getFilename()) || null;
  }

  /**
   * Get literal data as text
   * @returns {(String|null)} Literal body of the message interpreted as text.
   */
  getText() {
    const msg = this.unwrapCompressed();
    const literal = msg.packets.findPacket(enums.packet.literalData);
    if (literal) {
      return literal.getText();
    }
    return null;
  }

  /**
   * Generate a new session key object, taking the algorithm preferences of the passed encryption keys into account, if any.
   * @param {Array<PublicKey>} [encryptionKeys] - Public key(s) to select algorithm preferences for
   * @param {Date} [date] - Date to select algorithm preferences at
   * @param {Array<Object>} [userIDs] - User IDs to select algorithm preferences for
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<{ data: Uint8Array, algorithm: String, aeadAlgorithm: undefined|String }>} Object with session key data and algorithms.
   * @async
   */
  static async generateSessionKey(encryptionKeys = [], date = new Date(), userIDs = [], config$1 = config) {
    const algo = await getPreferredAlgo('symmetric', encryptionKeys, date, userIDs, config$1);
    const algorithmName = enums.read(enums.symmetric, algo);
    const aeadAlgorithmName = config$1.aeadProtect && await isAEADSupported(encryptionKeys, date, userIDs, config$1) ?
      enums.read(enums.aead, await getPreferredAlgo('aead', encryptionKeys, date, userIDs, config$1)) :
      undefined;

    await Promise.all(encryptionKeys.map(key => key.getEncryptionKey()
      .catch(() => null) // ignore key strength requirements
      .then(maybeKey => {
        if (maybeKey && (maybeKey.keyPacket.algorithm === enums.publicKey.x25519) && !util.isAES(algo)) {
          throw new Error('Could not generate a session key compatible with the given `encryptionKeys`: X22519 keys can only be used to encrypt AES session keys; change `config.preferredSymmetricAlgorithm` accordingly.');
        }
      })
    ));

    const sessionKeyData = mod.generateSessionKey(algo);
    return { data: sessionKeyData, algorithm: algorithmName, aeadAlgorithm: aeadAlgorithmName };
  }

  /**
   * Encrypt the message either with public keys, passwords, or both at once.
   * @param {Array<PublicKey>} [encryptionKeys] - Public key(s) for message encryption
   * @param {Array<String>} [passwords] - Password(s) for message encryption
   * @param {Object} [sessionKey] - Session key in the form: { data:Uint8Array, algorithm:String, [aeadAlgorithm:String] }
   * @param {Boolean} [wildcard] - Use a key ID of 0 instead of the public key IDs
   * @param {Array<module:type/keyid~KeyID>} [encryptionKeyIDs] - Array of key IDs to use for encryption. Each encryptionKeyIDs[i] corresponds to keys[i]
   * @param {Date} [date] - Override the creation date of the literal package
   * @param {Array<Object>} [userIDs] - User IDs to encrypt for, e.g. [{ name:'Robert Receiver', email:'robert@openpgp.org' }]
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Message>} New message with encrypted content.
   * @async
   */
  async encrypt(encryptionKeys, passwords, sessionKey, wildcard = false, encryptionKeyIDs = [], date = new Date(), userIDs = [], config$1 = config) {
    if (sessionKey) {
      if (!util.isUint8Array(sessionKey.data) || !util.isString(sessionKey.algorithm)) {
        throw new Error('Invalid session key for encryption.');
      }
    } else if (encryptionKeys && encryptionKeys.length) {
      sessionKey = await Message.generateSessionKey(encryptionKeys, date, userIDs, config$1);
    } else if (passwords && passwords.length) {
      sessionKey = await Message.generateSessionKey(undefined, undefined, undefined, config$1);
    } else {
      throw new Error('No keys, passwords, or session key provided.');
    }

    const { data: sessionKeyData, algorithm: algorithmName, aeadAlgorithm: aeadAlgorithmName } = sessionKey;

    const msg = await Message.encryptSessionKey(sessionKeyData, algorithmName, aeadAlgorithmName, encryptionKeys, passwords, wildcard, encryptionKeyIDs, date, userIDs, config$1);

    let symEncryptedPacket;
    if (aeadAlgorithmName) {
      symEncryptedPacket = new AEADEncryptedDataPacket();
      symEncryptedPacket.aeadAlgorithm = enums.write(enums.aead, aeadAlgorithmName);
    } else {
      symEncryptedPacket = new SymEncryptedIntegrityProtectedDataPacket();
    }
    symEncryptedPacket.packets = this.packets;

    const algorithm = enums.write(enums.symmetric, algorithmName);
    await symEncryptedPacket.encrypt(algorithm, sessionKeyData, config$1);

    msg.packets.push(symEncryptedPacket);
    symEncryptedPacket.packets = new PacketList(); // remove packets after encryption
    return msg;
  }

  /**
   * Encrypt a session key either with public keys, passwords, or both at once.
   * @param {Uint8Array} sessionKey - session key for encryption
   * @param {String} algorithmName - session key algorithm
   * @param {String} [aeadAlgorithmName] - AEAD algorithm, e.g. 'eax' or 'ocb'
   * @param {Array<PublicKey>} [encryptionKeys] - Public key(s) for message encryption
   * @param {Array<String>} [passwords] - For message encryption
   * @param {Boolean} [wildcard] - Use a key ID of 0 instead of the public key IDs
   * @param {Array<module:type/keyid~KeyID>} [encryptionKeyIDs] - Array of key IDs to use for encryption. Each encryptionKeyIDs[i] corresponds to encryptionKeys[i]
   * @param {Date} [date] - Override the date
   * @param {Array} [userIDs] - User IDs to encrypt for, e.g. [{ name:'Robert Receiver', email:'robert@openpgp.org' }]
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Message>} New message with encrypted content.
   * @async
   */
  static async encryptSessionKey(sessionKey, algorithmName, aeadAlgorithmName, encryptionKeys, passwords, wildcard = false, encryptionKeyIDs = [], date = new Date(), userIDs = [], config$1 = config) {
    const packetlist = new PacketList();
    const algorithm = enums.write(enums.symmetric, algorithmName);
    const aeadAlgorithm = aeadAlgorithmName && enums.write(enums.aead, aeadAlgorithmName);

    if (encryptionKeys) {
      const results = await Promise.all(encryptionKeys.map(async function(primaryKey, i) {
        const encryptionKey = await primaryKey.getEncryptionKey(encryptionKeyIDs[i], date, userIDs, config$1);
        const pkESKeyPacket = new PublicKeyEncryptedSessionKeyPacket();
        pkESKeyPacket.publicKeyID = wildcard ? KeyID.wildcard() : encryptionKey.getKeyID();
        pkESKeyPacket.publicKeyAlgorithm = encryptionKey.keyPacket.algorithm;
        pkESKeyPacket.sessionKey = sessionKey;
        pkESKeyPacket.sessionKeyAlgorithm = algorithm;
        await pkESKeyPacket.encrypt(encryptionKey.keyPacket);
        delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption
        return pkESKeyPacket;
      }));
      packetlist.push(...results);
    }
    if (passwords) {
      const testDecrypt = async function(keyPacket, password) {
        try {
          await keyPacket.decrypt(password);
          return 1;
        } catch (e) {
          return 0;
        }
      };

      const sum = (accumulator, currentValue) => accumulator + currentValue;

      const encryptPassword = async function(sessionKey, algorithm, aeadAlgorithm, password) {
        const symEncryptedSessionKeyPacket = new SymEncryptedSessionKeyPacket(config$1);
        symEncryptedSessionKeyPacket.sessionKey = sessionKey;
        symEncryptedSessionKeyPacket.sessionKeyAlgorithm = algorithm;
        if (aeadAlgorithm) {
          symEncryptedSessionKeyPacket.aeadAlgorithm = aeadAlgorithm;
        }
        await symEncryptedSessionKeyPacket.encrypt(password, config$1);

        if (config$1.passwordCollisionCheck) {
          const results = await Promise.all(passwords.map(pwd => testDecrypt(symEncryptedSessionKeyPacket, pwd)));
          if (results.reduce(sum) !== 1) {
            return encryptPassword(sessionKey, algorithm, password);
          }
        }

        delete symEncryptedSessionKeyPacket.sessionKey; // delete plaintext session key after encryption
        return symEncryptedSessionKeyPacket;
      };

      const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, algorithm, aeadAlgorithm, pwd)));
      packetlist.push(...results);
    }

    return new Message(packetlist);
  }

  /**
   * Sign the message (the literal data packet of the message)
   * @param {Array<PrivateKey>} signingKeys - private keys with decrypted secret key data for signing
   * @param {Signature} [signature] - Any existing detached signature to add to the message
   * @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
   * @param {Date} [date] - Override the creation time of the signature
   * @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
   * @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Message>} New message with signed content.
   * @async
   */
  async sign(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config$1 = config) {
    const packetlist = new PacketList();

    const literalDataPacket = this.packets.findPacket(enums.packet.literalData);
    if (!literalDataPacket) {
      throw new Error('No literal data packet to sign.');
    }

    let i;
    let existingSigPacketlist;
    // If data packet was created from Uint8Array, use binary, otherwise use text
    const signatureType = literalDataPacket.text === null ?
      enums.signature.binary : enums.signature.text;

    if (signature) {
      existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature);
      for (i = existingSigPacketlist.length - 1; i >= 0; i--) {
        const signaturePacket = existingSigPacketlist[i];
        const onePassSig = new OnePassSignaturePacket();
        onePassSig.signatureType = signaturePacket.signatureType;
        onePassSig.hashAlgorithm = signaturePacket.hashAlgorithm;
        onePassSig.publicKeyAlgorithm = signaturePacket.publicKeyAlgorithm;
        onePassSig.issuerKeyID = signaturePacket.issuerKeyID;
        if (!signingKeys.length && i === 0) {
          onePassSig.flags = 1;
        }
        packetlist.push(onePassSig);
      }
    }

    await Promise.all(Array.from(signingKeys).reverse().map(async function (primaryKey, i) {
      if (!primaryKey.isPrivate()) {
        throw new Error('Need private key for signing');
      }
      const signingKeyID = signingKeyIDs[signingKeys.length - 1 - i];
      const signingKey = await primaryKey.getSigningKey(signingKeyID, date, userIDs, config$1);
      const onePassSig = new OnePassSignaturePacket();
      onePassSig.signatureType = signatureType;
      onePassSig.hashAlgorithm = await getPreferredHashAlgo$2(primaryKey, signingKey.keyPacket, date, userIDs, config$1);
      onePassSig.publicKeyAlgorithm = signingKey.keyPacket.algorithm;
      onePassSig.issuerKeyID = signingKey.getKeyID();
      if (i === signingKeys.length - 1) {
        onePassSig.flags = 1;
      }
      return onePassSig;
    })).then(onePassSignatureList => {
      onePassSignatureList.forEach(onePassSig => packetlist.push(onePassSig));
    });

    packetlist.push(literalDataPacket);
    packetlist.push(...(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, false, config$1)));

    return new Message(packetlist);
  }

  /**
   * Compresses the message (the literal and -if signed- signature data packets of the message)
   * @param {module:enums.compression} algo - compression algorithm
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Message} New message with compressed content.
   */
  compress(algo, config$1 = config) {
    if (algo === enums.compression.uncompressed) {
      return this;
    }

    const compressed = new CompressedDataPacket(config$1);
    compressed.algorithm = algo;
    compressed.packets = this.packets;

    const packetList = new PacketList();
    packetList.push(compressed);

    return new Message(packetList);
  }

  /**
   * Create a detached signature for the message (the literal data packet of the message)
   * @param {Array<PrivateKey>} signingKeys - private keys with decrypted secret key data for signing
   * @param {Signature} [signature] - Any existing detached signature
   * @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
   * @param {Date} [date] - Override the creation time of the signature
   * @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
   * @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Signature>} New detached signature of message content.
   * @async
   */
  async signDetached(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config$1 = config) {
    const literalDataPacket = this.packets.findPacket(enums.packet.literalData);
    if (!literalDataPacket) {
      throw new Error('No literal data packet to sign.');
    }
    return new Signature(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, true, config$1));
  }

  /**
   * Verify message signatures
   * @param {Array<PublicKey>} verificationKeys - Array of public keys to verify signatures
   * @param {Date} [date] - Verify the signature against the given date, i.e. check signature creation time < date < expiration time
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Array<{
   *   keyID: module:type/keyid~KeyID,
   *   signature: Promise<Signature>,
   *   verified: Promise<true>
   * }>>} List of signer's keyID and validity of signatures.
   * @async
   */
  async verify(verificationKeys, date = new Date(), config$1 = config) {
    const msg = this.unwrapCompressed();
    const literalDataList = msg.packets.filterByTag(enums.packet.literalData);
    if (literalDataList.length !== 1) {
      throw new Error('Can only verify message with one literal data packet.');
    }
    if (isArrayStream(msg.packets.stream)) {
      msg.packets.push(...await readToEnd(msg.packets.stream, _ => _ || []));
    }
    const onePassSigList = msg.packets.filterByTag(enums.packet.onePassSignature).reverse();
    const signatureList = msg.packets.filterByTag(enums.packet.signature);
    if (onePassSigList.length && !signatureList.length && util.isStream(msg.packets.stream) && !isArrayStream(msg.packets.stream)) {
      await Promise.all(onePassSigList.map(async onePassSig => {
        onePassSig.correspondingSig = new Promise((resolve, reject) => {
          onePassSig.correspondingSigResolve = resolve;
          onePassSig.correspondingSigReject = reject;
        });
        onePassSig.signatureData = fromAsync(async () => (await onePassSig.correspondingSig).signatureData);
        onePassSig.hashed = readToEnd(await onePassSig.hash(onePassSig.signatureType, literalDataList[0], undefined, false));
        onePassSig.hashed.catch(() => {});
      }));
      msg.packets.stream = transformPair(msg.packets.stream, async (readable, writable) => {
        const reader = getReader(readable);
        const writer = getWriter(writable);
        try {
          for (let i = 0; i < onePassSigList.length; i++) {
            const { value: signature } = await reader.read();
            onePassSigList[i].correspondingSigResolve(signature);
          }
          await reader.readToEnd();
          await writer.ready;
          await writer.close();
        } catch (e) {
          onePassSigList.forEach(onePassSig => {
            onePassSig.correspondingSigReject(e);
          });
          await writer.abort(e);
        }
      });
      return createVerificationObjects(onePassSigList, literalDataList, verificationKeys, date, false, config$1);
    }
    return createVerificationObjects(signatureList, literalDataList, verificationKeys, date, false, config$1);
  }

  /**
   * Verify detached message signature
   * @param {Array<PublicKey>} verificationKeys - Array of public keys to verify signatures
   * @param {Signature} signature
   * @param {Date} date - Verify the signature against the given date, i.e. check signature creation time < date < expiration time
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Array<{
   *   keyID: module:type/keyid~KeyID,
   *   signature: Promise<Signature>,
   *   verified: Promise<true>
   * }>>} List of signer's keyID and validity of signature.
   * @async
   */
  verifyDetached(signature, verificationKeys, date = new Date(), config$1 = config) {
    const msg = this.unwrapCompressed();
    const literalDataList = msg.packets.filterByTag(enums.packet.literalData);
    if (literalDataList.length !== 1) {
      throw new Error('Can only verify message with one literal data packet.');
    }
    const signatureList = signature.packets.filterByTag(enums.packet.signature); // drop UnparsablePackets
    return createVerificationObjects(signatureList, literalDataList, verificationKeys, date, true, config$1);
  }

  /**
   * Unwrap compressed message
   * @returns {Message} Message Content of compressed message.
   */
  unwrapCompressed() {
    const compressed = this.packets.filterByTag(enums.packet.compressedData);
    if (compressed.length) {
      return new Message(compressed[0].packets);
    }
    return this;
  }

  /**
   * Append signature to unencrypted message object
   * @param {String|Uint8Array} detachedSignature - The detached ASCII-armored or Uint8Array PGP signature
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   */
  async appendSignature(detachedSignature, config$1 = config) {
    await this.packets.read(
      util.isUint8Array(detachedSignature) ? detachedSignature : (await unarmor(detachedSignature)).data,
      allowedDetachedSignaturePackets,
      config$1
    );
  }

  /**
   * Returns binary encoded message
   * @returns {ReadableStream<Uint8Array>} Binary message.
   */
  write() {
    return this.packets.write();
  }

  /**
   * Returns ASCII armored text of message
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {ReadableStream<String>} ASCII armor.
   */
  armor(config$1 = config) {
    return armor(enums.armor.message, this.write(), null, null, null, config$1);
  }
}

/**
 * Create signature packets for the message
 * @param {LiteralDataPacket} literalDataPacket - the literal data packet to sign
 * @param {Array<PrivateKey>} [signingKeys] - private keys with decrypted secret key data for signing
 * @param {Signature} [signature] - Any existing detached signature to append
 * @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
 * @param {Date} [date] - Override the creationtime of the signature
 * @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
 * @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
 * @param {Boolean} [detached] - Whether to create detached signature packets
 * @param {Object} [config] - Full configuration, defaults to openpgp.config
 * @returns {Promise<PacketList>} List of signature packets.
 * @async
 * @private
 */
async function createSignaturePackets(literalDataPacket, signingKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], detached = false, config$1 = config) {
  const packetlist = new PacketList();

  // If data packet was created from Uint8Array, use binary, otherwise use text
  const signatureType = literalDataPacket.text === null ?
    enums.signature.binary : enums.signature.text;

  await Promise.all(signingKeys.map(async (primaryKey, i) => {
    const userID = userIDs[i];
    if (!primaryKey.isPrivate()) {
      throw new Error('Need private key for signing');
    }
    const signingKey = await primaryKey.getSigningKey(signingKeyIDs[i], date, userID, config$1);
    return createSignaturePacket(literalDataPacket, primaryKey, signingKey.keyPacket, { signatureType }, date, userID, notations, detached, config$1);
  })).then(signatureList => {
    packetlist.push(...signatureList);
  });

  if (signature) {
    const existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature);
    packetlist.push(...existingSigPacketlist);
  }
  return packetlist;
}

/**
 * Create object containing signer's keyID and validity of signature
 * @param {SignaturePacket} signature - Signature packet
 * @param {Array<LiteralDataPacket>} literalDataList - Array of literal data packets
 * @param {Array<PublicKey>} verificationKeys - Array of public keys to verify signatures
 * @param {Date} [date] - Check signature validity with respect to the given date
 * @param {Boolean} [detached] - Whether to verify detached signature packets
 * @param {Object} [config] - Full configuration, defaults to openpgp.config
 * @returns {Promise<{
 *   keyID: module:type/keyid~KeyID,
 *   signature: Promise<Signature>,
 *   verified: Promise<true>
 * }>} signer's keyID and validity of signature
 * @async
 * @private
 */
async function createVerificationObject(signature, literalDataList, verificationKeys, date = new Date(), detached = false, config$1 = config) {
  let primaryKey;
  let unverifiedSigningKey;

  for (const key of verificationKeys) {
    const issuerKeys = key.getKeys(signature.issuerKeyID);
    if (issuerKeys.length > 0) {
      primaryKey = key;
      unverifiedSigningKey = issuerKeys[0];
      break;
    }
  }

  const isOnePassSignature = signature instanceof OnePassSignaturePacket;
  const signaturePacketPromise = isOnePassSignature ? signature.correspondingSig : signature;

  const verifiedSig = {
    keyID: signature.issuerKeyID,
    verified: (async () => {
      if (!unverifiedSigningKey) {
        throw new Error(`Could not find signing key with key ID ${signature.issuerKeyID.toHex()}`);
      }

      await signature.verify(unverifiedSigningKey.keyPacket, signature.signatureType, literalDataList[0], date, detached, config$1);
      const signaturePacket = await signaturePacketPromise;
      if (unverifiedSigningKey.getCreationTime() > signaturePacket.created) {
        throw new Error('Key is newer than the signature');
      }
      // We pass the signature creation time to check whether the key was expired at the time of signing.
      // We check this after signature verification because for streamed one-pass signatures, the creation time is not available before
      try {
        await primaryKey.getSigningKey(unverifiedSigningKey.getKeyID(), signaturePacket.created, undefined, config$1);
      } catch (e) {
        // If a key was reformatted then the self-signatures of the signing key might be in the future compared to the message signature,
        // making the key invalid at the time of signing.
        // However, if the key is valid at the given `date`, we still allow using it provided the relevant `config` setting is enabled.
        // Note: we do not support the edge case of a key that was reformatted and it has expired.
        if (config$1.allowInsecureVerificationWithReformattedKeys && e.message.match(/Signature creation time is in the future/)) {
          await primaryKey.getSigningKey(unverifiedSigningKey.getKeyID(), date, undefined, config$1);
        } else {
          throw e;
        }
      }
      return true;
    })(),
    signature: (async () => {
      const signaturePacket = await signaturePacketPromise;
      const packetlist = new PacketList();
      signaturePacket && packetlist.push(signaturePacket);
      return new Signature(packetlist);
    })()
  };

  // Mark potential promise rejections as "handled". This is needed because in
  // some cases, we reject them before the user has a reasonable chance to
  // handle them (e.g. `await readToEnd(result.data); await result.verified` and
  // the data stream errors).
  verifiedSig.signature.catch(() => {});
  verifiedSig.verified.catch(() => {});

  return verifiedSig;
}

/**
 * Create list of objects containing signer's keyID and validity of signature
 * @param {Array<SignaturePacket>} signatureList - Array of signature packets
 * @param {Array<LiteralDataPacket>} literalDataList - Array of literal data packets
 * @param {Array<PublicKey>} verificationKeys - Array of public keys to verify signatures
 * @param {Date} date - Verify the signature against the given date,
 *                    i.e. check signature creation time < date < expiration time
 * @param {Boolean} [detached] - Whether to verify detached signature packets
 * @param {Object} [config] - Full configuration, defaults to openpgp.config
 * @returns {Promise<Array<{
 *   keyID: module:type/keyid~KeyID,
 *   signature: Promise<Signature>,
 *   verified: Promise<true>
 * }>>} list of signer's keyID and validity of signatures (one entry per signature packet in input)
 * @async
 * @private
 */
async function createVerificationObjects(signatureList, literalDataList, verificationKeys, date = new Date(), detached = false, config$1 = config) {
  return Promise.all(signatureList.filter(function(signature) {
    return ['text', 'binary'].includes(enums.read(enums.signature, signature.signatureType));
  }).map(async function(signature) {
    return createVerificationObject(signature, literalDataList, verificationKeys, date, detached, config$1);
  }));
}

/**
 * Reads an (optionally armored) OpenPGP message and returns a Message object
 * @param {Object} options
 * @param {String | ReadableStream<String>} [options.armoredMessage] - Armored message to be parsed
 * @param {Uint8Array | ReadableStream<Uint8Array>} [options.binaryMessage] - Binary to be parsed
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<Message>} New message object.
 * @async
 * @static
 */
async function readMessage({ armoredMessage, binaryMessage, config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 };
  let input = armoredMessage || binaryMessage;
  if (!input) {
    throw new Error('readMessage: must pass options object containing `armoredMessage` or `binaryMessage`');
  }
  if (armoredMessage && !util.isString(armoredMessage) && !util.isStream(armoredMessage)) {
    throw new Error('readMessage: options.armoredMessage must be a string or stream');
  }
  if (binaryMessage && !util.isUint8Array(binaryMessage) && !util.isStream(binaryMessage)) {
    throw new Error('readMessage: options.binaryMessage must be a Uint8Array or stream');
  }
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  const streamType = util.isStream(input);
  if (streamType) {
    await loadStreamsPonyfill();
    input = toStream(input);
  }
  if (armoredMessage) {
    const { type, data } = await unarmor(input, config$1);
    if (type !== enums.armor.message) {
      throw new Error('Armored text not of type message');
    }
    input = data;
  }
  const packetlist = await PacketList.fromBinary(input, allowedMessagePackets, config$1);
  const message = new Message(packetlist);
  message.fromStream = streamType;
  return message;
}

/**
 * Creates new message object from text or binary data.
 * @param {Object} options
 * @param {String | ReadableStream<String>} [options.text] - The text message contents
 * @param {Uint8Array | ReadableStream<Uint8Array>} [options.binary] - The binary message contents
 * @param {String} [options.filename=""] - Name of the file (if any)
 * @param {Date} [options.date=current date] - Date of the message, or modification date of the file
 * @param {'utf8'|'binary'|'text'|'mime'} [options.format='utf8' if text is passed, 'binary' otherwise] - Data packet type
 * @returns {Promise<Message>} New message object.
 * @async
 * @static
 */
async function createMessage({ text, binary, filename, date = new Date(), format = text !== undefined ? 'utf8' : 'binary', ...rest }) {
  let input = text !== undefined ? text : binary;
  if (input === undefined) {
    throw new Error('createMessage: must pass options object containing `text` or `binary`');
  }
  if (text && !util.isString(text) && !util.isStream(text)) {
    throw new Error('createMessage: options.text must be a string or stream');
  }
  if (binary && !util.isUint8Array(binary) && !util.isStream(binary)) {
    throw new Error('createMessage: options.binary must be a Uint8Array or stream');
  }
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  const streamType = util.isStream(input);
  if (streamType) {
    await loadStreamsPonyfill();
    input = toStream(input);
  }
  const literalDataPacket = new LiteralDataPacket(date);
  if (text !== undefined) {
    literalDataPacket.setText(input, enums.write(enums.literal, format));
  } else {
    literalDataPacket.setBytes(input, enums.write(enums.literal, format));
  }
  if (filename !== undefined) {
    literalDataPacket.setFilename(filename);
  }
  const literalDataPacketlist = new PacketList();
  literalDataPacketlist.push(literalDataPacket);
  const message = new Message(literalDataPacketlist);
  message.fromStream = streamType;
  return message;
}

// GPG4Browsers - An OpenPGP implementation in javascript

// A Cleartext message can contain the following packets
const allowedPackets$5 = /*#__PURE__*/ util.constructAllowedPackets([SignaturePacket]);

/**
 * Class that represents an OpenPGP cleartext signed message.
 * See {@link https://tools.ietf.org/html/rfc4880#section-7}
 */
class CleartextMessage {
  /**
   * @param {String} text - The cleartext of the signed message
   * @param {Signature} signature - The detached signature or an empty signature for unsigned messages
   */
  constructor(text, signature) {
    // remove trailing whitespace and normalize EOL to canonical form <CR><LF>
    this.text = util.removeTrailingSpaces(text).replace(/\r?\n/g, '\r\n');
    if (signature && !(signature instanceof Signature)) {
      throw new Error('Invalid signature input');
    }
    this.signature = signature || new Signature(new PacketList());
  }

  /**
   * Returns the key IDs of the keys that signed the cleartext message
   * @returns {Array<module:type/keyid~KeyID>} Array of keyID objects.
   */
  getSigningKeyIDs() {
    const keyIDs = [];
    const signatureList = this.signature.packets;
    signatureList.forEach(function(packet) {
      keyIDs.push(packet.issuerKeyID);
    });
    return keyIDs;
  }

  /**
   * Sign the cleartext message
   * @param {Array<Key>} privateKeys - private keys with decrypted secret key data for signing
   * @param {Signature} [signature] - Any existing detached signature
   * @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to privateKeys[i]
   * @param {Date} [date] - The creation time of the signature that should be created
   * @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
   * @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<CleartextMessage>} New cleartext message with signed content.
   * @async
   */
  async sign(privateKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config$1 = config) {
    const literalDataPacket = new LiteralDataPacket();
    literalDataPacket.setText(this.text);
    const newSignature = new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIDs, date, userIDs, notations, true, config$1));
    return new CleartextMessage(this.text, newSignature);
  }

  /**
   * Verify signatures of cleartext signed message
   * @param {Array<Key>} keys - Array of keys to verify signatures
   * @param {Date} [date] - Verify the signature against the given date, i.e. check signature creation time < date < expiration time
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {Promise<Array<{
   *   keyID: module:type/keyid~KeyID,
   *   signature: Promise<Signature>,
   *   verified: Promise<true>
   * }>>} List of signer's keyID and validity of signature.
   * @async
   */
  verify(keys, date = new Date(), config$1 = config) {
    const signatureList = this.signature.packets.filterByTag(enums.packet.signature); // drop UnparsablePackets
    const literalDataPacket = new LiteralDataPacket();
    // we assume that cleartext signature is generated based on UTF8 cleartext
    literalDataPacket.setText(this.text);
    return createVerificationObjects(signatureList, [literalDataPacket], keys, date, true, config$1);
  }

  /**
   * Get cleartext
   * @returns {String} Cleartext of message.
   */
  getText() {
    // normalize end of line to \n
    return this.text.replace(/\r\n/g, '\n');
  }

  /**
   * Returns ASCII armored text of cleartext signed message
   * @param {Object} [config] - Full configuration, defaults to openpgp.config
   * @returns {String | ReadableStream<String>} ASCII armor.
   */
  armor(config$1 = config) {
    let hashes = this.signature.packets.map(function(packet) {
      return enums.read(enums.hash, packet.hashAlgorithm).toUpperCase();
    });
    hashes = hashes.filter(function(item, i, ar) { return ar.indexOf(item) === i; });
    const body = {
      hash: hashes.join(),
      text: this.text,
      data: this.signature.packets.write()
    };
    return armor(enums.armor.signed, body, undefined, undefined, undefined, config$1);
  }
}

/**
 * Reads an OpenPGP cleartext signed message and returns a CleartextMessage object
 * @param {Object} options
 * @param {String} options.cleartextMessage - Text to be parsed
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<CleartextMessage>} New cleartext message object.
 * @async
 * @static
 */
async function readCleartextMessage({ cleartextMessage, config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 };
  if (!cleartextMessage) {
    throw new Error('readCleartextMessage: must pass options object containing `cleartextMessage`');
  }
  if (!util.isString(cleartextMessage)) {
    throw new Error('readCleartextMessage: options.cleartextMessage must be a string');
  }
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  const input = await unarmor(cleartextMessage);
  if (input.type !== enums.armor.signed) {
    throw new Error('No cleartext signed message.');
  }
  const packetlist = await PacketList.fromBinary(input.data, allowedPackets$5, config$1);
  verifyHeaders$1(input.headers, packetlist);
  const signature = new Signature(packetlist);
  return new CleartextMessage(input.text, signature);
}

/**
 * Compare hash algorithm specified in the armor header with signatures
 * @param {Array<String>} headers - Armor headers
 * @param {PacketList} packetlist - The packetlist with signature packets
 * @private
 */
function verifyHeaders$1(headers, packetlist) {
  const checkHashAlgos = function(hashAlgos) {
    const check = packet => algo => packet.hashAlgorithm === algo;

    for (let i = 0; i < packetlist.length; i++) {
      if (packetlist[i].constructor.tag === enums.packet.signature && !hashAlgos.some(check(packetlist[i]))) {
        return false;
      }
    }
    return true;
  };

  let oneHeader = null;
  let hashAlgos = [];
  headers.forEach(function(header) {
    oneHeader = header.match(/^Hash: (.+)$/); // get header value
    if (oneHeader) {
      oneHeader = oneHeader[1].replace(/\s/g, ''); // remove whitespace
      oneHeader = oneHeader.split(',');
      oneHeader = oneHeader.map(function(hash) {
        hash = hash.toLowerCase();
        try {
          return enums.write(enums.hash, hash);
        } catch (e) {
          throw new Error('Unknown hash algorithm in armor header: ' + hash);
        }
      });
      hashAlgos = hashAlgos.concat(oneHeader);
    } else {
      throw new Error('Only "Hash" header allowed in cleartext signed message');
    }
  });

  if (!hashAlgos.length && !checkHashAlgos([enums.hash.md5])) {
    throw new Error('If no "Hash" header in cleartext signed message, then only MD5 signatures allowed');
  } else if (hashAlgos.length && !checkHashAlgos(hashAlgos)) {
    throw new Error('Hash algorithm mismatch in armor header and signature');
  }
}

/**
 * Creates a new CleartextMessage object from text
 * @param {Object} options
 * @param {String} options.text
 * @static
 * @async
 */
async function createCleartextMessage({ text, ...rest }) {
  if (!text) {
    throw new Error('createCleartextMessage: must pass options object containing `text`');
  }
  if (!util.isString(text)) {
    throw new Error('createCleartextMessage: options.text must be a string');
  }
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  return new CleartextMessage(text);
}

// OpenPGP.js - An OpenPGP implementation in javascript


//////////////////////
//                  //
//   Key handling   //
//                  //
//////////////////////


/**
 * Generates a new OpenPGP key pair. Supports RSA and ECC keys. By default, primary and subkeys will be of same type.
 * The generated primary key will have signing capabilities. By default, one subkey with encryption capabilities is also generated.
 * @param {Object} options
 * @param {Object|Array<Object>} options.userIDs - User IDs as objects: `{ name: 'Jo Doe', email: 'info@jo.com' }`
 * @param {'ecc'|'rsa'} [options.type='ecc'] - The primary key algorithm type: ECC (default) or RSA
 * @param {String} [options.passphrase=(not protected)] - The passphrase used to encrypt the generated private key. If omitted or empty, the key won't be encrypted.
 * @param {Number} [options.rsaBits=4096] - Number of bits for RSA keys
 * @param {String} [options.curve='curve25519'] - Elliptic curve for ECC keys:
 *                                             curve25519 (default), p256, p384, p521, secp256k1,
 *                                             brainpoolP256r1, brainpoolP384r1, or brainpoolP512r1
 * @param {Date} [options.date=current date] - Override the creation date of the key and the key signatures
 * @param {Number} [options.keyExpirationTime=0 (never expires)] - Number of seconds from the key creation time after which the key expires
 * @param {Array<Object>} [options.subkeys=a single encryption subkey] - Options for each subkey e.g. `[{sign: true, passphrase: '123'}]`
 *                                             default to main key options, except for `sign` parameter that defaults to false, and indicates whether the subkey should sign rather than encrypt
 * @param {'armored'|'binary'|'object'} [options.format='armored'] - format of the output keys
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<Object>} The generated key object in the form:
 *                                     { privateKey:PrivateKey|Uint8Array|String, publicKey:PublicKey|Uint8Array|String, revocationCertificate:String }
 * @async
 * @static
 */
async function generateKey({ userIDs = [], passphrase, type = 'ecc', rsaBits = 4096, curve = 'curve25519', keyExpirationTime = 0, date = new Date(), subkeys = [{}], format = 'armored', config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  userIDs = toArray$1(userIDs);
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  if (userIDs.length === 0) {
    throw new Error('UserIDs are required for key generation');
  }
  if (type === 'rsa' && rsaBits < config$1.minRSABits) {
    throw new Error(`rsaBits should be at least ${config$1.minRSABits}, got: ${rsaBits}`);
  }

  const options = { userIDs, passphrase, type, rsaBits, curve, keyExpirationTime, date, subkeys };

  try {
    const { key, revocationCertificate } = await generate$4(options, config$1);
    key.getKeys().forEach(({ keyPacket }) => checkKeyRequirements(keyPacket, config$1));

    return {
      privateKey: formatObject(key, format, config$1),
      publicKey: formatObject(key.toPublic(), format, config$1),
      revocationCertificate
    };
  } catch (err) {
    throw util.wrapError('Error generating keypair', err);
  }
}

/**
 * Reformats signature packets for a key and rewraps key object.
 * @param {Object} options
 * @param {PrivateKey} options.privateKey - Private key to reformat
 * @param {Object|Array<Object>} options.userIDs - User IDs as objects: `{ name: 'Jo Doe', email: 'info@jo.com' }`
 * @param {String} [options.passphrase=(not protected)] - The passphrase used to encrypt the reformatted private key. If omitted or empty, the key won't be encrypted.
 * @param {Number} [options.keyExpirationTime=0 (never expires)] - Number of seconds from the key creation time after which the key expires
 * @param {Date}   [options.date] - Override the creation date of the key signatures. If the key was previously used to sign messages, it is recommended
 *                                  to set the same date as the key creation time to ensure that old message signatures will still be verifiable using the reformatted key.
 * @param {'armored'|'binary'|'object'} [options.format='armored'] - format of the output keys
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<Object>} The generated key object in the form:
 *                                     { privateKey:PrivateKey|Uint8Array|String, publicKey:PublicKey|Uint8Array|String, revocationCertificate:String }
 * @async
 * @static
 */
async function reformatKey({ privateKey, userIDs = [], passphrase, keyExpirationTime = 0, date, format = 'armored', config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  userIDs = toArray$1(userIDs);
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  if (userIDs.length === 0) {
    throw new Error('UserIDs are required for key reformat');
  }
  const options = { privateKey, userIDs, passphrase, keyExpirationTime, date };

  try {
    const { key: reformattedKey, revocationCertificate } = await reformat(options, config$1);

    return {
      privateKey: formatObject(reformattedKey, format, config$1),
      publicKey: formatObject(reformattedKey.toPublic(), format, config$1),
      revocationCertificate
    };
  } catch (err) {
    throw util.wrapError('Error reformatting keypair', err);
  }
}

/**
 * Revokes a key. Requires either a private key or a revocation certificate.
 *   If a revocation certificate is passed, the reasonForRevocation parameter will be ignored.
 * @param {Object} options
 * @param {Key} options.key - Public or private key to revoke
 * @param {String} [options.revocationCertificate] - Revocation certificate to revoke the key with
 * @param {Object} [options.reasonForRevocation] - Object indicating the reason for revocation
 * @param {module:enums.reasonForRevocation} [options.reasonForRevocation.flag=[noReason]{@link module:enums.reasonForRevocation}] - Flag indicating the reason for revocation
 * @param {String} [options.reasonForRevocation.string=""] - String explaining the reason for revocation
 * @param {Date} [options.date] - Use the given date instead of the current time to verify validity of revocation certificate (if provided), or as creation time of the revocation signature
 * @param {'armored'|'binary'|'object'} [options.format='armored'] - format of the output key(s)
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<Object>} The revoked key in the form:
 *                              { privateKey:PrivateKey|Uint8Array|String, publicKey:PublicKey|Uint8Array|String } if private key is passed, or
 *                              { privateKey: null, publicKey:PublicKey|Uint8Array|String } otherwise
 * @async
 * @static
 */
async function revokeKey({ key, revocationCertificate, reasonForRevocation, date = new Date(), format = 'armored', config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  try {
    const revokedKey = revocationCertificate ?
      await key.applyRevocationCertificate(revocationCertificate, date, config$1) :
      await key.revoke(reasonForRevocation, date, config$1);

    return revokedKey.isPrivate() ? {
      privateKey: formatObject(revokedKey, format, config$1),
      publicKey: formatObject(revokedKey.toPublic(), format, config$1)
    } : {
      privateKey: null,
      publicKey: formatObject(revokedKey, format, config$1)
    };
  } catch (err) {
    throw util.wrapError('Error revoking key', err);
  }
}

/**
 * Unlock a private key with the given passphrase.
 * This method does not change the original key.
 * @param {Object} options
 * @param {PrivateKey} options.privateKey - The private key to decrypt
 * @param {String|Array<String>} options.passphrase - The user's passphrase(s)
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<PrivateKey>} The unlocked key object.
 * @async
 */
async function decryptKey({ privateKey, passphrase, config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  if (!privateKey.isPrivate()) {
    throw new Error('Cannot decrypt a public key');
  }
  const clonedPrivateKey = privateKey.clone(true);
  const passphrases = util.isArray(passphrase) ? passphrase : [passphrase];

  try {
    await Promise.all(clonedPrivateKey.getKeys().map(key => (
      // try to decrypt each key with any of the given passphrases
      util.anyPromise(passphrases.map(passphrase => key.keyPacket.decrypt(passphrase)))
    )));

    await clonedPrivateKey.validate(config$1);
    return clonedPrivateKey;
  } catch (err) {
    clonedPrivateKey.clearPrivateParams();
    throw util.wrapError('Error decrypting private key', err);
  }
}

/**
 * Lock a private key with the given passphrase.
 * This method does not change the original key.
 * @param {Object} options
 * @param {PrivateKey} options.privateKey - The private key to encrypt
 * @param {String|Array<String>} options.passphrase - If multiple passphrases, they should be in the same order as the packets each should encrypt
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<PrivateKey>} The locked key object.
 * @async
 */
async function encryptKey({ privateKey, passphrase, config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  if (!privateKey.isPrivate()) {
    throw new Error('Cannot encrypt a public key');
  }
  const clonedPrivateKey = privateKey.clone(true);

  const keys = clonedPrivateKey.getKeys();
  const passphrases = util.isArray(passphrase) ? passphrase : new Array(keys.length).fill(passphrase);
  if (passphrases.length !== keys.length) {
    throw new Error('Invalid number of passphrases given for key encryption');
  }

  try {
    await Promise.all(keys.map(async (key, i) => {
      const { keyPacket } = key;
      await keyPacket.encrypt(passphrases[i], config$1);
      keyPacket.clearPrivateParams();
    }));
    return clonedPrivateKey;
  } catch (err) {
    clonedPrivateKey.clearPrivateParams();
    throw util.wrapError('Error encrypting private key', err);
  }
}


///////////////////////////////////////////
//                                       //
//   Message encryption and decryption   //
//                                       //
///////////////////////////////////////////


/**
 * Encrypts a message using public keys, passwords or both at once. At least one of `encryptionKeys`, `passwords` or `sessionKeys`
 *   must be specified. If signing keys are specified, those will be used to sign the message.
 * @param {Object} options
 * @param {Message} options.message - Message to be encrypted as created by {@link createMessage}
 * @param {PublicKey|PublicKey[]} [options.encryptionKeys] - Array of keys or single key, used to encrypt the message
 * @param {PrivateKey|PrivateKey[]} [options.signingKeys] - Private keys for signing. If omitted message will not be signed
 * @param {String|String[]} [options.passwords] - Array of passwords or a single password to encrypt the message
 * @param {Object} [options.sessionKey] - Session key in the form: `{ data:Uint8Array, algorithm:String }`
 * @param {'armored'|'binary'|'object'} [options.format='armored'] - Format of the returned message
 * @param {Signature} [options.signature] - A detached signature to add to the encrypted message
 * @param {Boolean} [options.wildcard=false] - Use a key ID of 0 instead of the public key IDs
 * @param {KeyID|KeyID[]} [options.signingKeyIDs=latest-created valid signing (sub)keys] - Array of key IDs to use for signing. Each `signingKeyIDs[i]` corresponds to `signingKeys[i]`
 * @param {KeyID|KeyID[]} [options.encryptionKeyIDs=latest-created valid encryption (sub)keys] - Array of key IDs to use for encryption. Each `encryptionKeyIDs[i]` corresponds to `encryptionKeys[i]`
 * @param {Date} [options.date=current date] - Override the creation date of the message signature
 * @param {Object|Object[]} [options.signingUserIDs=primary user IDs] - Array of user IDs to sign with, one per key in `signingKeys`, e.g. `[{ name: 'Steve Sender', email: 'steve@openpgp.org' }]`
 * @param {Object|Object[]} [options.encryptionUserIDs=primary user IDs] - Array of user IDs to encrypt for, one per key in `encryptionKeys`, e.g. `[{ name: 'Robert Receiver', email: 'robert@openpgp.org' }]`
 * @param {Object|Object[]} [options.signatureNotations=[]] - Array of notations to add to the signatures, e.g. `[{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]`
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<MaybeStream<String>|MaybeStream<Uint8Array>>} Encrypted message (string if `armor` was true, the default; Uint8Array if `armor` was false).
 * @async
 * @static
 */
async function encrypt$5({ message, encryptionKeys, signingKeys, passwords, sessionKey, format = 'armored', signature = null, wildcard = false, signingKeyIDs = [], encryptionKeyIDs = [], date = new Date(), signingUserIDs = [], encryptionUserIDs = [], signatureNotations = [], config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  checkMessage(message); checkOutputMessageFormat(format);
  encryptionKeys = toArray$1(encryptionKeys); signingKeys = toArray$1(signingKeys); passwords = toArray$1(passwords);
  signingKeyIDs = toArray$1(signingKeyIDs); encryptionKeyIDs = toArray$1(encryptionKeyIDs); signingUserIDs = toArray$1(signingUserIDs); encryptionUserIDs = toArray$1(encryptionUserIDs); signatureNotations = toArray$1(signatureNotations);
  if (rest.detached) {
    throw new Error("The `detached` option has been removed from openpgp.encrypt, separately call openpgp.sign instead. Don't forget to remove the `privateKeys` option as well.");
  }
  if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.encrypt, pass `encryptionKeys` instead');
  if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.encrypt, pass `signingKeys` instead');
  if (rest.armor !== undefined) throw new Error('The `armor` option has been removed from openpgp.encrypt, pass `format` instead.');
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  if (!signingKeys) {
    signingKeys = [];
  }
  const streaming = message.fromStream;
  try {
    if (signingKeys.length || signature) { // sign the message only if signing keys or signature is specified
      message = await message.sign(signingKeys, signature, signingKeyIDs, date, signingUserIDs, signatureNotations, config$1);
    }
    message = message.compress(
      await getPreferredAlgo('compression', encryptionKeys, date, encryptionUserIDs, config$1),
      config$1
    );
    message = await message.encrypt(encryptionKeys, passwords, sessionKey, wildcard, encryptionKeyIDs, date, encryptionUserIDs, config$1);
    if (format === 'object') return message;
    // serialize data
    const armor = format === 'armored';
    const data = armor ? message.armor(config$1) : message.write();
    return convertStream(data, streaming, armor ? 'utf8' : 'binary');
  } catch (err) {
    throw util.wrapError('Error encrypting message', err);
  }
}

/**
 * Decrypts a message with the user's private key, a session key or a password.
 * One of `decryptionKeys`, `sessionkeys` or `passwords` must be specified (passing a combination of these options is not supported).
 * @param {Object} options
 * @param {Message} options.message - The message object with the encrypted data
 * @param {PrivateKey|PrivateKey[]} [options.decryptionKeys] - Private keys with decrypted secret key data or session key
 * @param {String|String[]} [options.passwords] - Passwords to decrypt the message
 * @param {Object|Object[]} [options.sessionKeys] - Session keys in the form: { data:Uint8Array, algorithm:String }
 * @param {PublicKey|PublicKey[]} [options.verificationKeys] - Array of public keys or single key, to verify signatures
 * @param {Boolean} [options.expectSigned=false] - If true, data decryption fails if the message is not signed with the provided publicKeys
 * @param {'utf8'|'binary'} [options.format='utf8'] - Whether to return data as a string(Stream) or Uint8Array(Stream). If 'utf8' (the default), also normalize newlines.
 * @param {Signature} [options.signature] - Detached signature for verification
 * @param {Date} [options.date=current date] - Use the given date for verification instead of the current time
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<Object>} Object containing decrypted and verified message in the form:
 *
 *     {
 *       data: MaybeStream<String>, (if format was 'utf8', the default)
 *       data: MaybeStream<Uint8Array>, (if format was 'binary')
 *       filename: String,
 *       signatures: [
 *         {
 *           keyID: module:type/keyid~KeyID,
 *           verified: Promise<true>,
 *           signature: Promise<Signature>
 *         }, ...
 *       ]
 *     }
 *
 *     where `signatures` contains a separate entry for each signature packet found in the input message.
 * @async
 * @static
 */
async function decrypt$5({ message, decryptionKeys, passwords, sessionKeys, verificationKeys, expectSigned = false, format = 'utf8', signature = null, date = new Date(), config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  checkMessage(message); verificationKeys = toArray$1(verificationKeys); decryptionKeys = toArray$1(decryptionKeys); passwords = toArray$1(passwords); sessionKeys = toArray$1(sessionKeys);
  if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.decrypt, pass `decryptionKeys` instead');
  if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.decrypt, pass `verificationKeys` instead');
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  try {
    const decrypted = await message.decrypt(decryptionKeys, passwords, sessionKeys, date, config$1);
    if (!verificationKeys) {
      verificationKeys = [];
    }

    const result = {};
    result.signatures = signature ? await decrypted.verifyDetached(signature, verificationKeys, date, config$1) : await decrypted.verify(verificationKeys, date, config$1);
    result.data = format === 'binary' ? decrypted.getLiteralData() : decrypted.getText();
    result.filename = decrypted.getFilename();
    linkStreams(result, message);
    if (expectSigned) {
      if (verificationKeys.length === 0) {
        throw new Error('Verification keys are required to verify message signatures');
      }
      if (result.signatures.length === 0) {
        throw new Error('Message is not signed');
      }
      result.data = concat([
        result.data,
        fromAsync(async () => {
          await util.anyPromise(result.signatures.map(sig => sig.verified));
        })
      ]);
    }
    result.data = await convertStream(result.data, message.fromStream, format);
    return result;
  } catch (err) {
    throw util.wrapError('Error decrypting message', err);
  }
}


//////////////////////////////////////////
//                                      //
//   Message signing and verification   //
//                                      //
//////////////////////////////////////////


/**
 * Signs a message.
 * @param {Object} options
 * @param {CleartextMessage|Message} options.message - (cleartext) message to be signed
 * @param {PrivateKey|PrivateKey[]} options.signingKeys - Array of keys or single key with decrypted secret key data to sign cleartext
 * @param {'armored'|'binary'|'object'} [options.format='armored'] - Format of the returned message
 * @param {Boolean} [options.detached=false] - If the return value should contain a detached signature
 * @param {KeyID|KeyID[]} [options.signingKeyIDs=latest-created valid signing (sub)keys] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
 * @param {Date} [options.date=current date] - Override the creation date of the signature
 * @param {Object|Object[]} [options.signingUserIDs=primary user IDs] - Array of user IDs to sign with, one per key in `signingKeys`, e.g. `[{ name: 'Steve Sender', email: 'steve@openpgp.org' }]`
 * @param {Object|Object[]} [options.signatureNotations=[]] - Array of notations to add to the signatures, e.g. `[{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]`
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<MaybeStream<String|Uint8Array>>} Signed message (string if `armor` was true, the default; Uint8Array if `armor` was false).
 * @async
 * @static
 */
async function sign$6({ message, signingKeys, format = 'armored', detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], signatureNotations = [], config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  checkCleartextOrMessage(message); checkOutputMessageFormat(format);
  signingKeys = toArray$1(signingKeys); signingKeyIDs = toArray$1(signingKeyIDs); signingUserIDs = toArray$1(signingUserIDs); signatureNotations = toArray$1(signatureNotations);

  if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.sign, pass `signingKeys` instead');
  if (rest.armor !== undefined) throw new Error('The `armor` option has been removed from openpgp.sign, pass `format` instead.');
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  if (message instanceof CleartextMessage && format === 'binary') throw new Error('Cannot return signed cleartext message in binary format');
  if (message instanceof CleartextMessage && detached) throw new Error('Cannot detach-sign a cleartext message');

  if (!signingKeys || signingKeys.length === 0) {
    throw new Error('No signing keys provided');
  }

  try {
    let signature;
    if (detached) {
      signature = await message.signDetached(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, signatureNotations, config$1);
    } else {
      signature = await message.sign(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, signatureNotations, config$1);
    }
    if (format === 'object') return signature;

    const armor = format === 'armored';
    signature = armor ? signature.armor(config$1) : signature.write();
    if (detached) {
      signature = transformPair(message.packets.write(), async (readable, writable) => {
        await Promise.all([
          pipe(signature, writable),
          readToEnd(readable).catch(() => {})
        ]);
      });
    }
    return convertStream(signature, message.fromStream, armor ? 'utf8' : 'binary');
  } catch (err) {
    throw util.wrapError('Error signing message', err);
  }
}

/**
 * Verifies signatures of cleartext signed message
 * @param {Object} options
 * @param {CleartextMessage|Message} options.message - (cleartext) message object with signatures
 * @param {PublicKey|PublicKey[]} options.verificationKeys - Array of publicKeys or single key, to verify signatures
 * @param {Boolean} [options.expectSigned=false] - If true, verification throws if the message is not signed with the provided publicKeys
 * @param {'utf8'|'binary'} [options.format='utf8'] - Whether to return data as a string(Stream) or Uint8Array(Stream). If 'utf8' (the default), also normalize newlines.
 * @param {Signature} [options.signature] - Detached signature for verification
 * @param {Date} [options.date=current date] - Use the given date for verification instead of the current time
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<Object>} Object containing verified message in the form:
 *
 *     {
 *       data: MaybeStream<String>, (if `message` was a CleartextMessage)
 *       data: MaybeStream<Uint8Array>, (if `message` was a Message)
 *       signatures: [
 *         {
 *           keyID: module:type/keyid~KeyID,
 *           verified: Promise<true>,
 *           signature: Promise<Signature>
 *         }, ...
 *       ]
 *     }
 *
 *     where `signatures` contains a separate entry for each signature packet found in the input message.
 * @async
 * @static
 */
async function verify$6({ message, verificationKeys, expectSigned = false, format = 'utf8', signature = null, date = new Date(), config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  checkCleartextOrMessage(message); verificationKeys = toArray$1(verificationKeys);
  if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.verify, pass `verificationKeys` instead');
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  if (message instanceof CleartextMessage && format === 'binary') throw new Error("Can't return cleartext message data as binary");
  if (message instanceof CleartextMessage && signature) throw new Error("Can't verify detached cleartext signature");

  try {
    const result = {};
    if (signature) {
      result.signatures = await message.verifyDetached(signature, verificationKeys, date, config$1);
    } else {
      result.signatures = await message.verify(verificationKeys, date, config$1);
    }
    result.data = format === 'binary' ? message.getLiteralData() : message.getText();
    if (message.fromStream && !signature) linkStreams(result, message);
    if (expectSigned) {
      if (result.signatures.length === 0) {
        throw new Error('Message is not signed');
      }
      result.data = concat([
        result.data,
        fromAsync(async () => {
          await util.anyPromise(result.signatures.map(sig => sig.verified));
        })
      ]);
    }
    result.data = await convertStream(result.data, message.fromStream, format);
    return result;
  } catch (err) {
    throw util.wrapError('Error verifying signed message', err);
  }
}


///////////////////////////////////////////////
//                                           //
//   Session key encryption and decryption   //
//                                           //
///////////////////////////////////////////////

/**
 * Generate a new session key object, taking the algorithm preferences of the passed public keys into account, if any.
 * @param {Object} options
 * @param {PublicKey|PublicKey[]} [options.encryptionKeys] - Array of public keys or single key used to select algorithm preferences for. If no keys are given, the algorithm will be [config.preferredSymmetricAlgorithm]{@link module:config.preferredSymmetricAlgorithm}
 * @param {Date} [options.date=current date] - Date to select algorithm preferences at
 * @param {Object|Object[]} [options.encryptionUserIDs=primary user IDs] - User IDs to select algorithm preferences for
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<{ data: Uint8Array, algorithm: String }>} Object with session key data and algorithm.
 * @async
 * @static
 */
async function generateSessionKey$1({ encryptionKeys, date = new Date(), encryptionUserIDs = [], config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  encryptionKeys = toArray$1(encryptionKeys); encryptionUserIDs = toArray$1(encryptionUserIDs);
  if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.generateSessionKey, pass `encryptionKeys` instead');
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  try {
    const sessionKeys = await Message.generateSessionKey(encryptionKeys, date, encryptionUserIDs, config$1);
    return sessionKeys;
  } catch (err) {
    throw util.wrapError('Error generating session key', err);
  }
}

/**
 * Encrypt a symmetric session key with public keys, passwords, or both at once.
 * At least one of `encryptionKeys` or `passwords` must be specified.
 * @param {Object} options
 * @param {Uint8Array} options.data - The session key to be encrypted e.g. 16 random bytes (for aes128)
 * @param {String} options.algorithm - Algorithm of the symmetric session key e.g. 'aes128' or 'aes256'
 * @param {String} [options.aeadAlgorithm] - AEAD algorithm, e.g. 'eax' or 'ocb'
 * @param {PublicKey|PublicKey[]} [options.encryptionKeys] - Array of public keys or single key, used to encrypt the key
 * @param {String|String[]} [options.passwords] - Passwords for the message
 * @param {'armored'|'binary'} [options.format='armored'] - Format of the returned value
 * @param {Boolean} [options.wildcard=false] - Use a key ID of 0 instead of the public key IDs
 * @param {KeyID|KeyID[]} [options.encryptionKeyIDs=latest-created valid encryption (sub)keys] - Array of key IDs to use for encryption. Each encryptionKeyIDs[i] corresponds to encryptionKeys[i]
 * @param {Date} [options.date=current date] - Override the date
 * @param {Object|Object[]} [options.encryptionUserIDs=primary user IDs] - Array of user IDs to encrypt for, one per key in `encryptionKeys`, e.g. `[{ name: 'Phil Zimmermann', email: 'phil@openpgp.org' }]`
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<String|Uint8Array>} Encrypted session keys (string if `armor` was true, the default; Uint8Array if `armor` was false).
 * @async
 * @static
 */
async function encryptSessionKey({ data, algorithm, aeadAlgorithm, encryptionKeys, passwords, format = 'armored', wildcard = false, encryptionKeyIDs = [], date = new Date(), encryptionUserIDs = [], config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  checkBinary(data); checkString(algorithm, 'algorithm'); checkOutputMessageFormat(format);
  encryptionKeys = toArray$1(encryptionKeys); passwords = toArray$1(passwords); encryptionKeyIDs = toArray$1(encryptionKeyIDs); encryptionUserIDs = toArray$1(encryptionUserIDs);
  if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.encryptSessionKey, pass `encryptionKeys` instead');
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  if ((!encryptionKeys || encryptionKeys.length === 0) && (!passwords || passwords.length === 0)) {
    throw new Error('No encryption keys or passwords provided.');
  }

  try {
    const message = await Message.encryptSessionKey(data, algorithm, aeadAlgorithm, encryptionKeys, passwords, wildcard, encryptionKeyIDs, date, encryptionUserIDs, config$1);
    return formatObject(message, format, config$1);
  } catch (err) {
    throw util.wrapError('Error encrypting session key', err);
  }
}

/**
 * Decrypt symmetric session keys using private keys or passwords (not both).
 * One of `decryptionKeys` or `passwords` must be specified.
 * @param {Object} options
 * @param {Message} options.message - A message object containing the encrypted session key packets
 * @param {PrivateKey|PrivateKey[]} [options.decryptionKeys] - Private keys with decrypted secret key data
 * @param {String|String[]} [options.passwords] - Passwords to decrypt the session key
 * @param {Date} [options.date] - Date to use for key verification instead of the current time
 * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
 * @returns {Promise<Object[]>} Array of decrypted session key, algorithm pairs in the form:
 *                                            { data:Uint8Array, algorithm:String }
 * @throws if no session key could be found or decrypted
 * @async
 * @static
 */
async function decryptSessionKeys({ message, decryptionKeys, passwords, date = new Date(), config: config$1, ...rest }) {
  config$1 = { ...config, ...config$1 }; checkConfig(config$1);
  checkMessage(message); decryptionKeys = toArray$1(decryptionKeys); passwords = toArray$1(passwords);
  if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.decryptSessionKeys, pass `decryptionKeys` instead');
  const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);

  try {
    const sessionKeys = await message.decryptSessionKeys(decryptionKeys, passwords, date, config$1);
    return sessionKeys;
  } catch (err) {
    throw util.wrapError('Error decrypting session keys', err);
  }
}


//////////////////////////
//                      //
//   Helper functions   //
//                      //
//////////////////////////


/**
 * Input validation
 * @private
 */
function checkString(data, name) {
  if (!util.isString(data)) {
    throw new Error('Parameter [' + (name || 'data') + '] must be of type String');
  }
}
function checkBinary(data, name) {
  if (!util.isUint8Array(data)) {
    throw new Error('Parameter [' + (name || 'data') + '] must be of type Uint8Array');
  }
}
function checkMessage(message) {
  if (!(message instanceof Message)) {
    throw new Error('Parameter [message] needs to be of type Message');
  }
}
function checkCleartextOrMessage(message) {
  if (!(message instanceof CleartextMessage) && !(message instanceof Message)) {
    throw new Error('Parameter [message] needs to be of type Message or CleartextMessage');
  }
}
function checkOutputMessageFormat(format) {
  if (format !== 'armored' && format !== 'binary' && format !== 'object') {
    throw new Error(`Unsupported format ${format}`);
  }
}
const defaultConfigPropsCount = Object.keys(config).length;
function checkConfig(config$1) {
  const inputConfigProps = Object.keys(config$1);
  if (inputConfigProps.length !== defaultConfigPropsCount) {
    for (const inputProp of inputConfigProps) {
      if (config[inputProp] === undefined) {
        throw new Error(`Unknown config property: ${inputProp}`);
      }
    }
  }
}

/**
 * Normalize parameter to an array if it is not undefined.
 * @param {Object} param - the parameter to be normalized
 * @returns {Array<Object>|undefined} The resulting array or undefined.
 * @private
 */
function toArray$1(param) {
  if (param && !util.isArray(param)) {
    param = [param];
  }
  return param;
}

/**
 * Convert data to or from Stream
 * @param {Object} data - the data to convert
 * @param {'web'|'ponyfill'|'node'|false} streaming - Whether to return a ReadableStream, and of what type
 * @param {'utf8'|'binary'} [encoding] - How to return data in Node Readable streams
 * @returns {Promise<Object>} The data in the respective format.
 * @async
 * @private
 */
async function convertStream(data, streaming, encoding = 'utf8') {
  const streamType = util.isStream(data);
  if (streamType === 'array') {
    return readToEnd(data);
  }
  if (streaming === 'node') {
    data = webToNode(data);
    if (encoding !== 'binary') data.setEncoding(encoding);
    return data;
  }
  if (streaming === 'web' && streamType === 'ponyfill') {
    return toNativeReadable(data);
  }
  return data;
}

/**
 * Link result.data to the message stream for cancellation.
 * Also, forward errors in the message to result.data.
 * @param {Object} result - the data to convert
 * @param {Message} message - message object
 * @returns {Object}
 * @private
 */
function linkStreams(result, message) {
  result.data = transformPair(message.packets.stream, async (readable, writable) => {
    await pipe(result.data, writable, {
      preventClose: true
    });
    const writer = getWriter(writable);
    try {
      // Forward errors in the message stream to result.data.
      await readToEnd(readable, _ => _);
      await writer.close();
    } catch (e) {
      await writer.abort(e);
    }
  });
}

/**
 * Convert the object to the given format
 * @param {Key|Message} object
 * @param {'armored'|'binary'|'object'} format
 * @param {Object} config - Full configuration
 * @returns {String|Uint8Array|Object}
 */
function formatObject(object, format, config) {
  switch (format) {
    case 'object':
      return object;
    case 'armored':
      return object.armor(config);
    case 'binary':
      return object.write();
    default:
      throw new Error(`Unsupported format ${format}`);
  }
}

/**
 * web-streams-polyfill v3.0.3
 */
/// <reference lib="es2015.symbol" />
const SymbolPolyfill = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ?
    Symbol :
    description => `Symbol(${description})`;

/// <reference lib="dom" />
function noop() {
    return undefined;
}
function getGlobals() {
    if (typeof self !== 'undefined') {
        return self;
    }
    else if (typeof window !== 'undefined') {
        return window;
    }
    else if (typeof global !== 'undefined') {
        return global;
    }
    return undefined;
}
const globals = getGlobals();

function typeIsObject(x) {
    return (typeof x === 'object' && x !== null) || typeof x === 'function';
}
const rethrowAssertionErrorRejection = noop;

const originalPromise = Promise;
const originalPromiseThen = Promise.prototype.then;
const originalPromiseResolve = Promise.resolve.bind(originalPromise);
const originalPromiseReject = Promise.reject.bind(originalPromise);
function newPromise(executor) {
    return new originalPromise(executor);
}
function promiseResolvedWith(value) {
    return originalPromiseResolve(value);
}
function promiseRejectedWith(reason) {
    return originalPromiseReject(reason);
}
function PerformPromiseThen(promise, onFulfilled, onRejected) {
    // There doesn't appear to be any way to correctly emulate the behaviour from JavaScript, so this is just an
    // approximation.
    return originalPromiseThen.call(promise, onFulfilled, onRejected);
}
function uponPromise(promise, onFulfilled, onRejected) {
    PerformPromiseThen(PerformPromiseThen(promise, onFulfilled, onRejected), undefined, rethrowAssertionErrorRejection);
}
function uponFulfillment(promise, onFulfilled) {
    uponPromise(promise, onFulfilled);
}
function uponRejection(promise, onRejected) {
    uponPromise(promise, undefined, onRejected);
}
function transformPromiseWith(promise, fulfillmentHandler, rejectionHandler) {
    return PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler);
}
function setPromiseIsHandledToTrue(promise) {
    PerformPromiseThen(promise, undefined, rethrowAssertionErrorRejection);
}
const queueMicrotask = (() => {
    const globalQueueMicrotask = globals && globals.queueMicrotask;
    if (typeof globalQueueMicrotask === 'function') {
        return globalQueueMicrotask;
    }
    const resolvedPromise = promiseResolvedWith(undefined);
    return (fn) => PerformPromiseThen(resolvedPromise, fn);
})();
function reflectCall(F, V, args) {
    if (typeof F !== 'function') {
        throw new TypeError('Argument is not a function');
    }
    return Function.prototype.apply.call(F, V, args);
}
function promiseCall(F, V, args) {
    try {
        return promiseResolvedWith(reflectCall(F, V, args));
    }
    catch (value) {
        return promiseRejectedWith(value);
    }
}

// Original from Chromium
// https://chromium.googlesource.com/chromium/src/+/0aee4434a4dba42a42abaea9bfbc0cd196a63bc1/third_party/blink/renderer/core/streams/SimpleQueue.js
const QUEUE_MAX_ARRAY_SIZE = 16384;
/**
 * Simple queue structure.
 *
 * Avoids scalability issues with using a packed array directly by using
 * multiple arrays in a linked list and keeping the array size bounded.
 */
class SimpleQueue {
    constructor() {
        this._cursor = 0;
        this._size = 0;
        // _front and _back are always defined.
        this._front = {
            _elements: [],
            _next: undefined
        };
        this._back = this._front;
        // The cursor is used to avoid calling Array.shift().
        // It contains the index of the front element of the array inside the
        // front-most node. It is always in the range [0, QUEUE_MAX_ARRAY_SIZE).
        this._cursor = 0;
        // When there is only one node, size === elements.length - cursor.
        this._size = 0;
    }
    get length() {
        return this._size;
    }
    // For exception safety, this method is structured in order:
    // 1. Read state
    // 2. Calculate required state mutations
    // 3. Perform state mutations
    push(element) {
        const oldBack = this._back;
        let newBack = oldBack;
        if (oldBack._elements.length === QUEUE_MAX_ARRAY_SIZE - 1) {
            newBack = {
                _elements: [],
                _next: undefined
            };
        }
        // push() is the mutation most likely to throw an exception, so it
        // goes first.
        oldBack._elements.push(element);
        if (newBack !== oldBack) {
            this._back = newBack;
            oldBack._next = newBack;
        }
        ++this._size;
    }
    // Like push(), shift() follows the read -> calculate -> mutate pattern for
    // exception safety.
    shift() { // must not be called on an empty queue
        const oldFront = this._front;
        let newFront = oldFront;
        const oldCursor = this._cursor;
        let newCursor = oldCursor + 1;
        const elements = oldFront._elements;
        const element = elements[oldCursor];
        if (newCursor === QUEUE_MAX_ARRAY_SIZE) {
            newFront = oldFront._next;
            newCursor = 0;
        }
        // No mutations before this point.
        --this._size;
        this._cursor = newCursor;
        if (oldFront !== newFront) {
            this._front = newFront;
        }
        // Permit shifted element to be garbage collected.
        elements[oldCursor] = undefined;
        return element;
    }
    // The tricky thing about forEach() is that it can be called
    // re-entrantly. The queue may be mutated inside the callback. It is easy to
    // see that push() within the callback has no negative effects since the end
    // of the queue is checked for on every iteration. If shift() is called
    // repeatedly within the callback then the next iteration may return an
    // element that has been removed. In this case the callback will be called
    // with undefined values until we either "catch up" with elements that still
    // exist or reach the back of the queue.
    forEach(callback) {
        let i = this._cursor;
        let node = this._front;
        let elements = node._elements;
        while (i !== elements.length || node._next !== undefined) {
            if (i === elements.length) {
                node = node._next;
                elements = node._elements;
                i = 0;
                if (elements.length === 0) {
                    break;
                }
            }
            callback(elements[i]);
            ++i;
        }
    }
    // Return the element that would be returned if shift() was called now,
    // without modifying the queue.
    peek() { // must not be called on an empty queue
        const front = this._front;
        const cursor = this._cursor;
        return front._elements[cursor];
    }
}

function ReadableStreamReaderGenericInitialize(reader, stream) {
    reader._ownerReadableStream = stream;
    stream._reader = reader;
    if (stream._state === 'readable') {
        defaultReaderClosedPromiseInitialize(reader);
    }
    else if (stream._state === 'closed') {
        defaultReaderClosedPromiseInitializeAsResolved(reader);
    }
    else {
        defaultReaderClosedPromiseInitializeAsRejected(reader, stream._storedError);
    }
}
// A client of ReadableStreamDefaultReader and ReadableStreamBYOBReader may use these functions directly to bypass state
// check.
function ReadableStreamReaderGenericCancel(reader, reason) {
    const stream = reader._ownerReadableStream;
    return ReadableStreamCancel(stream, reason);
}
function ReadableStreamReaderGenericRelease(reader) {
    if (reader._ownerReadableStream._state === 'readable') {
        defaultReaderClosedPromiseReject(reader, new TypeError(`Reader was released and can no longer be used to monitor the stream's closedness`));
    }
    else {
        defaultReaderClosedPromiseResetToRejected(reader, new TypeError(`Reader was released and can no longer be used to monitor the stream's closedness`));
    }
    reader._ownerReadableStream._reader = undefined;
    reader._ownerReadableStream = undefined;
}
// Helper functions for the readers.
function readerLockException(name) {
    return new TypeError('Cannot ' + name + ' a stream using a released reader');
}
// Helper functions for the ReadableStreamDefaultReader.
function defaultReaderClosedPromiseInitialize(reader) {
    reader._closedPromise = newPromise((resolve, reject) => {
        reader._closedPromise_resolve = resolve;
        reader._closedPromise_reject = reject;
    });
}
function defaultReaderClosedPromiseInitializeAsRejected(reader, reason) {
    defaultReaderClosedPromiseInitialize(reader);
    defaultReaderClosedPromiseReject(reader, reason);
}
function defaultReaderClosedPromiseInitializeAsResolved(reader) {
    defaultReaderClosedPromiseInitialize(reader);
    defaultReaderClosedPromiseResolve(reader);
}
function defaultReaderClosedPromiseReject(reader, reason) {
    if (reader._closedPromise_reject === undefined) {
        return;
    }
    setPromiseIsHandledToTrue(reader._closedPromise);
    reader._closedPromise_reject(reason);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
}
function defaultReaderClosedPromiseResetToRejected(reader, reason) {
    defaultReaderClosedPromiseInitializeAsRejected(reader, reason);
}
function defaultReaderClosedPromiseResolve(reader) {
    if (reader._closedPromise_resolve === undefined) {
        return;
    }
    reader._closedPromise_resolve(undefined);
    reader._closedPromise_resolve = undefined;
    reader._closedPromise_reject = undefined;
}

const AbortSteps = SymbolPolyfill('[[AbortSteps]]');
const ErrorSteps = SymbolPolyfill('[[ErrorSteps]]');
const CancelSteps = SymbolPolyfill('[[CancelSteps]]');
const PullSteps = SymbolPolyfill('[[PullSteps]]');

/// <reference lib="es2015.core" />
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite#Polyfill
const NumberIsFinite = Number.isFinite || function (x) {
    return typeof x === 'number' && isFinite(x);
};

/// <reference lib="es2015.core" />
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc#Polyfill
const MathTrunc = Math.trunc || function (v) {
    return v < 0 ? Math.ceil(v) : Math.floor(v);
};

// https://heycam.github.io/webidl/#idl-dictionaries
function isDictionary(x) {
    return typeof x === 'object' || typeof x === 'function';
}
function assertDictionary(obj, context) {
    if (obj !== undefined && !isDictionary(obj)) {
        throw new TypeError(`${context} is not an object.`);
    }
}
// https://heycam.github.io/webidl/#idl-callback-functions
function assertFunction(x, context) {
    if (typeof x !== 'function') {
        throw new TypeError(`${context} is not a function.`);
    }
}
// https://heycam.github.io/webidl/#idl-object
function isObject(x) {
    return (typeof x === 'object' && x !== null) || typeof x === 'function';
}
function assertObject(x, context) {
    if (!isObject(x)) {
        throw new TypeError(`${context} is not an object.`);
    }
}
function assertRequiredArgument(x, position, context) {
    if (x === undefined) {
        throw new TypeError(`Parameter ${position} is required in '${context}'.`);
    }
}
function assertRequiredField(x, field, context) {
    if (x === undefined) {
        throw new TypeError(`${field} is required in '${context}'.`);
    }
}
// https://heycam.github.io/webidl/#idl-unrestricted-double
function convertUnrestrictedDouble(value) {
    return Number(value);
}
function censorNegativeZero(x) {
    return x === 0 ? 0 : x;
}
function integerPart(x) {
    return censorNegativeZero(MathTrunc(x));
}
// https://heycam.github.io/webidl/#idl-unsigned-long-long
function convertUnsignedLongLongWithEnforceRange(value, context) {
    const lowerBound = 0;
    const upperBound = Number.MAX_SAFE_INTEGER;
    let x = Number(value);
    x = censorNegativeZero(x);
    if (!NumberIsFinite(x)) {
        throw new TypeError(`${context} is not a finite number`);
    }
    x = integerPart(x);
    if (x < lowerBound || x > upperBound) {
        throw new TypeError(`${context} is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`);
    }
    if (!NumberIsFinite(x) || x === 0) {
        return 0;
    }
    // TODO Use BigInt if supported?
    // let xBigInt = BigInt(integerPart(x));
    // xBigInt = BigInt.asUintN(64, xBigInt);
    // return Number(xBigInt);
    return x;
}

function assertReadableStream(x, context) {
    if (!IsReadableStream(x)) {
        throw new TypeError(`${context} is not a ReadableStream.`);
    }
}

// Abstract operations for the ReadableStream.
function AcquireReadableStreamDefaultReader(stream) {
    return new ReadableStreamDefaultReader(stream);
}
// ReadableStream API exposed for controllers.
function ReadableStreamAddReadRequest(stream, readRequest) {
    stream._reader._readRequests.push(readRequest);
}
function ReadableStreamFulfillReadRequest(stream, chunk, done) {
    const reader = stream._reader;
    const readRequest = reader._readRequests.shift();
    if (done) {
        readRequest._closeSteps();
    }
    else {
        readRequest._chunkSteps(chunk);
    }
}
function ReadableStreamGetNumReadRequests(stream) {
    return stream._reader._readRequests.length;
}
function ReadableStreamHasDefaultReader(stream) {
    const reader = stream._reader;
    if (reader === undefined) {
        return false;
    }
    if (!IsReadableStreamDefaultReader(reader)) {
        return false;
    }
    return true;
}
/**
 * A default reader vended by a {@link ReadableStream}.
 *
 * @public
 */
class ReadableStreamDefaultReader {
    constructor(stream) {
        assertRequiredArgument(stream, 1, 'ReadableStreamDefaultReader');
        assertReadableStream(stream, 'First parameter');
        if (IsReadableStreamLocked(stream)) {
            throw new TypeError('This stream has already been locked for exclusive reading by another reader');
        }
        ReadableStreamReaderGenericInitialize(this, stream);
        this._readRequests = new SimpleQueue();
    }
    /**
     * Returns a promise that will be fulfilled when the stream becomes closed,
     * or rejected if the stream ever errors or the reader's lock is released before the stream finishes closing.
     */
    get closed() {
        if (!IsReadableStreamDefaultReader(this)) {
            return promiseRejectedWith(defaultReaderBrandCheckException('closed'));
        }
        return this._closedPromise;
    }
    /**
     * If the reader is active, behaves the same as {@link ReadableStream.cancel | stream.cancel(reason)}.
     */
    cancel(reason = undefined) {
        if (!IsReadableStreamDefaultReader(this)) {
            return promiseRejectedWith(defaultReaderBrandCheckException('cancel'));
        }
        if (this._ownerReadableStream === undefined) {
            return promiseRejectedWith(readerLockException('cancel'));
        }
        return ReadableStreamReaderGenericCancel(this, reason);
    }
    /**
     * Returns a promise that allows access to the next chunk from the stream's internal queue, if available.
     *
     * If reading a chunk causes the queue to become empty, more data will be pulled from the underlying source.
     */
    read() {
        if (!IsReadableStreamDefaultReader(this)) {
            return promiseRejectedWith(defaultReaderBrandCheckException('read'));
        }
        if (this._ownerReadableStream === undefined) {
            return promiseRejectedWith(readerLockException('read from'));
        }
        let resolvePromise;
        let rejectPromise;
        const promise = newPromise((resolve, reject) => {
            resolvePromise = resolve;
            rejectPromise = reject;
        });
        const readRequest = {
            _chunkSteps: chunk => resolvePromise({ value: chunk, done: false }),
            _closeSteps: () => resolvePromise({ value: undefined, done: true }),
            _errorSteps: e => rejectPromise(e)
        };
        ReadableStreamDefaultReaderRead(this, readRequest);
        return promise;
    }
    /**
     * Releases the reader's lock on the corresponding stream. After the lock is released, the reader is no longer active.
     * If the associated stream is errored when the lock is released, the reader will appear errored in the same way
     * from now on; otherwise, the reader will appear closed.
     *
     * A reader's lock cannot be released while it still has a pending read request, i.e., if a promise returned by
     * the reader's {@link ReadableStreamDefaultReader.read | read()} method has not yet been settled. Attempting to
     * do so will throw a `TypeError` and leave the reader locked to the stream.
     */
    releaseLock() {
        if (!IsReadableStreamDefaultReader(this)) {
            throw defaultReaderBrandCheckException('releaseLock');
        }
        if (this._ownerReadableStream === undefined) {
            return;
        }
        if (this._readRequests.length > 0) {
            throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled');
        }
        ReadableStreamReaderGenericRelease(this);
    }
}
Object.defineProperties(ReadableStreamDefaultReader.prototype, {
    cancel: { enumerable: true },
    read: { enumerable: true },
    releaseLock: { enumerable: true },
    closed: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(ReadableStreamDefaultReader.prototype, SymbolPolyfill.toStringTag, {
        value: 'ReadableStreamDefaultReader',
        configurable: true
    });
}
// Abstract operations for the readers.
function IsReadableStreamDefaultReader(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readRequests')) {
        return false;
    }
    return true;
}
function ReadableStreamDefaultReaderRead(reader, readRequest) {
    const stream = reader._ownerReadableStream;
    stream._disturbed = true;
    if (stream._state === 'closed') {
        readRequest._closeSteps();
    }
    else if (stream._state === 'errored') {
        readRequest._errorSteps(stream._storedError);
    }
    else {
        stream._readableStreamController[PullSteps](readRequest);
    }
}
// Helper functions for the ReadableStreamDefaultReader.
function defaultReaderBrandCheckException(name) {
    return new TypeError(`ReadableStreamDefaultReader.prototype.${name} can only be used on a ReadableStreamDefaultReader`);
}

/// <reference lib="es2018.asynciterable" />
let AsyncIteratorPrototype;
if (typeof SymbolPolyfill.asyncIterator === 'symbol') {
    // We're running inside a ES2018+ environment, but we're compiling to an older syntax.
    // We cannot access %AsyncIteratorPrototype% without non-ES2018 syntax, but we can re-create it.
    AsyncIteratorPrototype = {
        // 25.1.3.1 %AsyncIteratorPrototype% [ @@asyncIterator ] ( )
        // https://tc39.github.io/ecma262/#sec-asynciteratorprototype-asynciterator
        [SymbolPolyfill.asyncIterator]() {
            return this;
        }
    };
    Object.defineProperty(AsyncIteratorPrototype, SymbolPolyfill.asyncIterator, { enumerable: false });
}

/// <reference lib="es2018.asynciterable" />
class ReadableStreamAsyncIteratorImpl {
    constructor(reader, preventCancel) {
        this._ongoingPromise = undefined;
        this._isFinished = false;
        this._reader = reader;
        this._preventCancel = preventCancel;
    }
    next() {
        const nextSteps = () => this._nextSteps();
        this._ongoingPromise = this._ongoingPromise ?
            transformPromiseWith(this._ongoingPromise, nextSteps, nextSteps) :
            nextSteps();
        return this._ongoingPromise;
    }
    return(value) {
        const returnSteps = () => this._returnSteps(value);
        return this._ongoingPromise ?
            transformPromiseWith(this._ongoingPromise, returnSteps, returnSteps) :
            returnSteps();
    }
    _nextSteps() {
        if (this._isFinished) {
            return Promise.resolve({ value: undefined, done: true });
        }
        const reader = this._reader;
        if (reader._ownerReadableStream === undefined) {
            return promiseRejectedWith(readerLockException('iterate'));
        }
        let resolvePromise;
        let rejectPromise;
        const promise = newPromise((resolve, reject) => {
            resolvePromise = resolve;
            rejectPromise = reject;
        });
        const readRequest = {
            _chunkSteps: chunk => {
                this._ongoingPromise = undefined;
                // This needs to be delayed by one microtask, otherwise we stop pulling too early which breaks a test.
                // FIXME Is this a bug in the specification, or in the test?
                queueMicrotask(() => resolvePromise({ value: chunk, done: false }));
            },
            _closeSteps: () => {
                this._ongoingPromise = undefined;
                this._isFinished = true;
                ReadableStreamReaderGenericRelease(reader);
                resolvePromise({ value: undefined, done: true });
            },
            _errorSteps: reason => {
                this._ongoingPromise = undefined;
                this._isFinished = true;
                ReadableStreamReaderGenericRelease(reader);
                rejectPromise(reason);
            }
        };
        ReadableStreamDefaultReaderRead(reader, readRequest);
        return promise;
    }
    _returnSteps(value) {
        if (this._isFinished) {
            return Promise.resolve({ value, done: true });
        }
        this._isFinished = true;
        const reader = this._reader;
        if (reader._ownerReadableStream === undefined) {
            return promiseRejectedWith(readerLockException('finish iterating'));
        }
        if (!this._preventCancel) {
            const result = ReadableStreamReaderGenericCancel(reader, value);
            ReadableStreamReaderGenericRelease(reader);
            return transformPromiseWith(result, () => ({ value, done: true }));
        }
        ReadableStreamReaderGenericRelease(reader);
        return promiseResolvedWith({ value, done: true });
    }
}
const ReadableStreamAsyncIteratorPrototype = {
    next() {
        if (!IsReadableStreamAsyncIterator(this)) {
            return promiseRejectedWith(streamAsyncIteratorBrandCheckException('next'));
        }
        return this._asyncIteratorImpl.next();
    },
    return(value) {
        if (!IsReadableStreamAsyncIterator(this)) {
            return promiseRejectedWith(streamAsyncIteratorBrandCheckException('return'));
        }
        return this._asyncIteratorImpl.return(value);
    }
};
if (AsyncIteratorPrototype !== undefined) {
    Object.setPrototypeOf(ReadableStreamAsyncIteratorPrototype, AsyncIteratorPrototype);
}
// Abstract operations for the ReadableStream.
function AcquireReadableStreamAsyncIterator(stream, preventCancel) {
    const reader = AcquireReadableStreamDefaultReader(stream);
    const impl = new ReadableStreamAsyncIteratorImpl(reader, preventCancel);
    const iterator = Object.create(ReadableStreamAsyncIteratorPrototype);
    iterator._asyncIteratorImpl = impl;
    return iterator;
}
function IsReadableStreamAsyncIterator(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_asyncIteratorImpl')) {
        return false;
    }
    return true;
}
// Helper functions for the ReadableStream.
function streamAsyncIteratorBrandCheckException(name) {
    return new TypeError(`ReadableStreamAsyncIterator.${name} can only be used on a ReadableSteamAsyncIterator`);
}

/// <reference lib="es2015.core" />
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN#Polyfill
const NumberIsNaN = Number.isNaN || function (x) {
    // eslint-disable-next-line no-self-compare
    return x !== x;
};

function IsFiniteNonNegativeNumber(v) {
    if (!IsNonNegativeNumber(v)) {
        return false;
    }
    if (v === Infinity) {
        return false;
    }
    return true;
}
function IsNonNegativeNumber(v) {
    if (typeof v !== 'number') {
        return false;
    }
    if (NumberIsNaN(v)) {
        return false;
    }
    if (v < 0) {
        return false;
    }
    return true;
}

function DequeueValue(container) {
    const pair = container._queue.shift();
    container._queueTotalSize -= pair.size;
    if (container._queueTotalSize < 0) {
        container._queueTotalSize = 0;
    }
    return pair.value;
}
function EnqueueValueWithSize(container, value, size) {
    size = Number(size);
    if (!IsFiniteNonNegativeNumber(size)) {
        throw new RangeError('Size must be a finite, non-NaN, non-negative number.');
    }
    container._queue.push({ value, size });
    container._queueTotalSize += size;
}
function PeekQueueValue(container) {
    const pair = container._queue.peek();
    return pair.value;
}
function ResetQueue(container) {
    container._queue = new SimpleQueue();
    container._queueTotalSize = 0;
}

function CreateArrayFromList(elements) {
    // We use arrays to represent lists, so this is basically a no-op.
    // Do a slice though just in case we happen to depend on the unique-ness.
    return elements.slice();
}
function CopyDataBlockBytes(dest, destOffset, src, srcOffset, n) {
    new Uint8Array(dest).set(new Uint8Array(src, srcOffset, n), destOffset);
}
// Not implemented correctly
function TransferArrayBuffer(O) {
    return O;
}
// Not implemented correctly
function IsDetachedBuffer(O) {
    return false;
}

/**
 * A pull-into request in a {@link ReadableByteStreamController}.
 *
 * @public
 */
class ReadableStreamBYOBRequest {
    constructor() {
        throw new TypeError('Illegal constructor');
    }
    /**
     * Returns the view for writing in to, or `null` if the BYOB request has already been responded to.
     */
    get view() {
        if (!IsReadableStreamBYOBRequest(this)) {
            throw byobRequestBrandCheckException('view');
        }
        return this._view;
    }
    respond(bytesWritten) {
        if (!IsReadableStreamBYOBRequest(this)) {
            throw byobRequestBrandCheckException('respond');
        }
        assertRequiredArgument(bytesWritten, 1, 'respond');
        bytesWritten = convertUnsignedLongLongWithEnforceRange(bytesWritten, 'First parameter');
        if (this._associatedReadableByteStreamController === undefined) {
            throw new TypeError('This BYOB request has been invalidated');
        }
        if (IsDetachedBuffer(this._view.buffer)) ;
        ReadableByteStreamControllerRespond(this._associatedReadableByteStreamController, bytesWritten);
    }
    respondWithNewView(view) {
        if (!IsReadableStreamBYOBRequest(this)) {
            throw byobRequestBrandCheckException('respondWithNewView');
        }
        assertRequiredArgument(view, 1, 'respondWithNewView');
        if (!ArrayBuffer.isView(view)) {
            throw new TypeError('You can only respond with array buffer views');
        }
        if (view.byteLength === 0) {
            throw new TypeError('chunk must have non-zero byteLength');
        }
        if (view.buffer.byteLength === 0) {
            throw new TypeError(`chunk's buffer must have non-zero byteLength`);
        }
        if (this._associatedReadableByteStreamController === undefined) {
            throw new TypeError('This BYOB request has been invalidated');
        }
        ReadableByteStreamControllerRespondWithNewView(this._associatedReadableByteStreamController, view);
    }
}
Object.defineProperties(ReadableStreamBYOBRequest.prototype, {
    respond: { enumerable: true },
    respondWithNewView: { enumerable: true },
    view: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(ReadableStreamBYOBRequest.prototype, SymbolPolyfill.toStringTag, {
        value: 'ReadableStreamBYOBRequest',
        configurable: true
    });
}
/**
 * Allows control of a {@link ReadableStream | readable byte stream}'s state and internal queue.
 *
 * @public
 */
class ReadableByteStreamController {
    constructor() {
        throw new TypeError('Illegal constructor');
    }
    /**
     * Returns the current BYOB pull request, or `null` if there isn't one.
     */
    get byobRequest() {
        if (!IsReadableByteStreamController(this)) {
            throw byteStreamControllerBrandCheckException('byobRequest');
        }
        if (this._byobRequest === null && this._pendingPullIntos.length > 0) {
            const firstDescriptor = this._pendingPullIntos.peek();
            const view = new Uint8Array(firstDescriptor.buffer, firstDescriptor.byteOffset + firstDescriptor.bytesFilled, firstDescriptor.byteLength - firstDescriptor.bytesFilled);
            const byobRequest = Object.create(ReadableStreamBYOBRequest.prototype);
            SetUpReadableStreamBYOBRequest(byobRequest, this, view);
            this._byobRequest = byobRequest;
        }
        return this._byobRequest;
    }
    /**
     * Returns the desired size to fill the controlled stream's internal queue. It can be negative, if the queue is
     * over-full. An underlying byte source ought to use this information to determine when and how to apply backpressure.
     */
    get desiredSize() {
        if (!IsReadableByteStreamController(this)) {
            throw byteStreamControllerBrandCheckException('desiredSize');
        }
        return ReadableByteStreamControllerGetDesiredSize(this);
    }
    /**
     * Closes the controlled readable stream. Consumers will still be able to read any previously-enqueued chunks from
     * the stream, but once those are read, the stream will become closed.
     */
    close() {
        if (!IsReadableByteStreamController(this)) {
            throw byteStreamControllerBrandCheckException('close');
        }
        if (this._closeRequested) {
            throw new TypeError('The stream has already been closed; do not close it again!');
        }
        const state = this._controlledReadableByteStream._state;
        if (state !== 'readable') {
            throw new TypeError(`The stream (in ${state} state) is not in the readable state and cannot be closed`);
        }
        ReadableByteStreamControllerClose(this);
    }
    enqueue(chunk) {
        if (!IsReadableByteStreamController(this)) {
            throw byteStreamControllerBrandCheckException('enqueue');
        }
        assertRequiredArgument(chunk, 1, 'enqueue');
        if (!ArrayBuffer.isView(chunk)) {
            throw new TypeError('chunk must be an array buffer view');
        }
        if (chunk.byteLength === 0) {
            throw new TypeError('chunk must have non-zero byteLength');
        }
        if (chunk.buffer.byteLength === 0) {
            throw new TypeError(`chunk's buffer must have non-zero byteLength`);
        }
        if (this._closeRequested) {
            throw new TypeError('stream is closed or draining');
        }
        const state = this._controlledReadableByteStream._state;
        if (state !== 'readable') {
            throw new TypeError(`The stream (in ${state} state) is not in the readable state and cannot be enqueued to`);
        }
        ReadableByteStreamControllerEnqueue(this, chunk);
    }
    /**
     * Errors the controlled readable stream, making all future interactions with it fail with the given error `e`.
     */
    error(e = undefined) {
        if (!IsReadableByteStreamController(this)) {
            throw byteStreamControllerBrandCheckException('error');
        }
        ReadableByteStreamControllerError(this, e);
    }
    /** @internal */
    [CancelSteps](reason) {
        if (this._pendingPullIntos.length > 0) {
            const firstDescriptor = this._pendingPullIntos.peek();
            firstDescriptor.bytesFilled = 0;
        }
        ResetQueue(this);
        const result = this._cancelAlgorithm(reason);
        ReadableByteStreamControllerClearAlgorithms(this);
        return result;
    }
    /** @internal */
    [PullSteps](readRequest) {
        const stream = this._controlledReadableByteStream;
        if (this._queueTotalSize > 0) {
            const entry = this._queue.shift();
            this._queueTotalSize -= entry.byteLength;
            ReadableByteStreamControllerHandleQueueDrain(this);
            const view = new Uint8Array(entry.buffer, entry.byteOffset, entry.byteLength);
            readRequest._chunkSteps(view);
            return;
        }
        const autoAllocateChunkSize = this._autoAllocateChunkSize;
        if (autoAllocateChunkSize !== undefined) {
            let buffer;
            try {
                buffer = new ArrayBuffer(autoAllocateChunkSize);
            }
            catch (bufferE) {
                readRequest._errorSteps(bufferE);
                return;
            }
            const pullIntoDescriptor = {
                buffer,
                byteOffset: 0,
                byteLength: autoAllocateChunkSize,
                bytesFilled: 0,
                elementSize: 1,
                viewConstructor: Uint8Array,
                readerType: 'default'
            };
            this._pendingPullIntos.push(pullIntoDescriptor);
        }
        ReadableStreamAddReadRequest(stream, readRequest);
        ReadableByteStreamControllerCallPullIfNeeded(this);
    }
}
Object.defineProperties(ReadableByteStreamController.prototype, {
    close: { enumerable: true },
    enqueue: { enumerable: true },
    error: { enumerable: true },
    byobRequest: { enumerable: true },
    desiredSize: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(ReadableByteStreamController.prototype, SymbolPolyfill.toStringTag, {
        value: 'ReadableByteStreamController',
        configurable: true
    });
}
// Abstract operations for the ReadableByteStreamController.
function IsReadableByteStreamController(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_controlledReadableByteStream')) {
        return false;
    }
    return true;
}
function IsReadableStreamBYOBRequest(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_associatedReadableByteStreamController')) {
        return false;
    }
    return true;
}
function ReadableByteStreamControllerCallPullIfNeeded(controller) {
    const shouldPull = ReadableByteStreamControllerShouldCallPull(controller);
    if (!shouldPull) {
        return;
    }
    if (controller._pulling) {
        controller._pullAgain = true;
        return;
    }
    controller._pulling = true;
    // TODO: Test controller argument
    const pullPromise = controller._pullAlgorithm();
    uponPromise(pullPromise, () => {
        controller._pulling = false;
        if (controller._pullAgain) {
            controller._pullAgain = false;
            ReadableByteStreamControllerCallPullIfNeeded(controller);
        }
    }, e => {
        ReadableByteStreamControllerError(controller, e);
    });
}
function ReadableByteStreamControllerClearPendingPullIntos(controller) {
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    controller._pendingPullIntos = new SimpleQueue();
}
function ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor) {
    let done = false;
    if (stream._state === 'closed') {
        done = true;
    }
    const filledView = ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor);
    if (pullIntoDescriptor.readerType === 'default') {
        ReadableStreamFulfillReadRequest(stream, filledView, done);
    }
    else {
        ReadableStreamFulfillReadIntoRequest(stream, filledView, done);
    }
}
function ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor) {
    const bytesFilled = pullIntoDescriptor.bytesFilled;
    const elementSize = pullIntoDescriptor.elementSize;
    return new pullIntoDescriptor.viewConstructor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, bytesFilled / elementSize);
}
function ReadableByteStreamControllerEnqueueChunkToQueue(controller, buffer, byteOffset, byteLength) {
    controller._queue.push({ buffer, byteOffset, byteLength });
    controller._queueTotalSize += byteLength;
}
function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) {
    const elementSize = pullIntoDescriptor.elementSize;
    const currentAlignedBytes = pullIntoDescriptor.bytesFilled - pullIntoDescriptor.bytesFilled % elementSize;
    const maxBytesToCopy = Math.min(controller._queueTotalSize, pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled);
    const maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy;
    const maxAlignedBytes = maxBytesFilled - maxBytesFilled % elementSize;
    let totalBytesToCopyRemaining = maxBytesToCopy;
    let ready = false;
    if (maxAlignedBytes > currentAlignedBytes) {
        totalBytesToCopyRemaining = maxAlignedBytes - pullIntoDescriptor.bytesFilled;
        ready = true;
    }
    const queue = controller._queue;
    while (totalBytesToCopyRemaining > 0) {
        const headOfQueue = queue.peek();
        const bytesToCopy = Math.min(totalBytesToCopyRemaining, headOfQueue.byteLength);
        const destStart = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled;
        CopyDataBlockBytes(pullIntoDescriptor.buffer, destStart, headOfQueue.buffer, headOfQueue.byteOffset, bytesToCopy);
        if (headOfQueue.byteLength === bytesToCopy) {
            queue.shift();
        }
        else {
            headOfQueue.byteOffset += bytesToCopy;
            headOfQueue.byteLength -= bytesToCopy;
        }
        controller._queueTotalSize -= bytesToCopy;
        ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesToCopy, pullIntoDescriptor);
        totalBytesToCopyRemaining -= bytesToCopy;
    }
    return ready;
}
function ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, size, pullIntoDescriptor) {
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    pullIntoDescriptor.bytesFilled += size;
}
function ReadableByteStreamControllerHandleQueueDrain(controller) {
    if (controller._queueTotalSize === 0 && controller._closeRequested) {
        ReadableByteStreamControllerClearAlgorithms(controller);
        ReadableStreamClose(controller._controlledReadableByteStream);
    }
    else {
        ReadableByteStreamControllerCallPullIfNeeded(controller);
    }
}
function ReadableByteStreamControllerInvalidateBYOBRequest(controller) {
    if (controller._byobRequest === null) {
        return;
    }
    controller._byobRequest._associatedReadableByteStreamController = undefined;
    controller._byobRequest._view = null;
    controller._byobRequest = null;
}
function ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller) {
    while (controller._pendingPullIntos.length > 0) {
        if (controller._queueTotalSize === 0) {
            return;
        }
        const pullIntoDescriptor = controller._pendingPullIntos.peek();
        if (ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor)) {
            ReadableByteStreamControllerShiftPendingPullInto(controller);
            ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableByteStream, pullIntoDescriptor);
        }
    }
}
function ReadableByteStreamControllerPullInto(controller, view, readIntoRequest) {
    const stream = controller._controlledReadableByteStream;
    let elementSize = 1;
    if (view.constructor !== DataView) {
        elementSize = view.constructor.BYTES_PER_ELEMENT;
    }
    const ctor = view.constructor;
    const buffer = TransferArrayBuffer(view.buffer);
    const pullIntoDescriptor = {
        buffer,
        byteOffset: view.byteOffset,
        byteLength: view.byteLength,
        bytesFilled: 0,
        elementSize,
        viewConstructor: ctor,
        readerType: 'byob'
    };
    if (controller._pendingPullIntos.length > 0) {
        controller._pendingPullIntos.push(pullIntoDescriptor);
        // No ReadableByteStreamControllerCallPullIfNeeded() call since:
        // - No change happens on desiredSize
        // - The source has already been notified of that there's at least 1 pending read(view)
        ReadableStreamAddReadIntoRequest(stream, readIntoRequest);
        return;
    }
    if (stream._state === 'closed') {
        const emptyView = new ctor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, 0);
        readIntoRequest._closeSteps(emptyView);
        return;
    }
    if (controller._queueTotalSize > 0) {
        if (ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor)) {
            const filledView = ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor);
            ReadableByteStreamControllerHandleQueueDrain(controller);
            readIntoRequest._chunkSteps(filledView);
            return;
        }
        if (controller._closeRequested) {
            const e = new TypeError('Insufficient bytes to fill elements in the given buffer');
            ReadableByteStreamControllerError(controller, e);
            readIntoRequest._errorSteps(e);
            return;
        }
    }
    controller._pendingPullIntos.push(pullIntoDescriptor);
    ReadableStreamAddReadIntoRequest(stream, readIntoRequest);
    ReadableByteStreamControllerCallPullIfNeeded(controller);
}
function ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor) {
    firstDescriptor.buffer = TransferArrayBuffer(firstDescriptor.buffer);
    const stream = controller._controlledReadableByteStream;
    if (ReadableStreamHasBYOBReader(stream)) {
        while (ReadableStreamGetNumReadIntoRequests(stream) > 0) {
            const pullIntoDescriptor = ReadableByteStreamControllerShiftPendingPullInto(controller);
            ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor);
        }
    }
}
function ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, pullIntoDescriptor) {
    if (pullIntoDescriptor.bytesFilled + bytesWritten > pullIntoDescriptor.byteLength) {
        throw new RangeError('bytesWritten out of range');
    }
    ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesWritten, pullIntoDescriptor);
    if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) {
        // TODO: Figure out whether we should detach the buffer or not here.
        return;
    }
    ReadableByteStreamControllerShiftPendingPullInto(controller);
    const remainderSize = pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize;
    if (remainderSize > 0) {
        const end = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled;
        const remainder = pullIntoDescriptor.buffer.slice(end - remainderSize, end);
        ReadableByteStreamControllerEnqueueChunkToQueue(controller, remainder, 0, remainder.byteLength);
    }
    pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer);
    pullIntoDescriptor.bytesFilled -= remainderSize;
    ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableByteStream, pullIntoDescriptor);
    ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller);
}
function ReadableByteStreamControllerRespondInternal(controller, bytesWritten) {
    const firstDescriptor = controller._pendingPullIntos.peek();
    const state = controller._controlledReadableByteStream._state;
    if (state === 'closed') {
        if (bytesWritten !== 0) {
            throw new TypeError('bytesWritten must be 0 when calling respond() on a closed stream');
        }
        ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor);
    }
    else {
        ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, firstDescriptor);
    }
    ReadableByteStreamControllerCallPullIfNeeded(controller);
}
function ReadableByteStreamControllerShiftPendingPullInto(controller) {
    const descriptor = controller._pendingPullIntos.shift();
    ReadableByteStreamControllerInvalidateBYOBRequest(controller);
    return descriptor;
}
function ReadableByteStreamControllerShouldCallPull(controller) {
    const stream = controller._controlledReadableByteStream;
    if (stream._state !== 'readable') {
        return false;
    }
    if (controller._closeRequested) {
        return false;
    }
    if (!controller._started) {
        return false;
    }
    if (ReadableStreamHasDefaultReader(stream) && ReadableStreamGetNumReadRequests(stream) > 0) {
        return true;
    }
    if (ReadableStreamHasBYOBReader(stream) && ReadableStreamGetNumReadIntoRequests(stream) > 0) {
        return true;
    }
    const desiredSize = ReadableByteStreamControllerGetDesiredSize(controller);
    if (desiredSize > 0) {
        return true;
    }
    return false;
}
function ReadableByteStreamControllerClearAlgorithms(controller) {
    controller._pullAlgorithm = undefined;
    controller._cancelAlgorithm = undefined;
}
// A client of ReadableByteStreamController may use these functions directly to bypass state check.
function ReadableByteStreamControllerClose(controller) {
    const stream = controller._controlledReadableByteStream;
    if (controller._closeRequested || stream._state !== 'readable') {
        return;
    }
    if (controller._queueTotalSize > 0) {
        controller._closeRequested = true;
        return;
    }
    if (controller._pendingPullIntos.length > 0) {
        const firstPendingPullInto = controller._pendingPullIntos.peek();
        if (firstPendingPullInto.bytesFilled > 0) {
            const e = new TypeError('Insufficient bytes to fill elements in the given buffer');
            ReadableByteStreamControllerError(controller, e);
            throw e;
        }
    }
    ReadableByteStreamControllerClearAlgorithms(controller);
    ReadableStreamClose(stream);
}
function ReadableByteStreamControllerEnqueue(controller, chunk) {
    const stream = controller._controlledReadableByteStream;
    if (controller._closeRequested || stream._state !== 'readable') {
        return;
    }
    const buffer = chunk.buffer;
    const byteOffset = chunk.byteOffset;
    const byteLength = chunk.byteLength;
    const transferredBuffer = TransferArrayBuffer(buffer);
    if (ReadableStreamHasDefaultReader(stream)) {
        if (ReadableStreamGetNumReadRequests(stream) === 0) {
            ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
        }
        else {
            const transferredView = new Uint8Array(transferredBuffer, byteOffset, byteLength);
            ReadableStreamFulfillReadRequest(stream, transferredView, false);
        }
    }
    else if (ReadableStreamHasBYOBReader(stream)) {
        // TODO: Ideally in this branch detaching should happen only if the buffer is not consumed fully.
        ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
        ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller);
    }
    else {
        ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
    }
    ReadableByteStreamControllerCallPullIfNeeded(controller);
}
function ReadableByteStreamControllerError(controller, e) {
    const stream = controller._controlledReadableByteStream;
    if (stream._state !== 'readable') {
        return;
    }
    ReadableByteStreamControllerClearPendingPullIntos(controller);
    ResetQueue(controller);
    ReadableByteStreamControllerClearAlgorithms(controller);
    ReadableStreamError(stream, e);
}
function ReadableByteStreamControllerGetDesiredSize(controller) {
    const state = controller._controlledReadableByteStream._state;
    if (state === 'errored') {
        return null;
    }
    if (state === 'closed') {
        return 0;
    }
    return controller._strategyHWM - controller._queueTotalSize;
}
function ReadableByteStreamControllerRespond(controller, bytesWritten) {
    bytesWritten = Number(bytesWritten);
    if (!IsFiniteNonNegativeNumber(bytesWritten)) {
        throw new RangeError('bytesWritten must be a finite');
    }
    ReadableByteStreamControllerRespondInternal(controller, bytesWritten);
}
function ReadableByteStreamControllerRespondWithNewView(controller, view) {
    const firstDescriptor = controller._pendingPullIntos.peek();
    if (firstDescriptor.byteOffset + firstDescriptor.bytesFilled !== view.byteOffset) {
        throw new RangeError('The region specified by view does not match byobRequest');
    }
    if (firstDescriptor.byteLength !== view.byteLength) {
        throw new RangeError('The buffer of view has different capacity than byobRequest');
    }
    firstDescriptor.buffer = view.buffer;
    ReadableByteStreamControllerRespondInternal(controller, view.byteLength);
}
function SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, autoAllocateChunkSize) {
    controller._controlledReadableByteStream = stream;
    controller._pullAgain = false;
    controller._pulling = false;
    controller._byobRequest = null;
    // Need to set the slots so that the assert doesn't fire. In the spec the slots already exist implicitly.
    controller._queue = controller._queueTotalSize = undefined;
    ResetQueue(controller);
    controller._closeRequested = false;
    controller._started = false;
    controller._strategyHWM = highWaterMark;
    controller._pullAlgorithm = pullAlgorithm;
    controller._cancelAlgorithm = cancelAlgorithm;
    controller._autoAllocateChunkSize = autoAllocateChunkSize;
    controller._pendingPullIntos = new SimpleQueue();
    stream._readableStreamController = controller;
    const startResult = startAlgorithm();
    uponPromise(promiseResolvedWith(startResult), () => {
        controller._started = true;
        ReadableByteStreamControllerCallPullIfNeeded(controller);
    }, r => {
        ReadableByteStreamControllerError(controller, r);
    });
}
function SetUpReadableByteStreamControllerFromUnderlyingSource(stream, underlyingByteSource, highWaterMark) {
    const controller = Object.create(ReadableByteStreamController.prototype);
    let startAlgorithm = () => undefined;
    let pullAlgorithm = () => promiseResolvedWith(undefined);
    let cancelAlgorithm = () => promiseResolvedWith(undefined);
    if (underlyingByteSource.start !== undefined) {
        startAlgorithm = () => underlyingByteSource.start(controller);
    }
    if (underlyingByteSource.pull !== undefined) {
        pullAlgorithm = () => underlyingByteSource.pull(controller);
    }
    if (underlyingByteSource.cancel !== undefined) {
        cancelAlgorithm = reason => underlyingByteSource.cancel(reason);
    }
    const autoAllocateChunkSize = underlyingByteSource.autoAllocateChunkSize;
    if (autoAllocateChunkSize === 0) {
        throw new TypeError('autoAllocateChunkSize must be greater than 0');
    }
    SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, autoAllocateChunkSize);
}
function SetUpReadableStreamBYOBRequest(request, controller, view) {
    request._associatedReadableByteStreamController = controller;
    request._view = view;
}
// Helper functions for the ReadableStreamBYOBRequest.
function byobRequestBrandCheckException(name) {
    return new TypeError(`ReadableStreamBYOBRequest.prototype.${name} can only be used on a ReadableStreamBYOBRequest`);
}
// Helper functions for the ReadableByteStreamController.
function byteStreamControllerBrandCheckException(name) {
    return new TypeError(`ReadableByteStreamController.prototype.${name} can only be used on a ReadableByteStreamController`);
}

// Abstract operations for the ReadableStream.
function AcquireReadableStreamBYOBReader(stream) {
    return new ReadableStreamBYOBReader(stream);
}
// ReadableStream API exposed for controllers.
function ReadableStreamAddReadIntoRequest(stream, readIntoRequest) {
    stream._reader._readIntoRequests.push(readIntoRequest);
}
function ReadableStreamFulfillReadIntoRequest(stream, chunk, done) {
    const reader = stream._reader;
    const readIntoRequest = reader._readIntoRequests.shift();
    if (done) {
        readIntoRequest._closeSteps(chunk);
    }
    else {
        readIntoRequest._chunkSteps(chunk);
    }
}
function ReadableStreamGetNumReadIntoRequests(stream) {
    return stream._reader._readIntoRequests.length;
}
function ReadableStreamHasBYOBReader(stream) {
    const reader = stream._reader;
    if (reader === undefined) {
        return false;
    }
    if (!IsReadableStreamBYOBReader(reader)) {
        return false;
    }
    return true;
}
/**
 * A BYOB reader vended by a {@link ReadableStream}.
 *
 * @public
 */
class ReadableStreamBYOBReader {
    constructor(stream) {
        assertRequiredArgument(stream, 1, 'ReadableStreamBYOBReader');
        assertReadableStream(stream, 'First parameter');
        if (IsReadableStreamLocked(stream)) {
            throw new TypeError('This stream has already been locked for exclusive reading by another reader');
        }
        if (!IsReadableByteStreamController(stream._readableStreamController)) {
            throw new TypeError('Cannot construct a ReadableStreamBYOBReader for a stream not constructed with a byte ' +
                'source');
        }
        ReadableStreamReaderGenericInitialize(this, stream);
        this._readIntoRequests = new SimpleQueue();
    }
    /**
     * Returns a promise that will be fulfilled when the stream becomes closed, or rejected if the stream ever errors or
     * the reader's lock is released before the stream finishes closing.
     */
    get closed() {
        if (!IsReadableStreamBYOBReader(this)) {
            return promiseRejectedWith(byobReaderBrandCheckException('closed'));
        }
        return this._closedPromise;
    }
    /**
     * If the reader is active, behaves the same as {@link ReadableStream.cancel | stream.cancel(reason)}.
     */
    cancel(reason = undefined) {
        if (!IsReadableStreamBYOBReader(this)) {
            return promiseRejectedWith(byobReaderBrandCheckException('cancel'));
        }
        if (this._ownerReadableStream === undefined) {
            return promiseRejectedWith(readerLockException('cancel'));
        }
        return ReadableStreamReaderGenericCancel(this, reason);
    }
    /**
     * Attempts to reads bytes into view, and returns a promise resolved with the result.
     *
     * If reading a chunk causes the queue to become empty, more data will be pulled from the underlying source.
     */
    read(view) {
        if (!IsReadableStreamBYOBReader(this)) {
            return promiseRejectedWith(byobReaderBrandCheckException('read'));
        }
        if (!ArrayBuffer.isView(view)) {
            return promiseRejectedWith(new TypeError('view must be an array buffer view'));
        }
        if (view.byteLength === 0) {
            return promiseRejectedWith(new TypeError('view must have non-zero byteLength'));
        }
        if (view.buffer.byteLength === 0) {
            return promiseRejectedWith(new TypeError(`view's buffer must have non-zero byteLength`));
        }
        if (this._ownerReadableStream === undefined) {
            return promiseRejectedWith(readerLockException('read from'));
        }
        let resolvePromise;
        let rejectPromise;
        const promise = newPromise((resolve, reject) => {
            resolvePromise = resolve;
            rejectPromise = reject;
        });
        const readIntoRequest = {
            _chunkSteps: chunk => resolvePromise({ value: chunk, done: false }),
            _closeSteps: chunk => resolvePromise({ value: chunk, done: true }),
            _errorSteps: e => rejectPromise(e)
        };
        ReadableStreamBYOBReaderRead(this, view, readIntoRequest);
        return promise;
    }
    /**
     * Releases the reader's lock on the corresponding stream. After the lock is released, the reader is no longer active.
     * If the associated stream is errored when the lock is released, the reader will appear errored in the same way
     * from now on; otherwise, the reader will appear closed.
     *
     * A reader's lock cannot be released while it still has a pending read request, i.e., if a promise returned by
     * the reader's {@link ReadableStreamBYOBReader.read | read()} method has not yet been settled. Attempting to
     * do so will throw a `TypeError` and leave the reader locked to the stream.
     */
    releaseLock() {
        if (!IsReadableStreamBYOBReader(this)) {
            throw byobReaderBrandCheckException('releaseLock');
        }
        if (this._ownerReadableStream === undefined) {
            return;
        }
        if (this._readIntoRequests.length > 0) {
            throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled');
        }
        ReadableStreamReaderGenericRelease(this);
    }
}
Object.defineProperties(ReadableStreamBYOBReader.prototype, {
    cancel: { enumerable: true },
    read: { enumerable: true },
    releaseLock: { enumerable: true },
    closed: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(ReadableStreamBYOBReader.prototype, SymbolPolyfill.toStringTag, {
        value: 'ReadableStreamBYOBReader',
        configurable: true
    });
}
// Abstract operations for the readers.
function IsReadableStreamBYOBReader(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readIntoRequests')) {
        return false;
    }
    return true;
}
function ReadableStreamBYOBReaderRead(reader, view, readIntoRequest) {
    const stream = reader._ownerReadableStream;
    stream._disturbed = true;
    if (stream._state === 'errored') {
        readIntoRequest._errorSteps(stream._storedError);
    }
    else {
        ReadableByteStreamControllerPullInto(stream._readableStreamController, view, readIntoRequest);
    }
}
// Helper functions for the ReadableStreamBYOBReader.
function byobReaderBrandCheckException(name) {
    return new TypeError(`ReadableStreamBYOBReader.prototype.${name} can only be used on a ReadableStreamBYOBReader`);
}

function ExtractHighWaterMark(strategy, defaultHWM) {
    const { highWaterMark } = strategy;
    if (highWaterMark === undefined) {
        return defaultHWM;
    }
    if (NumberIsNaN(highWaterMark) || highWaterMark < 0) {
        throw new RangeError('Invalid highWaterMark');
    }
    return highWaterMark;
}
function ExtractSizeAlgorithm(strategy) {
    const { size } = strategy;
    if (!size) {
        return () => 1;
    }
    return size;
}

function convertQueuingStrategy(init, context) {
    assertDictionary(init, context);
    const highWaterMark = init === null || init === void 0 ? void 0 : init.highWaterMark;
    const size = init === null || init === void 0 ? void 0 : init.size;
    return {
        highWaterMark: highWaterMark === undefined ? undefined : convertUnrestrictedDouble(highWaterMark),
        size: size === undefined ? undefined : convertQueuingStrategySize(size, `${context} has member 'size' that`)
    };
}
function convertQueuingStrategySize(fn, context) {
    assertFunction(fn, context);
    return chunk => convertUnrestrictedDouble(fn(chunk));
}

function convertUnderlyingSink(original, context) {
    assertDictionary(original, context);
    const abort = original === null || original === void 0 ? void 0 : original.abort;
    const close = original === null || original === void 0 ? void 0 : original.close;
    const start = original === null || original === void 0 ? void 0 : original.start;
    const type = original === null || original === void 0 ? void 0 : original.type;
    const write = original === null || original === void 0 ? void 0 : original.write;
    return {
        abort: abort === undefined ?
            undefined :
            convertUnderlyingSinkAbortCallback(abort, original, `${context} has member 'abort' that`),
        close: close === undefined ?
            undefined :
            convertUnderlyingSinkCloseCallback(close, original, `${context} has member 'close' that`),
        start: start === undefined ?
            undefined :
            convertUnderlyingSinkStartCallback(start, original, `${context} has member 'start' that`),
        write: write === undefined ?
            undefined :
            convertUnderlyingSinkWriteCallback(write, original, `${context} has member 'write' that`),
        type
    };
}
function convertUnderlyingSinkAbortCallback(fn, original, context) {
    assertFunction(fn, context);
    return (reason) => promiseCall(fn, original, [reason]);
}
function convertUnderlyingSinkCloseCallback(fn, original, context) {
    assertFunction(fn, context);
    return () => promiseCall(fn, original, []);
}
function convertUnderlyingSinkStartCallback(fn, original, context) {
    assertFunction(fn, context);
    return (controller) => reflectCall(fn, original, [controller]);
}
function convertUnderlyingSinkWriteCallback(fn, original, context) {
    assertFunction(fn, context);
    return (chunk, controller) => promiseCall(fn, original, [chunk, controller]);
}

function assertWritableStream(x, context) {
    if (!IsWritableStream(x)) {
        throw new TypeError(`${context} is not a WritableStream.`);
    }
}

/**
 * A writable stream represents a destination for data, into which you can write.
 *
 * @public
 */
class WritableStream$1 {
    constructor(rawUnderlyingSink = {}, rawStrategy = {}) {
        if (rawUnderlyingSink === undefined) {
            rawUnderlyingSink = null;
        }
        else {
            assertObject(rawUnderlyingSink, 'First parameter');
        }
        const strategy = convertQueuingStrategy(rawStrategy, 'Second parameter');
        const underlyingSink = convertUnderlyingSink(rawUnderlyingSink, 'First parameter');
        InitializeWritableStream(this);
        const type = underlyingSink.type;
        if (type !== undefined) {
            throw new RangeError('Invalid type is specified');
        }
        const sizeAlgorithm = ExtractSizeAlgorithm(strategy);
        const highWaterMark = ExtractHighWaterMark(strategy, 1);
        SetUpWritableStreamDefaultControllerFromUnderlyingSink(this, underlyingSink, highWaterMark, sizeAlgorithm);
    }
    /**
     * Returns whether or not the writable stream is locked to a writer.
     */
    get locked() {
        if (!IsWritableStream(this)) {
            throw streamBrandCheckException$2('locked');
        }
        return IsWritableStreamLocked(this);
    }
    /**
     * Aborts the stream, signaling that the producer can no longer successfully write to the stream and it is to be
     * immediately moved to an errored state, with any queued-up writes discarded. This will also execute any abort
     * mechanism of the underlying sink.
     *
     * The returned promise will fulfill if the stream shuts down successfully, or reject if the underlying sink signaled
     * that there was an error doing so. Additionally, it will reject with a `TypeError` (without attempting to cancel
     * the stream) if the stream is currently locked.
     */
    abort(reason = undefined) {
        if (!IsWritableStream(this)) {
            return promiseRejectedWith(streamBrandCheckException$2('abort'));
        }
        if (IsWritableStreamLocked(this)) {
            return promiseRejectedWith(new TypeError('Cannot abort a stream that already has a writer'));
        }
        return WritableStreamAbort(this, reason);
    }
    /**
     * Closes the stream. The underlying sink will finish processing any previously-written chunks, before invoking its
     * close behavior. During this time any further attempts to write will fail (without erroring the stream).
     *
     * The method returns a promise that will fulfill if all remaining chunks are successfully written and the stream
     * successfully closes, or rejects if an error is encountered during this process. Additionally, it will reject with
     * a `TypeError` (without attempting to cancel the stream) if the stream is currently locked.
     */
    close() {
        if (!IsWritableStream(this)) {
            return promiseRejectedWith(streamBrandCheckException$2('close'));
        }
        if (IsWritableStreamLocked(this)) {
            return promiseRejectedWith(new TypeError('Cannot close a stream that already has a writer'));
        }
        if (WritableStreamCloseQueuedOrInFlight(this)) {
            return promiseRejectedWith(new TypeError('Cannot close an already-closing stream'));
        }
        return WritableStreamClose(this);
    }
    /**
     * Creates a {@link WritableStreamDefaultWriter | writer} and locks the stream to the new writer. While the stream
     * is locked, no other writer can be acquired until this one is released.
     *
     * This functionality is especially useful for creating abstractions that desire the ability to write to a stream
     * without interruption or interleaving. By getting a writer for the stream, you can ensure nobody else can write at
     * the same time, which would cause the resulting written data to be unpredictable and probably useless.
     */
    getWriter() {
        if (!IsWritableStream(this)) {
            throw streamBrandCheckException$2('getWriter');
        }
        return AcquireWritableStreamDefaultWriter(this);
    }
}
Object.defineProperties(WritableStream$1.prototype, {
    abort: { enumerable: true },
    close: { enumerable: true },
    getWriter: { enumerable: true },
    locked: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(WritableStream$1.prototype, SymbolPolyfill.toStringTag, {
        value: 'WritableStream',
        configurable: true
    });
}
// Abstract operations for the WritableStream.
function AcquireWritableStreamDefaultWriter(stream) {
    return new WritableStreamDefaultWriter(stream);
}
// Throws if and only if startAlgorithm throws.
function CreateWritableStream(startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark = 1, sizeAlgorithm = () => 1) {
    const stream = Object.create(WritableStream$1.prototype);
    InitializeWritableStream(stream);
    const controller = Object.create(WritableStreamDefaultController.prototype);
    SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm);
    return stream;
}
function InitializeWritableStream(stream) {
    stream._state = 'writable';
    // The error that will be reported by new method calls once the state becomes errored. Only set when [[state]] is
    // 'erroring' or 'errored'. May be set to an undefined value.
    stream._storedError = undefined;
    stream._writer = undefined;
    // Initialize to undefined first because the constructor of the controller checks this
    // variable to validate the caller.
    stream._writableStreamController = undefined;
    // This queue is placed here instead of the writer class in order to allow for passing a writer to the next data
    // producer without waiting for the queued writes to finish.
    stream._writeRequests = new SimpleQueue();
    // Write requests are removed from _writeRequests when write() is called on the underlying sink. This prevents
    // them from being erroneously rejected on error. If a write() call is in-flight, the request is stored here.
    stream._inFlightWriteRequest = undefined;
    // The promise that was returned from writer.close(). Stored here because it may be fulfilled after the writer
    // has been detached.
    stream._closeRequest = undefined;
    // Close request is removed from _closeRequest when close() is called on the underlying sink. This prevents it
    // from being erroneously rejected on error. If a close() call is in-flight, the request is stored here.
    stream._inFlightCloseRequest = undefined;
    // The promise that was returned from writer.abort(). This may also be fulfilled after the writer has detached.
    stream._pendingAbortRequest = undefined;
    // The backpressure signal set by the controller.
    stream._backpressure = false;
}
function IsWritableStream(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_writableStreamController')) {
        return false;
    }
    return true;
}
function IsWritableStreamLocked(stream) {
    if (stream._writer === undefined) {
        return false;
    }
    return true;
}
function WritableStreamAbort(stream, reason) {
    const state = stream._state;
    if (state === 'closed' || state === 'errored') {
        return promiseResolvedWith(undefined);
    }
    if (stream._pendingAbortRequest !== undefined) {
        return stream._pendingAbortRequest._promise;
    }
    let wasAlreadyErroring = false;
    if (state === 'erroring') {
        wasAlreadyErroring = true;
        // reason will not be used, so don't keep a reference to it.
        reason = undefined;
    }
    const promise = newPromise((resolve, reject) => {
        stream._pendingAbortRequest = {
            _promise: undefined,
            _resolve: resolve,
            _reject: reject,
            _reason: reason,
            _wasAlreadyErroring: wasAlreadyErroring
        };
    });
    stream._pendingAbortRequest._promise = promise;
    if (!wasAlreadyErroring) {
        WritableStreamStartErroring(stream, reason);
    }
    return promise;
}
function WritableStreamClose(stream) {
    const state = stream._state;
    if (state === 'closed' || state === 'errored') {
        return promiseRejectedWith(new TypeError(`The stream (in ${state} state) is not in the writable state and cannot be closed`));
    }
    const promise = newPromise((resolve, reject) => {
        const closeRequest = {
            _resolve: resolve,
            _reject: reject
        };
        stream._closeRequest = closeRequest;
    });
    const writer = stream._writer;
    if (writer !== undefined && stream._backpressure && state === 'writable') {
        defaultWriterReadyPromiseResolve(writer);
    }
    WritableStreamDefaultControllerClose(stream._writableStreamController);
    return promise;
}
// WritableStream API exposed for controllers.
function WritableStreamAddWriteRequest(stream) {
    const promise = newPromise((resolve, reject) => {
        const writeRequest = {
            _resolve: resolve,
            _reject: reject
        };
        stream._writeRequests.push(writeRequest);
    });
    return promise;
}
function WritableStreamDealWithRejection(stream, error) {
    const state = stream._state;
    if (state === 'writable') {
        WritableStreamStartErroring(stream, error);
        return;
    }
    WritableStreamFinishErroring(stream);
}
function WritableStreamStartErroring(stream, reason) {
    const controller = stream._writableStreamController;
    stream._state = 'erroring';
    stream._storedError = reason;
    const writer = stream._writer;
    if (writer !== undefined) {
        WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason);
    }
    if (!WritableStreamHasOperationMarkedInFlight(stream) && controller._started) {
        WritableStreamFinishErroring(stream);
    }
}
function WritableStreamFinishErroring(stream) {
    stream._state = 'errored';
    stream._writableStreamController[ErrorSteps]();
    const storedError = stream._storedError;
    stream._writeRequests.forEach(writeRequest => {
        writeRequest._reject(storedError);
    });
    stream._writeRequests = new SimpleQueue();
    if (stream._pendingAbortRequest === undefined) {
        WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
        return;
    }
    const abortRequest = stream._pendingAbortRequest;
    stream._pendingAbortRequest = undefined;
    if (abortRequest._wasAlreadyErroring) {
        abortRequest._reject(storedError);
        WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
        return;
    }
    const promise = stream._writableStreamController[AbortSteps](abortRequest._reason);
    uponPromise(promise, () => {
        abortRequest._resolve();
        WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
    }, (reason) => {
        abortRequest._reject(reason);
        WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
    });
}
function WritableStreamFinishInFlightWrite(stream) {
    stream._inFlightWriteRequest._resolve(undefined);
    stream._inFlightWriteRequest = undefined;
}
function WritableStreamFinishInFlightWriteWithError(stream, error) {
    stream._inFlightWriteRequest._reject(error);
    stream._inFlightWriteRequest = undefined;
    WritableStreamDealWithRejection(stream, error);
}
function WritableStreamFinishInFlightClose(stream) {
    stream._inFlightCloseRequest._resolve(undefined);
    stream._inFlightCloseRequest = undefined;
    const state = stream._state;
    if (state === 'erroring') {
        // The error was too late to do anything, so it is ignored.
        stream._storedError = undefined;
        if (stream._pendingAbortRequest !== undefined) {
            stream._pendingAbortRequest._resolve();
            stream._pendingAbortRequest = undefined;
        }
    }
    stream._state = 'closed';
    const writer = stream._writer;
    if (writer !== undefined) {
        defaultWriterClosedPromiseResolve(writer);
    }
}
function WritableStreamFinishInFlightCloseWithError(stream, error) {
    stream._inFlightCloseRequest._reject(error);
    stream._inFlightCloseRequest = undefined;
    // Never execute sink abort() after sink close().
    if (stream._pendingAbortRequest !== undefined) {
        stream._pendingAbortRequest._reject(error);
        stream._pendingAbortRequest = undefined;
    }
    WritableStreamDealWithRejection(stream, error);
}
// TODO(ricea): Fix alphabetical order.
function WritableStreamCloseQueuedOrInFlight(stream) {
    if (stream._closeRequest === undefined && stream._inFlightCloseRequest === undefined) {
        return false;
    }
    return true;
}
function WritableStreamHasOperationMarkedInFlight(stream) {
    if (stream._inFlightWriteRequest === undefined && stream._inFlightCloseRequest === undefined) {
        return false;
    }
    return true;
}
function WritableStreamMarkCloseRequestInFlight(stream) {
    stream._inFlightCloseRequest = stream._closeRequest;
    stream._closeRequest = undefined;
}
function WritableStreamMarkFirstWriteRequestInFlight(stream) {
    stream._inFlightWriteRequest = stream._writeRequests.shift();
}
function WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream) {
    if (stream._closeRequest !== undefined) {
        stream._closeRequest._reject(stream._storedError);
        stream._closeRequest = undefined;
    }
    const writer = stream._writer;
    if (writer !== undefined) {
        defaultWriterClosedPromiseReject(writer, stream._storedError);
    }
}
function WritableStreamUpdateBackpressure(stream, backpressure) {
    const writer = stream._writer;
    if (writer !== undefined && backpressure !== stream._backpressure) {
        if (backpressure) {
            defaultWriterReadyPromiseReset(writer);
        }
        else {
            defaultWriterReadyPromiseResolve(writer);
        }
    }
    stream._backpressure = backpressure;
}
/**
 * A default writer vended by a {@link WritableStream}.
 *
 * @public
 */
class WritableStreamDefaultWriter {
    constructor(stream) {
        assertRequiredArgument(stream, 1, 'WritableStreamDefaultWriter');
        assertWritableStream(stream, 'First parameter');
        if (IsWritableStreamLocked(stream)) {
            throw new TypeError('This stream has already been locked for exclusive writing by another writer');
        }
        this._ownerWritableStream = stream;
        stream._writer = this;
        const state = stream._state;
        if (state === 'writable') {
            if (!WritableStreamCloseQueuedOrInFlight(stream) && stream._backpressure) {
                defaultWriterReadyPromiseInitialize(this);
            }
            else {
                defaultWriterReadyPromiseInitializeAsResolved(this);
            }
            defaultWriterClosedPromiseInitialize(this);
        }
        else if (state === 'erroring') {
            defaultWriterReadyPromiseInitializeAsRejected(this, stream._storedError);
            defaultWriterClosedPromiseInitialize(this);
        }
        else if (state === 'closed') {
            defaultWriterReadyPromiseInitializeAsResolved(this);
            defaultWriterClosedPromiseInitializeAsResolved(this);
        }
        else {
            const storedError = stream._storedError;
            defaultWriterReadyPromiseInitializeAsRejected(this, storedError);
            defaultWriterClosedPromiseInitializeAsRejected(this, storedError);
        }
    }
    /**
     * Returns a promise that will be fulfilled when the stream becomes closed, or rejected if the stream ever errors or
     * the writer’s lock is released before the stream finishes closing.
     */
    get closed() {
        if (!IsWritableStreamDefaultWriter(this)) {
            return promiseRejectedWith(defaultWriterBrandCheckException('closed'));
        }
        return this._closedPromise;
    }
    /**
     * Returns the desired size to fill the stream’s internal queue. It can be negative, if the queue is over-full.
     * A producer can use this information to determine the right amount of data to write.
     *
     * It will be `null` if the stream cannot be successfully written to (due to either being errored, or having an abort
     * queued up). It will return zero if the stream is closed. And the getter will throw an exception if invoked when
     * the writer’s lock is released.
     */
    get desiredSize() {
        if (!IsWritableStreamDefaultWriter(this)) {
            throw defaultWriterBrandCheckException('desiredSize');
        }
        if (this._ownerWritableStream === undefined) {
            throw defaultWriterLockException('desiredSize');
        }
        return WritableStreamDefaultWriterGetDesiredSize(this);
    }
    /**
     * Returns a promise that will be fulfilled when the desired size to fill the stream’s internal queue transitions
     * from non-positive to positive, signaling that it is no longer applying backpressure. Once the desired size dips
     * back to zero or below, the getter will return a new promise that stays pending until the next transition.
     *
     * If the stream becomes errored or aborted, or the writer’s lock is released, the returned promise will become
     * rejected.
     */
    get ready() {
        if (!IsWritableStreamDefaultWriter(this)) {
            return promiseRejectedWith(defaultWriterBrandCheckException('ready'));
        }
        return this._readyPromise;
    }
    /**
     * If the reader is active, behaves the same as {@link WritableStream.abort | stream.abort(reason)}.
     */
    abort(reason = undefined) {
        if (!IsWritableStreamDefaultWriter(this)) {
            return promiseRejectedWith(defaultWriterBrandCheckException('abort'));
        }
        if (this._ownerWritableStream === undefined) {
            return promiseRejectedWith(defaultWriterLockException('abort'));
        }
        return WritableStreamDefaultWriterAbort(this, reason);
    }
    /**
     * If the reader is active, behaves the same as {@link WritableStream.close | stream.close()}.
     */
    close() {
        if (!IsWritableStreamDefaultWriter(this)) {
            return promiseRejectedWith(defaultWriterBrandCheckException('close'));
        }
        const stream = this._ownerWritableStream;
        if (stream === undefined) {
            return promiseRejectedWith(defaultWriterLockException('close'));
        }
        if (WritableStreamCloseQueuedOrInFlight(stream)) {
            return promiseRejectedWith(new TypeError('Cannot close an already-closing stream'));
        }
        return WritableStreamDefaultWriterClose(this);
    }
    /**
     * Releases the writer’s lock on the corresponding stream. After the lock is released, the writer is no longer active.
     * If the associated stream is errored when the lock is released, the writer will appear errored in the same way from
     * now on; otherwise, the writer will appear closed.
     *
     * Note that the lock can still be released even if some ongoing writes have not yet finished (i.e. even if the
     * promises returned from previous calls to {@link WritableStreamDefaultWriter.write | write()} have not yet settled).
     * It’s not necessary to hold the lock on the writer for the duration of the write; the lock instead simply prevents
     * other producers from writing in an interleaved manner.
     */
    releaseLock() {
        if (!IsWritableStreamDefaultWriter(this)) {
            throw defaultWriterBrandCheckException('releaseLock');
        }
        const stream = this._ownerWritableStream;
        if (stream === undefined) {
            return;
        }
        WritableStreamDefaultWriterRelease(this);
    }
    write(chunk = undefined) {
        if (!IsWritableStreamDefaultWriter(this)) {
            return promiseRejectedWith(defaultWriterBrandCheckException('write'));
        }
        if (this._ownerWritableStream === undefined) {
            return promiseRejectedWith(defaultWriterLockException('write to'));
        }
        return WritableStreamDefaultWriterWrite(this, chunk);
    }
}
Object.defineProperties(WritableStreamDefaultWriter.prototype, {
    abort: { enumerable: true },
    close: { enumerable: true },
    releaseLock: { enumerable: true },
    write: { enumerable: true },
    closed: { enumerable: true },
    desiredSize: { enumerable: true },
    ready: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(WritableStreamDefaultWriter.prototype, SymbolPolyfill.toStringTag, {
        value: 'WritableStreamDefaultWriter',
        configurable: true
    });
}
// Abstract operations for the WritableStreamDefaultWriter.
function IsWritableStreamDefaultWriter(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_ownerWritableStream')) {
        return false;
    }
    return true;
}
// A client of WritableStreamDefaultWriter may use these functions directly to bypass state check.
function WritableStreamDefaultWriterAbort(writer, reason) {
    const stream = writer._ownerWritableStream;
    return WritableStreamAbort(stream, reason);
}
function WritableStreamDefaultWriterClose(writer) {
    const stream = writer._ownerWritableStream;
    return WritableStreamClose(stream);
}
function WritableStreamDefaultWriterCloseWithErrorPropagation(writer) {
    const stream = writer._ownerWritableStream;
    const state = stream._state;
    if (WritableStreamCloseQueuedOrInFlight(stream) || state === 'closed') {
        return promiseResolvedWith(undefined);
    }
    if (state === 'errored') {
        return promiseRejectedWith(stream._storedError);
    }
    return WritableStreamDefaultWriterClose(writer);
}
function WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, error) {
    if (writer._closedPromiseState === 'pending') {
        defaultWriterClosedPromiseReject(writer, error);
    }
    else {
        defaultWriterClosedPromiseResetToRejected(writer, error);
    }
}
function WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) {
    if (writer._readyPromiseState === 'pending') {
        defaultWriterReadyPromiseReject(writer, error);
    }
    else {
        defaultWriterReadyPromiseResetToRejected(writer, error);
    }
}
function WritableStreamDefaultWriterGetDesiredSize(writer) {
    const stream = writer._ownerWritableStream;
    const state = stream._state;
    if (state === 'errored' || state === 'erroring') {
        return null;
    }
    if (state === 'closed') {
        return 0;
    }
    return WritableStreamDefaultControllerGetDesiredSize(stream._writableStreamController);
}
function WritableStreamDefaultWriterRelease(writer) {
    const stream = writer._ownerWritableStream;
    const releasedError = new TypeError(`Writer was released and can no longer be used to monitor the stream's closedness`);
    WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError);
    // The state transitions to "errored" before the sink abort() method runs, but the writer.closed promise is not
    // rejected until afterwards. This means that simply testing state will not work.
    WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError);
    stream._writer = undefined;
    writer._ownerWritableStream = undefined;
}
function WritableStreamDefaultWriterWrite(writer, chunk) {
    const stream = writer._ownerWritableStream;
    const controller = stream._writableStreamController;
    const chunkSize = WritableStreamDefaultControllerGetChunkSize(controller, chunk);
    if (stream !== writer._ownerWritableStream) {
        return promiseRejectedWith(defaultWriterLockException('write to'));
    }
    const state = stream._state;
    if (state === 'errored') {
        return promiseRejectedWith(stream._storedError);
    }
    if (WritableStreamCloseQueuedOrInFlight(stream) || state === 'closed') {
        return promiseRejectedWith(new TypeError('The stream is closing or closed and cannot be written to'));
    }
    if (state === 'erroring') {
        return promiseRejectedWith(stream._storedError);
    }
    const promise = WritableStreamAddWriteRequest(stream);
    WritableStreamDefaultControllerWrite(controller, chunk, chunkSize);
    return promise;
}
const closeSentinel = {};
/**
 * Allows control of a {@link WritableStream | writable stream}'s state and internal queue.
 *
 * @public
 */
class WritableStreamDefaultController {
    constructor() {
        throw new TypeError('Illegal constructor');
    }
    /**
     * Closes the controlled writable stream, making all future interactions with it fail with the given error `e`.
     *
     * This method is rarely used, since usually it suffices to return a rejected promise from one of the underlying
     * sink's methods. However, it can be useful for suddenly shutting down a stream in response to an event outside the
     * normal lifecycle of interactions with the underlying sink.
     */
    error(e = undefined) {
        if (!IsWritableStreamDefaultController(this)) {
            throw new TypeError('WritableStreamDefaultController.prototype.error can only be used on a WritableStreamDefaultController');
        }
        const state = this._controlledWritableStream._state;
        if (state !== 'writable') {
            // The stream is closed, errored or will be soon. The sink can't do anything useful if it gets an error here, so
            // just treat it as a no-op.
            return;
        }
        WritableStreamDefaultControllerError(this, e);
    }
    /** @internal */
    [AbortSteps](reason) {
        const result = this._abortAlgorithm(reason);
        WritableStreamDefaultControllerClearAlgorithms(this);
        return result;
    }
    /** @internal */
    [ErrorSteps]() {
        ResetQueue(this);
    }
}
Object.defineProperties(WritableStreamDefaultController.prototype, {
    error: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(WritableStreamDefaultController.prototype, SymbolPolyfill.toStringTag, {
        value: 'WritableStreamDefaultController',
        configurable: true
    });
}
// Abstract operations implementing interface required by the WritableStream.
function IsWritableStreamDefaultController(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_controlledWritableStream')) {
        return false;
    }
    return true;
}
function SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm) {
    controller._controlledWritableStream = stream;
    stream._writableStreamController = controller;
    // Need to set the slots so that the assert doesn't fire. In the spec the slots already exist implicitly.
    controller._queue = undefined;
    controller._queueTotalSize = undefined;
    ResetQueue(controller);
    controller._started = false;
    controller._strategySizeAlgorithm = sizeAlgorithm;
    controller._strategyHWM = highWaterMark;
    controller._writeAlgorithm = writeAlgorithm;
    controller._closeAlgorithm = closeAlgorithm;
    controller._abortAlgorithm = abortAlgorithm;
    const backpressure = WritableStreamDefaultControllerGetBackpressure(controller);
    WritableStreamUpdateBackpressure(stream, backpressure);
    const startResult = startAlgorithm();
    const startPromise = promiseResolvedWith(startResult);
    uponPromise(startPromise, () => {
        controller._started = true;
        WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
    }, r => {
        controller._started = true;
        WritableStreamDealWithRejection(stream, r);
    });
}
function SetUpWritableStreamDefaultControllerFromUnderlyingSink(stream, underlyingSink, highWaterMark, sizeAlgorithm) {
    const controller = Object.create(WritableStreamDefaultController.prototype);
    let startAlgorithm = () => undefined;
    let writeAlgorithm = () => promiseResolvedWith(undefined);
    let closeAlgorithm = () => promiseResolvedWith(undefined);
    let abortAlgorithm = () => promiseResolvedWith(undefined);
    if (underlyingSink.start !== undefined) {
        startAlgorithm = () => underlyingSink.start(controller);
    }
    if (underlyingSink.write !== undefined) {
        writeAlgorithm = chunk => underlyingSink.write(chunk, controller);
    }
    if (underlyingSink.close !== undefined) {
        closeAlgorithm = () => underlyingSink.close();
    }
    if (underlyingSink.abort !== undefined) {
        abortAlgorithm = reason => underlyingSink.abort(reason);
    }
    SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm);
}
// ClearAlgorithms may be called twice. Erroring the same stream in multiple ways will often result in redundant calls.
function WritableStreamDefaultControllerClearAlgorithms(controller) {
    controller._writeAlgorithm = undefined;
    controller._closeAlgorithm = undefined;
    controller._abortAlgorithm = undefined;
    controller._strategySizeAlgorithm = undefined;
}
function WritableStreamDefaultControllerClose(controller) {
    EnqueueValueWithSize(controller, closeSentinel, 0);
    WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
}
function WritableStreamDefaultControllerGetChunkSize(controller, chunk) {
    try {
        return controller._strategySizeAlgorithm(chunk);
    }
    catch (chunkSizeE) {
        WritableStreamDefaultControllerErrorIfNeeded(controller, chunkSizeE);
        return 1;
    }
}
function WritableStreamDefaultControllerGetDesiredSize(controller) {
    return controller._strategyHWM - controller._queueTotalSize;
}
function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) {
    try {
        EnqueueValueWithSize(controller, chunk, chunkSize);
    }
    catch (enqueueE) {
        WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueE);
        return;
    }
    const stream = controller._controlledWritableStream;
    if (!WritableStreamCloseQueuedOrInFlight(stream) && stream._state === 'writable') {
        const backpressure = WritableStreamDefaultControllerGetBackpressure(controller);
        WritableStreamUpdateBackpressure(stream, backpressure);
    }
    WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
}
// Abstract operations for the WritableStreamDefaultController.
function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) {
    const stream = controller._controlledWritableStream;
    if (!controller._started) {
        return;
    }
    if (stream._inFlightWriteRequest !== undefined) {
        return;
    }
    const state = stream._state;
    if (state === 'erroring') {
        WritableStreamFinishErroring(stream);
        return;
    }
    if (controller._queue.length === 0) {
        return;
    }
    const value = PeekQueueValue(controller);
    if (value === closeSentinel) {
        WritableStreamDefaultControllerProcessClose(controller);
    }
    else {
        WritableStreamDefaultControllerProcessWrite(controller, value);
    }
}
function WritableStreamDefaultControllerErrorIfNeeded(controller, error) {
    if (controller._controlledWritableStream._state === 'writable') {
        WritableStreamDefaultControllerError(controller, error);
    }
}
function WritableStreamDefaultControllerProcessClose(controller) {
    const stream = controller._controlledWritableStream;
    WritableStreamMarkCloseRequestInFlight(stream);
    DequeueValue(controller);
    const sinkClosePromise = controller._closeAlgorithm();
    WritableStreamDefaultControllerClearAlgorithms(controller);
    uponPromise(sinkClosePromise, () => {
        WritableStreamFinishInFlightClose(stream);
    }, reason => {
        WritableStreamFinishInFlightCloseWithError(stream, reason);
    });
}
function WritableStreamDefaultControllerProcessWrite(controller, chunk) {
    const stream = controller._controlledWritableStream;
    WritableStreamMarkFirstWriteRequestInFlight(stream);
    const sinkWritePromise = controller._writeAlgorithm(chunk);
    uponPromise(sinkWritePromise, () => {
        WritableStreamFinishInFlightWrite(stream);
        const state = stream._state;
        DequeueValue(controller);
        if (!WritableStreamCloseQueuedOrInFlight(stream) && state === 'writable') {
            const backpressure = WritableStreamDefaultControllerGetBackpressure(controller);
            WritableStreamUpdateBackpressure(stream, backpressure);
        }
        WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
    }, reason => {
        if (stream._state === 'writable') {
            WritableStreamDefaultControllerClearAlgorithms(controller);
        }
        WritableStreamFinishInFlightWriteWithError(stream, reason);
    });
}
function WritableStreamDefaultControllerGetBackpressure(controller) {
    const desiredSize = WritableStreamDefaultControllerGetDesiredSize(controller);
    return desiredSize <= 0;
}
// A client of WritableStreamDefaultController may use these functions directly to bypass state check.
function WritableStreamDefaultControllerError(controller, error) {
    const stream = controller._controlledWritableStream;
    WritableStreamDefaultControllerClearAlgorithms(controller);
    WritableStreamStartErroring(stream, error);
}
// Helper functions for the WritableStream.
function streamBrandCheckException$2(name) {
    return new TypeError(`WritableStream.prototype.${name} can only be used on a WritableStream`);
}
// Helper functions for the WritableStreamDefaultWriter.
function defaultWriterBrandCheckException(name) {
    return new TypeError(`WritableStreamDefaultWriter.prototype.${name} can only be used on a WritableStreamDefaultWriter`);
}
function defaultWriterLockException(name) {
    return new TypeError('Cannot ' + name + ' a stream using a released writer');
}
function defaultWriterClosedPromiseInitialize(writer) {
    writer._closedPromise = newPromise((resolve, reject) => {
        writer._closedPromise_resolve = resolve;
        writer._closedPromise_reject = reject;
        writer._closedPromiseState = 'pending';
    });
}
function defaultWriterClosedPromiseInitializeAsRejected(writer, reason) {
    defaultWriterClosedPromiseInitialize(writer);
    defaultWriterClosedPromiseReject(writer, reason);
}
function defaultWriterClosedPromiseInitializeAsResolved(writer) {
    defaultWriterClosedPromiseInitialize(writer);
    defaultWriterClosedPromiseResolve(writer);
}
function defaultWriterClosedPromiseReject(writer, reason) {
    if (writer._closedPromise_reject === undefined) {
        return;
    }
    setPromiseIsHandledToTrue(writer._closedPromise);
    writer._closedPromise_reject(reason);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'rejected';
}
function defaultWriterClosedPromiseResetToRejected(writer, reason) {
    defaultWriterClosedPromiseInitializeAsRejected(writer, reason);
}
function defaultWriterClosedPromiseResolve(writer) {
    if (writer._closedPromise_resolve === undefined) {
        return;
    }
    writer._closedPromise_resolve(undefined);
    writer._closedPromise_resolve = undefined;
    writer._closedPromise_reject = undefined;
    writer._closedPromiseState = 'resolved';
}
function defaultWriterReadyPromiseInitialize(writer) {
    writer._readyPromise = newPromise((resolve, reject) => {
        writer._readyPromise_resolve = resolve;
        writer._readyPromise_reject = reject;
    });
    writer._readyPromiseState = 'pending';
}
function defaultWriterReadyPromiseInitializeAsRejected(writer, reason) {
    defaultWriterReadyPromiseInitialize(writer);
    defaultWriterReadyPromiseReject(writer, reason);
}
function defaultWriterReadyPromiseInitializeAsResolved(writer) {
    defaultWriterReadyPromiseInitialize(writer);
    defaultWriterReadyPromiseResolve(writer);
}
function defaultWriterReadyPromiseReject(writer, reason) {
    if (writer._readyPromise_reject === undefined) {
        return;
    }
    setPromiseIsHandledToTrue(writer._readyPromise);
    writer._readyPromise_reject(reason);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'rejected';
}
function defaultWriterReadyPromiseReset(writer) {
    defaultWriterReadyPromiseInitialize(writer);
}
function defaultWriterReadyPromiseResetToRejected(writer, reason) {
    defaultWriterReadyPromiseInitializeAsRejected(writer, reason);
}
function defaultWriterReadyPromiseResolve(writer) {
    if (writer._readyPromise_resolve === undefined) {
        return;
    }
    writer._readyPromise_resolve(undefined);
    writer._readyPromise_resolve = undefined;
    writer._readyPromise_reject = undefined;
    writer._readyPromiseState = 'fulfilled';
}

function isAbortSignal(value) {
    if (typeof value !== 'object' || value === null) {
        return false;
    }
    try {
        return typeof value.aborted === 'boolean';
    }
    catch (_a) {
        // AbortSignal.prototype.aborted throws if its brand check fails
        return false;
    }
}

/// <reference lib="dom" />
const NativeDOMException = typeof DOMException !== 'undefined' ? DOMException : undefined;

/// <reference types="node" />
function isDOMExceptionConstructor(ctor) {
    if (!(typeof ctor === 'function' || typeof ctor === 'object')) {
        return false;
    }
    try {
        new ctor();
        return true;
    }
    catch (_a) {
        return false;
    }
}
function createDOMExceptionPolyfill() {
    // eslint-disable-next-line no-shadow
    const ctor = function DOMException(message, name) {
        this.message = message || '';
        this.name = name || 'Error';
        if (Error.captureStackTrace) {
            Error.captureStackTrace(this, this.constructor);
        }
    };
    ctor.prototype = Object.create(Error.prototype);
    Object.defineProperty(ctor.prototype, 'constructor', { value: ctor, writable: true, configurable: true });
    return ctor;
}
// eslint-disable-next-line no-redeclare
const DOMException$1 = isDOMExceptionConstructor(NativeDOMException) ? NativeDOMException : createDOMExceptionPolyfill();

function ReadableStreamPipeTo(source, dest, preventClose, preventAbort, preventCancel, signal) {
    const reader = AcquireReadableStreamDefaultReader(source);
    const writer = AcquireWritableStreamDefaultWriter(dest);
    source._disturbed = true;
    let shuttingDown = false;
    // This is used to keep track of the spec's requirement that we wait for ongoing writes during shutdown.
    let currentWrite = promiseResolvedWith(undefined);
    return newPromise((resolve, reject) => {
        let abortAlgorithm;
        if (signal !== undefined) {
            abortAlgorithm = () => {
                const error = new DOMException$1('Aborted', 'AbortError');
                const actions = [];
                if (!preventAbort) {
                    actions.push(() => {
                        if (dest._state === 'writable') {
                            return WritableStreamAbort(dest, error);
                        }
                        return promiseResolvedWith(undefined);
                    });
                }
                if (!preventCancel) {
                    actions.push(() => {
                        if (source._state === 'readable') {
                            return ReadableStreamCancel(source, error);
                        }
                        return promiseResolvedWith(undefined);
                    });
                }
                shutdownWithAction(() => Promise.all(actions.map(action => action())), true, error);
            };
            if (signal.aborted) {
                abortAlgorithm();
                return;
            }
            signal.addEventListener('abort', abortAlgorithm);
        }
        // Using reader and writer, read all chunks from this and write them to dest
        // - Backpressure must be enforced
        // - Shutdown must stop all activity
        function pipeLoop() {
            return newPromise((resolveLoop, rejectLoop) => {
                function next(done) {
                    if (done) {
                        resolveLoop();
                    }
                    else {
                        // Use `PerformPromiseThen` instead of `uponPromise` to avoid
                        // adding unnecessary `.catch(rethrowAssertionErrorRejection)` handlers
                        PerformPromiseThen(pipeStep(), next, rejectLoop);
                    }
                }
                next(false);
            });
        }
        function pipeStep() {
            if (shuttingDown) {
                return promiseResolvedWith(true);
            }
            return PerformPromiseThen(writer._readyPromise, () => {
                return newPromise((resolveRead, rejectRead) => {
                    ReadableStreamDefaultReaderRead(reader, {
                        _chunkSteps: chunk => {
                            currentWrite = PerformPromiseThen(WritableStreamDefaultWriterWrite(writer, chunk), undefined, noop);
                            resolveRead(false);
                        },
                        _closeSteps: () => resolveRead(true),
                        _errorSteps: rejectRead
                    });
                });
            });
        }
        // Errors must be propagated forward
        isOrBecomesErrored(source, reader._closedPromise, storedError => {
            if (!preventAbort) {
                shutdownWithAction(() => WritableStreamAbort(dest, storedError), true, storedError);
            }
            else {
                shutdown(true, storedError);
            }
        });
        // Errors must be propagated backward
        isOrBecomesErrored(dest, writer._closedPromise, storedError => {
            if (!preventCancel) {
                shutdownWithAction(() => ReadableStreamCancel(source, storedError), true, storedError);
            }
            else {
                shutdown(true, storedError);
            }
        });
        // Closing must be propagated forward
        isOrBecomesClosed(source, reader._closedPromise, () => {
            if (!preventClose) {
                shutdownWithAction(() => WritableStreamDefaultWriterCloseWithErrorPropagation(writer));
            }
            else {
                shutdown();
            }
        });
        // Closing must be propagated backward
        if (WritableStreamCloseQueuedOrInFlight(dest) || dest._state === 'closed') {
            const destClosed = new TypeError('the destination writable stream closed before all data could be piped to it');
            if (!preventCancel) {
                shutdownWithAction(() => ReadableStreamCancel(source, destClosed), true, destClosed);
            }
            else {
                shutdown(true, destClosed);
            }
        }
        setPromiseIsHandledToTrue(pipeLoop());
        function waitForWritesToFinish() {
            // Another write may have started while we were waiting on this currentWrite, so we have to be sure to wait
            // for that too.
            const oldCurrentWrite = currentWrite;
            return PerformPromiseThen(currentWrite, () => oldCurrentWrite !== currentWrite ? waitForWritesToFinish() : undefined);
        }
        function isOrBecomesErrored(stream, promise, action) {
            if (stream._state === 'errored') {
                action(stream._storedError);
            }
            else {
                uponRejection(promise, action);
            }
        }
        function isOrBecomesClosed(stream, promise, action) {
            if (stream._state === 'closed') {
                action();
            }
            else {
                uponFulfillment(promise, action);
            }
        }
        function shutdownWithAction(action, originalIsError, originalError) {
            if (shuttingDown) {
                return;
            }
            shuttingDown = true;
            if (dest._state === 'writable' && !WritableStreamCloseQueuedOrInFlight(dest)) {
                uponFulfillment(waitForWritesToFinish(), doTheRest);
            }
            else {
                doTheRest();
            }
            function doTheRest() {
                uponPromise(action(), () => finalize(originalIsError, originalError), newError => finalize(true, newError));
            }
        }
        function shutdown(isError, error) {
            if (shuttingDown) {
                return;
            }
            shuttingDown = true;
            if (dest._state === 'writable' && !WritableStreamCloseQueuedOrInFlight(dest)) {
                uponFulfillment(waitForWritesToFinish(), () => finalize(isError, error));
            }
            else {
                finalize(isError, error);
            }
        }
        function finalize(isError, error) {
            WritableStreamDefaultWriterRelease(writer);
            ReadableStreamReaderGenericRelease(reader);
            if (signal !== undefined) {
                signal.removeEventListener('abort', abortAlgorithm);
            }
            if (isError) {
                reject(error);
            }
            else {
                resolve(undefined);
            }
        }
    });
}

/**
 * Allows control of a {@link ReadableStream | readable stream}'s state and internal queue.
 *
 * @public
 */
class ReadableStreamDefaultController {
    constructor() {
        throw new TypeError('Illegal constructor');
    }
    /**
     * Returns the desired size to fill the controlled stream's internal queue. It can be negative, if the queue is
     * over-full. An underlying source ought to use this information to determine when and how to apply backpressure.
     */
    get desiredSize() {
        if (!IsReadableStreamDefaultController(this)) {
            throw defaultControllerBrandCheckException$1('desiredSize');
        }
        return ReadableStreamDefaultControllerGetDesiredSize(this);
    }
    /**
     * Closes the controlled readable stream. Consumers will still be able to read any previously-enqueued chunks from
     * the stream, but once those are read, the stream will become closed.
     */
    close() {
        if (!IsReadableStreamDefaultController(this)) {
            throw defaultControllerBrandCheckException$1('close');
        }
        if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(this)) {
            throw new TypeError('The stream is not in a state that permits close');
        }
        ReadableStreamDefaultControllerClose(this);
    }
    enqueue(chunk = undefined) {
        if (!IsReadableStreamDefaultController(this)) {
            throw defaultControllerBrandCheckException$1('enqueue');
        }
        if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(this)) {
            throw new TypeError('The stream is not in a state that permits enqueue');
        }
        return ReadableStreamDefaultControllerEnqueue(this, chunk);
    }
    /**
     * Errors the controlled readable stream, making all future interactions with it fail with the given error `e`.
     */
    error(e = undefined) {
        if (!IsReadableStreamDefaultController(this)) {
            throw defaultControllerBrandCheckException$1('error');
        }
        ReadableStreamDefaultControllerError(this, e);
    }
    /** @internal */
    [CancelSteps](reason) {
        ResetQueue(this);
        const result = this._cancelAlgorithm(reason);
        ReadableStreamDefaultControllerClearAlgorithms(this);
        return result;
    }
    /** @internal */
    [PullSteps](readRequest) {
        const stream = this._controlledReadableStream;
        if (this._queue.length > 0) {
            const chunk = DequeueValue(this);
            if (this._closeRequested && this._queue.length === 0) {
                ReadableStreamDefaultControllerClearAlgorithms(this);
                ReadableStreamClose(stream);
            }
            else {
                ReadableStreamDefaultControllerCallPullIfNeeded(this);
            }
            readRequest._chunkSteps(chunk);
        }
        else {
            ReadableStreamAddReadRequest(stream, readRequest);
            ReadableStreamDefaultControllerCallPullIfNeeded(this);
        }
    }
}
Object.defineProperties(ReadableStreamDefaultController.prototype, {
    close: { enumerable: true },
    enqueue: { enumerable: true },
    error: { enumerable: true },
    desiredSize: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(ReadableStreamDefaultController.prototype, SymbolPolyfill.toStringTag, {
        value: 'ReadableStreamDefaultController',
        configurable: true
    });
}
// Abstract operations for the ReadableStreamDefaultController.
function IsReadableStreamDefaultController(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_controlledReadableStream')) {
        return false;
    }
    return true;
}
function ReadableStreamDefaultControllerCallPullIfNeeded(controller) {
    const shouldPull = ReadableStreamDefaultControllerShouldCallPull(controller);
    if (!shouldPull) {
        return;
    }
    if (controller._pulling) {
        controller._pullAgain = true;
        return;
    }
    controller._pulling = true;
    const pullPromise = controller._pullAlgorithm();
    uponPromise(pullPromise, () => {
        controller._pulling = false;
        if (controller._pullAgain) {
            controller._pullAgain = false;
            ReadableStreamDefaultControllerCallPullIfNeeded(controller);
        }
    }, e => {
        ReadableStreamDefaultControllerError(controller, e);
    });
}
function ReadableStreamDefaultControllerShouldCallPull(controller) {
    const stream = controller._controlledReadableStream;
    if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)) {
        return false;
    }
    if (!controller._started) {
        return false;
    }
    if (IsReadableStreamLocked(stream) && ReadableStreamGetNumReadRequests(stream) > 0) {
        return true;
    }
    const desiredSize = ReadableStreamDefaultControllerGetDesiredSize(controller);
    if (desiredSize > 0) {
        return true;
    }
    return false;
}
function ReadableStreamDefaultControllerClearAlgorithms(controller) {
    controller._pullAlgorithm = undefined;
    controller._cancelAlgorithm = undefined;
    controller._strategySizeAlgorithm = undefined;
}
// A client of ReadableStreamDefaultController may use these functions directly to bypass state check.
function ReadableStreamDefaultControllerClose(controller) {
    if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)) {
        return;
    }
    const stream = controller._controlledReadableStream;
    controller._closeRequested = true;
    if (controller._queue.length === 0) {
        ReadableStreamDefaultControllerClearAlgorithms(controller);
        ReadableStreamClose(stream);
    }
}
function ReadableStreamDefaultControllerEnqueue(controller, chunk) {
    if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)) {
        return;
    }
    const stream = controller._controlledReadableStream;
    if (IsReadableStreamLocked(stream) && ReadableStreamGetNumReadRequests(stream) > 0) {
        ReadableStreamFulfillReadRequest(stream, chunk, false);
    }
    else {
        let chunkSize;
        try {
            chunkSize = controller._strategySizeAlgorithm(chunk);
        }
        catch (chunkSizeE) {
            ReadableStreamDefaultControllerError(controller, chunkSizeE);
            throw chunkSizeE;
        }
        try {
            EnqueueValueWithSize(controller, chunk, chunkSize);
        }
        catch (enqueueE) {
            ReadableStreamDefaultControllerError(controller, enqueueE);
            throw enqueueE;
        }
    }
    ReadableStreamDefaultControllerCallPullIfNeeded(controller);
}
function ReadableStreamDefaultControllerError(controller, e) {
    const stream = controller._controlledReadableStream;
    if (stream._state !== 'readable') {
        return;
    }
    ResetQueue(controller);
    ReadableStreamDefaultControllerClearAlgorithms(controller);
    ReadableStreamError(stream, e);
}
function ReadableStreamDefaultControllerGetDesiredSize(controller) {
    const state = controller._controlledReadableStream._state;
    if (state === 'errored') {
        return null;
    }
    if (state === 'closed') {
        return 0;
    }
    return controller._strategyHWM - controller._queueTotalSize;
}
// This is used in the implementation of TransformStream.
function ReadableStreamDefaultControllerHasBackpressure(controller) {
    if (ReadableStreamDefaultControllerShouldCallPull(controller)) {
        return false;
    }
    return true;
}
function ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) {
    const state = controller._controlledReadableStream._state;
    if (!controller._closeRequested && state === 'readable') {
        return true;
    }
    return false;
}
function SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm) {
    controller._controlledReadableStream = stream;
    controller._queue = undefined;
    controller._queueTotalSize = undefined;
    ResetQueue(controller);
    controller._started = false;
    controller._closeRequested = false;
    controller._pullAgain = false;
    controller._pulling = false;
    controller._strategySizeAlgorithm = sizeAlgorithm;
    controller._strategyHWM = highWaterMark;
    controller._pullAlgorithm = pullAlgorithm;
    controller._cancelAlgorithm = cancelAlgorithm;
    stream._readableStreamController = controller;
    const startResult = startAlgorithm();
    uponPromise(promiseResolvedWith(startResult), () => {
        controller._started = true;
        ReadableStreamDefaultControllerCallPullIfNeeded(controller);
    }, r => {
        ReadableStreamDefaultControllerError(controller, r);
    });
}
function SetUpReadableStreamDefaultControllerFromUnderlyingSource(stream, underlyingSource, highWaterMark, sizeAlgorithm) {
    const controller = Object.create(ReadableStreamDefaultController.prototype);
    let startAlgorithm = () => undefined;
    let pullAlgorithm = () => promiseResolvedWith(undefined);
    let cancelAlgorithm = () => promiseResolvedWith(undefined);
    if (underlyingSource.start !== undefined) {
        startAlgorithm = () => underlyingSource.start(controller);
    }
    if (underlyingSource.pull !== undefined) {
        pullAlgorithm = () => underlyingSource.pull(controller);
    }
    if (underlyingSource.cancel !== undefined) {
        cancelAlgorithm = reason => underlyingSource.cancel(reason);
    }
    SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm);
}
// Helper functions for the ReadableStreamDefaultController.
function defaultControllerBrandCheckException$1(name) {
    return new TypeError(`ReadableStreamDefaultController.prototype.${name} can only be used on a ReadableStreamDefaultController`);
}

function ReadableStreamTee(stream, cloneForBranch2) {
    const reader = AcquireReadableStreamDefaultReader(stream);
    let reading = false;
    let canceled1 = false;
    let canceled2 = false;
    let reason1;
    let reason2;
    let branch1;
    let branch2;
    let resolveCancelPromise;
    const cancelPromise = newPromise(resolve => {
        resolveCancelPromise = resolve;
    });
    function pullAlgorithm() {
        if (reading) {
            return promiseResolvedWith(undefined);
        }
        reading = true;
        const readRequest = {
            _chunkSteps: value => {
                // This needs to be delayed a microtask because it takes at least a microtask to detect errors (using
                // reader._closedPromise below), and we want errors in stream to error both branches immediately. We cannot let
                // successful synchronously-available reads get ahead of asynchronously-available errors.
                queueMicrotask(() => {
                    reading = false;
                    const value1 = value;
                    const value2 = value;
                    // There is no way to access the cloning code right now in the reference implementation.
                    // If we add one then we'll need an implementation for serializable objects.
                    // if (!canceled2 && cloneForBranch2) {
                    //   value2 = StructuredDeserialize(StructuredSerialize(value2));
                    // }
                    if (!canceled1) {
                        ReadableStreamDefaultControllerEnqueue(branch1._readableStreamController, value1);
                    }
                    if (!canceled2) {
                        ReadableStreamDefaultControllerEnqueue(branch2._readableStreamController, value2);
                    }
                });
            },
            _closeSteps: () => {
                reading = false;
                if (!canceled1) {
                    ReadableStreamDefaultControllerClose(branch1._readableStreamController);
                }
                if (!canceled2) {
                    ReadableStreamDefaultControllerClose(branch2._readableStreamController);
                }
                if (!canceled1 || !canceled2) {
                    resolveCancelPromise(undefined);
                }
            },
            _errorSteps: () => {
                reading = false;
            }
        };
        ReadableStreamDefaultReaderRead(reader, readRequest);
        return promiseResolvedWith(undefined);
    }
    function cancel1Algorithm(reason) {
        canceled1 = true;
        reason1 = reason;
        if (canceled2) {
            const compositeReason = CreateArrayFromList([reason1, reason2]);
            const cancelResult = ReadableStreamCancel(stream, compositeReason);
            resolveCancelPromise(cancelResult);
        }
        return cancelPromise;
    }
    function cancel2Algorithm(reason) {
        canceled2 = true;
        reason2 = reason;
        if (canceled1) {
            const compositeReason = CreateArrayFromList([reason1, reason2]);
            const cancelResult = ReadableStreamCancel(stream, compositeReason);
            resolveCancelPromise(cancelResult);
        }
        return cancelPromise;
    }
    function startAlgorithm() {
        // do nothing
    }
    branch1 = CreateReadableStream(startAlgorithm, pullAlgorithm, cancel1Algorithm);
    branch2 = CreateReadableStream(startAlgorithm, pullAlgorithm, cancel2Algorithm);
    uponRejection(reader._closedPromise, (r) => {
        ReadableStreamDefaultControllerError(branch1._readableStreamController, r);
        ReadableStreamDefaultControllerError(branch2._readableStreamController, r);
        if (!canceled1 || !canceled2) {
            resolveCancelPromise(undefined);
        }
    });
    return [branch1, branch2];
}

function convertUnderlyingDefaultOrByteSource(source, context) {
    assertDictionary(source, context);
    const original = source;
    const autoAllocateChunkSize = original === null || original === void 0 ? void 0 : original.autoAllocateChunkSize;
    const cancel = original === null || original === void 0 ? void 0 : original.cancel;
    const pull = original === null || original === void 0 ? void 0 : original.pull;
    const start = original === null || original === void 0 ? void 0 : original.start;
    const type = original === null || original === void 0 ? void 0 : original.type;
    return {
        autoAllocateChunkSize: autoAllocateChunkSize === undefined ?
            undefined :
            convertUnsignedLongLongWithEnforceRange(autoAllocateChunkSize, `${context} has member 'autoAllocateChunkSize' that`),
        cancel: cancel === undefined ?
            undefined :
            convertUnderlyingSourceCancelCallback(cancel, original, `${context} has member 'cancel' that`),
        pull: pull === undefined ?
            undefined :
            convertUnderlyingSourcePullCallback(pull, original, `${context} has member 'pull' that`),
        start: start === undefined ?
            undefined :
            convertUnderlyingSourceStartCallback(start, original, `${context} has member 'start' that`),
        type: type === undefined ? undefined : convertReadableStreamType(type, `${context} has member 'type' that`)
    };
}
function convertUnderlyingSourceCancelCallback(fn, original, context) {
    assertFunction(fn, context);
    return (reason) => promiseCall(fn, original, [reason]);
}
function convertUnderlyingSourcePullCallback(fn, original, context) {
    assertFunction(fn, context);
    return (controller) => promiseCall(fn, original, [controller]);
}
function convertUnderlyingSourceStartCallback(fn, original, context) {
    assertFunction(fn, context);
    return (controller) => reflectCall(fn, original, [controller]);
}
function convertReadableStreamType(type, context) {
    type = `${type}`;
    if (type !== 'bytes') {
        throw new TypeError(`${context} '${type}' is not a valid enumeration value for ReadableStreamType`);
    }
    return type;
}

function convertReaderOptions(options, context) {
    assertDictionary(options, context);
    const mode = options === null || options === void 0 ? void 0 : options.mode;
    return {
        mode: mode === undefined ? undefined : convertReadableStreamReaderMode(mode, `${context} has member 'mode' that`)
    };
}
function convertReadableStreamReaderMode(mode, context) {
    mode = `${mode}`;
    if (mode !== 'byob') {
        throw new TypeError(`${context} '${mode}' is not a valid enumeration value for ReadableStreamReaderMode`);
    }
    return mode;
}

function convertIteratorOptions(options, context) {
    assertDictionary(options, context);
    const preventCancel = options === null || options === void 0 ? void 0 : options.preventCancel;
    return { preventCancel: Boolean(preventCancel) };
}

function convertPipeOptions(options, context) {
    assertDictionary(options, context);
    const preventAbort = options === null || options === void 0 ? void 0 : options.preventAbort;
    const preventCancel = options === null || options === void 0 ? void 0 : options.preventCancel;
    const preventClose = options === null || options === void 0 ? void 0 : options.preventClose;
    const signal = options === null || options === void 0 ? void 0 : options.signal;
    if (signal !== undefined) {
        assertAbortSignal(signal, `${context} has member 'signal' that`);
    }
    return {
        preventAbort: Boolean(preventAbort),
        preventCancel: Boolean(preventCancel),
        preventClose: Boolean(preventClose),
        signal
    };
}
function assertAbortSignal(signal, context) {
    if (!isAbortSignal(signal)) {
        throw new TypeError(`${context} is not an AbortSignal.`);
    }
}

function convertReadableWritablePair(pair, context) {
    assertDictionary(pair, context);
    const readable = pair === null || pair === void 0 ? void 0 : pair.readable;
    assertRequiredField(readable, 'readable', 'ReadableWritablePair');
    assertReadableStream(readable, `${context} has member 'readable' that`);
    const writable = pair === null || pair === void 0 ? void 0 : pair.writable;
    assertRequiredField(writable, 'writable', 'ReadableWritablePair');
    assertWritableStream(writable, `${context} has member 'writable' that`);
    return { readable, writable };
}

/**
 * A readable stream represents a source of data, from which you can read.
 *
 * @public
 */
class ReadableStream$1 {
    constructor(rawUnderlyingSource = {}, rawStrategy = {}) {
        if (rawUnderlyingSource === undefined) {
            rawUnderlyingSource = null;
        }
        else {
            assertObject(rawUnderlyingSource, 'First parameter');
        }
        const strategy = convertQueuingStrategy(rawStrategy, 'Second parameter');
        const underlyingSource = convertUnderlyingDefaultOrByteSource(rawUnderlyingSource, 'First parameter');
        InitializeReadableStream(this);
        if (underlyingSource.type === 'bytes') {
            if (strategy.size !== undefined) {
                throw new RangeError('The strategy for a byte stream cannot have a size function');
            }
            const highWaterMark = ExtractHighWaterMark(strategy, 0);
            SetUpReadableByteStreamControllerFromUnderlyingSource(this, underlyingSource, highWaterMark);
        }
        else {
            const sizeAlgorithm = ExtractSizeAlgorithm(strategy);
            const highWaterMark = ExtractHighWaterMark(strategy, 1);
            SetUpReadableStreamDefaultControllerFromUnderlyingSource(this, underlyingSource, highWaterMark, sizeAlgorithm);
        }
    }
    /**
     * Whether or not the readable stream is locked to a {@link ReadableStreamDefaultReader | reader}.
     */
    get locked() {
        if (!IsReadableStream(this)) {
            throw streamBrandCheckException$1('locked');
        }
        return IsReadableStreamLocked(this);
    }
    /**
     * Cancels the stream, signaling a loss of interest in the stream by a consumer.
     *
     * The supplied `reason` argument will be given to the underlying source's {@link UnderlyingSource.cancel | cancel()}
     * method, which might or might not use it.
     */
    cancel(reason = undefined) {
        if (!IsReadableStream(this)) {
            return promiseRejectedWith(streamBrandCheckException$1('cancel'));
        }
        if (IsReadableStreamLocked(this)) {
            return promiseRejectedWith(new TypeError('Cannot cancel a stream that already has a reader'));
        }
        return ReadableStreamCancel(this, reason);
    }
    getReader(rawOptions = undefined) {
        if (!IsReadableStream(this)) {
            throw streamBrandCheckException$1('getReader');
        }
        const options = convertReaderOptions(rawOptions, 'First parameter');
        if (options.mode === undefined) {
            return AcquireReadableStreamDefaultReader(this);
        }
        return AcquireReadableStreamBYOBReader(this);
    }
    pipeThrough(rawTransform, rawOptions = {}) {
        if (!IsReadableStream(this)) {
            throw streamBrandCheckException$1('pipeThrough');
        }
        assertRequiredArgument(rawTransform, 1, 'pipeThrough');
        const transform = convertReadableWritablePair(rawTransform, 'First parameter');
        const options = convertPipeOptions(rawOptions, 'Second parameter');
        if (IsReadableStreamLocked(this)) {
            throw new TypeError('ReadableStream.prototype.pipeThrough cannot be used on a locked ReadableStream');
        }
        if (IsWritableStreamLocked(transform.writable)) {
            throw new TypeError('ReadableStream.prototype.pipeThrough cannot be used on a locked WritableStream');
        }
        const promise = ReadableStreamPipeTo(this, transform.writable, options.preventClose, options.preventAbort, options.preventCancel, options.signal);
        setPromiseIsHandledToTrue(promise);
        return transform.readable;
    }
    pipeTo(destination, rawOptions = {}) {
        if (!IsReadableStream(this)) {
            return promiseRejectedWith(streamBrandCheckException$1('pipeTo'));
        }
        if (destination === undefined) {
            return promiseRejectedWith(`Parameter 1 is required in 'pipeTo'.`);
        }
        if (!IsWritableStream(destination)) {
            return promiseRejectedWith(new TypeError(`ReadableStream.prototype.pipeTo's first argument must be a WritableStream`));
        }
        let options;
        try {
            options = convertPipeOptions(rawOptions, 'Second parameter');
        }
        catch (e) {
            return promiseRejectedWith(e);
        }
        if (IsReadableStreamLocked(this)) {
            return promiseRejectedWith(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked ReadableStream'));
        }
        if (IsWritableStreamLocked(destination)) {
            return promiseRejectedWith(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked WritableStream'));
        }
        return ReadableStreamPipeTo(this, destination, options.preventClose, options.preventAbort, options.preventCancel, options.signal);
    }
    /**
     * Tees this readable stream, returning a two-element array containing the two resulting branches as
     * new {@link ReadableStream} instances.
     *
     * Teeing a stream will lock it, preventing any other consumer from acquiring a reader.
     * To cancel the stream, cancel both of the resulting branches; a composite cancellation reason will then be
     * propagated to the stream's underlying source.
     *
     * Note that the chunks seen in each branch will be the same object. If the chunks are not immutable,
     * this could allow interference between the two branches.
     */
    tee() {
        if (!IsReadableStream(this)) {
            throw streamBrandCheckException$1('tee');
        }
        const branches = ReadableStreamTee(this);
        return CreateArrayFromList(branches);
    }
    values(rawOptions = undefined) {
        if (!IsReadableStream(this)) {
            throw streamBrandCheckException$1('values');
        }
        const options = convertIteratorOptions(rawOptions, 'First parameter');
        return AcquireReadableStreamAsyncIterator(this, options.preventCancel);
    }
}
Object.defineProperties(ReadableStream$1.prototype, {
    cancel: { enumerable: true },
    getReader: { enumerable: true },
    pipeThrough: { enumerable: true },
    pipeTo: { enumerable: true },
    tee: { enumerable: true },
    values: { enumerable: true },
    locked: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(ReadableStream$1.prototype, SymbolPolyfill.toStringTag, {
        value: 'ReadableStream',
        configurable: true
    });
}
if (typeof SymbolPolyfill.asyncIterator === 'symbol') {
    Object.defineProperty(ReadableStream$1.prototype, SymbolPolyfill.asyncIterator, {
        value: ReadableStream$1.prototype.values,
        writable: true,
        configurable: true
    });
}
// Abstract operations for the ReadableStream.
// Throws if and only if startAlgorithm throws.
function CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark = 1, sizeAlgorithm = () => 1) {
    const stream = Object.create(ReadableStream$1.prototype);
    InitializeReadableStream(stream);
    const controller = Object.create(ReadableStreamDefaultController.prototype);
    SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm);
    return stream;
}
function InitializeReadableStream(stream) {
    stream._state = 'readable';
    stream._reader = undefined;
    stream._storedError = undefined;
    stream._disturbed = false;
}
function IsReadableStream(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_readableStreamController')) {
        return false;
    }
    return true;
}
function IsReadableStreamLocked(stream) {
    if (stream._reader === undefined) {
        return false;
    }
    return true;
}
// ReadableStream API exposed for controllers.
function ReadableStreamCancel(stream, reason) {
    stream._disturbed = true;
    if (stream._state === 'closed') {
        return promiseResolvedWith(undefined);
    }
    if (stream._state === 'errored') {
        return promiseRejectedWith(stream._storedError);
    }
    ReadableStreamClose(stream);
    const sourceCancelPromise = stream._readableStreamController[CancelSteps](reason);
    return transformPromiseWith(sourceCancelPromise, noop);
}
function ReadableStreamClose(stream) {
    stream._state = 'closed';
    const reader = stream._reader;
    if (reader === undefined) {
        return;
    }
    defaultReaderClosedPromiseResolve(reader);
    if (IsReadableStreamDefaultReader(reader)) {
        reader._readRequests.forEach(readRequest => {
            readRequest._closeSteps();
        });
        reader._readRequests = new SimpleQueue();
    }
}
function ReadableStreamError(stream, e) {
    stream._state = 'errored';
    stream._storedError = e;
    const reader = stream._reader;
    if (reader === undefined) {
        return;
    }
    defaultReaderClosedPromiseReject(reader, e);
    if (IsReadableStreamDefaultReader(reader)) {
        reader._readRequests.forEach(readRequest => {
            readRequest._errorSteps(e);
        });
        reader._readRequests = new SimpleQueue();
    }
    else {
        reader._readIntoRequests.forEach(readIntoRequest => {
            readIntoRequest._errorSteps(e);
        });
        reader._readIntoRequests = new SimpleQueue();
    }
}
// Helper functions for the ReadableStream.
function streamBrandCheckException$1(name) {
    return new TypeError(`ReadableStream.prototype.${name} can only be used on a ReadableStream`);
}

function convertQueuingStrategyInit(init, context) {
    assertDictionary(init, context);
    const highWaterMark = init === null || init === void 0 ? void 0 : init.highWaterMark;
    assertRequiredField(highWaterMark, 'highWaterMark', 'QueuingStrategyInit');
    return {
        highWaterMark: convertUnrestrictedDouble(highWaterMark)
    };
}

const byteLengthSizeFunction = function size(chunk) {
    return chunk.byteLength;
};
/**
 * A queuing strategy that counts the number of bytes in each chunk.
 *
 * @public
 */
class ByteLengthQueuingStrategy {
    constructor(options) {
        assertRequiredArgument(options, 1, 'ByteLengthQueuingStrategy');
        options = convertQueuingStrategyInit(options, 'First parameter');
        this._byteLengthQueuingStrategyHighWaterMark = options.highWaterMark;
    }
    /**
     * Returns the high water mark provided to the constructor.
     */
    get highWaterMark() {
        if (!IsByteLengthQueuingStrategy(this)) {
            throw byteLengthBrandCheckException('highWaterMark');
        }
        return this._byteLengthQueuingStrategyHighWaterMark;
    }
    /**
     * Measures the size of `chunk` by returning the value of its `byteLength` property.
     */
    get size() {
        if (!IsByteLengthQueuingStrategy(this)) {
            throw byteLengthBrandCheckException('size');
        }
        return byteLengthSizeFunction;
    }
}
Object.defineProperties(ByteLengthQueuingStrategy.prototype, {
    highWaterMark: { enumerable: true },
    size: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(ByteLengthQueuingStrategy.prototype, SymbolPolyfill.toStringTag, {
        value: 'ByteLengthQueuingStrategy',
        configurable: true
    });
}
// Helper functions for the ByteLengthQueuingStrategy.
function byteLengthBrandCheckException(name) {
    return new TypeError(`ByteLengthQueuingStrategy.prototype.${name} can only be used on a ByteLengthQueuingStrategy`);
}
function IsByteLengthQueuingStrategy(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_byteLengthQueuingStrategyHighWaterMark')) {
        return false;
    }
    return true;
}

const countSizeFunction = function size() {
    return 1;
};
/**
 * A queuing strategy that counts the number of chunks.
 *
 * @public
 */
class CountQueuingStrategy {
    constructor(options) {
        assertRequiredArgument(options, 1, 'CountQueuingStrategy');
        options = convertQueuingStrategyInit(options, 'First parameter');
        this._countQueuingStrategyHighWaterMark = options.highWaterMark;
    }
    /**
     * Returns the high water mark provided to the constructor.
     */
    get highWaterMark() {
        if (!IsCountQueuingStrategy(this)) {
            throw countBrandCheckException('highWaterMark');
        }
        return this._countQueuingStrategyHighWaterMark;
    }
    /**
     * Measures the size of `chunk` by always returning 1.
     * This ensures that the total queue size is a count of the number of chunks in the queue.
     */
    get size() {
        if (!IsCountQueuingStrategy(this)) {
            throw countBrandCheckException('size');
        }
        return countSizeFunction;
    }
}
Object.defineProperties(CountQueuingStrategy.prototype, {
    highWaterMark: { enumerable: true },
    size: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(CountQueuingStrategy.prototype, SymbolPolyfill.toStringTag, {
        value: 'CountQueuingStrategy',
        configurable: true
    });
}
// Helper functions for the CountQueuingStrategy.
function countBrandCheckException(name) {
    return new TypeError(`CountQueuingStrategy.prototype.${name} can only be used on a CountQueuingStrategy`);
}
function IsCountQueuingStrategy(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_countQueuingStrategyHighWaterMark')) {
        return false;
    }
    return true;
}

function convertTransformer(original, context) {
    assertDictionary(original, context);
    const flush = original === null || original === void 0 ? void 0 : original.flush;
    const readableType = original === null || original === void 0 ? void 0 : original.readableType;
    const start = original === null || original === void 0 ? void 0 : original.start;
    const transform = original === null || original === void 0 ? void 0 : original.transform;
    const writableType = original === null || original === void 0 ? void 0 : original.writableType;
    return {
        flush: flush === undefined ?
            undefined :
            convertTransformerFlushCallback(flush, original, `${context} has member 'flush' that`),
        readableType,
        start: start === undefined ?
            undefined :
            convertTransformerStartCallback(start, original, `${context} has member 'start' that`),
        transform: transform === undefined ?
            undefined :
            convertTransformerTransformCallback(transform, original, `${context} has member 'transform' that`),
        writableType
    };
}
function convertTransformerFlushCallback(fn, original, context) {
    assertFunction(fn, context);
    return (controller) => promiseCall(fn, original, [controller]);
}
function convertTransformerStartCallback(fn, original, context) {
    assertFunction(fn, context);
    return (controller) => reflectCall(fn, original, [controller]);
}
function convertTransformerTransformCallback(fn, original, context) {
    assertFunction(fn, context);
    return (chunk, controller) => promiseCall(fn, original, [chunk, controller]);
}

// Class TransformStream
/**
 * A transform stream consists of a pair of streams: a {@link WritableStream | writable stream},
 * known as its writable side, and a {@link ReadableStream | readable stream}, known as its readable side.
 * In a manner specific to the transform stream in question, writes to the writable side result in new data being
 * made available for reading from the readable side.
 *
 * @public
 */
class TransformStream$1 {
    constructor(rawTransformer = {}, rawWritableStrategy = {}, rawReadableStrategy = {}) {
        if (rawTransformer === undefined) {
            rawTransformer = null;
        }
        const writableStrategy = convertQueuingStrategy(rawWritableStrategy, 'Second parameter');
        const readableStrategy = convertQueuingStrategy(rawReadableStrategy, 'Third parameter');
        const transformer = convertTransformer(rawTransformer, 'First parameter');
        if (transformer.readableType !== undefined) {
            throw new RangeError('Invalid readableType specified');
        }
        if (transformer.writableType !== undefined) {
            throw new RangeError('Invalid writableType specified');
        }
        const readableHighWaterMark = ExtractHighWaterMark(readableStrategy, 0);
        const readableSizeAlgorithm = ExtractSizeAlgorithm(readableStrategy);
        const writableHighWaterMark = ExtractHighWaterMark(writableStrategy, 1);
        const writableSizeAlgorithm = ExtractSizeAlgorithm(writableStrategy);
        let startPromise_resolve;
        const startPromise = newPromise(resolve => {
            startPromise_resolve = resolve;
        });
        InitializeTransformStream(this, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm);
        SetUpTransformStreamDefaultControllerFromTransformer(this, transformer);
        if (transformer.start !== undefined) {
            startPromise_resolve(transformer.start(this._transformStreamController));
        }
        else {
            startPromise_resolve(undefined);
        }
    }
    /**
     * The readable side of the transform stream.
     */
    get readable() {
        if (!IsTransformStream(this)) {
            throw streamBrandCheckException('readable');
        }
        return this._readable;
    }
    /**
     * The writable side of the transform stream.
     */
    get writable() {
        if (!IsTransformStream(this)) {
            throw streamBrandCheckException('writable');
        }
        return this._writable;
    }
}
Object.defineProperties(TransformStream$1.prototype, {
    readable: { enumerable: true },
    writable: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(TransformStream$1.prototype, SymbolPolyfill.toStringTag, {
        value: 'TransformStream',
        configurable: true
    });
}
function InitializeTransformStream(stream, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm) {
    function startAlgorithm() {
        return startPromise;
    }
    function writeAlgorithm(chunk) {
        return TransformStreamDefaultSinkWriteAlgorithm(stream, chunk);
    }
    function abortAlgorithm(reason) {
        return TransformStreamDefaultSinkAbortAlgorithm(stream, reason);
    }
    function closeAlgorithm() {
        return TransformStreamDefaultSinkCloseAlgorithm(stream);
    }
    stream._writable = CreateWritableStream(startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark, writableSizeAlgorithm);
    function pullAlgorithm() {
        return TransformStreamDefaultSourcePullAlgorithm(stream);
    }
    function cancelAlgorithm(reason) {
        TransformStreamErrorWritableAndUnblockWrite(stream, reason);
        return promiseResolvedWith(undefined);
    }
    stream._readable = CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, readableHighWaterMark, readableSizeAlgorithm);
    // The [[backpressure]] slot is set to undefined so that it can be initialised by TransformStreamSetBackpressure.
    stream._backpressure = undefined;
    stream._backpressureChangePromise = undefined;
    stream._backpressureChangePromise_resolve = undefined;
    TransformStreamSetBackpressure(stream, true);
    stream._transformStreamController = undefined;
}
function IsTransformStream(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_transformStreamController')) {
        return false;
    }
    return true;
}
// This is a no-op if both sides are already errored.
function TransformStreamError(stream, e) {
    ReadableStreamDefaultControllerError(stream._readable._readableStreamController, e);
    TransformStreamErrorWritableAndUnblockWrite(stream, e);
}
function TransformStreamErrorWritableAndUnblockWrite(stream, e) {
    TransformStreamDefaultControllerClearAlgorithms(stream._transformStreamController);
    WritableStreamDefaultControllerErrorIfNeeded(stream._writable._writableStreamController, e);
    if (stream._backpressure) {
        // Pretend that pull() was called to permit any pending write() calls to complete. TransformStreamSetBackpressure()
        // cannot be called from enqueue() or pull() once the ReadableStream is errored, so this will will be the final time
        // _backpressure is set.
        TransformStreamSetBackpressure(stream, false);
    }
}
function TransformStreamSetBackpressure(stream, backpressure) {
    // Passes also when called during construction.
    if (stream._backpressureChangePromise !== undefined) {
        stream._backpressureChangePromise_resolve();
    }
    stream._backpressureChangePromise = newPromise(resolve => {
        stream._backpressureChangePromise_resolve = resolve;
    });
    stream._backpressure = backpressure;
}
// Class TransformStreamDefaultController
/**
 * Allows control of the {@link ReadableStream} and {@link WritableStream} of the associated {@link TransformStream}.
 *
 * @public
 */
class TransformStreamDefaultController {
    constructor() {
        throw new TypeError('Illegal constructor');
    }
    /**
     * Returns the desired size to fill the readable side’s internal queue. It can be negative, if the queue is over-full.
     */
    get desiredSize() {
        if (!IsTransformStreamDefaultController(this)) {
            throw defaultControllerBrandCheckException('desiredSize');
        }
        const readableController = this._controlledTransformStream._readable._readableStreamController;
        return ReadableStreamDefaultControllerGetDesiredSize(readableController);
    }
    enqueue(chunk = undefined) {
        if (!IsTransformStreamDefaultController(this)) {
            throw defaultControllerBrandCheckException('enqueue');
        }
        TransformStreamDefaultControllerEnqueue(this, chunk);
    }
    /**
     * Errors both the readable side and the writable side of the controlled transform stream, making all future
     * interactions with it fail with the given error `e`. Any chunks queued for transformation will be discarded.
     */
    error(reason = undefined) {
        if (!IsTransformStreamDefaultController(this)) {
            throw defaultControllerBrandCheckException('error');
        }
        TransformStreamDefaultControllerError(this, reason);
    }
    /**
     * Closes the readable side and errors the writable side of the controlled transform stream. This is useful when the
     * transformer only needs to consume a portion of the chunks written to the writable side.
     */
    terminate() {
        if (!IsTransformStreamDefaultController(this)) {
            throw defaultControllerBrandCheckException('terminate');
        }
        TransformStreamDefaultControllerTerminate(this);
    }
}
Object.defineProperties(TransformStreamDefaultController.prototype, {
    enqueue: { enumerable: true },
    error: { enumerable: true },
    terminate: { enumerable: true },
    desiredSize: { enumerable: true }
});
if (typeof SymbolPolyfill.toStringTag === 'symbol') {
    Object.defineProperty(TransformStreamDefaultController.prototype, SymbolPolyfill.toStringTag, {
        value: 'TransformStreamDefaultController',
        configurable: true
    });
}
// Transform Stream Default Controller Abstract Operations
function IsTransformStreamDefaultController(x) {
    if (!typeIsObject(x)) {
        return false;
    }
    if (!Object.prototype.hasOwnProperty.call(x, '_controlledTransformStream')) {
        return false;
    }
    return true;
}
function SetUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm) {
    controller._controlledTransformStream = stream;
    stream._transformStreamController = controller;
    controller._transformAlgorithm = transformAlgorithm;
    controller._flushAlgorithm = flushAlgorithm;
}
function SetUpTransformStreamDefaultControllerFromTransformer(stream, transformer) {
    const controller = Object.create(TransformStreamDefaultController.prototype);
    let transformAlgorithm = (chunk) => {
        try {
            TransformStreamDefaultControllerEnqueue(controller, chunk);
            return promiseResolvedWith(undefined);
        }
        catch (transformResultE) {
            return promiseRejectedWith(transformResultE);
        }
    };
    let flushAlgorithm = () => promiseResolvedWith(undefined);
    if (transformer.transform !== undefined) {
        transformAlgorithm = chunk => transformer.transform(chunk, controller);
    }
    if (transformer.flush !== undefined) {
        flushAlgorithm = () => transformer.flush(controller);
    }
    SetUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm);
}
function TransformStreamDefaultControllerClearAlgorithms(controller) {
    controller._transformAlgorithm = undefined;
    controller._flushAlgorithm = undefined;
}
function TransformStreamDefaultControllerEnqueue(controller, chunk) {
    const stream = controller._controlledTransformStream;
    const readableController = stream._readable._readableStreamController;
    if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController)) {
        throw new TypeError('Readable side is not in a state that permits enqueue');
    }
    // We throttle transform invocations based on the backpressure of the ReadableStream, but we still
    // accept TransformStreamDefaultControllerEnqueue() calls.
    try {
        ReadableStreamDefaultControllerEnqueue(readableController, chunk);
    }
    catch (e) {
        // This happens when readableStrategy.size() throws.
        TransformStreamErrorWritableAndUnblockWrite(stream, e);
        throw stream._readable._storedError;
    }
    const backpressure = ReadableStreamDefaultControllerHasBackpressure(readableController);
    if (backpressure !== stream._backpressure) {
        TransformStreamSetBackpressure(stream, true);
    }
}
function TransformStreamDefaultControllerError(controller, e) {
    TransformStreamError(controller._controlledTransformStream, e);
}
function TransformStreamDefaultControllerPerformTransform(controller, chunk) {
    const transformPromise = controller._transformAlgorithm(chunk);
    return transformPromiseWith(transformPromise, undefined, r => {
        TransformStreamError(controller._controlledTransformStream, r);
        throw r;
    });
}
function TransformStreamDefaultControllerTerminate(controller) {
    const stream = controller._controlledTransformStream;
    const readableController = stream._readable._readableStreamController;
    ReadableStreamDefaultControllerClose(readableController);
    const error = new TypeError('TransformStream terminated');
    TransformStreamErrorWritableAndUnblockWrite(stream, error);
}
// TransformStreamDefaultSink Algorithms
function TransformStreamDefaultSinkWriteAlgorithm(stream, chunk) {
    const controller = stream._transformStreamController;
    if (stream._backpressure) {
        const backpressureChangePromise = stream._backpressureChangePromise;
        return transformPromiseWith(backpressureChangePromise, () => {
            const writable = stream._writable;
            const state = writable._state;
            if (state === 'erroring') {
                throw writable._storedError;
            }
            return TransformStreamDefaultControllerPerformTransform(controller, chunk);
        });
    }
    return TransformStreamDefaultControllerPerformTransform(controller, chunk);
}
function TransformStreamDefaultSinkAbortAlgorithm(stream, reason) {
    // abort() is not called synchronously, so it is possible for abort() to be called when the stream is already
    // errored.
    TransformStreamError(stream, reason);
    return promiseResolvedWith(undefined);
}
function TransformStreamDefaultSinkCloseAlgorithm(stream) {
    // stream._readable cannot change after construction, so caching it across a call to user code is safe.
    const readable = stream._readable;
    const controller = stream._transformStreamController;
    const flushPromise = controller._flushAlgorithm();
    TransformStreamDefaultControllerClearAlgorithms(controller);
    // Return a promise that is fulfilled with undefined on success.
    return transformPromiseWith(flushPromise, () => {
        if (readable._state === 'errored') {
            throw readable._storedError;
        }
        ReadableStreamDefaultControllerClose(readable._readableStreamController);
    }, r => {
        TransformStreamError(stream, r);
        throw readable._storedError;
    });
}
// TransformStreamDefaultSource Algorithms
function TransformStreamDefaultSourcePullAlgorithm(stream) {
    // Invariant. Enforced by the promises returned by start() and pull().
    TransformStreamSetBackpressure(stream, false);
    // Prevent the next pull() call until there is backpressure.
    return stream._backpressureChangePromise;
}
// Helper functions for the TransformStreamDefaultController.
function defaultControllerBrandCheckException(name) {
    return new TypeError(`TransformStreamDefaultController.prototype.${name} can only be used on a TransformStreamDefaultController`);
}
// Helper functions for the TransformStream.
function streamBrandCheckException(name) {
    return new TypeError(`TransformStream.prototype.${name} can only be used on a TransformStream`);
}

var ponyfill_es6 = /*#__PURE__*/Object.freeze({
  __proto__: null,
  ByteLengthQueuingStrategy: ByteLengthQueuingStrategy,
  CountQueuingStrategy: CountQueuingStrategy,
  ReadableByteStreamController: ReadableByteStreamController,
  ReadableStream: ReadableStream$1,
  ReadableStreamBYOBReader: ReadableStreamBYOBReader,
  ReadableStreamBYOBRequest: ReadableStreamBYOBRequest,
  ReadableStreamDefaultController: ReadableStreamDefaultController,
  ReadableStreamDefaultReader: ReadableStreamDefaultReader,
  TransformStream: TransformStream$1,
  TransformStreamDefaultController: TransformStreamDefaultController,
  WritableStream: WritableStream$1,
  WritableStreamDefaultController: WritableStreamDefaultController,
  WritableStreamDefaultWriter: WritableStreamDefaultWriter
});

/*! *****************************************************************************
Copyright (c) Microsoft Corporation.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */

var extendStatics = function(d, b) {
    extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
    return extendStatics(d, b);
};

function __extends(d, b) {
    if (typeof b !== "function" && b !== null)
        throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
    extendStatics(d, b);
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}

function assert$1(test) {
    if (!test) {
        throw new TypeError('Assertion failed');
    }
}

function noop$1() {
    return;
}
function typeIsObject$1(x) {
    return (typeof x === 'object' && x !== null) || typeof x === 'function';
}

function isStreamConstructor(ctor) {
    if (typeof ctor !== 'function') {
        return false;
    }
    var startCalled = false;
    try {
        new ctor({
            start: function () {
                startCalled = true;
            }
        });
    }
    catch (e) {
        // ignore
    }
    return startCalled;
}
function isReadableStream(readable) {
    if (!typeIsObject$1(readable)) {
        return false;
    }
    if (typeof readable.getReader !== 'function') {
        return false;
    }
    return true;
}
function isReadableStreamConstructor(ctor) {
    if (!isStreamConstructor(ctor)) {
        return false;
    }
    if (!isReadableStream(new ctor())) {
        return false;
    }
    return true;
}
function isWritableStream(writable) {
    if (!typeIsObject$1(writable)) {
        return false;
    }
    if (typeof writable.getWriter !== 'function') {
        return false;
    }
    return true;
}
function isWritableStreamConstructor(ctor) {
    if (!isStreamConstructor(ctor)) {
        return false;
    }
    if (!isWritableStream(new ctor())) {
        return false;
    }
    return true;
}
function isTransformStream(transform) {
    if (!typeIsObject$1(transform)) {
        return false;
    }
    if (!isReadableStream(transform.readable)) {
        return false;
    }
    if (!isWritableStream(transform.writable)) {
        return false;
    }
    return true;
}
function isTransformStreamConstructor(ctor) {
    if (!isStreamConstructor(ctor)) {
        return false;
    }
    if (!isTransformStream(new ctor())) {
        return false;
    }
    return true;
}
function supportsByobReader(readable) {
    try {
        var reader = readable.getReader({ mode: 'byob' });
        reader.releaseLock();
        return true;
    }
    catch (_a) {
        return false;
    }
}
function supportsByteSource(ctor) {
    try {
        new ctor({ type: 'bytes' });
        return true;
    }
    catch (_a) {
        return false;
    }
}

function createReadableStreamWrapper(ctor) {
    assert$1(isReadableStreamConstructor(ctor));
    var byteSourceSupported = supportsByteSource(ctor);
    return function (readable, _a) {
        var _b = _a === void 0 ? {} : _a, type = _b.type;
        type = parseReadableType(type);
        if (type === 'bytes' && !byteSourceSupported) {
            type = undefined;
        }
        if (readable.constructor === ctor) {
            if (type !== 'bytes' || supportsByobReader(readable)) {
                return readable;
            }
        }
        if (type === 'bytes') {
            var source = createWrappingReadableSource(readable, { type: type });
            return new ctor(source);
        }
        else {
            var source = createWrappingReadableSource(readable);
            return new ctor(source);
        }
    };
}
function createWrappingReadableSource(readable, _a) {
    var _b = _a === void 0 ? {} : _a, type = _b.type;
    assert$1(isReadableStream(readable));
    assert$1(readable.locked === false);
    type = parseReadableType(type);
    var source;
    if (type === 'bytes') {
        source = new WrappingReadableByteStreamSource(readable);
    }
    else {
        source = new WrappingReadableStreamDefaultSource(readable);
    }
    return source;
}
function parseReadableType(type) {
    var typeString = String(type);
    if (typeString === 'bytes') {
        return typeString;
    }
    else if (type === undefined) {
        return type;
    }
    else {
        throw new RangeError('Invalid type is specified');
    }
}
var AbstractWrappingReadableStreamSource = /** @class */ (function () {
    function AbstractWrappingReadableStreamSource(underlyingStream) {
        this._underlyingReader = undefined;
        this._readerMode = undefined;
        this._readableStreamController = undefined;
        this._pendingRead = undefined;
        this._underlyingStream = underlyingStream;
        // always keep a reader attached to detect close/error
        this._attachDefaultReader();
    }
    AbstractWrappingReadableStreamSource.prototype.start = function (controller) {
        this._readableStreamController = controller;
    };
    AbstractWrappingReadableStreamSource.prototype.cancel = function (reason) {
        assert$1(this._underlyingReader !== undefined);
        return this._underlyingReader.cancel(reason);
    };
    AbstractWrappingReadableStreamSource.prototype._attachDefaultReader = function () {
        if (this._readerMode === "default" /* DEFAULT */) {
            return;
        }
        this._detachReader();
        var reader = this._underlyingStream.getReader();
        this._readerMode = "default" /* DEFAULT */;
        this._attachReader(reader);
    };
    AbstractWrappingReadableStreamSource.prototype._attachReader = function (reader) {
        var _this = this;
        assert$1(this._underlyingReader === undefined);
        this._underlyingReader = reader;
        var closed = this._underlyingReader.closed;
        if (!closed) {
            return;
        }
        closed
            .then(function () { return _this._finishPendingRead(); })
            .then(function () {
            if (reader === _this._underlyingReader) {
                _this._readableStreamController.close();
            }
        }, function (reason) {
            if (reader === _this._underlyingReader) {
                _this._readableStreamController.error(reason);
            }
        })
            .catch(noop$1);
    };
    AbstractWrappingReadableStreamSource.prototype._detachReader = function () {
        if (this._underlyingReader === undefined) {
            return;
        }
        this._underlyingReader.releaseLock();
        this._underlyingReader = undefined;
        this._readerMode = undefined;
    };
    AbstractWrappingReadableStreamSource.prototype._pullWithDefaultReader = function () {
        var _this = this;
        this._attachDefaultReader();
        // TODO Backpressure?
        var read = this._underlyingReader.read()
            .then(function (result) {
            var controller = _this._readableStreamController;
            if (result.done) {
                _this._tryClose();
            }
            else {
                controller.enqueue(result.value);
            }
        });
        this._setPendingRead(read);
        return read;
    };
    AbstractWrappingReadableStreamSource.prototype._tryClose = function () {
        try {
            this._readableStreamController.close();
        }
        catch (_a) {
            // already errored or closed
        }
    };
    AbstractWrappingReadableStreamSource.prototype._setPendingRead = function (readPromise) {
        var _this = this;
        var pendingRead;
        var finishRead = function () {
            if (_this._pendingRead === pendingRead) {
                _this._pendingRead = undefined;
            }
        };
        this._pendingRead = pendingRead = readPromise.then(finishRead, finishRead);
    };
    AbstractWrappingReadableStreamSource.prototype._finishPendingRead = function () {
        var _this = this;
        if (!this._pendingRead) {
            return undefined;
        }
        var afterRead = function () { return _this._finishPendingRead(); };
        return this._pendingRead.then(afterRead, afterRead);
    };
    return AbstractWrappingReadableStreamSource;
}());
var WrappingReadableStreamDefaultSource = /** @class */ (function (_super) {
    __extends(WrappingReadableStreamDefaultSource, _super);
    function WrappingReadableStreamDefaultSource() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    WrappingReadableStreamDefaultSource.prototype.pull = function () {
        return this._pullWithDefaultReader();
    };
    return WrappingReadableStreamDefaultSource;
}(AbstractWrappingReadableStreamSource));
function toUint8Array(view) {
    return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
}
function copyArrayBufferView(from, to) {
    var fromArray = toUint8Array(from);
    var toArray = toUint8Array(to);
    toArray.set(fromArray, 0);
}
var WrappingReadableByteStreamSource = /** @class */ (function (_super) {
    __extends(WrappingReadableByteStreamSource, _super);
    function WrappingReadableByteStreamSource(underlyingStream) {
        var _this = this;
        var supportsByob = supportsByobReader(underlyingStream);
        _this = _super.call(this, underlyingStream) || this;
        _this._supportsByob = supportsByob;
        return _this;
    }
    Object.defineProperty(WrappingReadableByteStreamSource.prototype, "type", {
        get: function () {
            return 'bytes';
        },
        enumerable: false,
        configurable: true
    });
    WrappingReadableByteStreamSource.prototype._attachByobReader = function () {
        if (this._readerMode === "byob" /* BYOB */) {
            return;
        }
        assert$1(this._supportsByob);
        this._detachReader();
        var reader = this._underlyingStream.getReader({ mode: 'byob' });
        this._readerMode = "byob" /* BYOB */;
        this._attachReader(reader);
    };
    WrappingReadableByteStreamSource.prototype.pull = function () {
        if (this._supportsByob) {
            var byobRequest = this._readableStreamController.byobRequest;
            if (byobRequest) {
                return this._pullWithByobRequest(byobRequest);
            }
        }
        return this._pullWithDefaultReader();
    };
    WrappingReadableByteStreamSource.prototype._pullWithByobRequest = function (byobRequest) {
        var _this = this;
        this._attachByobReader();
        // reader.read(view) detaches the input view, therefore we cannot pass byobRequest.view directly
        // create a separate buffer to read into, then copy that to byobRequest.view
        var buffer = new Uint8Array(byobRequest.view.byteLength);
        // TODO Backpressure?
        var read = this._underlyingReader.read(buffer)
            .then(function (result) {
            _this._readableStreamController;
            if (result.done) {
                _this._tryClose();
                byobRequest.respond(0);
            }
            else {
                copyArrayBufferView(result.value, byobRequest.view);
                byobRequest.respond(result.value.byteLength);
            }
        });
        this._setPendingRead(read);
        return read;
    };
    return WrappingReadableByteStreamSource;
}(AbstractWrappingReadableStreamSource));

function createWritableStreamWrapper(ctor) {
    assert$1(isWritableStreamConstructor(ctor));
    return function (writable) {
        if (writable.constructor === ctor) {
            return writable;
        }
        var sink = createWrappingWritableSink(writable);
        return new ctor(sink);
    };
}
function createWrappingWritableSink(writable) {
    assert$1(isWritableStream(writable));
    assert$1(writable.locked === false);
    var writer = writable.getWriter();
    return new WrappingWritableStreamSink(writer);
}
var WrappingWritableStreamSink = /** @class */ (function () {
    function WrappingWritableStreamSink(underlyingWriter) {
        var _this = this;
        this._writableStreamController = undefined;
        this._pendingWrite = undefined;
        this._state = "writable" /* WRITABLE */;
        this._storedError = undefined;
        this._underlyingWriter = underlyingWriter;
        this._errorPromise = new Promise(function (resolve, reject) {
            _this._errorPromiseReject = reject;
        });
        this._errorPromise.catch(noop$1);
    }
    WrappingWritableStreamSink.prototype.start = function (controller) {
        var _this = this;
        this._writableStreamController = controller;
        this._underlyingWriter.closed
            .then(function () {
            _this._state = "closed" /* CLOSED */;
        })
            .catch(function (reason) { return _this._finishErroring(reason); });
    };
    WrappingWritableStreamSink.prototype.write = function (chunk) {
        var _this = this;
        var writer = this._underlyingWriter;
        // Detect past errors
        if (writer.desiredSize === null) {
            return writer.ready;
        }
        var writeRequest = writer.write(chunk);
        // Detect future errors
        writeRequest.catch(function (reason) { return _this._finishErroring(reason); });
        writer.ready.catch(function (reason) { return _this._startErroring(reason); });
        // Reject write when errored
        var write = Promise.race([writeRequest, this._errorPromise]);
        this._setPendingWrite(write);
        return write;
    };
    WrappingWritableStreamSink.prototype.close = function () {
        var _this = this;
        if (this._pendingWrite === undefined) {
            return this._underlyingWriter.close();
        }
        return this._finishPendingWrite().then(function () { return _this.close(); });
    };
    WrappingWritableStreamSink.prototype.abort = function (reason) {
        if (this._state === "errored" /* ERRORED */) {
            return undefined;
        }
        var writer = this._underlyingWriter;
        return writer.abort(reason);
    };
    WrappingWritableStreamSink.prototype._setPendingWrite = function (writePromise) {
        var _this = this;
        var pendingWrite;
        var finishWrite = function () {
            if (_this._pendingWrite === pendingWrite) {
                _this._pendingWrite = undefined;
            }
        };
        this._pendingWrite = pendingWrite = writePromise.then(finishWrite, finishWrite);
    };
    WrappingWritableStreamSink.prototype._finishPendingWrite = function () {
        var _this = this;
        if (this._pendingWrite === undefined) {
            return Promise.resolve();
        }
        var afterWrite = function () { return _this._finishPendingWrite(); };
        return this._pendingWrite.then(afterWrite, afterWrite);
    };
    WrappingWritableStreamSink.prototype._startErroring = function (reason) {
        var _this = this;
        if (this._state === "writable" /* WRITABLE */) {
            this._state = "erroring" /* ERRORING */;
            this._storedError = reason;
            var afterWrite = function () { return _this._finishErroring(reason); };
            if (this._pendingWrite === undefined) {
                afterWrite();
            }
            else {
                this._finishPendingWrite().then(afterWrite, afterWrite);
            }
            this._writableStreamController.error(reason);
        }
    };
    WrappingWritableStreamSink.prototype._finishErroring = function (reason) {
        if (this._state === "writable" /* WRITABLE */) {
            this._startErroring(reason);
        }
        if (this._state === "erroring" /* ERRORING */) {
            this._state = "errored" /* ERRORED */;
            this._errorPromiseReject(this._storedError);
        }
    };
    return WrappingWritableStreamSink;
}());

function createTransformStreamWrapper(ctor) {
    assert$1(isTransformStreamConstructor(ctor));
    return function (transform) {
        if (transform.constructor === ctor) {
            return transform;
        }
        var transformer = createWrappingTransformer(transform);
        return new ctor(transformer);
    };
}
function createWrappingTransformer(transform) {
    assert$1(isTransformStream(transform));
    var readable = transform.readable, writable = transform.writable;
    assert$1(readable.locked === false);
    assert$1(writable.locked === false);
    var reader = readable.getReader();
    var writer;
    try {
        writer = writable.getWriter();
    }
    catch (e) {
        reader.releaseLock(); // do not leak reader
        throw e;
    }
    return new WrappingTransformStreamTransformer(reader, writer);
}
var WrappingTransformStreamTransformer = /** @class */ (function () {
    function WrappingTransformStreamTransformer(reader, writer) {
        var _this = this;
        this._transformStreamController = undefined;
        this._onRead = function (result) {
            if (result.done) {
                return;
            }
            _this._transformStreamController.enqueue(result.value);
            return _this._reader.read().then(_this._onRead);
        };
        this._onError = function (reason) {
            _this._flushReject(reason);
            _this._transformStreamController.error(reason);
            _this._reader.cancel(reason).catch(noop$1);
            _this._writer.abort(reason).catch(noop$1);
        };
        this._onTerminate = function () {
            _this._flushResolve();
            _this._transformStreamController.terminate();
            var error = new TypeError('TransformStream terminated');
            _this._writer.abort(error).catch(noop$1);
        };
        this._reader = reader;
        this._writer = writer;
        this._flushPromise = new Promise(function (resolve, reject) {
            _this._flushResolve = resolve;
            _this._flushReject = reject;
        });
    }
    WrappingTransformStreamTransformer.prototype.start = function (controller) {
        this._transformStreamController = controller;
        this._reader.read()
            .then(this._onRead)
            .then(this._onTerminate, this._onError);
        var readerClosed = this._reader.closed;
        if (readerClosed) {
            readerClosed
                .then(this._onTerminate, this._onError);
        }
    };
    WrappingTransformStreamTransformer.prototype.transform = function (chunk) {
        return this._writer.write(chunk);
    };
    WrappingTransformStreamTransformer.prototype.flush = function () {
        var _this = this;
        return this._writer.close()
            .then(function () { return _this._flushPromise; });
    };
    return WrappingTransformStreamTransformer;
}());

var webStreamsAdapter = /*#__PURE__*/Object.freeze({
  __proto__: null,
  createReadableStreamWrapper: createReadableStreamWrapper,
  createTransformStreamWrapper: createTransformStreamWrapper,
  createWrappingReadableSource: createWrappingReadableSource,
  createWrappingTransformer: createWrappingTransformer,
  createWrappingWritableSink: createWrappingWritableSink,
  createWritableStreamWrapper: createWritableStreamWrapper
});

var bn = createCommonjsModule(function (module) {
(function (module, exports) {

  // Utils
  function assert (val, msg) {
    if (!val) throw new Error(msg || 'Assertion failed');
  }

  // Could use `inherits` module, but don't want to move from single file
  // architecture yet.
  function inherits (ctor, superCtor) {
    ctor.super_ = superCtor;
    var TempCtor = function () {};
    TempCtor.prototype = superCtor.prototype;
    ctor.prototype = new TempCtor();
    ctor.prototype.constructor = ctor;
  }

  // BN

  function BN (number, base, endian) {
    if (BN.isBN(number)) {
      return number;
    }

    this.negative = 0;
    this.words = null;
    this.length = 0;

    // Reduction context
    this.red = null;

    if (number !== null) {
      if (base === 'le' || base === 'be') {
        endian = base;
        base = 10;
      }

      this._init(number || 0, base || 10, endian || 'be');
    }
  }
  if (typeof module === 'object') {
    module.exports = BN;
  } else {
    exports.BN = BN;
  }

  BN.BN = BN;
  BN.wordSize = 26;

  var Buffer;
  try {
    Buffer = buffer__default['default'].Buffer;
  } catch (e) {
  }

  BN.isBN = function isBN (num) {
    if (num instanceof BN) {
      return true;
    }

    return num !== null && typeof num === 'object' &&
      num.constructor.wordSize === BN.wordSize && Array.isArray(num.words);
  };

  BN.max = function max (left, right) {
    if (left.cmp(right) > 0) return left;
    return right;
  };

  BN.min = function min (left, right) {
    if (left.cmp(right) < 0) return left;
    return right;
  };

  BN.prototype._init = function init (number, base, endian) {
    if (typeof number === 'number') {
      return this._initNumber(number, base, endian);
    }

    if (typeof number === 'object') {
      return this._initArray(number, base, endian);
    }

    if (base === 'hex') {
      base = 16;
    }
    assert(base === (base | 0) && base >= 2 && base <= 36);

    number = number.toString().replace(/\s+/g, '');
    var start = 0;
    if (number[0] === '-') {
      start++;
    }

    if (base === 16) {
      this._parseHex(number, start);
    } else {
      this._parseBase(number, base, start);
    }

    if (number[0] === '-') {
      this.negative = 1;
    }

    this.strip();

    if (endian !== 'le') return;

    this._initArray(this.toArray(), base, endian);
  };

  BN.prototype._initNumber = function _initNumber (number, base, endian) {
    if (number < 0) {
      this.negative = 1;
      number = -number;
    }
    if (number < 0x4000000) {
      this.words = [ number & 0x3ffffff ];
      this.length = 1;
    } else if (number < 0x10000000000000) {
      this.words = [
        number & 0x3ffffff,
        (number / 0x4000000) & 0x3ffffff
      ];
      this.length = 2;
    } else {
      assert(number < 0x20000000000000); // 2 ^ 53 (unsafe)
      this.words = [
        number & 0x3ffffff,
        (number / 0x4000000) & 0x3ffffff,
        1
      ];
      this.length = 3;
    }

    if (endian !== 'le') return;

    // Reverse the bytes
    this._initArray(this.toArray(), base, endian);
  };

  BN.prototype._initArray = function _initArray (number, base, endian) {
    // Perhaps a Uint8Array
    assert(typeof number.length === 'number');
    if (number.length <= 0) {
      this.words = [ 0 ];
      this.length = 1;
      return this;
    }

    this.length = Math.ceil(number.length / 3);
    this.words = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
      this.words[i] = 0;
    }

    var j, w;
    var off = 0;
    if (endian === 'be') {
      for (i = number.length - 1, j = 0; i >= 0; i -= 3) {
        w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16);
        this.words[j] |= (w << off) & 0x3ffffff;
        this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff;
        off += 24;
        if (off >= 26) {
          off -= 26;
          j++;
        }
      }
    } else if (endian === 'le') {
      for (i = 0, j = 0; i < number.length; i += 3) {
        w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16);
        this.words[j] |= (w << off) & 0x3ffffff;
        this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff;
        off += 24;
        if (off >= 26) {
          off -= 26;
          j++;
        }
      }
    }
    return this.strip();
  };

  function parseHex (str, start, end) {
    var r = 0;
    var len = Math.min(str.length, end);
    for (var i = start; i < len; i++) {
      var c = str.charCodeAt(i) - 48;

      r <<= 4;

      // 'a' - 'f'
      if (c >= 49 && c <= 54) {
        r |= c - 49 + 0xa;

      // 'A' - 'F'
      } else if (c >= 17 && c <= 22) {
        r |= c - 17 + 0xa;

      // '0' - '9'
      } else {
        r |= c & 0xf;
      }
    }
    return r;
  }

  BN.prototype._parseHex = function _parseHex (number, start) {
    // Create possibly bigger array to ensure that it fits the number
    this.length = Math.ceil((number.length - start) / 6);
    this.words = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
      this.words[i] = 0;
    }

    var j, w;
    // Scan 24-bit chunks and add them to the number
    var off = 0;
    for (i = number.length - 6, j = 0; i >= start; i -= 6) {
      w = parseHex(number, i, i + 6);
      this.words[j] |= (w << off) & 0x3ffffff;
      // NOTE: `0x3fffff` is intentional here, 26bits max shift + 24bit hex limb
      this.words[j + 1] |= w >>> (26 - off) & 0x3fffff;
      off += 24;
      if (off >= 26) {
        off -= 26;
        j++;
      }
    }
    if (i + 6 !== start) {
      w = parseHex(number, start, i + 6);
      this.words[j] |= (w << off) & 0x3ffffff;
      this.words[j + 1] |= w >>> (26 - off) & 0x3fffff;
    }
    this.strip();
  };

  function parseBase (str, start, end, mul) {
    var r = 0;
    var len = Math.min(str.length, end);
    for (var i = start; i < len; i++) {
      var c = str.charCodeAt(i) - 48;

      r *= mul;

      // 'a'
      if (c >= 49) {
        r += c - 49 + 0xa;

      // 'A'
      } else if (c >= 17) {
        r += c - 17 + 0xa;

      // '0' - '9'
      } else {
        r += c;
      }
    }
    return r;
  }

  BN.prototype._parseBase = function _parseBase (number, base, start) {
    // Initialize as zero
    this.words = [ 0 ];
    this.length = 1;

    // Find length of limb in base
    for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) {
      limbLen++;
    }
    limbLen--;
    limbPow = (limbPow / base) | 0;

    var total = number.length - start;
    var mod = total % limbLen;
    var end = Math.min(total, total - mod) + start;

    var word = 0;
    for (var i = start; i < end; i += limbLen) {
      word = parseBase(number, i, i + limbLen, base);

      this.imuln(limbPow);
      if (this.words[0] + word < 0x4000000) {
        this.words[0] += word;
      } else {
        this._iaddn(word);
      }
    }

    if (mod !== 0) {
      var pow = 1;
      word = parseBase(number, i, number.length, base);

      for (i = 0; i < mod; i++) {
        pow *= base;
      }

      this.imuln(pow);
      if (this.words[0] + word < 0x4000000) {
        this.words[0] += word;
      } else {
        this._iaddn(word);
      }
    }
  };

  BN.prototype.copy = function copy (dest) {
    dest.words = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
      dest.words[i] = this.words[i];
    }
    dest.length = this.length;
    dest.negative = this.negative;
    dest.red = this.red;
  };

  BN.prototype.clone = function clone () {
    var r = new BN(null);
    this.copy(r);
    return r;
  };

  BN.prototype._expand = function _expand (size) {
    while (this.length < size) {
      this.words[this.length++] = 0;
    }
    return this;
  };

  // Remove leading `0` from `this`
  BN.prototype.strip = function strip () {
    while (this.length > 1 && this.words[this.length - 1] === 0) {
      this.length--;
    }
    return this._normSign();
  };

  BN.prototype._normSign = function _normSign () {
    // -0 = 0
    if (this.length === 1 && this.words[0] === 0) {
      this.negative = 0;
    }
    return this;
  };

  BN.prototype.inspect = function inspect () {
    return (this.red ? '<BN-R: ' : '<BN: ') + this.toString(16) + '>';
  };

  /*

  var zeros = [];
  var groupSizes = [];
  var groupBases = [];

  var s = '';
  var i = -1;
  while (++i < BN.wordSize) {
    zeros[i] = s;
    s += '0';
  }
  groupSizes[0] = 0;
  groupSizes[1] = 0;
  groupBases[0] = 0;
  groupBases[1] = 0;
  var base = 2 - 1;
  while (++base < 36 + 1) {
    var groupSize = 0;
    var groupBase = 1;
    while (groupBase < (1 << BN.wordSize) / base) {
      groupBase *= base;
      groupSize += 1;
    }
    groupSizes[base] = groupSize;
    groupBases[base] = groupBase;
  }

  */

  var zeros = [
    '',
    '0',
    '00',
    '000',
    '0000',
    '00000',
    '000000',
    '0000000',
    '00000000',
    '000000000',
    '0000000000',
    '00000000000',
    '000000000000',
    '0000000000000',
    '00000000000000',
    '000000000000000',
    '0000000000000000',
    '00000000000000000',
    '000000000000000000',
    '0000000000000000000',
    '00000000000000000000',
    '000000000000000000000',
    '0000000000000000000000',
    '00000000000000000000000',
    '000000000000000000000000',
    '0000000000000000000000000'
  ];

  var groupSizes = [
    0, 0,
    25, 16, 12, 11, 10, 9, 8,
    8, 7, 7, 7, 7, 6, 6,
    6, 6, 6, 6, 6, 5, 5,
    5, 5, 5, 5, 5, 5, 5,
    5, 5, 5, 5, 5, 5, 5
  ];

  var groupBases = [
    0, 0,
    33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216,
    43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625,
    16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632,
    6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149,
    24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176
  ];

  BN.prototype.toString = function toString (base, padding) {
    base = base || 10;
    padding = padding | 0 || 1;

    var out;
    if (base === 16 || base === 'hex') {
      out = '';
      var off = 0;
      var carry = 0;
      for (var i = 0; i < this.length; i++) {
        var w = this.words[i];
        var word = (((w << off) | carry) & 0xffffff).toString(16);
        carry = (w >>> (24 - off)) & 0xffffff;
        if (carry !== 0 || i !== this.length - 1) {
          out = zeros[6 - word.length] + word + out;
        } else {
          out = word + out;
        }
        off += 2;
        if (off >= 26) {
          off -= 26;
          i--;
        }
      }
      if (carry !== 0) {
        out = carry.toString(16) + out;
      }
      while (out.length % padding !== 0) {
        out = '0' + out;
      }
      if (this.negative !== 0) {
        out = '-' + out;
      }
      return out;
    }

    if (base === (base | 0) && base >= 2 && base <= 36) {
      // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base));
      var groupSize = groupSizes[base];
      // var groupBase = Math.pow(base, groupSize);
      var groupBase = groupBases[base];
      out = '';
      var c = this.clone();
      c.negative = 0;
      while (!c.isZero()) {
        var r = c.modn(groupBase).toString(base);
        c = c.idivn(groupBase);

        if (!c.isZero()) {
          out = zeros[groupSize - r.length] + r + out;
        } else {
          out = r + out;
        }
      }
      if (this.isZero()) {
        out = '0' + out;
      }
      while (out.length % padding !== 0) {
        out = '0' + out;
      }
      if (this.negative !== 0) {
        out = '-' + out;
      }
      return out;
    }

    assert(false, 'Base should be between 2 and 36');
  };

  BN.prototype.toNumber = function toNumber () {
    var ret = this.words[0];
    if (this.length === 2) {
      ret += this.words[1] * 0x4000000;
    } else if (this.length === 3 && this.words[2] === 0x01) {
      // NOTE: at this stage it is known that the top bit is set
      ret += 0x10000000000000 + (this.words[1] * 0x4000000);
    } else if (this.length > 2) {
      assert(false, 'Number can only safely store up to 53 bits');
    }
    return (this.negative !== 0) ? -ret : ret;
  };

  BN.prototype.toJSON = function toJSON () {
    return this.toString(16);
  };

  BN.prototype.toBuffer = function toBuffer (endian, length) {
    assert(typeof Buffer !== 'undefined');
    return this.toArrayLike(Buffer, endian, length);
  };

  BN.prototype.toArray = function toArray (endian, length) {
    return this.toArrayLike(Array, endian, length);
  };

  BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) {
    var byteLength = this.byteLength();
    var reqLength = length || Math.max(1, byteLength);
    assert(byteLength <= reqLength, 'byte array longer than desired length');
    assert(reqLength > 0, 'Requested array length <= 0');

    this.strip();
    var littleEndian = endian === 'le';
    var res = new ArrayType(reqLength);

    var b, i;
    var q = this.clone();
    if (!littleEndian) {
      // Assume big-endian
      for (i = 0; i < reqLength - byteLength; i++) {
        res[i] = 0;
      }

      for (i = 0; !q.isZero(); i++) {
        b = q.andln(0xff);
        q.iushrn(8);

        res[reqLength - i - 1] = b;
      }
    } else {
      for (i = 0; !q.isZero(); i++) {
        b = q.andln(0xff);
        q.iushrn(8);

        res[i] = b;
      }

      for (; i < reqLength; i++) {
        res[i] = 0;
      }
    }

    return res;
  };

  if (Math.clz32) {
    BN.prototype._countBits = function _countBits (w) {
      return 32 - Math.clz32(w);
    };
  } else {
    BN.prototype._countBits = function _countBits (w) {
      var t = w;
      var r = 0;
      if (t >= 0x1000) {
        r += 13;
        t >>>= 13;
      }
      if (t >= 0x40) {
        r += 7;
        t >>>= 7;
      }
      if (t >= 0x8) {
        r += 4;
        t >>>= 4;
      }
      if (t >= 0x02) {
        r += 2;
        t >>>= 2;
      }
      return r + t;
    };
  }

  BN.prototype._zeroBits = function _zeroBits (w) {
    // Short-cut
    if (w === 0) return 26;

    var t = w;
    var r = 0;
    if ((t & 0x1fff) === 0) {
      r += 13;
      t >>>= 13;
    }
    if ((t & 0x7f) === 0) {
      r += 7;
      t >>>= 7;
    }
    if ((t & 0xf) === 0) {
      r += 4;
      t >>>= 4;
    }
    if ((t & 0x3) === 0) {
      r += 2;
      t >>>= 2;
    }
    if ((t & 0x1) === 0) {
      r++;
    }
    return r;
  };

  // Return number of used bits in a BN
  BN.prototype.bitLength = function bitLength () {
    var w = this.words[this.length - 1];
    var hi = this._countBits(w);
    return (this.length - 1) * 26 + hi;
  };

  function toBitArray (num) {
    var w = new Array(num.bitLength());

    for (var bit = 0; bit < w.length; bit++) {
      var off = (bit / 26) | 0;
      var wbit = bit % 26;

      w[bit] = (num.words[off] & (1 << wbit)) >>> wbit;
    }

    return w;
  }

  // Number of trailing zero bits
  BN.prototype.zeroBits = function zeroBits () {
    if (this.isZero()) return 0;

    var r = 0;
    for (var i = 0; i < this.length; i++) {
      var b = this._zeroBits(this.words[i]);
      r += b;
      if (b !== 26) break;
    }
    return r;
  };

  BN.prototype.byteLength = function byteLength () {
    return Math.ceil(this.bitLength() / 8);
  };

  BN.prototype.toTwos = function toTwos (width) {
    if (this.negative !== 0) {
      return this.abs().inotn(width).iaddn(1);
    }
    return this.clone();
  };

  BN.prototype.fromTwos = function fromTwos (width) {
    if (this.testn(width - 1)) {
      return this.notn(width).iaddn(1).ineg();
    }
    return this.clone();
  };

  BN.prototype.isNeg = function isNeg () {
    return this.negative !== 0;
  };

  // Return negative clone of `this`
  BN.prototype.neg = function neg () {
    return this.clone().ineg();
  };

  BN.prototype.ineg = function ineg () {
    if (!this.isZero()) {
      this.negative ^= 1;
    }

    return this;
  };

  // Or `num` with `this` in-place
  BN.prototype.iuor = function iuor (num) {
    while (this.length < num.length) {
      this.words[this.length++] = 0;
    }

    for (var i = 0; i < num.length; i++) {
      this.words[i] = this.words[i] | num.words[i];
    }

    return this.strip();
  };

  BN.prototype.ior = function ior (num) {
    assert((this.negative | num.negative) === 0);
    return this.iuor(num);
  };

  // Or `num` with `this`
  BN.prototype.or = function or (num) {
    if (this.length > num.length) return this.clone().ior(num);
    return num.clone().ior(this);
  };

  BN.prototype.uor = function uor (num) {
    if (this.length > num.length) return this.clone().iuor(num);
    return num.clone().iuor(this);
  };

  // And `num` with `this` in-place
  BN.prototype.iuand = function iuand (num) {
    // b = min-length(num, this)
    var b;
    if (this.length > num.length) {
      b = num;
    } else {
      b = this;
    }

    for (var i = 0; i < b.length; i++) {
      this.words[i] = this.words[i] & num.words[i];
    }

    this.length = b.length;

    return this.strip();
  };

  BN.prototype.iand = function iand (num) {
    assert((this.negative | num.negative) === 0);
    return this.iuand(num);
  };

  // And `num` with `this`
  BN.prototype.and = function and (num) {
    if (this.length > num.length) return this.clone().iand(num);
    return num.clone().iand(this);
  };

  BN.prototype.uand = function uand (num) {
    if (this.length > num.length) return this.clone().iuand(num);
    return num.clone().iuand(this);
  };

  // Xor `num` with `this` in-place
  BN.prototype.iuxor = function iuxor (num) {
    // a.length > b.length
    var a;
    var b;
    if (this.length > num.length) {
      a = this;
      b = num;
    } else {
      a = num;
      b = this;
    }

    for (var i = 0; i < b.length; i++) {
      this.words[i] = a.words[i] ^ b.words[i];
    }

    if (this !== a) {
      for (; i < a.length; i++) {
        this.words[i] = a.words[i];
      }
    }

    this.length = a.length;

    return this.strip();
  };

  BN.prototype.ixor = function ixor (num) {
    assert((this.negative | num.negative) === 0);
    return this.iuxor(num);
  };

  // Xor `num` with `this`
  BN.prototype.xor = function xor (num) {
    if (this.length > num.length) return this.clone().ixor(num);
    return num.clone().ixor(this);
  };

  BN.prototype.uxor = function uxor (num) {
    if (this.length > num.length) return this.clone().iuxor(num);
    return num.clone().iuxor(this);
  };

  // Not ``this`` with ``width`` bitwidth
  BN.prototype.inotn = function inotn (width) {
    assert(typeof width === 'number' && width >= 0);

    var bytesNeeded = Math.ceil(width / 26) | 0;
    var bitsLeft = width % 26;

    // Extend the buffer with leading zeroes
    this._expand(bytesNeeded);

    if (bitsLeft > 0) {
      bytesNeeded--;
    }

    // Handle complete words
    for (var i = 0; i < bytesNeeded; i++) {
      this.words[i] = ~this.words[i] & 0x3ffffff;
    }

    // Handle the residue
    if (bitsLeft > 0) {
      this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft));
    }

    // And remove leading zeroes
    return this.strip();
  };

  BN.prototype.notn = function notn (width) {
    return this.clone().inotn(width);
  };

  // Set `bit` of `this`
  BN.prototype.setn = function setn (bit, val) {
    assert(typeof bit === 'number' && bit >= 0);

    var off = (bit / 26) | 0;
    var wbit = bit % 26;

    this._expand(off + 1);

    if (val) {
      this.words[off] = this.words[off] | (1 << wbit);
    } else {
      this.words[off] = this.words[off] & ~(1 << wbit);
    }

    return this.strip();
  };

  // Add `num` to `this` in-place
  BN.prototype.iadd = function iadd (num) {
    var r;

    // negative + positive
    if (this.negative !== 0 && num.negative === 0) {
      this.negative = 0;
      r = this.isub(num);
      this.negative ^= 1;
      return this._normSign();

    // positive + negative
    } else if (this.negative === 0 && num.negative !== 0) {
      num.negative = 0;
      r = this.isub(num);
      num.negative = 1;
      return r._normSign();
    }

    // a.length > b.length
    var a, b;
    if (this.length > num.length) {
      a = this;
      b = num;
    } else {
      a = num;
      b = this;
    }

    var carry = 0;
    for (var i = 0; i < b.length; i++) {
      r = (a.words[i] | 0) + (b.words[i] | 0) + carry;
      this.words[i] = r & 0x3ffffff;
      carry = r >>> 26;
    }
    for (; carry !== 0 && i < a.length; i++) {
      r = (a.words[i] | 0) + carry;
      this.words[i] = r & 0x3ffffff;
      carry = r >>> 26;
    }

    this.length = a.length;
    if (carry !== 0) {
      this.words[this.length] = carry;
      this.length++;
    // Copy the rest of the words
    } else if (a !== this) {
      for (; i < a.length; i++) {
        this.words[i] = a.words[i];
      }
    }

    return this;
  };

  // Add `num` to `this`
  BN.prototype.add = function add (num) {
    var res;
    if (num.negative !== 0 && this.negative === 0) {
      num.negative = 0;
      res = this.sub(num);
      num.negative ^= 1;
      return res;
    } else if (num.negative === 0 && this.negative !== 0) {
      this.negative = 0;
      res = num.sub(this);
      this.negative = 1;
      return res;
    }

    if (this.length > num.length) return this.clone().iadd(num);

    return num.clone().iadd(this);
  };

  // Subtract `num` from `this` in-place
  BN.prototype.isub = function isub (num) {
    // this - (-num) = this + num
    if (num.negative !== 0) {
      num.negative = 0;
      var r = this.iadd(num);
      num.negative = 1;
      return r._normSign();

    // -this - num = -(this + num)
    } else if (this.negative !== 0) {
      this.negative = 0;
      this.iadd(num);
      this.negative = 1;
      return this._normSign();
    }

    // At this point both numbers are positive
    var cmp = this.cmp(num);

    // Optimization - zeroify
    if (cmp === 0) {
      this.negative = 0;
      this.length = 1;
      this.words[0] = 0;
      return this;
    }

    // a > b
    var a, b;
    if (cmp > 0) {
      a = this;
      b = num;
    } else {
      a = num;
      b = this;
    }

    var carry = 0;
    for (var i = 0; i < b.length; i++) {
      r = (a.words[i] | 0) - (b.words[i] | 0) + carry;
      carry = r >> 26;
      this.words[i] = r & 0x3ffffff;
    }
    for (; carry !== 0 && i < a.length; i++) {
      r = (a.words[i] | 0) + carry;
      carry = r >> 26;
      this.words[i] = r & 0x3ffffff;
    }

    // Copy rest of the words
    if (carry === 0 && i < a.length && a !== this) {
      for (; i < a.length; i++) {
        this.words[i] = a.words[i];
      }
    }

    this.length = Math.max(this.length, i);

    if (a !== this) {
      this.negative = 1;
    }

    return this.strip();
  };

  // Subtract `num` from `this`
  BN.prototype.sub = function sub (num) {
    return this.clone().isub(num);
  };

  function smallMulTo (self, num, out) {
    out.negative = num.negative ^ self.negative;
    var len = (self.length + num.length) | 0;
    out.length = len;
    len = (len - 1) | 0;

    // Peel one iteration (compiler can't do it, because of code complexity)
    var a = self.words[0] | 0;
    var b = num.words[0] | 0;
    var r = a * b;

    var lo = r & 0x3ffffff;
    var carry = (r / 0x4000000) | 0;
    out.words[0] = lo;

    for (var k = 1; k < len; k++) {
      // Sum all words with the same `i + j = k` and accumulate `ncarry`,
      // note that ncarry could be >= 0x3ffffff
      var ncarry = carry >>> 26;
      var rword = carry & 0x3ffffff;
      var maxJ = Math.min(k, num.length - 1);
      for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) {
        var i = (k - j) | 0;
        a = self.words[i] | 0;
        b = num.words[j] | 0;
        r = a * b + rword;
        ncarry += (r / 0x4000000) | 0;
        rword = r & 0x3ffffff;
      }
      out.words[k] = rword | 0;
      carry = ncarry | 0;
    }
    if (carry !== 0) {
      out.words[k] = carry | 0;
    } else {
      out.length--;
    }

    return out.strip();
  }

  // TODO(indutny): it may be reasonable to omit it for users who don't need
  // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit
  // multiplication (like elliptic secp256k1).
  var comb10MulTo = function comb10MulTo (self, num, out) {
    var a = self.words;
    var b = num.words;
    var o = out.words;
    var c = 0;
    var lo;
    var mid;
    var hi;
    var a0 = a[0] | 0;
    var al0 = a0 & 0x1fff;
    var ah0 = a0 >>> 13;
    var a1 = a[1] | 0;
    var al1 = a1 & 0x1fff;
    var ah1 = a1 >>> 13;
    var a2 = a[2] | 0;
    var al2 = a2 & 0x1fff;
    var ah2 = a2 >>> 13;
    var a3 = a[3] | 0;
    var al3 = a3 & 0x1fff;
    var ah3 = a3 >>> 13;
    var a4 = a[4] | 0;
    var al4 = a4 & 0x1fff;
    var ah4 = a4 >>> 13;
    var a5 = a[5] | 0;
    var al5 = a5 & 0x1fff;
    var ah5 = a5 >>> 13;
    var a6 = a[6] | 0;
    var al6 = a6 & 0x1fff;
    var ah6 = a6 >>> 13;
    var a7 = a[7] | 0;
    var al7 = a7 & 0x1fff;
    var ah7 = a7 >>> 13;
    var a8 = a[8] | 0;
    var al8 = a8 & 0x1fff;
    var ah8 = a8 >>> 13;
    var a9 = a[9] | 0;
    var al9 = a9 & 0x1fff;
    var ah9 = a9 >>> 13;
    var b0 = b[0] | 0;
    var bl0 = b0 & 0x1fff;
    var bh0 = b0 >>> 13;
    var b1 = b[1] | 0;
    var bl1 = b1 & 0x1fff;
    var bh1 = b1 >>> 13;
    var b2 = b[2] | 0;
    var bl2 = b2 & 0x1fff;
    var bh2 = b2 >>> 13;
    var b3 = b[3] | 0;
    var bl3 = b3 & 0x1fff;
    var bh3 = b3 >>> 13;
    var b4 = b[4] | 0;
    var bl4 = b4 & 0x1fff;
    var bh4 = b4 >>> 13;
    var b5 = b[5] | 0;
    var bl5 = b5 & 0x1fff;
    var bh5 = b5 >>> 13;
    var b6 = b[6] | 0;
    var bl6 = b6 & 0x1fff;
    var bh6 = b6 >>> 13;
    var b7 = b[7] | 0;
    var bl7 = b7 & 0x1fff;
    var bh7 = b7 >>> 13;
    var b8 = b[8] | 0;
    var bl8 = b8 & 0x1fff;
    var bh8 = b8 >>> 13;
    var b9 = b[9] | 0;
    var bl9 = b9 & 0x1fff;
    var bh9 = b9 >>> 13;

    out.negative = self.negative ^ num.negative;
    out.length = 19;
    /* k = 0 */
    lo = Math.imul(al0, bl0);
    mid = Math.imul(al0, bh0);
    mid = (mid + Math.imul(ah0, bl0)) | 0;
    hi = Math.imul(ah0, bh0);
    var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0;
    w0 &= 0x3ffffff;
    /* k = 1 */
    lo = Math.imul(al1, bl0);
    mid = Math.imul(al1, bh0);
    mid = (mid + Math.imul(ah1, bl0)) | 0;
    hi = Math.imul(ah1, bh0);
    lo = (lo + Math.imul(al0, bl1)) | 0;
    mid = (mid + Math.imul(al0, bh1)) | 0;
    mid = (mid + Math.imul(ah0, bl1)) | 0;
    hi = (hi + Math.imul(ah0, bh1)) | 0;
    var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0;
    w1 &= 0x3ffffff;
    /* k = 2 */
    lo = Math.imul(al2, bl0);
    mid = Math.imul(al2, bh0);
    mid = (mid + Math.imul(ah2, bl0)) | 0;
    hi = Math.imul(ah2, bh0);
    lo = (lo + Math.imul(al1, bl1)) | 0;
    mid = (mid + Math.imul(al1, bh1)) | 0;
    mid = (mid + Math.imul(ah1, bl1)) | 0;
    hi = (hi + Math.imul(ah1, bh1)) | 0;
    lo = (lo + Math.imul(al0, bl2)) | 0;
    mid = (mid + Math.imul(al0, bh2)) | 0;
    mid = (mid + Math.imul(ah0, bl2)) | 0;
    hi = (hi + Math.imul(ah0, bh2)) | 0;
    var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0;
    w2 &= 0x3ffffff;
    /* k = 3 */
    lo = Math.imul(al3, bl0);
    mid = Math.imul(al3, bh0);
    mid = (mid + Math.imul(ah3, bl0)) | 0;
    hi = Math.imul(ah3, bh0);
    lo = (lo + Math.imul(al2, bl1)) | 0;
    mid = (mid + Math.imul(al2, bh1)) | 0;
    mid = (mid + Math.imul(ah2, bl1)) | 0;
    hi = (hi + Math.imul(ah2, bh1)) | 0;
    lo = (lo + Math.imul(al1, bl2)) | 0;
    mid = (mid + Math.imul(al1, bh2)) | 0;
    mid = (mid + Math.imul(ah1, bl2)) | 0;
    hi = (hi + Math.imul(ah1, bh2)) | 0;
    lo = (lo + Math.imul(al0, bl3)) | 0;
    mid = (mid + Math.imul(al0, bh3)) | 0;
    mid = (mid + Math.imul(ah0, bl3)) | 0;
    hi = (hi + Math.imul(ah0, bh3)) | 0;
    var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0;
    w3 &= 0x3ffffff;
    /* k = 4 */
    lo = Math.imul(al4, bl0);
    mid = Math.imul(al4, bh0);
    mid = (mid + Math.imul(ah4, bl0)) | 0;
    hi = Math.imul(ah4, bh0);
    lo = (lo + Math.imul(al3, bl1)) | 0;
    mid = (mid + Math.imul(al3, bh1)) | 0;
    mid = (mid + Math.imul(ah3, bl1)) | 0;
    hi = (hi + Math.imul(ah3, bh1)) | 0;
    lo = (lo + Math.imul(al2, bl2)) | 0;
    mid = (mid + Math.imul(al2, bh2)) | 0;
    mid = (mid + Math.imul(ah2, bl2)) | 0;
    hi = (hi + Math.imul(ah2, bh2)) | 0;
    lo = (lo + Math.imul(al1, bl3)) | 0;
    mid = (mid + Math.imul(al1, bh3)) | 0;
    mid = (mid + Math.imul(ah1, bl3)) | 0;
    hi = (hi + Math.imul(ah1, bh3)) | 0;
    lo = (lo + Math.imul(al0, bl4)) | 0;
    mid = (mid + Math.imul(al0, bh4)) | 0;
    mid = (mid + Math.imul(ah0, bl4)) | 0;
    hi = (hi + Math.imul(ah0, bh4)) | 0;
    var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0;
    w4 &= 0x3ffffff;
    /* k = 5 */
    lo = Math.imul(al5, bl0);
    mid = Math.imul(al5, bh0);
    mid = (mid + Math.imul(ah5, bl0)) | 0;
    hi = Math.imul(ah5, bh0);
    lo = (lo + Math.imul(al4, bl1)) | 0;
    mid = (mid + Math.imul(al4, bh1)) | 0;
    mid = (mid + Math.imul(ah4, bl1)) | 0;
    hi = (hi + Math.imul(ah4, bh1)) | 0;
    lo = (lo + Math.imul(al3, bl2)) | 0;
    mid = (mid + Math.imul(al3, bh2)) | 0;
    mid = (mid + Math.imul(ah3, bl2)) | 0;
    hi = (hi + Math.imul(ah3, bh2)) | 0;
    lo = (lo + Math.imul(al2, bl3)) | 0;
    mid = (mid + Math.imul(al2, bh3)) | 0;
    mid = (mid + Math.imul(ah2, bl3)) | 0;
    hi = (hi + Math.imul(ah2, bh3)) | 0;
    lo = (lo + Math.imul(al1, bl4)) | 0;
    mid = (mid + Math.imul(al1, bh4)) | 0;
    mid = (mid + Math.imul(ah1, bl4)) | 0;
    hi = (hi + Math.imul(ah1, bh4)) | 0;
    lo = (lo + Math.imul(al0, bl5)) | 0;
    mid = (mid + Math.imul(al0, bh5)) | 0;
    mid = (mid + Math.imul(ah0, bl5)) | 0;
    hi = (hi + Math.imul(ah0, bh5)) | 0;
    var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0;
    w5 &= 0x3ffffff;
    /* k = 6 */
    lo = Math.imul(al6, bl0);
    mid = Math.imul(al6, bh0);
    mid = (mid + Math.imul(ah6, bl0)) | 0;
    hi = Math.imul(ah6, bh0);
    lo = (lo + Math.imul(al5, bl1)) | 0;
    mid = (mid + Math.imul(al5, bh1)) | 0;
    mid = (mid + Math.imul(ah5, bl1)) | 0;
    hi = (hi + Math.imul(ah5, bh1)) | 0;
    lo = (lo + Math.imul(al4, bl2)) | 0;
    mid = (mid + Math.imul(al4, bh2)) | 0;
    mid = (mid + Math.imul(ah4, bl2)) | 0;
    hi = (hi + Math.imul(ah4, bh2)) | 0;
    lo = (lo + Math.imul(al3, bl3)) | 0;
    mid = (mid + Math.imul(al3, bh3)) | 0;
    mid = (mid + Math.imul(ah3, bl3)) | 0;
    hi = (hi + Math.imul(ah3, bh3)) | 0;
    lo = (lo + Math.imul(al2, bl4)) | 0;
    mid = (mid + Math.imul(al2, bh4)) | 0;
    mid = (mid + Math.imul(ah2, bl4)) | 0;
    hi = (hi + Math.imul(ah2, bh4)) | 0;
    lo = (lo + Math.imul(al1, bl5)) | 0;
    mid = (mid + Math.imul(al1, bh5)) | 0;
    mid = (mid + Math.imul(ah1, bl5)) | 0;
    hi = (hi + Math.imul(ah1, bh5)) | 0;
    lo = (lo + Math.imul(al0, bl6)) | 0;
    mid = (mid + Math.imul(al0, bh6)) | 0;
    mid = (mid + Math.imul(ah0, bl6)) | 0;
    hi = (hi + Math.imul(ah0, bh6)) | 0;
    var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0;
    w6 &= 0x3ffffff;
    /* k = 7 */
    lo = Math.imul(al7, bl0);
    mid = Math.imul(al7, bh0);
    mid = (mid + Math.imul(ah7, bl0)) | 0;
    hi = Math.imul(ah7, bh0);
    lo = (lo + Math.imul(al6, bl1)) | 0;
    mid = (mid + Math.imul(al6, bh1)) | 0;
    mid = (mid + Math.imul(ah6, bl1)) | 0;
    hi = (hi + Math.imul(ah6, bh1)) | 0;
    lo = (lo + Math.imul(al5, bl2)) | 0;
    mid = (mid + Math.imul(al5, bh2)) | 0;
    mid = (mid + Math.imul(ah5, bl2)) | 0;
    hi = (hi + Math.imul(ah5, bh2)) | 0;
    lo = (lo + Math.imul(al4, bl3)) | 0;
    mid = (mid + Math.imul(al4, bh3)) | 0;
    mid = (mid + Math.imul(ah4, bl3)) | 0;
    hi = (hi + Math.imul(ah4, bh3)) | 0;
    lo = (lo + Math.imul(al3, bl4)) | 0;
    mid = (mid + Math.imul(al3, bh4)) | 0;
    mid = (mid + Math.imul(ah3, bl4)) | 0;
    hi = (hi + Math.imul(ah3, bh4)) | 0;
    lo = (lo + Math.imul(al2, bl5)) | 0;
    mid = (mid + Math.imul(al2, bh5)) | 0;
    mid = (mid + Math.imul(ah2, bl5)) | 0;
    hi = (hi + Math.imul(ah2, bh5)) | 0;
    lo = (lo + Math.imul(al1, bl6)) | 0;
    mid = (mid + Math.imul(al1, bh6)) | 0;
    mid = (mid + Math.imul(ah1, bl6)) | 0;
    hi = (hi + Math.imul(ah1, bh6)) | 0;
    lo = (lo + Math.imul(al0, bl7)) | 0;
    mid = (mid + Math.imul(al0, bh7)) | 0;
    mid = (mid + Math.imul(ah0, bl7)) | 0;
    hi = (hi + Math.imul(ah0, bh7)) | 0;
    var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0;
    w7 &= 0x3ffffff;
    /* k = 8 */
    lo = Math.imul(al8, bl0);
    mid = Math.imul(al8, bh0);
    mid = (mid + Math.imul(ah8, bl0)) | 0;
    hi = Math.imul(ah8, bh0);
    lo = (lo + Math.imul(al7, bl1)) | 0;
    mid = (mid + Math.imul(al7, bh1)) | 0;
    mid = (mid + Math.imul(ah7, bl1)) | 0;
    hi = (hi + Math.imul(ah7, bh1)) | 0;
    lo = (lo + Math.imul(al6, bl2)) | 0;
    mid = (mid + Math.imul(al6, bh2)) | 0;
    mid = (mid + Math.imul(ah6, bl2)) | 0;
    hi = (hi + Math.imul(ah6, bh2)) | 0;
    lo = (lo + Math.imul(al5, bl3)) | 0;
    mid = (mid + Math.imul(al5, bh3)) | 0;
    mid = (mid + Math.imul(ah5, bl3)) | 0;
    hi = (hi + Math.imul(ah5, bh3)) | 0;
    lo = (lo + Math.imul(al4, bl4)) | 0;
    mid = (mid + Math.imul(al4, bh4)) | 0;
    mid = (mid + Math.imul(ah4, bl4)) | 0;
    hi = (hi + Math.imul(ah4, bh4)) | 0;
    lo = (lo + Math.imul(al3, bl5)) | 0;
    mid = (mid + Math.imul(al3, bh5)) | 0;
    mid = (mid + Math.imul(ah3, bl5)) | 0;
    hi = (hi + Math.imul(ah3, bh5)) | 0;
    lo = (lo + Math.imul(al2, bl6)) | 0;
    mid = (mid + Math.imul(al2, bh6)) | 0;
    mid = (mid + Math.imul(ah2, bl6)) | 0;
    hi = (hi + Math.imul(ah2, bh6)) | 0;
    lo = (lo + Math.imul(al1, bl7)) | 0;
    mid = (mid + Math.imul(al1, bh7)) | 0;
    mid = (mid + Math.imul(ah1, bl7)) | 0;
    hi = (hi + Math.imul(ah1, bh7)) | 0;
    lo = (lo + Math.imul(al0, bl8)) | 0;
    mid = (mid + Math.imul(al0, bh8)) | 0;
    mid = (mid + Math.imul(ah0, bl8)) | 0;
    hi = (hi + Math.imul(ah0, bh8)) | 0;
    var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0;
    w8 &= 0x3ffffff;
    /* k = 9 */
    lo = Math.imul(al9, bl0);
    mid = Math.imul(al9, bh0);
    mid = (mid + Math.imul(ah9, bl0)) | 0;
    hi = Math.imul(ah9, bh0);
    lo = (lo + Math.imul(al8, bl1)) | 0;
    mid = (mid + Math.imul(al8, bh1)) | 0;
    mid = (mid + Math.imul(ah8, bl1)) | 0;
    hi = (hi + Math.imul(ah8, bh1)) | 0;
    lo = (lo + Math.imul(al7, bl2)) | 0;
    mid = (mid + Math.imul(al7, bh2)) | 0;
    mid = (mid + Math.imul(ah7, bl2)) | 0;
    hi = (hi + Math.imul(ah7, bh2)) | 0;
    lo = (lo + Math.imul(al6, bl3)) | 0;
    mid = (mid + Math.imul(al6, bh3)) | 0;
    mid = (mid + Math.imul(ah6, bl3)) | 0;
    hi = (hi + Math.imul(ah6, bh3)) | 0;
    lo = (lo + Math.imul(al5, bl4)) | 0;
    mid = (mid + Math.imul(al5, bh4)) | 0;
    mid = (mid + Math.imul(ah5, bl4)) | 0;
    hi = (hi + Math.imul(ah5, bh4)) | 0;
    lo = (lo + Math.imul(al4, bl5)) | 0;
    mid = (mid + Math.imul(al4, bh5)) | 0;
    mid = (mid + Math.imul(ah4, bl5)) | 0;
    hi = (hi + Math.imul(ah4, bh5)) | 0;
    lo = (lo + Math.imul(al3, bl6)) | 0;
    mid = (mid + Math.imul(al3, bh6)) | 0;
    mid = (mid + Math.imul(ah3, bl6)) | 0;
    hi = (hi + Math.imul(ah3, bh6)) | 0;
    lo = (lo + Math.imul(al2, bl7)) | 0;
    mid = (mid + Math.imul(al2, bh7)) | 0;
    mid = (mid + Math.imul(ah2, bl7)) | 0;
    hi = (hi + Math.imul(ah2, bh7)) | 0;
    lo = (lo + Math.imul(al1, bl8)) | 0;
    mid = (mid + Math.imul(al1, bh8)) | 0;
    mid = (mid + Math.imul(ah1, bl8)) | 0;
    hi = (hi + Math.imul(ah1, bh8)) | 0;
    lo = (lo + Math.imul(al0, bl9)) | 0;
    mid = (mid + Math.imul(al0, bh9)) | 0;
    mid = (mid + Math.imul(ah0, bl9)) | 0;
    hi = (hi + Math.imul(ah0, bh9)) | 0;
    var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0;
    w9 &= 0x3ffffff;
    /* k = 10 */
    lo = Math.imul(al9, bl1);
    mid = Math.imul(al9, bh1);
    mid = (mid + Math.imul(ah9, bl1)) | 0;
    hi = Math.imul(ah9, bh1);
    lo = (lo + Math.imul(al8, bl2)) | 0;
    mid = (mid + Math.imul(al8, bh2)) | 0;
    mid = (mid + Math.imul(ah8, bl2)) | 0;
    hi = (hi + Math.imul(ah8, bh2)) | 0;
    lo = (lo + Math.imul(al7, bl3)) | 0;
    mid = (mid + Math.imul(al7, bh3)) | 0;
    mid = (mid + Math.imul(ah7, bl3)) | 0;
    hi = (hi + Math.imul(ah7, bh3)) | 0;
    lo = (lo + Math.imul(al6, bl4)) | 0;
    mid = (mid + Math.imul(al6, bh4)) | 0;
    mid = (mid + Math.imul(ah6, bl4)) | 0;
    hi = (hi + Math.imul(ah6, bh4)) | 0;
    lo = (lo + Math.imul(al5, bl5)) | 0;
    mid = (mid + Math.imul(al5, bh5)) | 0;
    mid = (mid + Math.imul(ah5, bl5)) | 0;
    hi = (hi + Math.imul(ah5, bh5)) | 0;
    lo = (lo + Math.imul(al4, bl6)) | 0;
    mid = (mid + Math.imul(al4, bh6)) | 0;
    mid = (mid + Math.imul(ah4, bl6)) | 0;
    hi = (hi + Math.imul(ah4, bh6)) | 0;
    lo = (lo + Math.imul(al3, bl7)) | 0;
    mid = (mid + Math.imul(al3, bh7)) | 0;
    mid = (mid + Math.imul(ah3, bl7)) | 0;
    hi = (hi + Math.imul(ah3, bh7)) | 0;
    lo = (lo + Math.imul(al2, bl8)) | 0;
    mid = (mid + Math.imul(al2, bh8)) | 0;
    mid = (mid + Math.imul(ah2, bl8)) | 0;
    hi = (hi + Math.imul(ah2, bh8)) | 0;
    lo = (lo + Math.imul(al1, bl9)) | 0;
    mid = (mid + Math.imul(al1, bh9)) | 0;
    mid = (mid + Math.imul(ah1, bl9)) | 0;
    hi = (hi + Math.imul(ah1, bh9)) | 0;
    var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0;
    w10 &= 0x3ffffff;
    /* k = 11 */
    lo = Math.imul(al9, bl2);
    mid = Math.imul(al9, bh2);
    mid = (mid + Math.imul(ah9, bl2)) | 0;
    hi = Math.imul(ah9, bh2);
    lo = (lo + Math.imul(al8, bl3)) | 0;
    mid = (mid + Math.imul(al8, bh3)) | 0;
    mid = (mid + Math.imul(ah8, bl3)) | 0;
    hi = (hi + Math.imul(ah8, bh3)) | 0;
    lo = (lo + Math.imul(al7, bl4)) | 0;
    mid = (mid + Math.imul(al7, bh4)) | 0;
    mid = (mid + Math.imul(ah7, bl4)) | 0;
    hi = (hi + Math.imul(ah7, bh4)) | 0;
    lo = (lo + Math.imul(al6, bl5)) | 0;
    mid = (mid + Math.imul(al6, bh5)) | 0;
    mid = (mid + Math.imul(ah6, bl5)) | 0;
    hi = (hi + Math.imul(ah6, bh5)) | 0;
    lo = (lo + Math.imul(al5, bl6)) | 0;
    mid = (mid + Math.imul(al5, bh6)) | 0;
    mid = (mid + Math.imul(ah5, bl6)) | 0;
    hi = (hi + Math.imul(ah5, bh6)) | 0;
    lo = (lo + Math.imul(al4, bl7)) | 0;
    mid = (mid + Math.imul(al4, bh7)) | 0;
    mid = (mid + Math.imul(ah4, bl7)) | 0;
    hi = (hi + Math.imul(ah4, bh7)) | 0;
    lo = (lo + Math.imul(al3, bl8)) | 0;
    mid = (mid + Math.imul(al3, bh8)) | 0;
    mid = (mid + Math.imul(ah3, bl8)) | 0;
    hi = (hi + Math.imul(ah3, bh8)) | 0;
    lo = (lo + Math.imul(al2, bl9)) | 0;
    mid = (mid + Math.imul(al2, bh9)) | 0;
    mid = (mid + Math.imul(ah2, bl9)) | 0;
    hi = (hi + Math.imul(ah2, bh9)) | 0;
    var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0;
    w11 &= 0x3ffffff;
    /* k = 12 */
    lo = Math.imul(al9, bl3);
    mid = Math.imul(al9, bh3);
    mid = (mid + Math.imul(ah9, bl3)) | 0;
    hi = Math.imul(ah9, bh3);
    lo = (lo + Math.imul(al8, bl4)) | 0;
    mid = (mid + Math.imul(al8, bh4)) | 0;
    mid = (mid + Math.imul(ah8, bl4)) | 0;
    hi = (hi + Math.imul(ah8, bh4)) | 0;
    lo = (lo + Math.imul(al7, bl5)) | 0;
    mid = (mid + Math.imul(al7, bh5)) | 0;
    mid = (mid + Math.imul(ah7, bl5)) | 0;
    hi = (hi + Math.imul(ah7, bh5)) | 0;
    lo = (lo + Math.imul(al6, bl6)) | 0;
    mid = (mid + Math.imul(al6, bh6)) | 0;
    mid = (mid + Math.imul(ah6, bl6)) | 0;
    hi = (hi + Math.imul(ah6, bh6)) | 0;
    lo = (lo + Math.imul(al5, bl7)) | 0;
    mid = (mid + Math.imul(al5, bh7)) | 0;
    mid = (mid + Math.imul(ah5, bl7)) | 0;
    hi = (hi + Math.imul(ah5, bh7)) | 0;
    lo = (lo + Math.imul(al4, bl8)) | 0;
    mid = (mid + Math.imul(al4, bh8)) | 0;
    mid = (mid + Math.imul(ah4, bl8)) | 0;
    hi = (hi + Math.imul(ah4, bh8)) | 0;
    lo = (lo + Math.imul(al3, bl9)) | 0;
    mid = (mid + Math.imul(al3, bh9)) | 0;
    mid = (mid + Math.imul(ah3, bl9)) | 0;
    hi = (hi + Math.imul(ah3, bh9)) | 0;
    var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0;
    w12 &= 0x3ffffff;
    /* k = 13 */
    lo = Math.imul(al9, bl4);
    mid = Math.imul(al9, bh4);
    mid = (mid + Math.imul(ah9, bl4)) | 0;
    hi = Math.imul(ah9, bh4);
    lo = (lo + Math.imul(al8, bl5)) | 0;
    mid = (mid + Math.imul(al8, bh5)) | 0;
    mid = (mid + Math.imul(ah8, bl5)) | 0;
    hi = (hi + Math.imul(ah8, bh5)) | 0;
    lo = (lo + Math.imul(al7, bl6)) | 0;
    mid = (mid + Math.imul(al7, bh6)) | 0;
    mid = (mid + Math.imul(ah7, bl6)) | 0;
    hi = (hi + Math.imul(ah7, bh6)) | 0;
    lo = (lo + Math.imul(al6, bl7)) | 0;
    mid = (mid + Math.imul(al6, bh7)) | 0;
    mid = (mid + Math.imul(ah6, bl7)) | 0;
    hi = (hi + Math.imul(ah6, bh7)) | 0;
    lo = (lo + Math.imul(al5, bl8)) | 0;
    mid = (mid + Math.imul(al5, bh8)) | 0;
    mid = (mid + Math.imul(ah5, bl8)) | 0;
    hi = (hi + Math.imul(ah5, bh8)) | 0;
    lo = (lo + Math.imul(al4, bl9)) | 0;
    mid = (mid + Math.imul(al4, bh9)) | 0;
    mid = (mid + Math.imul(ah4, bl9)) | 0;
    hi = (hi + Math.imul(ah4, bh9)) | 0;
    var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0;
    w13 &= 0x3ffffff;
    /* k = 14 */
    lo = Math.imul(al9, bl5);
    mid = Math.imul(al9, bh5);
    mid = (mid + Math.imul(ah9, bl5)) | 0;
    hi = Math.imul(ah9, bh5);
    lo = (lo + Math.imul(al8, bl6)) | 0;
    mid = (mid + Math.imul(al8, bh6)) | 0;
    mid = (mid + Math.imul(ah8, bl6)) | 0;
    hi = (hi + Math.imul(ah8, bh6)) | 0;
    lo = (lo + Math.imul(al7, bl7)) | 0;
    mid = (mid + Math.imul(al7, bh7)) | 0;
    mid = (mid + Math.imul(ah7, bl7)) | 0;
    hi = (hi + Math.imul(ah7, bh7)) | 0;
    lo = (lo + Math.imul(al6, bl8)) | 0;
    mid = (mid + Math.imul(al6, bh8)) | 0;
    mid = (mid + Math.imul(ah6, bl8)) | 0;
    hi = (hi + Math.imul(ah6, bh8)) | 0;
    lo = (lo + Math.imul(al5, bl9)) | 0;
    mid = (mid + Math.imul(al5, bh9)) | 0;
    mid = (mid + Math.imul(ah5, bl9)) | 0;
    hi = (hi + Math.imul(ah5, bh9)) | 0;
    var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0;
    w14 &= 0x3ffffff;
    /* k = 15 */
    lo = Math.imul(al9, bl6);
    mid = Math.imul(al9, bh6);
    mid = (mid + Math.imul(ah9, bl6)) | 0;
    hi = Math.imul(ah9, bh6);
    lo = (lo + Math.imul(al8, bl7)) | 0;
    mid = (mid + Math.imul(al8, bh7)) | 0;
    mid = (mid + Math.imul(ah8, bl7)) | 0;
    hi = (hi + Math.imul(ah8, bh7)) | 0;
    lo = (lo + Math.imul(al7, bl8)) | 0;
    mid = (mid + Math.imul(al7, bh8)) | 0;
    mid = (mid + Math.imul(ah7, bl8)) | 0;
    hi = (hi + Math.imul(ah7, bh8)) | 0;
    lo = (lo + Math.imul(al6, bl9)) | 0;
    mid = (mid + Math.imul(al6, bh9)) | 0;
    mid = (mid + Math.imul(ah6, bl9)) | 0;
    hi = (hi + Math.imul(ah6, bh9)) | 0;
    var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0;
    w15 &= 0x3ffffff;
    /* k = 16 */
    lo = Math.imul(al9, bl7);
    mid = Math.imul(al9, bh7);
    mid = (mid + Math.imul(ah9, bl7)) | 0;
    hi = Math.imul(ah9, bh7);
    lo = (lo + Math.imul(al8, bl8)) | 0;
    mid = (mid + Math.imul(al8, bh8)) | 0;
    mid = (mid + Math.imul(ah8, bl8)) | 0;
    hi = (hi + Math.imul(ah8, bh8)) | 0;
    lo = (lo + Math.imul(al7, bl9)) | 0;
    mid = (mid + Math.imul(al7, bh9)) | 0;
    mid = (mid + Math.imul(ah7, bl9)) | 0;
    hi = (hi + Math.imul(ah7, bh9)) | 0;
    var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0;
    w16 &= 0x3ffffff;
    /* k = 17 */
    lo = Math.imul(al9, bl8);
    mid = Math.imul(al9, bh8);
    mid = (mid + Math.imul(ah9, bl8)) | 0;
    hi = Math.imul(ah9, bh8);
    lo = (lo + Math.imul(al8, bl9)) | 0;
    mid = (mid + Math.imul(al8, bh9)) | 0;
    mid = (mid + Math.imul(ah8, bl9)) | 0;
    hi = (hi + Math.imul(ah8, bh9)) | 0;
    var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0;
    w17 &= 0x3ffffff;
    /* k = 18 */
    lo = Math.imul(al9, bl9);
    mid = Math.imul(al9, bh9);
    mid = (mid + Math.imul(ah9, bl9)) | 0;
    hi = Math.imul(ah9, bh9);
    var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0;
    c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0;
    w18 &= 0x3ffffff;
    o[0] = w0;
    o[1] = w1;
    o[2] = w2;
    o[3] = w3;
    o[4] = w4;
    o[5] = w5;
    o[6] = w6;
    o[7] = w7;
    o[8] = w8;
    o[9] = w9;
    o[10] = w10;
    o[11] = w11;
    o[12] = w12;
    o[13] = w13;
    o[14] = w14;
    o[15] = w15;
    o[16] = w16;
    o[17] = w17;
    o[18] = w18;
    if (c !== 0) {
      o[19] = c;
      out.length++;
    }
    return out;
  };

  // Polyfill comb
  if (!Math.imul) {
    comb10MulTo = smallMulTo;
  }

  function bigMulTo (self, num, out) {
    out.negative = num.negative ^ self.negative;
    out.length = self.length + num.length;

    var carry = 0;
    var hncarry = 0;
    for (var k = 0; k < out.length - 1; k++) {
      // Sum all words with the same `i + j = k` and accumulate `ncarry`,
      // note that ncarry could be >= 0x3ffffff
      var ncarry = hncarry;
      hncarry = 0;
      var rword = carry & 0x3ffffff;
      var maxJ = Math.min(k, num.length - 1);
      for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) {
        var i = k - j;
        var a = self.words[i] | 0;
        var b = num.words[j] | 0;
        var r = a * b;

        var lo = r & 0x3ffffff;
        ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0;
        lo = (lo + rword) | 0;
        rword = lo & 0x3ffffff;
        ncarry = (ncarry + (lo >>> 26)) | 0;

        hncarry += ncarry >>> 26;
        ncarry &= 0x3ffffff;
      }
      out.words[k] = rword;
      carry = ncarry;
      ncarry = hncarry;
    }
    if (carry !== 0) {
      out.words[k] = carry;
    } else {
      out.length--;
    }

    return out.strip();
  }

  function jumboMulTo (self, num, out) {
    var fftm = new FFTM();
    return fftm.mulp(self, num, out);
  }

  BN.prototype.mulTo = function mulTo (num, out) {
    var res;
    var len = this.length + num.length;
    if (this.length === 10 && num.length === 10) {
      res = comb10MulTo(this, num, out);
    } else if (len < 63) {
      res = smallMulTo(this, num, out);
    } else if (len < 1024) {
      res = bigMulTo(this, num, out);
    } else {
      res = jumboMulTo(this, num, out);
    }

    return res;
  };

  // Cooley-Tukey algorithm for FFT
  // slightly revisited to rely on looping instead of recursion

  function FFTM (x, y) {
    this.x = x;
    this.y = y;
  }

  FFTM.prototype.makeRBT = function makeRBT (N) {
    var t = new Array(N);
    var l = BN.prototype._countBits(N) - 1;
    for (var i = 0; i < N; i++) {
      t[i] = this.revBin(i, l, N);
    }

    return t;
  };

  // Returns binary-reversed representation of `x`
  FFTM.prototype.revBin = function revBin (x, l, N) {
    if (x === 0 || x === N - 1) return x;

    var rb = 0;
    for (var i = 0; i < l; i++) {
      rb |= (x & 1) << (l - i - 1);
      x >>= 1;
    }

    return rb;
  };

  // Performs "tweedling" phase, therefore 'emulating'
  // behaviour of the recursive algorithm
  FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) {
    for (var i = 0; i < N; i++) {
      rtws[i] = rws[rbt[i]];
      itws[i] = iws[rbt[i]];
    }
  };

  FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) {
    this.permute(rbt, rws, iws, rtws, itws, N);

    for (var s = 1; s < N; s <<= 1) {
      var l = s << 1;

      var rtwdf = Math.cos(2 * Math.PI / l);
      var itwdf = Math.sin(2 * Math.PI / l);

      for (var p = 0; p < N; p += l) {
        var rtwdf_ = rtwdf;
        var itwdf_ = itwdf;

        for (var j = 0; j < s; j++) {
          var re = rtws[p + j];
          var ie = itws[p + j];

          var ro = rtws[p + j + s];
          var io = itws[p + j + s];

          var rx = rtwdf_ * ro - itwdf_ * io;

          io = rtwdf_ * io + itwdf_ * ro;
          ro = rx;

          rtws[p + j] = re + ro;
          itws[p + j] = ie + io;

          rtws[p + j + s] = re - ro;
          itws[p + j + s] = ie - io;

          /* jshint maxdepth : false */
          if (j !== l) {
            rx = rtwdf * rtwdf_ - itwdf * itwdf_;

            itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_;
            rtwdf_ = rx;
          }
        }
      }
    }
  };

  FFTM.prototype.guessLen13b = function guessLen13b (n, m) {
    var N = Math.max(m, n) | 1;
    var odd = N & 1;
    var i = 0;
    for (N = N / 2 | 0; N; N = N >>> 1) {
      i++;
    }

    return 1 << i + 1 + odd;
  };

  FFTM.prototype.conjugate = function conjugate (rws, iws, N) {
    if (N <= 1) return;

    for (var i = 0; i < N / 2; i++) {
      var t = rws[i];

      rws[i] = rws[N - i - 1];
      rws[N - i - 1] = t;

      t = iws[i];

      iws[i] = -iws[N - i - 1];
      iws[N - i - 1] = -t;
    }
  };

  FFTM.prototype.normalize13b = function normalize13b (ws, N) {
    var carry = 0;
    for (var i = 0; i < N / 2; i++) {
      var w = Math.round(ws[2 * i + 1] / N) * 0x2000 +
        Math.round(ws[2 * i] / N) +
        carry;

      ws[i] = w & 0x3ffffff;

      if (w < 0x4000000) {
        carry = 0;
      } else {
        carry = w / 0x4000000 | 0;
      }
    }

    return ws;
  };

  FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) {
    var carry = 0;
    for (var i = 0; i < len; i++) {
      carry = carry + (ws[i] | 0);

      rws[2 * i] = carry & 0x1fff; carry = carry >>> 13;
      rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13;
    }

    // Pad with zeroes
    for (i = 2 * len; i < N; ++i) {
      rws[i] = 0;
    }

    assert(carry === 0);
    assert((carry & ~0x1fff) === 0);
  };

  FFTM.prototype.stub = function stub (N) {
    var ph = new Array(N);
    for (var i = 0; i < N; i++) {
      ph[i] = 0;
    }

    return ph;
  };

  FFTM.prototype.mulp = function mulp (x, y, out) {
    var N = 2 * this.guessLen13b(x.length, y.length);

    var rbt = this.makeRBT(N);

    var _ = this.stub(N);

    var rws = new Array(N);
    var rwst = new Array(N);
    var iwst = new Array(N);

    var nrws = new Array(N);
    var nrwst = new Array(N);
    var niwst = new Array(N);

    var rmws = out.words;
    rmws.length = N;

    this.convert13b(x.words, x.length, rws, N);
    this.convert13b(y.words, y.length, nrws, N);

    this.transform(rws, _, rwst, iwst, N, rbt);
    this.transform(nrws, _, nrwst, niwst, N, rbt);

    for (var i = 0; i < N; i++) {
      var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i];
      iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i];
      rwst[i] = rx;
    }

    this.conjugate(rwst, iwst, N);
    this.transform(rwst, iwst, rmws, _, N, rbt);
    this.conjugate(rmws, _, N);
    this.normalize13b(rmws, N);

    out.negative = x.negative ^ y.negative;
    out.length = x.length + y.length;
    return out.strip();
  };

  // Multiply `this` by `num`
  BN.prototype.mul = function mul (num) {
    var out = new BN(null);
    out.words = new Array(this.length + num.length);
    return this.mulTo(num, out);
  };

  // Multiply employing FFT
  BN.prototype.mulf = function mulf (num) {
    var out = new BN(null);
    out.words = new Array(this.length + num.length);
    return jumboMulTo(this, num, out);
  };

  // In-place Multiplication
  BN.prototype.imul = function imul (num) {
    return this.clone().mulTo(num, this);
  };

  BN.prototype.imuln = function imuln (num) {
    assert(typeof num === 'number');
    assert(num < 0x4000000);

    // Carry
    var carry = 0;
    for (var i = 0; i < this.length; i++) {
      var w = (this.words[i] | 0) * num;
      var lo = (w & 0x3ffffff) + (carry & 0x3ffffff);
      carry >>= 26;
      carry += (w / 0x4000000) | 0;
      // NOTE: lo is 27bit maximum
      carry += lo >>> 26;
      this.words[i] = lo & 0x3ffffff;
    }

    if (carry !== 0) {
      this.words[i] = carry;
      this.length++;
    }

    return this;
  };

  BN.prototype.muln = function muln (num) {
    return this.clone().imuln(num);
  };

  // `this` * `this`
  BN.prototype.sqr = function sqr () {
    return this.mul(this);
  };

  // `this` * `this` in-place
  BN.prototype.isqr = function isqr () {
    return this.imul(this.clone());
  };

  // Math.pow(`this`, `num`)
  BN.prototype.pow = function pow (num) {
    var w = toBitArray(num);
    if (w.length === 0) return new BN(1);

    // Skip leading zeroes
    var res = this;
    for (var i = 0; i < w.length; i++, res = res.sqr()) {
      if (w[i] !== 0) break;
    }

    if (++i < w.length) {
      for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) {
        if (w[i] === 0) continue;

        res = res.mul(q);
      }
    }

    return res;
  };

  // Shift-left in-place
  BN.prototype.iushln = function iushln (bits) {
    assert(typeof bits === 'number' && bits >= 0);
    var r = bits % 26;
    var s = (bits - r) / 26;
    var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r);
    var i;

    if (r !== 0) {
      var carry = 0;

      for (i = 0; i < this.length; i++) {
        var newCarry = this.words[i] & carryMask;
        var c = ((this.words[i] | 0) - newCarry) << r;
        this.words[i] = c | carry;
        carry = newCarry >>> (26 - r);
      }

      if (carry) {
        this.words[i] = carry;
        this.length++;
      }
    }

    if (s !== 0) {
      for (i = this.length - 1; i >= 0; i--) {
        this.words[i + s] = this.words[i];
      }

      for (i = 0; i < s; i++) {
        this.words[i] = 0;
      }

      this.length += s;
    }

    return this.strip();
  };

  BN.prototype.ishln = function ishln (bits) {
    // TODO(indutny): implement me
    assert(this.negative === 0);
    return this.iushln(bits);
  };

  // Shift-right in-place
  // NOTE: `hint` is a lowest bit before trailing zeroes
  // NOTE: if `extended` is present - it will be filled with destroyed bits
  BN.prototype.iushrn = function iushrn (bits, hint, extended) {
    assert(typeof bits === 'number' && bits >= 0);
    var h;
    if (hint) {
      h = (hint - (hint % 26)) / 26;
    } else {
      h = 0;
    }

    var r = bits % 26;
    var s = Math.min((bits - r) / 26, this.length);
    var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r);
    var maskedWords = extended;

    h -= s;
    h = Math.max(0, h);

    // Extended mode, copy masked part
    if (maskedWords) {
      for (var i = 0; i < s; i++) {
        maskedWords.words[i] = this.words[i];
      }
      maskedWords.length = s;
    }

    if (s === 0) ; else if (this.length > s) {
      this.length -= s;
      for (i = 0; i < this.length; i++) {
        this.words[i] = this.words[i + s];
      }
    } else {
      this.words[0] = 0;
      this.length = 1;
    }

    var carry = 0;
    for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) {
      var word = this.words[i] | 0;
      this.words[i] = (carry << (26 - r)) | (word >>> r);
      carry = word & mask;
    }

    // Push carried bits as a mask
    if (maskedWords && carry !== 0) {
      maskedWords.words[maskedWords.length++] = carry;
    }

    if (this.length === 0) {
      this.words[0] = 0;
      this.length = 1;
    }

    return this.strip();
  };

  BN.prototype.ishrn = function ishrn (bits, hint, extended) {
    // TODO(indutny): implement me
    assert(this.negative === 0);
    return this.iushrn(bits, hint, extended);
  };

  // Shift-left
  BN.prototype.shln = function shln (bits) {
    return this.clone().ishln(bits);
  };

  BN.prototype.ushln = function ushln (bits) {
    return this.clone().iushln(bits);
  };

  // Shift-right
  BN.prototype.shrn = function shrn (bits) {
    return this.clone().ishrn(bits);
  };

  BN.prototype.ushrn = function ushrn (bits) {
    return this.clone().iushrn(bits);
  };

  // Test if n bit is set
  BN.prototype.testn = function testn (bit) {
    assert(typeof bit === 'number' && bit >= 0);
    var r = bit % 26;
    var s = (bit - r) / 26;
    var q = 1 << r;

    // Fast case: bit is much higher than all existing words
    if (this.length <= s) return false;

    // Check bit and return
    var w = this.words[s];

    return !!(w & q);
  };

  // Return only lowers bits of number (in-place)
  BN.prototype.imaskn = function imaskn (bits) {
    assert(typeof bits === 'number' && bits >= 0);
    var r = bits % 26;
    var s = (bits - r) / 26;

    assert(this.negative === 0, 'imaskn works only with positive numbers');

    if (this.length <= s) {
      return this;
    }

    if (r !== 0) {
      s++;
    }
    this.length = Math.min(s, this.length);

    if (r !== 0) {
      var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r);
      this.words[this.length - 1] &= mask;
    }

    return this.strip();
  };

  // Return only lowers bits of number
  BN.prototype.maskn = function maskn (bits) {
    return this.clone().imaskn(bits);
  };

  // Add plain number `num` to `this`
  BN.prototype.iaddn = function iaddn (num) {
    assert(typeof num === 'number');
    assert(num < 0x4000000);
    if (num < 0) return this.isubn(-num);

    // Possible sign change
    if (this.negative !== 0) {
      if (this.length === 1 && (this.words[0] | 0) < num) {
        this.words[0] = num - (this.words[0] | 0);
        this.negative = 0;
        return this;
      }

      this.negative = 0;
      this.isubn(num);
      this.negative = 1;
      return this;
    }

    // Add without checks
    return this._iaddn(num);
  };

  BN.prototype._iaddn = function _iaddn (num) {
    this.words[0] += num;

    // Carry
    for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) {
      this.words[i] -= 0x4000000;
      if (i === this.length - 1) {
        this.words[i + 1] = 1;
      } else {
        this.words[i + 1]++;
      }
    }
    this.length = Math.max(this.length, i + 1);

    return this;
  };

  // Subtract plain number `num` from `this`
  BN.prototype.isubn = function isubn (num) {
    assert(typeof num === 'number');
    assert(num < 0x4000000);
    if (num < 0) return this.iaddn(-num);

    if (this.negative !== 0) {
      this.negative = 0;
      this.iaddn(num);
      this.negative = 1;
      return this;
    }

    this.words[0] -= num;

    if (this.length === 1 && this.words[0] < 0) {
      this.words[0] = -this.words[0];
      this.negative = 1;
    } else {
      // Carry
      for (var i = 0; i < this.length && this.words[i] < 0; i++) {
        this.words[i] += 0x4000000;
        this.words[i + 1] -= 1;
      }
    }

    return this.strip();
  };

  BN.prototype.addn = function addn (num) {
    return this.clone().iaddn(num);
  };

  BN.prototype.subn = function subn (num) {
    return this.clone().isubn(num);
  };

  BN.prototype.iabs = function iabs () {
    this.negative = 0;

    return this;
  };

  BN.prototype.abs = function abs () {
    return this.clone().iabs();
  };

  BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) {
    var len = num.length + shift;
    var i;

    this._expand(len);

    var w;
    var carry = 0;
    for (i = 0; i < num.length; i++) {
      w = (this.words[i + shift] | 0) + carry;
      var right = (num.words[i] | 0) * mul;
      w -= right & 0x3ffffff;
      carry = (w >> 26) - ((right / 0x4000000) | 0);
      this.words[i + shift] = w & 0x3ffffff;
    }
    for (; i < this.length - shift; i++) {
      w = (this.words[i + shift] | 0) + carry;
      carry = w >> 26;
      this.words[i + shift] = w & 0x3ffffff;
    }

    if (carry === 0) return this.strip();

    // Subtraction overflow
    assert(carry === -1);
    carry = 0;
    for (i = 0; i < this.length; i++) {
      w = -(this.words[i] | 0) + carry;
      carry = w >> 26;
      this.words[i] = w & 0x3ffffff;
    }
    this.negative = 1;

    return this.strip();
  };

  BN.prototype._wordDiv = function _wordDiv (num, mode) {
    var shift = this.length - num.length;

    var a = this.clone();
    var b = num;

    // Normalize
    var bhi = b.words[b.length - 1] | 0;
    var bhiBits = this._countBits(bhi);
    shift = 26 - bhiBits;
    if (shift !== 0) {
      b = b.ushln(shift);
      a.iushln(shift);
      bhi = b.words[b.length - 1] | 0;
    }

    // Initialize quotient
    var m = a.length - b.length;
    var q;

    if (mode !== 'mod') {
      q = new BN(null);
      q.length = m + 1;
      q.words = new Array(q.length);
      for (var i = 0; i < q.length; i++) {
        q.words[i] = 0;
      }
    }

    var diff = a.clone()._ishlnsubmul(b, 1, m);
    if (diff.negative === 0) {
      a = diff;
      if (q) {
        q.words[m] = 1;
      }
    }

    for (var j = m - 1; j >= 0; j--) {
      var qj = (a.words[b.length + j] | 0) * 0x4000000 +
        (a.words[b.length + j - 1] | 0);

      // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max
      // (0x7ffffff)
      qj = Math.min((qj / bhi) | 0, 0x3ffffff);

      a._ishlnsubmul(b, qj, j);
      while (a.negative !== 0) {
        qj--;
        a.negative = 0;
        a._ishlnsubmul(b, 1, j);
        if (!a.isZero()) {
          a.negative ^= 1;
        }
      }
      if (q) {
        q.words[j] = qj;
      }
    }
    if (q) {
      q.strip();
    }
    a.strip();

    // Denormalize
    if (mode !== 'div' && shift !== 0) {
      a.iushrn(shift);
    }

    return {
      div: q || null,
      mod: a
    };
  };

  // NOTE: 1) `mode` can be set to `mod` to request mod only,
  //       to `div` to request div only, or be absent to
  //       request both div & mod
  //       2) `positive` is true if unsigned mod is requested
  BN.prototype.divmod = function divmod (num, mode, positive) {
    assert(!num.isZero());

    if (this.isZero()) {
      return {
        div: new BN(0),
        mod: new BN(0)
      };
    }

    var div, mod, res;
    if (this.negative !== 0 && num.negative === 0) {
      res = this.neg().divmod(num, mode);

      if (mode !== 'mod') {
        div = res.div.neg();
      }

      if (mode !== 'div') {
        mod = res.mod.neg();
        if (positive && mod.negative !== 0) {
          mod.iadd(num);
        }
      }

      return {
        div: div,
        mod: mod
      };
    }

    if (this.negative === 0 && num.negative !== 0) {
      res = this.divmod(num.neg(), mode);

      if (mode !== 'mod') {
        div = res.div.neg();
      }

      return {
        div: div,
        mod: res.mod
      };
    }

    if ((this.negative & num.negative) !== 0) {
      res = this.neg().divmod(num.neg(), mode);

      if (mode !== 'div') {
        mod = res.mod.neg();
        if (positive && mod.negative !== 0) {
          mod.isub(num);
        }
      }

      return {
        div: res.div,
        mod: mod
      };
    }

    // Both numbers are positive at this point

    // Strip both numbers to approximate shift value
    if (num.length > this.length || this.cmp(num) < 0) {
      return {
        div: new BN(0),
        mod: this
      };
    }

    // Very short reduction
    if (num.length === 1) {
      if (mode === 'div') {
        return {
          div: this.divn(num.words[0]),
          mod: null
        };
      }

      if (mode === 'mod') {
        return {
          div: null,
          mod: new BN(this.modn(num.words[0]))
        };
      }

      return {
        div: this.divn(num.words[0]),
        mod: new BN(this.modn(num.words[0]))
      };
    }

    return this._wordDiv(num, mode);
  };

  // Find `this` / `num`
  BN.prototype.div = function div (num) {
    return this.divmod(num, 'div', false).div;
  };

  // Find `this` % `num`
  BN.prototype.mod = function mod (num) {
    return this.divmod(num, 'mod', false).mod;
  };

  BN.prototype.umod = function umod (num) {
    return this.divmod(num, 'mod', true).mod;
  };

  // Find Round(`this` / `num`)
  BN.prototype.divRound = function divRound (num) {
    var dm = this.divmod(num);

    // Fast case - exact division
    if (dm.mod.isZero()) return dm.div;

    var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod;

    var half = num.ushrn(1);
    var r2 = num.andln(1);
    var cmp = mod.cmp(half);

    // Round down
    if (cmp < 0 || r2 === 1 && cmp === 0) return dm.div;

    // Round up
    return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1);
  };

  BN.prototype.modn = function modn (num) {
    assert(num <= 0x3ffffff);
    var p = (1 << 26) % num;

    var acc = 0;
    for (var i = this.length - 1; i >= 0; i--) {
      acc = (p * acc + (this.words[i] | 0)) % num;
    }

    return acc;
  };

  // In-place division by number
  BN.prototype.idivn = function idivn (num) {
    assert(num <= 0x3ffffff);

    var carry = 0;
    for (var i = this.length - 1; i >= 0; i--) {
      var w = (this.words[i] | 0) + carry * 0x4000000;
      this.words[i] = (w / num) | 0;
      carry = w % num;
    }

    return this.strip();
  };

  BN.prototype.divn = function divn (num) {
    return this.clone().idivn(num);
  };

  BN.prototype.egcd = function egcd (p) {
    assert(p.negative === 0);
    assert(!p.isZero());

    var x = this;
    var y = p.clone();

    if (x.negative !== 0) {
      x = x.umod(p);
    } else {
      x = x.clone();
    }

    // A * x + B * y = x
    var A = new BN(1);
    var B = new BN(0);

    // C * x + D * y = y
    var C = new BN(0);
    var D = new BN(1);

    var g = 0;

    while (x.isEven() && y.isEven()) {
      x.iushrn(1);
      y.iushrn(1);
      ++g;
    }

    var yp = y.clone();
    var xp = x.clone();

    while (!x.isZero()) {
      for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1);
      if (i > 0) {
        x.iushrn(i);
        while (i-- > 0) {
          if (A.isOdd() || B.isOdd()) {
            A.iadd(yp);
            B.isub(xp);
          }

          A.iushrn(1);
          B.iushrn(1);
        }
      }

      for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1);
      if (j > 0) {
        y.iushrn(j);
        while (j-- > 0) {
          if (C.isOdd() || D.isOdd()) {
            C.iadd(yp);
            D.isub(xp);
          }

          C.iushrn(1);
          D.iushrn(1);
        }
      }

      if (x.cmp(y) >= 0) {
        x.isub(y);
        A.isub(C);
        B.isub(D);
      } else {
        y.isub(x);
        C.isub(A);
        D.isub(B);
      }
    }

    return {
      a: C,
      b: D,
      gcd: y.iushln(g)
    };
  };

  // This is reduced incarnation of the binary EEA
  // above, designated to invert members of the
  // _prime_ fields F(p) at a maximal speed
  BN.prototype._invmp = function _invmp (p) {
    assert(p.negative === 0);
    assert(!p.isZero());

    var a = this;
    var b = p.clone();

    if (a.negative !== 0) {
      a = a.umod(p);
    } else {
      a = a.clone();
    }

    var x1 = new BN(1);
    var x2 = new BN(0);

    var delta = b.clone();

    while (a.cmpn(1) > 0 && b.cmpn(1) > 0) {
      for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1);
      if (i > 0) {
        a.iushrn(i);
        while (i-- > 0) {
          if (x1.isOdd()) {
            x1.iadd(delta);
          }

          x1.iushrn(1);
        }
      }

      for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1);
      if (j > 0) {
        b.iushrn(j);
        while (j-- > 0) {
          if (x2.isOdd()) {
            x2.iadd(delta);
          }

          x2.iushrn(1);
        }
      }

      if (a.cmp(b) >= 0) {
        a.isub(b);
        x1.isub(x2);
      } else {
        b.isub(a);
        x2.isub(x1);
      }
    }

    var res;
    if (a.cmpn(1) === 0) {
      res = x1;
    } else {
      res = x2;
    }

    if (res.cmpn(0) < 0) {
      res.iadd(p);
    }

    return res;
  };

  BN.prototype.gcd = function gcd (num) {
    if (this.isZero()) return num.abs();
    if (num.isZero()) return this.abs();

    var a = this.clone();
    var b = num.clone();
    a.negative = 0;
    b.negative = 0;

    // Remove common factor of two
    for (var shift = 0; a.isEven() && b.isEven(); shift++) {
      a.iushrn(1);
      b.iushrn(1);
    }

    do {
      while (a.isEven()) {
        a.iushrn(1);
      }
      while (b.isEven()) {
        b.iushrn(1);
      }

      var r = a.cmp(b);
      if (r < 0) {
        // Swap `a` and `b` to make `a` always bigger than `b`
        var t = a;
        a = b;
        b = t;
      } else if (r === 0 || b.cmpn(1) === 0) {
        break;
      }

      a.isub(b);
    } while (true);

    return b.iushln(shift);
  };

  // Invert number in the field F(num)
  BN.prototype.invm = function invm (num) {
    return this.egcd(num).a.umod(num);
  };

  BN.prototype.isEven = function isEven () {
    return (this.words[0] & 1) === 0;
  };

  BN.prototype.isOdd = function isOdd () {
    return (this.words[0] & 1) === 1;
  };

  // And first word and num
  BN.prototype.andln = function andln (num) {
    return this.words[0] & num;
  };

  // Increment at the bit position in-line
  BN.prototype.bincn = function bincn (bit) {
    assert(typeof bit === 'number');
    var r = bit % 26;
    var s = (bit - r) / 26;
    var q = 1 << r;

    // Fast case: bit is much higher than all existing words
    if (this.length <= s) {
      this._expand(s + 1);
      this.words[s] |= q;
      return this;
    }

    // Add bit and propagate, if needed
    var carry = q;
    for (var i = s; carry !== 0 && i < this.length; i++) {
      var w = this.words[i] | 0;
      w += carry;
      carry = w >>> 26;
      w &= 0x3ffffff;
      this.words[i] = w;
    }
    if (carry !== 0) {
      this.words[i] = carry;
      this.length++;
    }
    return this;
  };

  BN.prototype.isZero = function isZero () {
    return this.length === 1 && this.words[0] === 0;
  };

  BN.prototype.cmpn = function cmpn (num) {
    var negative = num < 0;

    if (this.negative !== 0 && !negative) return -1;
    if (this.negative === 0 && negative) return 1;

    this.strip();

    var res;
    if (this.length > 1) {
      res = 1;
    } else {
      if (negative) {
        num = -num;
      }

      assert(num <= 0x3ffffff, 'Number is too big');

      var w = this.words[0] | 0;
      res = w === num ? 0 : w < num ? -1 : 1;
    }
    if (this.negative !== 0) return -res | 0;
    return res;
  };

  // Compare two numbers and return:
  // 1 - if `this` > `num`
  // 0 - if `this` == `num`
  // -1 - if `this` < `num`
  BN.prototype.cmp = function cmp (num) {
    if (this.negative !== 0 && num.negative === 0) return -1;
    if (this.negative === 0 && num.negative !== 0) return 1;

    var res = this.ucmp(num);
    if (this.negative !== 0) return -res | 0;
    return res;
  };

  // Unsigned comparison
  BN.prototype.ucmp = function ucmp (num) {
    // At this point both numbers have the same sign
    if (this.length > num.length) return 1;
    if (this.length < num.length) return -1;

    var res = 0;
    for (var i = this.length - 1; i >= 0; i--) {
      var a = this.words[i] | 0;
      var b = num.words[i] | 0;

      if (a === b) continue;
      if (a < b) {
        res = -1;
      } else if (a > b) {
        res = 1;
      }
      break;
    }
    return res;
  };

  BN.prototype.gtn = function gtn (num) {
    return this.cmpn(num) === 1;
  };

  BN.prototype.gt = function gt (num) {
    return this.cmp(num) === 1;
  };

  BN.prototype.gten = function gten (num) {
    return this.cmpn(num) >= 0;
  };

  BN.prototype.gte = function gte (num) {
    return this.cmp(num) >= 0;
  };

  BN.prototype.ltn = function ltn (num) {
    return this.cmpn(num) === -1;
  };

  BN.prototype.lt = function lt (num) {
    return this.cmp(num) === -1;
  };

  BN.prototype.lten = function lten (num) {
    return this.cmpn(num) <= 0;
  };

  BN.prototype.lte = function lte (num) {
    return this.cmp(num) <= 0;
  };

  BN.prototype.eqn = function eqn (num) {
    return this.cmpn(num) === 0;
  };

  BN.prototype.eq = function eq (num) {
    return this.cmp(num) === 0;
  };

  //
  // A reduce context, could be using montgomery or something better, depending
  // on the `m` itself.
  //
  BN.red = function red (num) {
    return new Red(num);
  };

  BN.prototype.toRed = function toRed (ctx) {
    assert(!this.red, 'Already a number in reduction context');
    assert(this.negative === 0, 'red works only with positives');
    return ctx.convertTo(this)._forceRed(ctx);
  };

  BN.prototype.fromRed = function fromRed () {
    assert(this.red, 'fromRed works only with numbers in reduction context');
    return this.red.convertFrom(this);
  };

  BN.prototype._forceRed = function _forceRed (ctx) {
    this.red = ctx;
    return this;
  };

  BN.prototype.forceRed = function forceRed (ctx) {
    assert(!this.red, 'Already a number in reduction context');
    return this._forceRed(ctx);
  };

  BN.prototype.redAdd = function redAdd (num) {
    assert(this.red, 'redAdd works only with red numbers');
    return this.red.add(this, num);
  };

  BN.prototype.redIAdd = function redIAdd (num) {
    assert(this.red, 'redIAdd works only with red numbers');
    return this.red.iadd(this, num);
  };

  BN.prototype.redSub = function redSub (num) {
    assert(this.red, 'redSub works only with red numbers');
    return this.red.sub(this, num);
  };

  BN.prototype.redISub = function redISub (num) {
    assert(this.red, 'redISub works only with red numbers');
    return this.red.isub(this, num);
  };

  BN.prototype.redShl = function redShl (num) {
    assert(this.red, 'redShl works only with red numbers');
    return this.red.shl(this, num);
  };

  BN.prototype.redMul = function redMul (num) {
    assert(this.red, 'redMul works only with red numbers');
    this.red._verify2(this, num);
    return this.red.mul(this, num);
  };

  BN.prototype.redIMul = function redIMul (num) {
    assert(this.red, 'redMul works only with red numbers');
    this.red._verify2(this, num);
    return this.red.imul(this, num);
  };

  BN.prototype.redSqr = function redSqr () {
    assert(this.red, 'redSqr works only with red numbers');
    this.red._verify1(this);
    return this.red.sqr(this);
  };

  BN.prototype.redISqr = function redISqr () {
    assert(this.red, 'redISqr works only with red numbers');
    this.red._verify1(this);
    return this.red.isqr(this);
  };

  // Square root over p
  BN.prototype.redSqrt = function redSqrt () {
    assert(this.red, 'redSqrt works only with red numbers');
    this.red._verify1(this);
    return this.red.sqrt(this);
  };

  BN.prototype.redInvm = function redInvm () {
    assert(this.red, 'redInvm works only with red numbers');
    this.red._verify1(this);
    return this.red.invm(this);
  };

  // Return negative clone of `this` % `red modulo`
  BN.prototype.redNeg = function redNeg () {
    assert(this.red, 'redNeg works only with red numbers');
    this.red._verify1(this);
    return this.red.neg(this);
  };

  BN.prototype.redPow = function redPow (num) {
    assert(this.red && !num.red, 'redPow(normalNum)');
    this.red._verify1(this);
    return this.red.pow(this, num);
  };

  // Prime numbers with efficient reduction
  var primes = {
    k256: null,
    p224: null,
    p192: null,
    p25519: null
  };

  // Pseudo-Mersenne prime
  function MPrime (name, p) {
    // P = 2 ^ N - K
    this.name = name;
    this.p = new BN(p, 16);
    this.n = this.p.bitLength();
    this.k = new BN(1).iushln(this.n).isub(this.p);

    this.tmp = this._tmp();
  }

  MPrime.prototype._tmp = function _tmp () {
    var tmp = new BN(null);
    tmp.words = new Array(Math.ceil(this.n / 13));
    return tmp;
  };

  MPrime.prototype.ireduce = function ireduce (num) {
    // Assumes that `num` is less than `P^2`
    // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P)
    var r = num;
    var rlen;

    do {
      this.split(r, this.tmp);
      r = this.imulK(r);
      r = r.iadd(this.tmp);
      rlen = r.bitLength();
    } while (rlen > this.n);

    var cmp = rlen < this.n ? -1 : r.ucmp(this.p);
    if (cmp === 0) {
      r.words[0] = 0;
      r.length = 1;
    } else if (cmp > 0) {
      r.isub(this.p);
    } else {
      r.strip();
    }

    return r;
  };

  MPrime.prototype.split = function split (input, out) {
    input.iushrn(this.n, 0, out);
  };

  MPrime.prototype.imulK = function imulK (num) {
    return num.imul(this.k);
  };

  function K256 () {
    MPrime.call(
      this,
      'k256',
      'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f');
  }
  inherits(K256, MPrime);

  K256.prototype.split = function split (input, output) {
    // 256 = 9 * 26 + 22
    var mask = 0x3fffff;

    var outLen = Math.min(input.length, 9);
    for (var i = 0; i < outLen; i++) {
      output.words[i] = input.words[i];
    }
    output.length = outLen;

    if (input.length <= 9) {
      input.words[0] = 0;
      input.length = 1;
      return;
    }

    // Shift by 9 limbs
    var prev = input.words[9];
    output.words[output.length++] = prev & mask;

    for (i = 10; i < input.length; i++) {
      var next = input.words[i] | 0;
      input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22);
      prev = next;
    }
    prev >>>= 22;
    input.words[i - 10] = prev;
    if (prev === 0 && input.length > 10) {
      input.length -= 10;
    } else {
      input.length -= 9;
    }
  };

  K256.prototype.imulK = function imulK (num) {
    // K = 0x1000003d1 = [ 0x40, 0x3d1 ]
    num.words[num.length] = 0;
    num.words[num.length + 1] = 0;
    num.length += 2;

    // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390
    var lo = 0;
    for (var i = 0; i < num.length; i++) {
      var w = num.words[i] | 0;
      lo += w * 0x3d1;
      num.words[i] = lo & 0x3ffffff;
      lo = w * 0x40 + ((lo / 0x4000000) | 0);
    }

    // Fast length reduction
    if (num.words[num.length - 1] === 0) {
      num.length--;
      if (num.words[num.length - 1] === 0) {
        num.length--;
      }
    }
    return num;
  };

  function P224 () {
    MPrime.call(
      this,
      'p224',
      'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001');
  }
  inherits(P224, MPrime);

  function P192 () {
    MPrime.call(
      this,
      'p192',
      'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff');
  }
  inherits(P192, MPrime);

  function P25519 () {
    // 2 ^ 255 - 19
    MPrime.call(
      this,
      '25519',
      '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed');
  }
  inherits(P25519, MPrime);

  P25519.prototype.imulK = function imulK (num) {
    // K = 0x13
    var carry = 0;
    for (var i = 0; i < num.length; i++) {
      var hi = (num.words[i] | 0) * 0x13 + carry;
      var lo = hi & 0x3ffffff;
      hi >>>= 26;

      num.words[i] = lo;
      carry = hi;
    }
    if (carry !== 0) {
      num.words[num.length++] = carry;
    }
    return num;
  };

  // Exported mostly for testing purposes, use plain name instead
  BN._prime = function prime (name) {
    // Cached version of prime
    if (primes[name]) return primes[name];

    var prime;
    if (name === 'k256') {
      prime = new K256();
    } else if (name === 'p224') {
      prime = new P224();
    } else if (name === 'p192') {
      prime = new P192();
    } else if (name === 'p25519') {
      prime = new P25519();
    } else {
      throw new Error('Unknown prime ' + name);
    }
    primes[name] = prime;

    return prime;
  };

  //
  // Base reduction engine
  //
  function Red (m) {
    if (typeof m === 'string') {
      var prime = BN._prime(m);
      this.m = prime.p;
      this.prime = prime;
    } else {
      assert(m.gtn(1), 'modulus must be greater than 1');
      this.m = m;
      this.prime = null;
    }
  }

  Red.prototype._verify1 = function _verify1 (a) {
    assert(a.negative === 0, 'red works only with positives');
    assert(a.red, 'red works only with red numbers');
  };

  Red.prototype._verify2 = function _verify2 (a, b) {
    assert((a.negative | b.negative) === 0, 'red works only with positives');
    assert(a.red && a.red === b.red,
      'red works only with red numbers');
  };

  Red.prototype.imod = function imod (a) {
    if (this.prime) return this.prime.ireduce(a)._forceRed(this);
    return a.umod(this.m)._forceRed(this);
  };

  Red.prototype.neg = function neg (a) {
    if (a.isZero()) {
      return a.clone();
    }

    return this.m.sub(a)._forceRed(this);
  };

  Red.prototype.add = function add (a, b) {
    this._verify2(a, b);

    var res = a.add(b);
    if (res.cmp(this.m) >= 0) {
      res.isub(this.m);
    }
    return res._forceRed(this);
  };

  Red.prototype.iadd = function iadd (a, b) {
    this._verify2(a, b);

    var res = a.iadd(b);
    if (res.cmp(this.m) >= 0) {
      res.isub(this.m);
    }
    return res;
  };

  Red.prototype.sub = function sub (a, b) {
    this._verify2(a, b);

    var res = a.sub(b);
    if (res.cmpn(0) < 0) {
      res.iadd(this.m);
    }
    return res._forceRed(this);
  };

  Red.prototype.isub = function isub (a, b) {
    this._verify2(a, b);

    var res = a.isub(b);
    if (res.cmpn(0) < 0) {
      res.iadd(this.m);
    }
    return res;
  };

  Red.prototype.shl = function shl (a, num) {
    this._verify1(a);
    return this.imod(a.ushln(num));
  };

  Red.prototype.imul = function imul (a, b) {
    this._verify2(a, b);
    return this.imod(a.imul(b));
  };

  Red.prototype.mul = function mul (a, b) {
    this._verify2(a, b);
    return this.imod(a.mul(b));
  };

  Red.prototype.isqr = function isqr (a) {
    return this.imul(a, a.clone());
  };

  Red.prototype.sqr = function sqr (a) {
    return this.mul(a, a);
  };

  Red.prototype.sqrt = function sqrt (a) {
    if (a.isZero()) return a.clone();

    var mod3 = this.m.andln(3);
    assert(mod3 % 2 === 1);

    // Fast case
    if (mod3 === 3) {
      var pow = this.m.add(new BN(1)).iushrn(2);
      return this.pow(a, pow);
    }

    // Tonelli-Shanks algorithm (Totally unoptimized and slow)
    //
    // Find Q and S, that Q * 2 ^ S = (P - 1)
    var q = this.m.subn(1);
    var s = 0;
    while (!q.isZero() && q.andln(1) === 0) {
      s++;
      q.iushrn(1);
    }
    assert(!q.isZero());

    var one = new BN(1).toRed(this);
    var nOne = one.redNeg();

    // Find quadratic non-residue
    // NOTE: Max is such because of generalized Riemann hypothesis.
    var lpow = this.m.subn(1).iushrn(1);
    var z = this.m.bitLength();
    z = new BN(2 * z * z).toRed(this);

    while (this.pow(z, lpow).cmp(nOne) !== 0) {
      z.redIAdd(nOne);
    }

    var c = this.pow(z, q);
    var r = this.pow(a, q.addn(1).iushrn(1));
    var t = this.pow(a, q);
    var m = s;
    while (t.cmp(one) !== 0) {
      var tmp = t;
      for (var i = 0; tmp.cmp(one) !== 0; i++) {
        tmp = tmp.redSqr();
      }
      assert(i < m);
      var b = this.pow(c, new BN(1).iushln(m - i - 1));

      r = r.redMul(b);
      c = b.redSqr();
      t = t.redMul(c);
      m = i;
    }

    return r;
  };

  Red.prototype.invm = function invm (a) {
    var inv = a._invmp(this.m);
    if (inv.negative !== 0) {
      inv.negative = 0;
      return this.imod(inv).redNeg();
    } else {
      return this.imod(inv);
    }
  };

  Red.prototype.pow = function pow (a, num) {
    if (num.isZero()) return new BN(1).toRed(this);
    if (num.cmpn(1) === 0) return a.clone();

    var windowSize = 4;
    var wnd = new Array(1 << windowSize);
    wnd[0] = new BN(1).toRed(this);
    wnd[1] = a;
    for (var i = 2; i < wnd.length; i++) {
      wnd[i] = this.mul(wnd[i - 1], a);
    }

    var res = wnd[0];
    var current = 0;
    var currentLen = 0;
    var start = num.bitLength() % 26;
    if (start === 0) {
      start = 26;
    }

    for (i = num.length - 1; i >= 0; i--) {
      var word = num.words[i];
      for (var j = start - 1; j >= 0; j--) {
        var bit = (word >> j) & 1;
        if (res !== wnd[0]) {
          res = this.sqr(res);
        }

        if (bit === 0 && current === 0) {
          currentLen = 0;
          continue;
        }

        current <<= 1;
        current |= bit;
        currentLen++;
        if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue;

        res = this.mul(res, wnd[current]);
        currentLen = 0;
        current = 0;
      }
      start = 26;
    }

    return res;
  };

  Red.prototype.convertTo = function convertTo (num) {
    var r = num.umod(this.m);

    return r === num ? r.clone() : r;
  };

  Red.prototype.convertFrom = function convertFrom (num) {
    var res = num.clone();
    res.red = null;
    return res;
  };

  //
  // Montgomery method engine
  //

  BN.mont = function mont (num) {
    return new Mont(num);
  };

  function Mont (m) {
    Red.call(this, m);

    this.shift = this.m.bitLength();
    if (this.shift % 26 !== 0) {
      this.shift += 26 - (this.shift % 26);
    }

    this.r = new BN(1).iushln(this.shift);
    this.r2 = this.imod(this.r.sqr());
    this.rinv = this.r._invmp(this.m);

    this.minv = this.rinv.mul(this.r).isubn(1).div(this.m);
    this.minv = this.minv.umod(this.r);
    this.minv = this.r.sub(this.minv);
  }
  inherits(Mont, Red);

  Mont.prototype.convertTo = function convertTo (num) {
    return this.imod(num.ushln(this.shift));
  };

  Mont.prototype.convertFrom = function convertFrom (num) {
    var r = this.imod(num.mul(this.rinv));
    r.red = null;
    return r;
  };

  Mont.prototype.imul = function imul (a, b) {
    if (a.isZero() || b.isZero()) {
      a.words[0] = 0;
      a.length = 1;
      return a;
    }

    var t = a.imul(b);
    var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m);
    var u = t.isub(c).iushrn(this.shift);
    var res = u;

    if (u.cmp(this.m) >= 0) {
      res = u.isub(this.m);
    } else if (u.cmpn(0) < 0) {
      res = u.iadd(this.m);
    }

    return res._forceRed(this);
  };

  Mont.prototype.mul = function mul (a, b) {
    if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this);

    var t = a.mul(b);
    var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m);
    var u = t.isub(c).iushrn(this.shift);
    var res = u;
    if (u.cmp(this.m) >= 0) {
      res = u.isub(this.m);
    } else if (u.cmpn(0) < 0) {
      res = u.iadd(this.m);
    }

    return res._forceRed(this);
  };

  Mont.prototype.invm = function invm (a) {
    // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R
    var res = this.imod(a._invmp(this.m).mul(this.r2));
    return res._forceRed(this);
  };
})(module, commonjsGlobal);
});

var bn$1 = /*#__PURE__*/Object.freeze({
  __proto__: null,
  'default': bn,
  __moduleExports: bn
});

/**
 * @fileoverview
 * BigInteger implementation of basic operations
 * Wrapper of bn.js library (wwww.github.com/indutny/bn.js)
 * @module biginteger/bn
 * @private
 */

/**
 * @private
 */
class BigInteger$1 {
  /**
   * Get a BigInteger (input must be big endian for strings and arrays)
   * @param {Number|String|Uint8Array} n - Value to convert
   * @throws {Error} on undefined input
   */
  constructor(n) {
    if (n === undefined) {
      throw new Error('Invalid BigInteger input');
    }

    this.value = new bn(n);
  }

  clone() {
    const clone = new BigInteger$1(null);
    this.value.copy(clone.value);
    return clone;
  }

  /**
   * BigInteger increment in place
   */
  iinc() {
    this.value.iadd(new bn(1));
    return this;
  }

  /**
   * BigInteger increment
   * @returns {BigInteger} this + 1.
   */
  inc() {
    return this.clone().iinc();
  }

  /**
   * BigInteger decrement in place
   */
  idec() {
    this.value.isub(new bn(1));
    return this;
  }

  /**
   * BigInteger decrement
   * @returns {BigInteger} this - 1.
   */
  dec() {
    return this.clone().idec();
  }


  /**
   * BigInteger addition in place
   * @param {BigInteger} x - Value to add
   */
  iadd(x) {
    this.value.iadd(x.value);
    return this;
  }

  /**
   * BigInteger addition
   * @param {BigInteger} x - Value to add
   * @returns {BigInteger} this + x.
   */
  add(x) {
    return this.clone().iadd(x);
  }

  /**
   * BigInteger subtraction in place
   * @param {BigInteger} x - Value to subtract
   */
  isub(x) {
    this.value.isub(x.value);
    return this;
  }

  /**
   * BigInteger subtraction
   * @param {BigInteger} x - Value to subtract
   * @returns {BigInteger} this - x.
   */
  sub(x) {
    return this.clone().isub(x);
  }

  /**
   * BigInteger multiplication in place
   * @param {BigInteger} x - Value to multiply
   */
  imul(x) {
    this.value.imul(x.value);
    return this;
  }

  /**
   * BigInteger multiplication
   * @param {BigInteger} x - Value to multiply
   * @returns {BigInteger} this * x.
   */
  mul(x) {
    return this.clone().imul(x);
  }

  /**
   * Compute value modulo m, in place
   * @param {BigInteger} m - Modulo
   */
  imod(m) {
    this.value = this.value.umod(m.value);
    return this;
  }

  /**
   * Compute value modulo m
   * @param {BigInteger} m - Modulo
   * @returns {BigInteger} this mod m.
   */
  mod(m) {
    return this.clone().imod(m);
  }

  /**
   * Compute modular exponentiation
   * Much faster than this.exp(e).mod(n)
   * @param {BigInteger} e - Exponent
   * @param {BigInteger} n - Modulo
   * @returns {BigInteger} this ** e mod n.
   */
  modExp(e, n) {
    // We use either Montgomery or normal reduction context
    // Montgomery requires coprime n and R (montogmery multiplier)
    //  bn.js picks R as power of 2, so n must be odd
    const nred = n.isEven() ? bn.red(n.value) : bn.mont(n.value);
    const x = this.clone();
    x.value = x.value.toRed(nred).redPow(e.value).fromRed();
    return x;
  }

  /**
   * Compute the inverse of this value modulo n
   * Note: this and and n must be relatively prime
   * @param {BigInteger} n - Modulo
   * @returns {BigInteger} x such that this*x = 1 mod n
   * @throws {Error} if the inverse does not exist
   */
  modInv(n) {
    // invm returns a wrong result if the inverse does not exist
    if (!this.gcd(n).isOne()) {
      throw new Error('Inverse does not exist');
    }
    return new BigInteger$1(this.value.invm(n.value));
  }

  /**
   * Compute greatest common divisor between this and n
   * @param {BigInteger} n - Operand
   * @returns {BigInteger} gcd
   */
  gcd(n) {
    return new BigInteger$1(this.value.gcd(n.value));
  }

  /**
   * Shift this to the left by x, in place
   * @param {BigInteger} x - Shift value
   */
  ileftShift(x) {
    this.value.ishln(x.value.toNumber());
    return this;
  }

  /**
   * Shift this to the left by x
   * @param {BigInteger} x - Shift value
   * @returns {BigInteger} this << x.
   */
  leftShift(x) {
    return this.clone().ileftShift(x);
  }

  /**
   * Shift this to the right by x, in place
   * @param {BigInteger} x - Shift value
   */
  irightShift(x) {
    this.value.ishrn(x.value.toNumber());
    return this;
  }

  /**
   * Shift this to the right by x
   * @param {BigInteger} x - Shift value
   * @returns {BigInteger} this >> x.
   */
  rightShift(x) {
    return this.clone().irightShift(x);
  }

  /**
   * Whether this value is equal to x
   * @param {BigInteger} x
   * @returns {Boolean}
   */
  equal(x) {
    return this.value.eq(x.value);
  }

  /**
   * Whether this value is less than x
   * @param {BigInteger} x
   * @returns {Boolean}
   */
  lt(x) {
    return this.value.lt(x.value);
  }

  /**
   * Whether this value is less than or equal to x
   * @param {BigInteger} x
   * @returns {Boolean}
   */
  lte(x) {
    return this.value.lte(x.value);
  }

  /**
   * Whether this value is greater than x
   * @param {BigInteger} x
   * @returns {Boolean}
   */
  gt(x) {
    return this.value.gt(x.value);
  }

  /**
   * Whether this value is greater than or equal to x
   * @param {BigInteger} x
   * @returns {Boolean}
   */
  gte(x) {
    return this.value.gte(x.value);
  }

  isZero() {
    return this.value.isZero();
  }

  isOne() {
    return this.value.eq(new bn(1));
  }

  isNegative() {
    return this.value.isNeg();
  }

  isEven() {
    return this.value.isEven();
  }

  abs() {
    const res = this.clone();
    res.value = res.value.abs();
    return res;
  }

  /**
   * Get this value as a string
   * @returns {String} this value.
   */
  toString() {
    return this.value.toString();
  }

  /**
   * Get this value as an exact Number (max 53 bits)
   * Fails if this value is too large
   * @returns {Number}
   */
  toNumber() {
    return this.value.toNumber();
  }

  /**
   * Get value of i-th bit
   * @param {Number} i - Bit index
   * @returns {Number} Bit value.
   */
  getBit(i) {
    return this.value.testn(i) ? 1 : 0;
  }

  /**
   * Compute bit length
   * @returns {Number} Bit length.
   */
  bitLength() {
    return this.value.bitLength();
  }

  /**
   * Compute byte length
   * @returns {Number} Byte length.
   */
  byteLength() {
    return this.value.byteLength();
  }

  /**
   * Get Uint8Array representation of this number
   * @param {String} endian - Endianess of output array (defaults to 'be')
   * @param {Number} length - Of output array
   * @returns {Uint8Array}
   */
  toUint8Array(endian = 'be', length) {
    return this.value.toArrayLike(Uint8Array, endian, length);
  }
}

var bn_interface = /*#__PURE__*/Object.freeze({
  __proto__: null,
  'default': BigInteger$1
});

var utils_1 = createCommonjsModule(function (module, exports) {

var utils = exports;

function toArray(msg, enc) {
  if (Array.isArray(msg))
    return msg.slice();
  if (!msg)
    return [];
  var res = [];
  if (typeof msg !== 'string') {
    for (var i = 0; i < msg.length; i++)
      res[i] = msg[i] | 0;
    return res;
  }
  if (enc === 'hex') {
    msg = msg.replace(/[^a-z0-9]+/ig, '');
    if (msg.length % 2 !== 0)
      msg = '0' + msg;
    for (var i = 0; i < msg.length; i += 2)
      res.push(parseInt(msg[i] + msg[i + 1], 16));
  } else {
    for (var i = 0; i < msg.length; i++) {
      var c = msg.charCodeAt(i);
      var hi = c >> 8;
      var lo = c & 0xff;
      if (hi)
        res.push(hi, lo);
      else
        res.push(lo);
    }
  }
  return res;
}
utils.toArray = toArray;

function zero2(word) {
  if (word.length === 1)
    return '0' + word;
  else
    return word;
}
utils.zero2 = zero2;

function toHex(msg) {
  var res = '';
  for (var i = 0; i < msg.length; i++)
    res += zero2(msg[i].toString(16));
  return res;
}
utils.toHex = toHex;

utils.encode = function encode(arr, enc) {
  if (enc === 'hex')
    return toHex(arr);
  else
    return arr;
};
});

var utils_1$1 = createCommonjsModule(function (module, exports) {

var utils = exports;




utils.assert = minimalisticAssert;
utils.toArray = utils_1.toArray;
utils.zero2 = utils_1.zero2;
utils.toHex = utils_1.toHex;
utils.encode = utils_1.encode;

// Represent num in a w-NAF form
function getNAF(num, w) {
  var naf = [];
  var ws = 1 << (w + 1);
  var k = num.clone();
  while (k.cmpn(1) >= 0) {
    var z;
    if (k.isOdd()) {
      var mod = k.andln(ws - 1);
      if (mod > (ws >> 1) - 1)
        z = (ws >> 1) - mod;
      else
        z = mod;
      k.isubn(z);
    } else {
      z = 0;
    }
    naf.push(z);

    // Optimization, shift by word if possible
    var shift = (k.cmpn(0) !== 0 && k.andln(ws - 1) === 0) ? (w + 1) : 1;
    for (var i = 1; i < shift; i++)
      naf.push(0);
    k.iushrn(shift);
  }

  return naf;
}
utils.getNAF = getNAF;

// Represent k1, k2 in a Joint Sparse Form
function getJSF(k1, k2) {
  var jsf = [
    [],
    []
  ];

  k1 = k1.clone();
  k2 = k2.clone();
  var d1 = 0;
  var d2 = 0;
  while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) {

    // First phase
    var m14 = (k1.andln(3) + d1) & 3;
    var m24 = (k2.andln(3) + d2) & 3;
    if (m14 === 3)
      m14 = -1;
    if (m24 === 3)
      m24 = -1;
    var u1;
    if ((m14 & 1) === 0) {
      u1 = 0;
    } else {
      var m8 = (k1.andln(7) + d1) & 7;
      if ((m8 === 3 || m8 === 5) && m24 === 2)
        u1 = -m14;
      else
        u1 = m14;
    }
    jsf[0].push(u1);

    var u2;
    if ((m24 & 1) === 0) {
      u2 = 0;
    } else {
      var m8 = (k2.andln(7) + d2) & 7;
      if ((m8 === 3 || m8 === 5) && m14 === 2)
        u2 = -m24;
      else
        u2 = m24;
    }
    jsf[1].push(u2);

    // Second phase
    if (2 * d1 === u1 + 1)
      d1 = 1 - d1;
    if (2 * d2 === u2 + 1)
      d2 = 1 - d2;
    k1.iushrn(1);
    k2.iushrn(1);
  }

  return jsf;
}
utils.getJSF = getJSF;

function cachedProperty(obj, name, computer) {
  var key = '_' + name;
  obj.prototype[name] = function cachedProperty() {
    return this[key] !== undefined ? this[key] :
           this[key] = computer.call(this);
  };
}
utils.cachedProperty = cachedProperty;

function parseBytes(bytes) {
  return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') :
                                     bytes;
}
utils.parseBytes = parseBytes;

function intFromLE(bytes) {
  return new bn(bytes, 'hex', 'le');
}
utils.intFromLE = intFromLE;
});

var r$1;

var brorand = function rand(len) {
  if (!r$1)
    r$1 = new Rand(null);

  return r$1.generate(len);
};

function Rand(rand) {
  this.rand = rand;
}
var Rand_1 = Rand;

Rand.prototype.generate = function generate(len) {
  return this._rand(len);
};

// Emulate crypto API using randy
Rand.prototype._rand = function _rand(n) {
  if (this.rand.getBytes)
    return this.rand.getBytes(n);

  var res = new Uint8Array(n);
  for (var i = 0; i < res.length; i++)
    res[i] = this.rand.getByte();
  return res;
};

if (typeof self === 'object') {
  if (self.crypto && self.crypto.getRandomValues) {
    // Modern browsers
    Rand.prototype._rand = function _rand(n) {
      var arr = new Uint8Array(n);
      self.crypto.getRandomValues(arr);
      return arr;
    };
  } else if (self.msCrypto && self.msCrypto.getRandomValues) {
    // IE
    Rand.prototype._rand = function _rand(n) {
      var arr = new Uint8Array(n);
      self.msCrypto.getRandomValues(arr);
      return arr;
    };

  // Safari's WebWorkers do not have `crypto`
  } else if (typeof window === 'object') {
    // Old junk
    Rand.prototype._rand = function() {
      throw new Error('Not implemented yet');
    };
  }
} else {
  // Node.js or Web worker with no crypto support
  try {
    var crypto$2 = crypto__default['default'];
    if (typeof crypto$2.randomBytes !== 'function')
      throw new Error('Not supported');

    Rand.prototype._rand = function _rand(n) {
      return crypto$2.randomBytes(n);
    };
  } catch (e) {
  }
}
brorand.Rand = Rand_1;

var getNAF = utils_1$1.getNAF;
var getJSF = utils_1$1.getJSF;
var assert$2 = utils_1$1.assert;

function BaseCurve(type, conf) {
  this.type = type;
  this.p = new bn(conf.p, 16);

  // Use Montgomery, when there is no fast reduction for the prime
  this.red = conf.prime ? bn.red(conf.prime) : bn.mont(this.p);

  // Useful for many curves
  this.zero = new bn(0).toRed(this.red);
  this.one = new bn(1).toRed(this.red);
  this.two = new bn(2).toRed(this.red);

  // Curve configuration, optional
  this.n = conf.n && new bn(conf.n, 16);
  this.g = conf.g && this.pointFromJSON(conf.g, conf.gRed);

  // Temporary arrays
  this._wnafT1 = new Array(4);
  this._wnafT2 = new Array(4);
  this._wnafT3 = new Array(4);
  this._wnafT4 = new Array(4);

  // Generalized Greg Maxwell's trick
  var adjustCount = this.n && this.p.div(this.n);
  if (!adjustCount || adjustCount.cmpn(100) > 0) {
    this.redN = null;
  } else {
    this._maxwellTrick = true;
    this.redN = this.n.toRed(this.red);
  }
}
var base = BaseCurve;

BaseCurve.prototype.point = function point() {
  throw new Error('Not implemented');
};

BaseCurve.prototype.validate = function validate() {
  throw new Error('Not implemented');
};

BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) {
  assert$2(p.precomputed);
  var doubles = p._getDoubles();

  var naf = getNAF(k, 1);
  var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0 ? 2 : 1);
  I /= 3;

  // Translate into more windowed form
  var repr = [];
  for (var j = 0; j < naf.length; j += doubles.step) {
    var nafW = 0;
    for (var k = j + doubles.step - 1; k >= j; k--)
      nafW = (nafW << 1) + naf[k];
    repr.push(nafW);
  }

  var a = this.jpoint(null, null, null);
  var b = this.jpoint(null, null, null);
  for (var i = I; i > 0; i--) {
    for (var j = 0; j < repr.length; j++) {
      var nafW = repr[j];
      if (nafW === i)
        b = b.mixedAdd(doubles.points[j]);
      else if (nafW === -i)
        b = b.mixedAdd(doubles.points[j].neg());
    }
    a = a.add(b);
  }
  return a.toP();
};

BaseCurve.prototype._wnafMul = function _wnafMul(p, k) {
  var w = 4;

  // Precompute window
  var nafPoints = p._getNAFPoints(w);
  w = nafPoints.wnd;
  var wnd = nafPoints.points;

  // Get NAF form
  var naf = getNAF(k, w);

  // Add `this`*(N+1) for every w-NAF index
  var acc = this.jpoint(null, null, null);
  for (var i = naf.length - 1; i >= 0; i--) {
    // Count zeroes
    for (var k = 0; i >= 0 && naf[i] === 0; i--)
      k++;
    if (i >= 0)
      k++;
    acc = acc.dblp(k);

    if (i < 0)
      break;
    var z = naf[i];
    assert$2(z !== 0);
    if (p.type === 'affine') {
      // J +- P
      if (z > 0)
        acc = acc.mixedAdd(wnd[(z - 1) >> 1]);
      else
        acc = acc.mixedAdd(wnd[(-z - 1) >> 1].neg());
    } else {
      // J +- J
      if (z > 0)
        acc = acc.add(wnd[(z - 1) >> 1]);
      else
        acc = acc.add(wnd[(-z - 1) >> 1].neg());
    }
  }
  return p.type === 'affine' ? acc.toP() : acc;
};

BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW,
                                                       points,
                                                       coeffs,
                                                       len,
                                                       jacobianResult) {
  var wndWidth = this._wnafT1;
  var wnd = this._wnafT2;
  var naf = this._wnafT3;

  // Fill all arrays
  var max = 0;
  for (var i = 0; i < len; i++) {
    var p = points[i];
    var nafPoints = p._getNAFPoints(defW);
    wndWidth[i] = nafPoints.wnd;
    wnd[i] = nafPoints.points;
  }

  // Comb small window NAFs
  for (var i = len - 1; i >= 1; i -= 2) {
    var a = i - 1;
    var b = i;
    if (wndWidth[a] !== 1 || wndWidth[b] !== 1) {
      naf[a] = getNAF(coeffs[a], wndWidth[a]);
      naf[b] = getNAF(coeffs[b], wndWidth[b]);
      max = Math.max(naf[a].length, max);
      max = Math.max(naf[b].length, max);
      continue;
    }

    var comb = [
      points[a], /* 1 */
      null, /* 3 */
      null, /* 5 */
      points[b] /* 7 */
    ];

    // Try to avoid Projective points, if possible
    if (points[a].y.cmp(points[b].y) === 0) {
      comb[1] = points[a].add(points[b]);
      comb[2] = points[a].toJ().mixedAdd(points[b].neg());
    } else if (points[a].y.cmp(points[b].y.redNeg()) === 0) {
      comb[1] = points[a].toJ().mixedAdd(points[b]);
      comb[2] = points[a].add(points[b].neg());
    } else {
      comb[1] = points[a].toJ().mixedAdd(points[b]);
      comb[2] = points[a].toJ().mixedAdd(points[b].neg());
    }

    var index = [
      -3, /* -1 -1 */
      -1, /* -1 0 */
      -5, /* -1 1 */
      -7, /* 0 -1 */
      0, /* 0 0 */
      7, /* 0 1 */
      5, /* 1 -1 */
      1, /* 1 0 */
      3  /* 1 1 */
    ];

    var jsf = getJSF(coeffs[a], coeffs[b]);
    max = Math.max(jsf[0].length, max);
    naf[a] = new Array(max);
    naf[b] = new Array(max);
    for (var j = 0; j < max; j++) {
      var ja = jsf[0][j] | 0;
      var jb = jsf[1][j] | 0;

      naf[a][j] = index[(ja + 1) * 3 + (jb + 1)];
      naf[b][j] = 0;
      wnd[a] = comb;
    }
  }

  var acc = this.jpoint(null, null, null);
  var tmp = this._wnafT4;
  for (var i = max; i >= 0; i--) {
    var k = 0;

    while (i >= 0) {
      var zero = true;
      for (var j = 0; j < len; j++) {
        tmp[j] = naf[j][i] | 0;
        if (tmp[j] !== 0)
          zero = false;
      }
      if (!zero)
        break;
      k++;
      i--;
    }
    if (i >= 0)
      k++;
    acc = acc.dblp(k);
    if (i < 0)
      break;

    for (var j = 0; j < len; j++) {
      var z = tmp[j];
      var p;
      if (z === 0)
        continue;
      else if (z > 0)
        p = wnd[j][(z - 1) >> 1];
      else if (z < 0)
        p = wnd[j][(-z - 1) >> 1].neg();

      if (p.type === 'affine')
        acc = acc.mixedAdd(p);
      else
        acc = acc.add(p);
    }
  }
  // Zeroify references
  for (var i = 0; i < len; i++)
    wnd[i] = null;

  if (jacobianResult)
    return acc;
  else
    return acc.toP();
};

function BasePoint(curve, type) {
  this.curve = curve;
  this.type = type;
  this.precomputed = null;
}
BaseCurve.BasePoint = BasePoint;

BasePoint.prototype.eq = function eq(/*other*/) {
  throw new Error('Not implemented');
};

BasePoint.prototype.validate = function validate() {
  return this.curve.validate(this);
};

BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) {
  bytes = utils_1$1.toArray(bytes, enc);

  var len = this.p.byteLength();

  // uncompressed, hybrid-odd, hybrid-even
  if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) &&
      bytes.length - 1 === 2 * len) {
    if (bytes[0] === 0x06)
      assert$2(bytes[bytes.length - 1] % 2 === 0);
    else if (bytes[0] === 0x07)
      assert$2(bytes[bytes.length - 1] % 2 === 1);

    var res =  this.point(bytes.slice(1, 1 + len),
                          bytes.slice(1 + len, 1 + 2 * len));

    return res;
  } else if ((bytes[0] === 0x02 || bytes[0] === 0x03) &&
              bytes.length - 1 === len) {
    return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03);
  }
  throw new Error('Unknown point format');
};

BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) {
  return this.encode(enc, true);
};

BasePoint.prototype._encode = function _encode(compact) {
  var len = this.curve.p.byteLength();
  var x = this.getX().toArray('be', len);

  if (compact)
    return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x);

  return [ 0x04 ].concat(x, this.getY().toArray('be', len)) ;
};

BasePoint.prototype.encode = function encode(enc, compact) {
  return utils_1$1.encode(this._encode(compact), enc);
};

BasePoint.prototype.precompute = function precompute(power) {
  if (this.precomputed)
    return this;

  var precomputed = {
    doubles: null,
    naf: null,
    beta: null
  };
  precomputed.naf = this._getNAFPoints(8);
  precomputed.doubles = this._getDoubles(4, power);
  precomputed.beta = this._getBeta();
  this.precomputed = precomputed;

  return this;
};

BasePoint.prototype._hasDoubles = function _hasDoubles(k) {
  if (!this.precomputed)
    return false;

  var doubles = this.precomputed.doubles;
  if (!doubles)
    return false;

  return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step);
};

BasePoint.prototype._getDoubles = function _getDoubles(step, power) {
  if (this.precomputed && this.precomputed.doubles)
    return this.precomputed.doubles;

  var doubles = [ this ];
  var acc = this;
  for (var i = 0; i < power; i += step) {
    for (var j = 0; j < step; j++)
      acc = acc.dbl();
    doubles.push(acc);
  }
  return {
    step: step,
    points: doubles
  };
};

BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) {
  if (this.precomputed && this.precomputed.naf)
    return this.precomputed.naf;

  var res = [ this ];
  var max = (1 << wnd) - 1;
  var dbl = max === 1 ? null : this.dbl();
  for (var i = 1; i < max; i++)
    res[i] = res[i - 1].add(dbl);
  return {
    wnd: wnd,
    points: res
  };
};

BasePoint.prototype._getBeta = function _getBeta() {
  return null;
};

BasePoint.prototype.dblp = function dblp(k) {
  var r = this;
  for (var i = 0; i < k; i++)
    r = r.dbl();
  return r;
};

var assert$3 = utils_1$1.assert;

function ShortCurve(conf) {
  base.call(this, 'short', conf);

  this.a = new bn(conf.a, 16).toRed(this.red);
  this.b = new bn(conf.b, 16).toRed(this.red);
  this.tinv = this.two.redInvm();

  this.zeroA = this.a.fromRed().cmpn(0) === 0;
  this.threeA = this.a.fromRed().sub(this.p).cmpn(-3) === 0;

  // If the curve is endomorphic, precalculate beta and lambda
  this.endo = this._getEndomorphism(conf);
  this._endoWnafT1 = new Array(4);
  this._endoWnafT2 = new Array(4);
}
inherits(ShortCurve, base);
var short_1 = ShortCurve;

ShortCurve.prototype._getEndomorphism = function _getEndomorphism(conf) {
  // No efficient endomorphism
  if (!this.zeroA || !this.g || !this.n || this.p.modn(3) !== 1)
    return;

  // Compute beta and lambda, that lambda * P = (beta * Px; Py)
  var beta;
  var lambda;
  if (conf.beta) {
    beta = new bn(conf.beta, 16).toRed(this.red);
  } else {
    var betas = this._getEndoRoots(this.p);
    // Choose the smallest beta
    beta = betas[0].cmp(betas[1]) < 0 ? betas[0] : betas[1];
    beta = beta.toRed(this.red);
  }
  if (conf.lambda) {
    lambda = new bn(conf.lambda, 16);
  } else {
    // Choose the lambda that is matching selected beta
    var lambdas = this._getEndoRoots(this.n);
    if (this.g.mul(lambdas[0]).x.cmp(this.g.x.redMul(beta)) === 0) {
      lambda = lambdas[0];
    } else {
      lambda = lambdas[1];
      assert$3(this.g.mul(lambda).x.cmp(this.g.x.redMul(beta)) === 0);
    }
  }

  // Get basis vectors, used for balanced length-two representation
  var basis;
  if (conf.basis) {
    basis = conf.basis.map(function(vec) {
      return {
        a: new bn(vec.a, 16),
        b: new bn(vec.b, 16)
      };
    });
  } else {
    basis = this._getEndoBasis(lambda);
  }

  return {
    beta: beta,
    lambda: lambda,
    basis: basis
  };
};

ShortCurve.prototype._getEndoRoots = function _getEndoRoots(num) {
  // Find roots of for x^2 + x + 1 in F
  // Root = (-1 +- Sqrt(-3)) / 2
  //
  var red = num === this.p ? this.red : bn.mont(num);
  var tinv = new bn(2).toRed(red).redInvm();
  var ntinv = tinv.redNeg();

  var s = new bn(3).toRed(red).redNeg().redSqrt().redMul(tinv);

  var l1 = ntinv.redAdd(s).fromRed();
  var l2 = ntinv.redSub(s).fromRed();
  return [ l1, l2 ];
};

ShortCurve.prototype._getEndoBasis = function _getEndoBasis(lambda) {
  // aprxSqrt >= sqrt(this.n)
  var aprxSqrt = this.n.ushrn(Math.floor(this.n.bitLength() / 2));

  // 3.74
  // Run EGCD, until r(L + 1) < aprxSqrt
  var u = lambda;
  var v = this.n.clone();
  var x1 = new bn(1);
  var y1 = new bn(0);
  var x2 = new bn(0);
  var y2 = new bn(1);

  // NOTE: all vectors are roots of: a + b * lambda = 0 (mod n)
  var a0;
  var b0;
  // First vector
  var a1;
  var b1;
  // Second vector
  var a2;
  var b2;

  var prevR;
  var i = 0;
  var r;
  var x;
  while (u.cmpn(0) !== 0) {
    var q = v.div(u);
    r = v.sub(q.mul(u));
    x = x2.sub(q.mul(x1));
    var y = y2.sub(q.mul(y1));

    if (!a1 && r.cmp(aprxSqrt) < 0) {
      a0 = prevR.neg();
      b0 = x1;
      a1 = r.neg();
      b1 = x;
    } else if (a1 && ++i === 2) {
      break;
    }
    prevR = r;

    v = u;
    u = r;
    x2 = x1;
    x1 = x;
    y2 = y1;
    y1 = y;
  }
  a2 = r.neg();
  b2 = x;

  var len1 = a1.sqr().add(b1.sqr());
  var len2 = a2.sqr().add(b2.sqr());
  if (len2.cmp(len1) >= 0) {
    a2 = a0;
    b2 = b0;
  }

  // Normalize signs
  if (a1.negative) {
    a1 = a1.neg();
    b1 = b1.neg();
  }
  if (a2.negative) {
    a2 = a2.neg();
    b2 = b2.neg();
  }

  return [
    { a: a1, b: b1 },
    { a: a2, b: b2 }
  ];
};

ShortCurve.prototype._endoSplit = function _endoSplit(k) {
  var basis = this.endo.basis;
  var v1 = basis[0];
  var v2 = basis[1];

  var c1 = v2.b.mul(k).divRound(this.n);
  var c2 = v1.b.neg().mul(k).divRound(this.n);

  var p1 = c1.mul(v1.a);
  var p2 = c2.mul(v2.a);
  var q1 = c1.mul(v1.b);
  var q2 = c2.mul(v2.b);

  // Calculate answer
  var k1 = k.sub(p1).sub(p2);
  var k2 = q1.add(q2).neg();
  return { k1: k1, k2: k2 };
};

ShortCurve.prototype.pointFromX = function pointFromX(x, odd) {
  x = new bn(x, 16);
  if (!x.red)
    x = x.toRed(this.red);

  var y2 = x.redSqr().redMul(x).redIAdd(x.redMul(this.a)).redIAdd(this.b);
  var y = y2.redSqrt();
  if (y.redSqr().redSub(y2).cmp(this.zero) !== 0)
    throw new Error('invalid point');

  // XXX Is there any way to tell if the number is odd without converting it
  // to non-red form?
  var isOdd = y.fromRed().isOdd();
  if (odd && !isOdd || !odd && isOdd)
    y = y.redNeg();

  return this.point(x, y);
};

ShortCurve.prototype.validate = function validate(point) {
  if (point.inf)
    return true;

  var x = point.x;
  var y = point.y;

  var ax = this.a.redMul(x);
  var rhs = x.redSqr().redMul(x).redIAdd(ax).redIAdd(this.b);
  return y.redSqr().redISub(rhs).cmpn(0) === 0;
};

ShortCurve.prototype._endoWnafMulAdd =
    function _endoWnafMulAdd(points, coeffs, jacobianResult) {
  var npoints = this._endoWnafT1;
  var ncoeffs = this._endoWnafT2;
  for (var i = 0; i < points.length; i++) {
    var split = this._endoSplit(coeffs[i]);
    var p = points[i];
    var beta = p._getBeta();

    if (split.k1.negative) {
      split.k1.ineg();
      p = p.neg(true);
    }
    if (split.k2.negative) {
      split.k2.ineg();
      beta = beta.neg(true);
    }

    npoints[i * 2] = p;
    npoints[i * 2 + 1] = beta;
    ncoeffs[i * 2] = split.k1;
    ncoeffs[i * 2 + 1] = split.k2;
  }
  var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2, jacobianResult);

  // Clean-up references to points and coefficients
  for (var j = 0; j < i * 2; j++) {
    npoints[j] = null;
    ncoeffs[j] = null;
  }
  return res;
};

function Point(curve, x, y, isRed) {
  base.BasePoint.call(this, curve, 'affine');
  if (x === null && y === null) {
    this.x = null;
    this.y = null;
    this.inf = true;
  } else {
    this.x = new bn(x, 16);
    this.y = new bn(y, 16);
    // Force redgomery representation when loading from JSON
    if (isRed) {
      this.x.forceRed(this.curve.red);
      this.y.forceRed(this.curve.red);
    }
    if (!this.x.red)
      this.x = this.x.toRed(this.curve.red);
    if (!this.y.red)
      this.y = this.y.toRed(this.curve.red);
    this.inf = false;
  }
}
inherits(Point, base.BasePoint);

ShortCurve.prototype.point = function point(x, y, isRed) {
  return new Point(this, x, y, isRed);
};

ShortCurve.prototype.pointFromJSON = function pointFromJSON(obj, red) {
  return Point.fromJSON(this, obj, red);
};

Point.prototype._getBeta = function _getBeta() {
  if (!this.curve.endo)
    return;

  var pre = this.precomputed;
  if (pre && pre.beta)
    return pre.beta;

  var beta = this.curve.point(this.x.redMul(this.curve.endo.beta), this.y);
  if (pre) {
    var curve = this.curve;
    var endoMul = function(p) {
      return curve.point(p.x.redMul(curve.endo.beta), p.y);
    };
    pre.beta = beta;
    beta.precomputed = {
      beta: null,
      naf: pre.naf && {
        wnd: pre.naf.wnd,
        points: pre.naf.points.map(endoMul)
      },
      doubles: pre.doubles && {
        step: pre.doubles.step,
        points: pre.doubles.points.map(endoMul)
      }
    };
  }
  return beta;
};

Point.prototype.toJSON = function toJSON() {
  if (!this.precomputed)
    return [ this.x, this.y ];

  return [ this.x, this.y, this.precomputed && {
    doubles: this.precomputed.doubles && {
      step: this.precomputed.doubles.step,
      points: this.precomputed.doubles.points.slice(1)
    },
    naf: this.precomputed.naf && {
      wnd: this.precomputed.naf.wnd,
      points: this.precomputed.naf.points.slice(1)
    }
  } ];
};

Point.fromJSON = function fromJSON(curve, obj, red) {
  if (typeof obj === 'string')
    obj = JSON.parse(obj);
  var res = curve.point(obj[0], obj[1], red);
  if (!obj[2])
    return res;

  function obj2point(obj) {
    return curve.point(obj[0], obj[1], red);
  }

  var pre = obj[2];
  res.precomputed = {
    beta: null,
    doubles: pre.doubles && {
      step: pre.doubles.step,
      points: [ res ].concat(pre.doubles.points.map(obj2point))
    },
    naf: pre.naf && {
      wnd: pre.naf.wnd,
      points: [ res ].concat(pre.naf.points.map(obj2point))
    }
  };
  return res;
};

Point.prototype.inspect = function inspect() {
  if (this.isInfinity())
    return '<EC Point Infinity>';
  return '<EC Point x: ' + this.x.fromRed().toString(16, 2) +
      ' y: ' + this.y.fromRed().toString(16, 2) + '>';
};

Point.prototype.isInfinity = function isInfinity() {
  return this.inf;
};

Point.prototype.add = function add(p) {
  // O + P = P
  if (this.inf)
    return p;

  // P + O = P
  if (p.inf)
    return this;

  // P + P = 2P
  if (this.eq(p))
    return this.dbl();

  // P + (-P) = O
  if (this.neg().eq(p))
    return this.curve.point(null, null);

  // P + Q = O
  if (this.x.cmp(p.x) === 0)
    return this.curve.point(null, null);

  var c = this.y.redSub(p.y);
  if (c.cmpn(0) !== 0)
    c = c.redMul(this.x.redSub(p.x).redInvm());
  var nx = c.redSqr().redISub(this.x).redISub(p.x);
  var ny = c.redMul(this.x.redSub(nx)).redISub(this.y);
  return this.curve.point(nx, ny);
};

Point.prototype.dbl = function dbl() {
  if (this.inf)
    return this;

  // 2P = O
  var ys1 = this.y.redAdd(this.y);
  if (ys1.cmpn(0) === 0)
    return this.curve.point(null, null);

  var a = this.curve.a;

  var x2 = this.x.redSqr();
  var dyinv = ys1.redInvm();
  var c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv);

  var nx = c.redSqr().redISub(this.x.redAdd(this.x));
  var ny = c.redMul(this.x.redSub(nx)).redISub(this.y);
  return this.curve.point(nx, ny);
};

Point.prototype.getX = function getX() {
  return this.x.fromRed();
};

Point.prototype.getY = function getY() {
  return this.y.fromRed();
};

Point.prototype.mul = function mul(k) {
  k = new bn(k, 16);
  if (this.isInfinity())
    return this;
  else if (this._hasDoubles(k))
    return this.curve._fixedNafMul(this, k);
  else if (this.curve.endo)
    return this.curve._endoWnafMulAdd([ this ], [ k ]);
  else
    return this.curve._wnafMul(this, k);
};

Point.prototype.mulAdd = function mulAdd(k1, p2, k2) {
  var points = [ this, p2 ];
  var coeffs = [ k1, k2 ];
  if (this.curve.endo)
    return this.curve._endoWnafMulAdd(points, coeffs);
  else
    return this.curve._wnafMulAdd(1, points, coeffs, 2);
};

Point.prototype.jmulAdd = function jmulAdd(k1, p2, k2) {
  var points = [ this, p2 ];
  var coeffs = [ k1, k2 ];
  if (this.curve.endo)
    return this.curve._endoWnafMulAdd(points, coeffs, true);
  else
    return this.curve._wnafMulAdd(1, points, coeffs, 2, true);
};

Point.prototype.eq = function eq(p) {
  return this === p ||
         this.inf === p.inf &&
             (this.inf || this.x.cmp(p.x) === 0 && this.y.cmp(p.y) === 0);
};

Point.prototype.neg = function neg(_precompute) {
  if (this.inf)
    return this;

  var res = this.curve.point(this.x, this.y.redNeg());
  if (_precompute && this.precomputed) {
    var pre = this.precomputed;
    var negate = function(p) {
      return p.neg();
    };
    res.precomputed = {
      naf: pre.naf && {
        wnd: pre.naf.wnd,
        points: pre.naf.points.map(negate)
      },
      doubles: pre.doubles && {
        step: pre.doubles.step,
        points: pre.doubles.points.map(negate)
      }
    };
  }
  return res;
};

Point.prototype.toJ = function toJ() {
  if (this.inf)
    return this.curve.jpoint(null, null, null);

  var res = this.curve.jpoint(this.x, this.y, this.curve.one);
  return res;
};

function JPoint(curve, x, y, z) {
  base.BasePoint.call(this, curve, 'jacobian');
  if (x === null && y === null && z === null) {
    this.x = this.curve.one;
    this.y = this.curve.one;
    this.z = new bn(0);
  } else {
    this.x = new bn(x, 16);
    this.y = new bn(y, 16);
    this.z = new bn(z, 16);
  }
  if (!this.x.red)
    this.x = this.x.toRed(this.curve.red);
  if (!this.y.red)
    this.y = this.y.toRed(this.curve.red);
  if (!this.z.red)
    this.z = this.z.toRed(this.curve.red);

  this.zOne = this.z === this.curve.one;
}
inherits(JPoint, base.BasePoint);

ShortCurve.prototype.jpoint = function jpoint(x, y, z) {
  return new JPoint(this, x, y, z);
};

JPoint.prototype.toP = function toP() {
  if (this.isInfinity())
    return this.curve.point(null, null);

  var zinv = this.z.redInvm();
  var zinv2 = zinv.redSqr();
  var ax = this.x.redMul(zinv2);
  var ay = this.y.redMul(zinv2).redMul(zinv);

  return this.curve.point(ax, ay);
};

JPoint.prototype.neg = function neg() {
  return this.curve.jpoint(this.x, this.y.redNeg(), this.z);
};

JPoint.prototype.add = function add(p) {
  // O + P = P
  if (this.isInfinity())
    return p;

  // P + O = P
  if (p.isInfinity())
    return this;

  // 12M + 4S + 7A
  var pz2 = p.z.redSqr();
  var z2 = this.z.redSqr();
  var u1 = this.x.redMul(pz2);
  var u2 = p.x.redMul(z2);
  var s1 = this.y.redMul(pz2.redMul(p.z));
  var s2 = p.y.redMul(z2.redMul(this.z));

  var h = u1.redSub(u2);
  var r = s1.redSub(s2);
  if (h.cmpn(0) === 0) {
    if (r.cmpn(0) !== 0)
      return this.curve.jpoint(null, null, null);
    else
      return this.dbl();
  }

  var h2 = h.redSqr();
  var h3 = h2.redMul(h);
  var v = u1.redMul(h2);

  var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v);
  var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3));
  var nz = this.z.redMul(p.z).redMul(h);

  return this.curve.jpoint(nx, ny, nz);
};

JPoint.prototype.mixedAdd = function mixedAdd(p) {
  // O + P = P
  if (this.isInfinity())
    return p.toJ();

  // P + O = P
  if (p.isInfinity())
    return this;

  // 8M + 3S + 7A
  var z2 = this.z.redSqr();
  var u1 = this.x;
  var u2 = p.x.redMul(z2);
  var s1 = this.y;
  var s2 = p.y.redMul(z2).redMul(this.z);

  var h = u1.redSub(u2);
  var r = s1.redSub(s2);
  if (h.cmpn(0) === 0) {
    if (r.cmpn(0) !== 0)
      return this.curve.jpoint(null, null, null);
    else
      return this.dbl();
  }

  var h2 = h.redSqr();
  var h3 = h2.redMul(h);
  var v = u1.redMul(h2);

  var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v);
  var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3));
  var nz = this.z.redMul(h);

  return this.curve.jpoint(nx, ny, nz);
};

JPoint.prototype.dblp = function dblp(pow) {
  if (pow === 0)
    return this;
  if (this.isInfinity())
    return this;
  if (!pow)
    return this.dbl();

  if (this.curve.zeroA || this.curve.threeA) {
    var r = this;
    for (var i = 0; i < pow; i++)
      r = r.dbl();
    return r;
  }

  // 1M + 2S + 1A + N * (4S + 5M + 8A)
  // N = 1 => 6M + 6S + 9A
  var a = this.curve.a;
  var tinv = this.curve.tinv;

  var jx = this.x;
  var jy = this.y;
  var jz = this.z;
  var jz4 = jz.redSqr().redSqr();

  // Reuse results
  var jyd = jy.redAdd(jy);
  for (var i = 0; i < pow; i++) {
    var jx2 = jx.redSqr();
    var jyd2 = jyd.redSqr();
    var jyd4 = jyd2.redSqr();
    var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4));

    var t1 = jx.redMul(jyd2);
    var nx = c.redSqr().redISub(t1.redAdd(t1));
    var t2 = t1.redISub(nx);
    var dny = c.redMul(t2);
    dny = dny.redIAdd(dny).redISub(jyd4);
    var nz = jyd.redMul(jz);
    if (i + 1 < pow)
      jz4 = jz4.redMul(jyd4);

    jx = nx;
    jz = nz;
    jyd = dny;
  }

  return this.curve.jpoint(jx, jyd.redMul(tinv), jz);
};

JPoint.prototype.dbl = function dbl() {
  if (this.isInfinity())
    return this;

  if (this.curve.zeroA)
    return this._zeroDbl();
  else if (this.curve.threeA)
    return this._threeDbl();
  else
    return this._dbl();
};

JPoint.prototype._zeroDbl = function _zeroDbl() {
  var nx;
  var ny;
  var nz;
  // Z = 1
  if (this.zOne) {
    // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html
    //     #doubling-mdbl-2007-bl
    // 1M + 5S + 14A

    // XX = X1^2
    var xx = this.x.redSqr();
    // YY = Y1^2
    var yy = this.y.redSqr();
    // YYYY = YY^2
    var yyyy = yy.redSqr();
    // S = 2 * ((X1 + YY)^2 - XX - YYYY)
    var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy);
    s = s.redIAdd(s);
    // M = 3 * XX + a; a = 0
    var m = xx.redAdd(xx).redIAdd(xx);
    // T = M ^ 2 - 2*S
    var t = m.redSqr().redISub(s).redISub(s);

    // 8 * YYYY
    var yyyy8 = yyyy.redIAdd(yyyy);
    yyyy8 = yyyy8.redIAdd(yyyy8);
    yyyy8 = yyyy8.redIAdd(yyyy8);

    // X3 = T
    nx = t;
    // Y3 = M * (S - T) - 8 * YYYY
    ny = m.redMul(s.redISub(t)).redISub(yyyy8);
    // Z3 = 2*Y1
    nz = this.y.redAdd(this.y);
  } else {
    // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html
    //     #doubling-dbl-2009-l
    // 2M + 5S + 13A

    // A = X1^2
    var a = this.x.redSqr();
    // B = Y1^2
    var b = this.y.redSqr();
    // C = B^2
    var c = b.redSqr();
    // D = 2 * ((X1 + B)^2 - A - C)
    var d = this.x.redAdd(b).redSqr().redISub(a).redISub(c);
    d = d.redIAdd(d);
    // E = 3 * A
    var e = a.redAdd(a).redIAdd(a);
    // F = E^2
    var f = e.redSqr();

    // 8 * C
    var c8 = c.redIAdd(c);
    c8 = c8.redIAdd(c8);
    c8 = c8.redIAdd(c8);

    // X3 = F - 2 * D
    nx = f.redISub(d).redISub(d);
    // Y3 = E * (D - X3) - 8 * C
    ny = e.redMul(d.redISub(nx)).redISub(c8);
    // Z3 = 2 * Y1 * Z1
    nz = this.y.redMul(this.z);
    nz = nz.redIAdd(nz);
  }

  return this.curve.jpoint(nx, ny, nz);
};

JPoint.prototype._threeDbl = function _threeDbl() {
  var nx;
  var ny;
  var nz;
  // Z = 1
  if (this.zOne) {
    // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html
    //     #doubling-mdbl-2007-bl
    // 1M + 5S + 15A

    // XX = X1^2
    var xx = this.x.redSqr();
    // YY = Y1^2
    var yy = this.y.redSqr();
    // YYYY = YY^2
    var yyyy = yy.redSqr();
    // S = 2 * ((X1 + YY)^2 - XX - YYYY)
    var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy);
    s = s.redIAdd(s);
    // M = 3 * XX + a
    var m = xx.redAdd(xx).redIAdd(xx).redIAdd(this.curve.a);
    // T = M^2 - 2 * S
    var t = m.redSqr().redISub(s).redISub(s);
    // X3 = T
    nx = t;
    // Y3 = M * (S - T) - 8 * YYYY
    var yyyy8 = yyyy.redIAdd(yyyy);
    yyyy8 = yyyy8.redIAdd(yyyy8);
    yyyy8 = yyyy8.redIAdd(yyyy8);
    ny = m.redMul(s.redISub(t)).redISub(yyyy8);
    // Z3 = 2 * Y1
    nz = this.y.redAdd(this.y);
  } else {
    // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
    // 3M + 5S

    // delta = Z1^2
    var delta = this.z.redSqr();
    // gamma = Y1^2
    var gamma = this.y.redSqr();
    // beta = X1 * gamma
    var beta = this.x.redMul(gamma);
    // alpha = 3 * (X1 - delta) * (X1 + delta)
    var alpha = this.x.redSub(delta).redMul(this.x.redAdd(delta));
    alpha = alpha.redAdd(alpha).redIAdd(alpha);
    // X3 = alpha^2 - 8 * beta
    var beta4 = beta.redIAdd(beta);
    beta4 = beta4.redIAdd(beta4);
    var beta8 = beta4.redAdd(beta4);
    nx = alpha.redSqr().redISub(beta8);
    // Z3 = (Y1 + Z1)^2 - gamma - delta
    nz = this.y.redAdd(this.z).redSqr().redISub(gamma).redISub(delta);
    // Y3 = alpha * (4 * beta - X3) - 8 * gamma^2
    var ggamma8 = gamma.redSqr();
    ggamma8 = ggamma8.redIAdd(ggamma8);
    ggamma8 = ggamma8.redIAdd(ggamma8);
    ggamma8 = ggamma8.redIAdd(ggamma8);
    ny = alpha.redMul(beta4.redISub(nx)).redISub(ggamma8);
  }

  return this.curve.jpoint(nx, ny, nz);
};

JPoint.prototype._dbl = function _dbl() {
  var a = this.curve.a;

  // 4M + 6S + 10A
  var jx = this.x;
  var jy = this.y;
  var jz = this.z;
  var jz4 = jz.redSqr().redSqr();

  var jx2 = jx.redSqr();
  var jy2 = jy.redSqr();

  var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4));

  var jxd4 = jx.redAdd(jx);
  jxd4 = jxd4.redIAdd(jxd4);
  var t1 = jxd4.redMul(jy2);
  var nx = c.redSqr().redISub(t1.redAdd(t1));
  var t2 = t1.redISub(nx);

  var jyd8 = jy2.redSqr();
  jyd8 = jyd8.redIAdd(jyd8);
  jyd8 = jyd8.redIAdd(jyd8);
  jyd8 = jyd8.redIAdd(jyd8);
  var ny = c.redMul(t2).redISub(jyd8);
  var nz = jy.redAdd(jy).redMul(jz);

  return this.curve.jpoint(nx, ny, nz);
};

JPoint.prototype.trpl = function trpl() {
  if (!this.curve.zeroA)
    return this.dbl().add(this);

  // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl
  // 5M + 10S + ...

  // XX = X1^2
  var xx = this.x.redSqr();
  // YY = Y1^2
  var yy = this.y.redSqr();
  // ZZ = Z1^2
  var zz = this.z.redSqr();
  // YYYY = YY^2
  var yyyy = yy.redSqr();
  // M = 3 * XX + a * ZZ2; a = 0
  var m = xx.redAdd(xx).redIAdd(xx);
  // MM = M^2
  var mm = m.redSqr();
  // E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM
  var e = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy);
  e = e.redIAdd(e);
  e = e.redAdd(e).redIAdd(e);
  e = e.redISub(mm);
  // EE = E^2
  var ee = e.redSqr();
  // T = 16*YYYY
  var t = yyyy.redIAdd(yyyy);
  t = t.redIAdd(t);
  t = t.redIAdd(t);
  t = t.redIAdd(t);
  // U = (M + E)^2 - MM - EE - T
  var u = m.redIAdd(e).redSqr().redISub(mm).redISub(ee).redISub(t);
  // X3 = 4 * (X1 * EE - 4 * YY * U)
  var yyu4 = yy.redMul(u);
  yyu4 = yyu4.redIAdd(yyu4);
  yyu4 = yyu4.redIAdd(yyu4);
  var nx = this.x.redMul(ee).redISub(yyu4);
  nx = nx.redIAdd(nx);
  nx = nx.redIAdd(nx);
  // Y3 = 8 * Y1 * (U * (T - U) - E * EE)
  var ny = this.y.redMul(u.redMul(t.redISub(u)).redISub(e.redMul(ee)));
  ny = ny.redIAdd(ny);
  ny = ny.redIAdd(ny);
  ny = ny.redIAdd(ny);
  // Z3 = (Z1 + E)^2 - ZZ - EE
  var nz = this.z.redAdd(e).redSqr().redISub(zz).redISub(ee);

  return this.curve.jpoint(nx, ny, nz);
};

JPoint.prototype.mul = function mul(k, kbase) {
  k = new bn(k, kbase);

  return this.curve._wnafMul(this, k);
};

JPoint.prototype.eq = function eq(p) {
  if (p.type === 'affine')
    return this.eq(p.toJ());

  if (this === p)
    return true;

  // x1 * z2^2 == x2 * z1^2
  var z2 = this.z.redSqr();
  var pz2 = p.z.redSqr();
  if (this.x.redMul(pz2).redISub(p.x.redMul(z2)).cmpn(0) !== 0)
    return false;

  // y1 * z2^3 == y2 * z1^3
  var z3 = z2.redMul(this.z);
  var pz3 = pz2.redMul(p.z);
  return this.y.redMul(pz3).redISub(p.y.redMul(z3)).cmpn(0) === 0;
};

JPoint.prototype.eqXToP = function eqXToP(x) {
  var zs = this.z.redSqr();
  var rx = x.toRed(this.curve.red).redMul(zs);
  if (this.x.cmp(rx) === 0)
    return true;

  var xc = x.clone();
  var t = this.curve.redN.redMul(zs);
  for (;;) {
    xc.iadd(this.curve.n);
    if (xc.cmp(this.curve.p) >= 0)
      return false;

    rx.redIAdd(t);
    if (this.x.cmp(rx) === 0)
      return true;
  }
};

JPoint.prototype.inspect = function inspect() {
  if (this.isInfinity())
    return '<EC JPoint Infinity>';
  return '<EC JPoint x: ' + this.x.toString(16, 2) +
      ' y: ' + this.y.toString(16, 2) +
      ' z: ' + this.z.toString(16, 2) + '>';
};

JPoint.prototype.isInfinity = function isInfinity() {
  // XXX This code assumes that zero is always zero in red
  return this.z.cmpn(0) === 0;
};

function MontCurve(conf) {
  base.call(this, 'mont', conf);

  this.a = new bn(conf.a, 16).toRed(this.red);
  this.b = new bn(conf.b, 16).toRed(this.red);
  this.i4 = new bn(4).toRed(this.red).redInvm();
  this.two = new bn(2).toRed(this.red);
  // Note: this implementation is according to the original paper
  // by P. Montgomery, NOT the one by D. J. Bernstein.
  this.a24 = this.i4.redMul(this.a.redAdd(this.two));
}
inherits(MontCurve, base);
var mont = MontCurve;

MontCurve.prototype.validate = function validate(point) {
  var x = point.normalize().x;
  var x2 = x.redSqr();
  var rhs = x2.redMul(x).redAdd(x2.redMul(this.a)).redAdd(x);
  var y = rhs.redSqrt();

  return y.redSqr().cmp(rhs) === 0;
};

function Point$1(curve, x, z) {
  base.BasePoint.call(this, curve, 'projective');
  if (x === null && z === null) {
    this.x = this.curve.one;
    this.z = this.curve.zero;
  } else {
    this.x = new bn(x, 16);
    this.z = new bn(z, 16);
    if (!this.x.red)
      this.x = this.x.toRed(this.curve.red);
    if (!this.z.red)
      this.z = this.z.toRed(this.curve.red);
  }
}
inherits(Point$1, base.BasePoint);

MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) {
  var bytes = utils_1$1.toArray(bytes, enc);

  // TODO Curve448
  // Montgomery curve points must be represented in the compressed format
  // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-02#appendix-B
  if (bytes.length === 33 && bytes[0] === 0x40)
    bytes = bytes.slice(1, 33).reverse(); // point must be little-endian
  if (bytes.length !== 32)
    throw new Error('Unknown point compression format');
  return this.point(bytes, 1);
};

MontCurve.prototype.point = function point(x, z) {
  return new Point$1(this, x, z);
};

MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) {
  return Point$1.fromJSON(this, obj);
};

Point$1.prototype.precompute = function precompute() {
  // No-op
};

Point$1.prototype._encode = function _encode(compact) {
  var len = this.curve.p.byteLength();

  // Note: the output should always be little-endian
  // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-02#appendix-B
  if (compact) {
    return [ 0x40 ].concat(this.getX().toArray('le', len));
  } else {
    return this.getX().toArray('be', len);
  }
};

Point$1.fromJSON = function fromJSON(curve, obj) {
  return new Point$1(curve, obj[0], obj[1] || curve.one);
};

Point$1.prototype.inspect = function inspect() {
  if (this.isInfinity())
    return '<EC Point Infinity>';
  return '<EC Point x: ' + this.x.fromRed().toString(16, 2) +
      ' z: ' + this.z.fromRed().toString(16, 2) + '>';
};

Point$1.prototype.isInfinity = function isInfinity() {
  // XXX This code assumes that zero is always zero in red
  return this.z.cmpn(0) === 0;
};

Point$1.prototype.dbl = function dbl() {
  // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3
  // 2M + 2S + 4A

  // A = X1 + Z1
  var a = this.x.redAdd(this.z);
  // AA = A^2
  var aa = a.redSqr();
  // B = X1 - Z1
  var b = this.x.redSub(this.z);
  // BB = B^2
  var bb = b.redSqr();
  // C = AA - BB
  var c = aa.redSub(bb);
  // X3 = AA * BB
  var nx = aa.redMul(bb);
  // Z3 = C * (BB + A24 * C)
  var nz = c.redMul(bb.redAdd(this.curve.a24.redMul(c)));
  return this.curve.point(nx, nz);
};

Point$1.prototype.add = function add() {
  throw new Error('Not supported on Montgomery curve');
};

Point$1.prototype.diffAdd = function diffAdd(p, diff) {
  // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3
  // 4M + 2S + 6A

  // A = X2 + Z2
  var a = this.x.redAdd(this.z);
  // B = X2 - Z2
  var b = this.x.redSub(this.z);
  // C = X3 + Z3
  var c = p.x.redAdd(p.z);
  // D = X3 - Z3
  var d = p.x.redSub(p.z);
  // DA = D * A
  var da = d.redMul(a);
  // CB = C * B
  var cb = c.redMul(b);
  // X5 = Z1 * (DA + CB)^2
  var nx = diff.z.redMul(da.redAdd(cb).redSqr());
  // Z5 = X1 * (DA - CB)^2
  var nz = diff.x.redMul(da.redISub(cb).redSqr());
  return this.curve.point(nx, nz);
};

Point$1.prototype.mul = function mul(k) {
  k = new bn(k, 16);

  var t = k.clone();
  var a = this; // (N / 2) * Q + Q
  var b = this.curve.point(null, null); // (N / 2) * Q
  var c = this; // Q

  for (var bits = []; t.cmpn(0) !== 0; t.iushrn(1))
    bits.push(t.andln(1));

  for (var i = bits.length - 1; i >= 0; i--) {
    if (bits[i] === 0) {
      // N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q
      a = a.diffAdd(b, c);
      // N * Q = 2 * ((N / 2) * Q + Q))
      b = b.dbl();
    } else {
      // N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q)
      b = a.diffAdd(b, c);
      // N * Q + Q = 2 * ((N / 2) * Q + Q)
      a = a.dbl();
    }
  }
  return b;
};

Point$1.prototype.mulAdd = function mulAdd() {
  throw new Error('Not supported on Montgomery curve');
};

Point$1.prototype.jumlAdd = function jumlAdd() {
  throw new Error('Not supported on Montgomery curve');
};

Point$1.prototype.eq = function eq(other) {
  return this.getX().cmp(other.getX()) === 0;
};

Point$1.prototype.normalize = function normalize() {
  this.x = this.x.redMul(this.z.redInvm());
  this.z = this.curve.one;
  return this;
};

Point$1.prototype.getX = function getX() {
  // Normalize coordinates
  this.normalize();

  return this.x.fromRed();
};

var assert$4 = utils_1$1.assert;

function EdwardsCurve(conf) {
  // NOTE: Important as we are creating point in Base.call()
  this.twisted = (conf.a | 0) !== 1;
  this.mOneA = this.twisted && (conf.a | 0) === -1;
  this.extended = this.mOneA;

  base.call(this, 'edwards', conf);

  this.a = new bn(conf.a, 16).umod(this.red.m);
  this.a = this.a.toRed(this.red);
  this.c = new bn(conf.c, 16).toRed(this.red);
  this.c2 = this.c.redSqr();
  this.d = new bn(conf.d, 16).toRed(this.red);
  this.dd = this.d.redAdd(this.d);

  assert$4(!this.twisted || this.c.fromRed().cmpn(1) === 0);
  this.oneC = (conf.c | 0) === 1;
}
inherits(EdwardsCurve, base);
var edwards = EdwardsCurve;

EdwardsCurve.prototype._mulA = function _mulA(num) {
  if (this.mOneA)
    return num.redNeg();
  else
    return this.a.redMul(num);
};

EdwardsCurve.prototype._mulC = function _mulC(num) {
  if (this.oneC)
    return num;
  else
    return this.c.redMul(num);
};

// Just for compatibility with Short curve
EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) {
  return this.point(x, y, z, t);
};

EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) {
  x = new bn(x, 16);
  if (!x.red)
    x = x.toRed(this.red);

  var x2 = x.redSqr();
  var rhs = this.c2.redSub(this.a.redMul(x2));
  var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2));

  var y2 = rhs.redMul(lhs.redInvm());
  var y = y2.redSqrt();
  if (y.redSqr().redSub(y2).cmp(this.zero) !== 0)
    throw new Error('invalid point');

  var isOdd = y.fromRed().isOdd();
  if (odd && !isOdd || !odd && isOdd)
    y = y.redNeg();

  return this.point(x, y);
};

EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) {
  y = new bn(y, 16);
  if (!y.red)
    y = y.toRed(this.red);

  // x^2 = (y^2 - c^2) / (c^2 d y^2 - a)
  var y2 = y.redSqr();
  var lhs = y2.redSub(this.c2);
  var rhs = y2.redMul(this.d).redMul(this.c2).redSub(this.a);
  var x2 = lhs.redMul(rhs.redInvm());

  if (x2.cmp(this.zero) === 0) {
    if (odd)
      throw new Error('invalid point');
    else
      return this.point(this.zero, y);
  }

  var x = x2.redSqrt();
  if (x.redSqr().redSub(x2).cmp(this.zero) !== 0)
    throw new Error('invalid point');

  if (x.fromRed().isOdd() !== odd)
    x = x.redNeg();

  return this.point(x, y);
};

EdwardsCurve.prototype.validate = function validate(point) {
  if (point.isInfinity())
    return true;

  // Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2)
  point.normalize();

  var x2 = point.x.redSqr();
  var y2 = point.y.redSqr();
  var lhs = x2.redMul(this.a).redAdd(y2);
  var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2)));

  return lhs.cmp(rhs) === 0;
};

function Point$2(curve, x, y, z, t) {
  base.BasePoint.call(this, curve, 'projective');
  if (x === null && y === null && z === null) {
    this.x = this.curve.zero;
    this.y = this.curve.one;
    this.z = this.curve.one;
    this.t = this.curve.zero;
    this.zOne = true;
  } else {
    this.x = new bn(x, 16);
    this.y = new bn(y, 16);
    this.z = z ? new bn(z, 16) : this.curve.one;
    this.t = t && new bn(t, 16);
    if (!this.x.red)
      this.x = this.x.toRed(this.curve.red);
    if (!this.y.red)
      this.y = this.y.toRed(this.curve.red);
    if (!this.z.red)
      this.z = this.z.toRed(this.curve.red);
    if (this.t && !this.t.red)
      this.t = this.t.toRed(this.curve.red);
    this.zOne = this.z === this.curve.one;

    // Use extended coordinates
    if (this.curve.extended && !this.t) {
      this.t = this.x.redMul(this.y);
      if (!this.zOne)
        this.t = this.t.redMul(this.z.redInvm());
    }
  }
}
inherits(Point$2, base.BasePoint);

EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) {
  return Point$2.fromJSON(this, obj);
};

EdwardsCurve.prototype.point = function point(x, y, z, t) {
  return new Point$2(this, x, y, z, t);
};

Point$2.fromJSON = function fromJSON(curve, obj) {
  return new Point$2(curve, obj[0], obj[1], obj[2]);
};

Point$2.prototype.inspect = function inspect() {
  if (this.isInfinity())
    return '<EC Point Infinity>';
  return '<EC Point x: ' + this.x.fromRed().toString(16, 2) +
      ' y: ' + this.y.fromRed().toString(16, 2) +
      ' z: ' + this.z.fromRed().toString(16, 2) + '>';
};

Point$2.prototype.isInfinity = function isInfinity() {
  // XXX This code assumes that zero is always zero in red
  return this.x.cmpn(0) === 0 &&
    (this.y.cmp(this.z) === 0 ||
    (this.zOne && this.y.cmp(this.curve.c) === 0));
};

Point$2.prototype._extDbl = function _extDbl() {
  // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
  //     #doubling-dbl-2008-hwcd
  // 4M + 4S

  // A = X1^2
  var a = this.x.redSqr();
  // B = Y1^2
  var b = this.y.redSqr();
  // C = 2 * Z1^2
  var c = this.z.redSqr();
  c = c.redIAdd(c);
  // D = a * A
  var d = this.curve._mulA(a);
  // E = (X1 + Y1)^2 - A - B
  var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b);
  // G = D + B
  var g = d.redAdd(b);
  // F = G - C
  var f = g.redSub(c);
  // H = D - B
  var h = d.redSub(b);
  // X3 = E * F
  var nx = e.redMul(f);
  // Y3 = G * H
  var ny = g.redMul(h);
  // T3 = E * H
  var nt = e.redMul(h);
  // Z3 = F * G
  var nz = f.redMul(g);
  return this.curve.point(nx, ny, nz, nt);
};

Point$2.prototype._projDbl = function _projDbl() {
  // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
  //     #doubling-dbl-2008-bbjlp
  //     #doubling-dbl-2007-bl
  // and others
  // Generally 3M + 4S or 2M + 4S

  // B = (X1 + Y1)^2
  var b = this.x.redAdd(this.y).redSqr();
  // C = X1^2
  var c = this.x.redSqr();
  // D = Y1^2
  var d = this.y.redSqr();

  var nx;
  var ny;
  var nz;
  if (this.curve.twisted) {
    // E = a * C
    var e = this.curve._mulA(c);
    // F = E + D
    var f = e.redAdd(d);
    if (this.zOne) {
      // X3 = (B - C - D) * (F - 2)
      nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two));
      // Y3 = F * (E - D)
      ny = f.redMul(e.redSub(d));
      // Z3 = F^2 - 2 * F
      nz = f.redSqr().redSub(f).redSub(f);
    } else {
      // H = Z1^2
      var h = this.z.redSqr();
      // J = F - 2 * H
      var j = f.redSub(h).redISub(h);
      // X3 = (B-C-D)*J
      nx = b.redSub(c).redISub(d).redMul(j);
      // Y3 = F * (E - D)
      ny = f.redMul(e.redSub(d));
      // Z3 = F * J
      nz = f.redMul(j);
    }
  } else {
    // E = C + D
    var e = c.redAdd(d);
    // H = (c * Z1)^2
    var h = this.curve._mulC(this.z).redSqr();
    // J = E - 2 * H
    var j = e.redSub(h).redSub(h);
    // X3 = c * (B - E) * J
    nx = this.curve._mulC(b.redISub(e)).redMul(j);
    // Y3 = c * E * (C - D)
    ny = this.curve._mulC(e).redMul(c.redISub(d));
    // Z3 = E * J
    nz = e.redMul(j);
  }
  return this.curve.point(nx, ny, nz);
};

Point$2.prototype.dbl = function dbl() {
  if (this.isInfinity())
    return this;

  // Double in extended coordinates
  if (this.curve.extended)
    return this._extDbl();
  else
    return this._projDbl();
};

Point$2.prototype._extAdd = function _extAdd(p) {
  // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
  //     #addition-add-2008-hwcd-3
  // 8M

  // A = (Y1 - X1) * (Y2 - X2)
  var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x));
  // B = (Y1 + X1) * (Y2 + X2)
  var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x));
  // C = T1 * k * T2
  var c = this.t.redMul(this.curve.dd).redMul(p.t);
  // D = Z1 * 2 * Z2
  var d = this.z.redMul(p.z.redAdd(p.z));
  // E = B - A
  var e = b.redSub(a);
  // F = D - C
  var f = d.redSub(c);
  // G = D + C
  var g = d.redAdd(c);
  // H = B + A
  var h = b.redAdd(a);
  // X3 = E * F
  var nx = e.redMul(f);
  // Y3 = G * H
  var ny = g.redMul(h);
  // T3 = E * H
  var nt = e.redMul(h);
  // Z3 = F * G
  var nz = f.redMul(g);
  return this.curve.point(nx, ny, nz, nt);
};

Point$2.prototype._projAdd = function _projAdd(p) {
  // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
  //     #addition-add-2008-bbjlp
  //     #addition-add-2007-bl
  // 10M + 1S

  // A = Z1 * Z2
  var a = this.z.redMul(p.z);
  // B = A^2
  var b = a.redSqr();
  // C = X1 * X2
  var c = this.x.redMul(p.x);
  // D = Y1 * Y2
  var d = this.y.redMul(p.y);
  // E = d * C * D
  var e = this.curve.d.redMul(c).redMul(d);
  // F = B - E
  var f = b.redSub(e);
  // G = B + E
  var g = b.redAdd(e);
  // X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D)
  var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d);
  var nx = a.redMul(f).redMul(tmp);
  var ny;
  var nz;
  if (this.curve.twisted) {
    // Y3 = A * G * (D - a * C)
    ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c)));
    // Z3 = F * G
    nz = f.redMul(g);
  } else {
    // Y3 = A * G * (D - C)
    ny = a.redMul(g).redMul(d.redSub(c));
    // Z3 = c * F * G
    nz = this.curve._mulC(f).redMul(g);
  }
  return this.curve.point(nx, ny, nz);
};

Point$2.prototype.add = function add(p) {
  if (this.isInfinity())
    return p;
  if (p.isInfinity())
    return this;

  if (this.curve.extended)
    return this._extAdd(p);
  else
    return this._projAdd(p);
};

Point$2.prototype.mul = function mul(k) {
  if (this._hasDoubles(k))
    return this.curve._fixedNafMul(this, k);
  else
    return this.curve._wnafMul(this, k);
};

Point$2.prototype.mulAdd = function mulAdd(k1, p, k2) {
  return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false);
};

Point$2.prototype.jmulAdd = function jmulAdd(k1, p, k2) {
  return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true);
};

Point$2.prototype.normalize = function normalize() {
  if (this.zOne)
    return this;

  // Normalize coordinates
  var zi = this.z.redInvm();
  this.x = this.x.redMul(zi);
  this.y = this.y.redMul(zi);
  if (this.t)
    this.t = this.t.redMul(zi);
  this.z = this.curve.one;
  this.zOne = true;
  return this;
};

Point$2.prototype.neg = function neg() {
  return this.curve.point(this.x.redNeg(),
                          this.y,
                          this.z,
                          this.t && this.t.redNeg());
};

Point$2.prototype.getX = function getX() {
  this.normalize();
  return this.x.fromRed();
};

Point$2.prototype.getY = function getY() {
  this.normalize();
  return this.y.fromRed();
};

Point$2.prototype.eq = function eq(other) {
  return this === other ||
         this.getX().cmp(other.getX()) === 0 &&
         this.getY().cmp(other.getY()) === 0;
};

Point$2.prototype.eqXToP = function eqXToP(x) {
  var rx = x.toRed(this.curve.red).redMul(this.z);
  if (this.x.cmp(rx) === 0)
    return true;

  var xc = x.clone();
  var t = this.curve.redN.redMul(this.z);
  for (;;) {
    xc.iadd(this.curve.n);
    if (xc.cmp(this.curve.p) >= 0)
      return false;

    rx.redIAdd(t);
    if (this.x.cmp(rx) === 0)
      return true;
  }
};

// Compatibility with BaseCurve
Point$2.prototype.toP = Point$2.prototype.normalize;
Point$2.prototype.mixedAdd = Point$2.prototype.add;

var curve_1 = createCommonjsModule(function (module, exports) {

var curve = exports;

curve.base = base;
curve.short = short_1;
curve.mont = mont;
curve.edwards = edwards;
});

var rotl32$2 = utils.rotl32;
var sum32$3 = utils.sum32;
var sum32_5$2 = utils.sum32_5;
var ft_1$1 = common$1.ft_1;
var BlockHash$4 = common.BlockHash;

var sha1_K = [
  0x5A827999, 0x6ED9EBA1,
  0x8F1BBCDC, 0xCA62C1D6
];

function SHA1() {
  if (!(this instanceof SHA1))
    return new SHA1();

  BlockHash$4.call(this);
  this.h = [
    0x67452301, 0xefcdab89, 0x98badcfe,
    0x10325476, 0xc3d2e1f0 ];
  this.W = new Array(80);
}

utils.inherits(SHA1, BlockHash$4);
var _1 = SHA1;

SHA1.blockSize = 512;
SHA1.outSize = 160;
SHA1.hmacStrength = 80;
SHA1.padLength = 64;

SHA1.prototype._update = function _update(msg, start) {
  var W = this.W;

  for (var i = 0; i < 16; i++)
    W[i] = msg[start + i];

  for(; i < W.length; i++)
    W[i] = rotl32$2(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);

  var a = this.h[0];
  var b = this.h[1];
  var c = this.h[2];
  var d = this.h[3];
  var e = this.h[4];

  for (i = 0; i < W.length; i++) {
    var s = ~~(i / 20);
    var t = sum32_5$2(rotl32$2(a, 5), ft_1$1(s, b, c, d), e, W[i], sha1_K[s]);
    e = d;
    d = c;
    c = rotl32$2(b, 30);
    b = a;
    a = t;
  }

  this.h[0] = sum32$3(this.h[0], a);
  this.h[1] = sum32$3(this.h[1], b);
  this.h[2] = sum32$3(this.h[2], c);
  this.h[3] = sum32$3(this.h[3], d);
  this.h[4] = sum32$3(this.h[4], e);
};

SHA1.prototype._digest = function digest(enc) {
  if (enc === 'hex')
    return utils.toHex32(this.h, 'big');
  else
    return utils.split32(this.h, 'big');
};

var sha1 = _1;
var sha224 = _224;
var sha256 = _256;
var sha384 = _384;
var sha512 = _512;

var sha = {
	sha1: sha1,
	sha224: sha224,
	sha256: sha256,
	sha384: sha384,
	sha512: sha512
};

function Hmac(hash, key, enc) {
  if (!(this instanceof Hmac))
    return new Hmac(hash, key, enc);
  this.Hash = hash;
  this.blockSize = hash.blockSize / 8;
  this.outSize = hash.outSize / 8;
  this.inner = null;
  this.outer = null;

  this._init(utils.toArray(key, enc));
}
var hmac = Hmac;

Hmac.prototype._init = function init(key) {
  // Shorten key, if needed
  if (key.length > this.blockSize)
    key = new this.Hash().update(key).digest();
  minimalisticAssert(key.length <= this.blockSize);

  // Add padding to key
  for (var i = key.length; i < this.blockSize; i++)
    key.push(0);

  for (i = 0; i < key.length; i++)
    key[i] ^= 0x36;
  this.inner = new this.Hash().update(key);

  // 0x36 ^ 0x5c = 0x6a
  for (i = 0; i < key.length; i++)
    key[i] ^= 0x6a;
  this.outer = new this.Hash().update(key);
};

Hmac.prototype.update = function update(msg, enc) {
  this.inner.update(msg, enc);
  return this;
};

Hmac.prototype.digest = function digest(enc) {
  this.outer.update(this.inner.digest());
  return this.outer.digest(enc);
};

var hash_1 = createCommonjsModule(function (module, exports) {
var hash = exports;

hash.utils = utils;
hash.common = common;
hash.sha = sha;
hash.ripemd = ripemd;
hash.hmac = hmac;

// Proxy hash functions to the main object
hash.sha1 = hash.sha.sha1;
hash.sha256 = hash.sha.sha256;
hash.sha224 = hash.sha.sha224;
hash.sha384 = hash.sha.sha384;
hash.sha512 = hash.sha.sha512;
hash.ripemd160 = hash.ripemd.ripemd160;
});

var secp256k1 = {
  doubles: {
    step: 4,
    points: [
      [
        'e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a',
        'f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821'
      ],
      [
        '8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508',
        '11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf'
      ],
      [
        '175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739',
        'd3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695'
      ],
      [
        '363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640',
        '4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9'
      ],
      [
        '8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c',
        '4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36'
      ],
      [
        '723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda',
        '96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f'
      ],
      [
        'eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa',
        '5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999'
      ],
      [
        '100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0',
        'cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09'
      ],
      [
        'e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d',
        '9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d'
      ],
      [
        'feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d',
        'e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088'
      ],
      [
        'da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1',
        '9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d'
      ],
      [
        '53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0',
        '5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8'
      ],
      [
        '8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047',
        '10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a'
      ],
      [
        '385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862',
        '283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453'
      ],
      [
        '6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7',
        '7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160'
      ],
      [
        '3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd',
        '56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0'
      ],
      [
        '85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83',
        '7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6'
      ],
      [
        '948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a',
        '53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589'
      ],
      [
        '6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8',
        'bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17'
      ],
      [
        'e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d',
        '4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda'
      ],
      [
        'e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725',
        '7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd'
      ],
      [
        '213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754',
        '4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2'
      ],
      [
        '4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c',
        '17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6'
      ],
      [
        'fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6',
        '6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f'
      ],
      [
        '76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39',
        'c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01'
      ],
      [
        'c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891',
        '893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3'
      ],
      [
        'd895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b',
        'febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f'
      ],
      [
        'b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03',
        '2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7'
      ],
      [
        'e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d',
        'eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78'
      ],
      [
        'a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070',
        '7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1'
      ],
      [
        '90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4',
        'e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150'
      ],
      [
        '8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da',
        '662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82'
      ],
      [
        'e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11',
        '1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc'
      ],
      [
        '8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e',
        'efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b'
      ],
      [
        'e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41',
        '2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51'
      ],
      [
        'b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef',
        '67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45'
      ],
      [
        'd68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8',
        'db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120'
      ],
      [
        '324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d',
        '648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84'
      ],
      [
        '4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96',
        '35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d'
      ],
      [
        '9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd',
        'ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d'
      ],
      [
        '6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5',
        '9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8'
      ],
      [
        'a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266',
        '40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8'
      ],
      [
        '7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71',
        '34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac'
      ],
      [
        '928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac',
        'c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f'
      ],
      [
        '85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751',
        '1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962'
      ],
      [
        'ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e',
        '493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907'
      ],
      [
        '827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241',
        'c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec'
      ],
      [
        'eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3',
        'be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d'
      ],
      [
        'e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f',
        '4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414'
      ],
      [
        '1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19',
        'aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd'
      ],
      [
        '146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be',
        'b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0'
      ],
      [
        'fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9',
        '6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811'
      ],
      [
        'da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2',
        '8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1'
      ],
      [
        'a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13',
        '7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c'
      ],
      [
        '174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c',
        'ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73'
      ],
      [
        '959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba',
        '2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd'
      ],
      [
        'd2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151',
        'e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405'
      ],
      [
        '64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073',
        'd99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589'
      ],
      [
        '8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458',
        '38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e'
      ],
      [
        '13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b',
        '69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27'
      ],
      [
        'bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366',
        'd3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1'
      ],
      [
        '8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa',
        '40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482'
      ],
      [
        '8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0',
        '620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945'
      ],
      [
        'dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787',
        '7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573'
      ],
      [
        'f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e',
        'ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82'
      ]
    ]
  },
  naf: {
    wnd: 7,
    points: [
      [
        'f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9',
        '388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672'
      ],
      [
        '2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4',
        'd8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6'
      ],
      [
        '5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc',
        '6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da'
      ],
      [
        'acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe',
        'cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37'
      ],
      [
        '774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb',
        'd984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b'
      ],
      [
        'f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8',
        'ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81'
      ],
      [
        'd7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e',
        '581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58'
      ],
      [
        'defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34',
        '4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77'
      ],
      [
        '2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c',
        '85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a'
      ],
      [
        '352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5',
        '321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c'
      ],
      [
        '2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f',
        '2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67'
      ],
      [
        '9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714',
        '73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402'
      ],
      [
        'daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729',
        'a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55'
      ],
      [
        'c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db',
        '2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482'
      ],
      [
        '6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4',
        'e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82'
      ],
      [
        '1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5',
        'b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396'
      ],
      [
        '605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479',
        '2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49'
      ],
      [
        '62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d',
        '80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf'
      ],
      [
        '80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f',
        '1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a'
      ],
      [
        '7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb',
        'd0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7'
      ],
      [
        'd528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9',
        'eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933'
      ],
      [
        '49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963',
        '758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a'
      ],
      [
        '77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74',
        '958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6'
      ],
      [
        'f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530',
        'e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37'
      ],
      [
        '463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b',
        '5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e'
      ],
      [
        'f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247',
        'cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6'
      ],
      [
        'caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1',
        'cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476'
      ],
      [
        '2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120',
        '4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40'
      ],
      [
        '7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435',
        '91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61'
      ],
      [
        '754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18',
        '673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683'
      ],
      [
        'e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8',
        '59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5'
      ],
      [
        '186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb',
        '3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b'
      ],
      [
        'df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f',
        '55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417'
      ],
      [
        '5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143',
        'efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868'
      ],
      [
        '290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba',
        'e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a'
      ],
      [
        'af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45',
        'f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6'
      ],
      [
        '766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a',
        '744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996'
      ],
      [
        '59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e',
        'c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e'
      ],
      [
        'f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8',
        'e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d'
      ],
      [
        '7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c',
        '30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2'
      ],
      [
        '948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519',
        'e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e'
      ],
      [
        '7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab',
        '100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437'
      ],
      [
        '3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca',
        'ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311'
      ],
      [
        'd3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf',
        '8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4'
      ],
      [
        '1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610',
        '68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575'
      ],
      [
        '733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4',
        'f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d'
      ],
      [
        '15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c',
        'd56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d'
      ],
      [
        'a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940',
        'edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629'
      ],
      [
        'e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980',
        'a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06'
      ],
      [
        '311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3',
        '66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374'
      ],
      [
        '34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf',
        '9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee'
      ],
      [
        'f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63',
        '4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1'
      ],
      [
        'd7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448',
        'fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b'
      ],
      [
        '32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf',
        '5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661'
      ],
      [
        '7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5',
        '8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6'
      ],
      [
        'ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6',
        '8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e'
      ],
      [
        '16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5',
        '5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d'
      ],
      [
        'eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99',
        'f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc'
      ],
      [
        '78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51',
        'f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4'
      ],
      [
        '494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5',
        '42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c'
      ],
      [
        'a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5',
        '204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b'
      ],
      [
        'c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997',
        '4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913'
      ],
      [
        '841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881',
        '73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154'
      ],
      [
        '5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5',
        '39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865'
      ],
      [
        '36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66',
        'd2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc'
      ],
      [
        '336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726',
        'ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224'
      ],
      [
        '8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede',
        '6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e'
      ],
      [
        '1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94',
        '60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6'
      ],
      [
        '85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31',
        '3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511'
      ],
      [
        '29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51',
        'b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b'
      ],
      [
        'a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252',
        'ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2'
      ],
      [
        '4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5',
        'cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c'
      ],
      [
        'd24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b',
        '6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3'
      ],
      [
        'ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4',
        '322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d'
      ],
      [
        'af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f',
        '6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700'
      ],
      [
        'e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889',
        '2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4'
      ],
      [
        '591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246',
        'b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196'
      ],
      [
        '11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984',
        '998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4'
      ],
      [
        '3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a',
        'b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257'
      ],
      [
        'cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030',
        'bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13'
      ],
      [
        'c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197',
        '6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096'
      ],
      [
        'c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593',
        'c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38'
      ],
      [
        'a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef',
        '21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f'
      ],
      [
        '347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38',
        '60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448'
      ],
      [
        'da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a',
        '49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a'
      ],
      [
        'c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111',
        '5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4'
      ],
      [
        '4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502',
        '7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437'
      ],
      [
        '3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea',
        'be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7'
      ],
      [
        'cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26',
        '8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d'
      ],
      [
        'b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986',
        '39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a'
      ],
      [
        'd4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e',
        '62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54'
      ],
      [
        '48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4',
        '25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77'
      ],
      [
        'dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda',
        'ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517'
      ],
      [
        '6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859',
        'cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10'
      ],
      [
        'e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f',
        'f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125'
      ],
      [
        'eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c',
        '6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e'
      ],
      [
        '13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942',
        'fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1'
      ],
      [
        'ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a',
        '1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2'
      ],
      [
        'b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80',
        '5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423'
      ],
      [
        'ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d',
        '438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8'
      ],
      [
        '8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1',
        'cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758'
      ],
      [
        '52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63',
        'c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375'
      ],
      [
        'e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352',
        '6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d'
      ],
      [
        '7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193',
        'ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec'
      ],
      [
        '5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00',
        '9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0'
      ],
      [
        '32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58',
        'ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c'
      ],
      [
        'e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7',
        'd3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4'
      ],
      [
        '8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8',
        'c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f'
      ],
      [
        '4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e',
        '67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649'
      ],
      [
        '3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d',
        'cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826'
      ],
      [
        '674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b',
        '299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5'
      ],
      [
        'd32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f',
        'f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87'
      ],
      [
        '30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6',
        '462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b'
      ],
      [
        'be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297',
        '62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc'
      ],
      [
        '93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a',
        '7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c'
      ],
      [
        'b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c',
        'ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f'
      ],
      [
        'd5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52',
        '4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a'
      ],
      [
        'd3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb',
        'bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46'
      ],
      [
        '463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065',
        'bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f'
      ],
      [
        '7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917',
        '603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03'
      ],
      [
        '74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9',
        'cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08'
      ],
      [
        '30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3',
        '553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8'
      ],
      [
        '9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57',
        '712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373'
      ],
      [
        '176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66',
        'ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3'
      ],
      [
        '75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8',
        '9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8'
      ],
      [
        '809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721',
        '9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1'
      ],
      [
        '1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180',
        '4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9'
      ]
    ]
  }
};

var curves_1 = createCommonjsModule(function (module, exports) {

var curves = exports;





var assert = utils_1$1.assert;

function PresetCurve(options) {
  if (options.type === 'short')
    this.curve = new curve_1.short(options);
  else if (options.type === 'edwards')
    this.curve = new curve_1.edwards(options);
  else if (options.type === 'mont')
    this.curve = new curve_1.mont(options);
  else throw new Error('Unknown curve type.');
  this.g = this.curve.g;
  this.n = this.curve.n;
  this.hash = options.hash;

  assert(this.g.validate(), 'Invalid curve');
  assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, n*G != O');
}
curves.PresetCurve = PresetCurve;

function defineCurve(name, options) {
  Object.defineProperty(curves, name, {
    configurable: true,
    enumerable: true,
    get: function() {
      var curve = new PresetCurve(options);
      Object.defineProperty(curves, name, {
        configurable: true,
        enumerable: true,
        value: curve
      });
      return curve;
    }
  });
}

defineCurve('p192', {
  type: 'short',
  prime: 'p192',
  p: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff',
  a: 'ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc',
  b: '64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1',
  n: 'ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831',
  hash: hash_1.sha256,
  gRed: false,
  g: [
    '188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012',
    '07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811'
  ]
});

defineCurve('p224', {
  type: 'short',
  prime: 'p224',
  p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001',
  a: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe',
  b: 'b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4',
  n: 'ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d',
  hash: hash_1.sha256,
  gRed: false,
  g: [
    'b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21',
    'bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34'
  ]
});

defineCurve('p256', {
  type: 'short',
  prime: null,
  p: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff',
  a: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc',
  b: '5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b',
  n: 'ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551',
  hash: hash_1.sha256,
  gRed: false,
  g: [
    '6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296',
    '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5'
  ]
});

defineCurve('p384', {
  type: 'short',
  prime: null,
  p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
     'fffffffe ffffffff 00000000 00000000 ffffffff',
  a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
     'fffffffe ffffffff 00000000 00000000 fffffffc',
  b: 'b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f ' +
     '5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef',
  n: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 ' +
     'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973',
  hash: hash_1.sha384,
  gRed: false,
  g: [
    'aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 ' +
    '5502f25d bf55296c 3a545e38 72760ab7',
    '3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 ' +
    '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f'
  ]
});

defineCurve('p521', {
  type: 'short',
  prime: null,
  p: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
     'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
     'ffffffff ffffffff ffffffff ffffffff ffffffff',
  a: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
     'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
     'ffffffff ffffffff ffffffff ffffffff fffffffc',
  b: '00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b ' +
     '99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' +
     '3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00',
  n: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
     'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' +
     'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409',
  hash: hash_1.sha512,
  gRed: false,
  g: [
    '000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 ' +
    '053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 ' +
    'a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66',
    '00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 ' +
    '579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 ' +
    '3fad0761 353c7086 a272c240 88be9476 9fd16650'
  ]
});

// https://tools.ietf.org/html/rfc7748#section-4.1
defineCurve('curve25519', {
  type: 'mont',
  prime: 'p25519',
  p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed',
  a: '76d06',
  b: '1',
  n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed',
  cofactor: '8',
  hash: hash_1.sha256,
  gRed: false,
  g: [
    '9'
  ]
});

defineCurve('ed25519', {
  type: 'edwards',
  prime: 'p25519',
  p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed',
  a: '-1',
  c: '1',
  // -121665 * (121666^(-1)) (mod P)
  d: '52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3',
  n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed',
  cofactor: '8',
  hash: hash_1.sha256,
  gRed: false,
  g: [
    '216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a',
    // 4/5
    '6666666666666666666666666666666666666666666666666666666666666658'
  ]
});

// https://tools.ietf.org/html/rfc5639#section-3.4
defineCurve('brainpoolP256r1', {
  type: 'short',
  prime: null,
  p: 'A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028 2013481D 1F6E5377',
  a: '7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C E94A4B44 F330B5D9',
  b: '26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE 6BCCDC18 FF8C07B6',
  n: 'A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7 901E0E82 974856A7',
  hash: hash_1.sha256, // or 384, or 512
  gRed: false,
  g: [
    '8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262',
    '547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997'
  ]
});

// https://tools.ietf.org/html/rfc5639#section-3.6
defineCurve('brainpoolP384r1', {
  type: 'short',
  prime: null,
  p: '8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4 12B1DA19 7FB71123' +
    'ACD3A729 901D1A71 87470013 3107EC53',
  a: '7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787 139165EF BA91F90F' +
    '8AA5814A 503AD4EB 04A8C7DD 22CE2826',
  b: '04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6 2E880EA5 3EEB62D5' +
    '7CB43902 95DBC994 3AB78696 FA504C11',
  n: '8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3 1F166E6C AC0425A7' +
    'CF3AB6AF 6B7FC310 3B883202 E9046565',
  hash: hash_1.sha384, // or 512
  gRed: false,
  g: [
    '1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10' +
      'E8E826E03436D646AAEF87B2E247D4AF1E',
    '8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129' +
      '280E4646217791811142820341263C5315'
  ]
});

// https://tools.ietf.org/html/rfc5639#section-3.7
defineCurve('brainpoolP512r1', {
  type: 'short',
  prime: null,
  p: 'AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E D6639CCA 70330871' +
    '7D4D9B00 9BC66842 AECDA12A E6A380E6 2881FF2F 2D82C685 28AA6056 583A48F3',
  a: '7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610 A83441CA EA9863BC' +
    '2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA',
  b: '3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5 7F1117A7' +
    '2BF2C7B9 E7C1AC4D 77FC94CA DC083E67 984050B7 5EBAE5DD 2809BD63 8016F723',
  n: 'AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E D6639CCA 70330870' +
    '553E5C41 4CA92619 41866119 7FAC1047 1DB1D381 085DDADD B5879682 9CA90069',
  hash: hash_1.sha512,
  gRed: false,
  g: [
    '81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D009' +
      '8EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822',
    '7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F81' +
      '11B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892'
  ]
});

// https://en.bitcoin.it/wiki/Secp256k1
var pre;
try {
  pre = secp256k1;
} catch (e) {
  pre = undefined;
}

defineCurve('secp256k1', {
  type: 'short',
  prime: 'k256',
  p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f',
  a: '0',
  b: '7',
  n: 'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141',
  h: '1',
  hash: hash_1.sha256,

  // Precomputed endomorphism
  beta: '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee',
  lambda: '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72',
  basis: [
    {
      a: '3086d221a7d46bcde86c90e49284eb15',
      b: '-e4437ed6010e88286f547fa90abfe4c3'
    },
    {
      a: '114ca50f7a8e2f3f657c1108d9d44cfd8',
      b: '3086d221a7d46bcde86c90e49284eb15'
    }
  ],

  gRed: false,
  g: [
    '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
    '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8',
    pre
  ]
});
});

function HmacDRBG(options) {
  if (!(this instanceof HmacDRBG))
    return new HmacDRBG(options);
  this.hash = options.hash;
  this.predResist = !!options.predResist;

  this.outLen = this.hash.outSize;
  this.minEntropy = options.minEntropy || this.hash.hmacStrength;

  this._reseed = null;
  this.reseedInterval = null;
  this.K = null;
  this.V = null;

  var entropy = utils_1.toArray(options.entropy, options.entropyEnc || 'hex');
  var nonce = utils_1.toArray(options.nonce, options.nonceEnc || 'hex');
  var pers = utils_1.toArray(options.pers, options.persEnc || 'hex');
  minimalisticAssert(entropy.length >= (this.minEntropy / 8),
         'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits');
  this._init(entropy, nonce, pers);
}
var hmacDrbg = HmacDRBG;

HmacDRBG.prototype._init = function init(entropy, nonce, pers) {
  var seed = entropy.concat(nonce).concat(pers);

  this.K = new Array(this.outLen / 8);
  this.V = new Array(this.outLen / 8);
  for (var i = 0; i < this.V.length; i++) {
    this.K[i] = 0x00;
    this.V[i] = 0x01;
  }

  this._update(seed);
  this._reseed = 1;
  this.reseedInterval = 0x1000000000000;  // 2^48
};

HmacDRBG.prototype._hmac = function hmac() {
  return new hash_1.hmac(this.hash, this.K);
};

HmacDRBG.prototype._update = function update(seed) {
  var kmac = this._hmac()
                 .update(this.V)
                 .update([ 0x00 ]);
  if (seed)
    kmac = kmac.update(seed);
  this.K = kmac.digest();
  this.V = this._hmac().update(this.V).digest();
  if (!seed)
    return;

  this.K = this._hmac()
               .update(this.V)
               .update([ 0x01 ])
               .update(seed)
               .digest();
  this.V = this._hmac().update(this.V).digest();
};

HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) {
  // Optional entropy enc
  if (typeof entropyEnc !== 'string') {
    addEnc = add;
    add = entropyEnc;
    entropyEnc = null;
  }

  entropy = utils_1.toArray(entropy, entropyEnc);
  add = utils_1.toArray(add, addEnc);

  minimalisticAssert(entropy.length >= (this.minEntropy / 8),
         'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits');

  this._update(entropy.concat(add || []));
  this._reseed = 1;
};

HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) {
  if (this._reseed > this.reseedInterval)
    throw new Error('Reseed is required');

  // Optional encoding
  if (typeof enc !== 'string') {
    addEnc = add;
    add = enc;
    enc = null;
  }

  // Optional additional data
  if (add) {
    add = utils_1.toArray(add, addEnc || 'hex');
    this._update(add);
  }

  var temp = [];
  while (temp.length < len) {
    this.V = this._hmac().update(this.V).digest();
    temp = temp.concat(this.V);
  }

  var res = temp.slice(0, len);
  this._update(add);
  this._reseed++;
  return utils_1.encode(res, enc);
};

var assert$5 = utils_1$1.assert;

function KeyPair(ec, options) {
  this.ec = ec;
  this.priv = null;
  this.pub = null;

  // KeyPair(ec, { priv: ..., pub: ... })
  if (options.priv)
    this._importPrivate(options.priv, options.privEnc);
  if (options.pub)
    this._importPublic(options.pub, options.pubEnc);
}
var key = KeyPair;

KeyPair.fromPublic = function fromPublic(ec, pub, enc) {
  if (pub instanceof KeyPair)
    return pub;

  return new KeyPair(ec, {
    pub: pub,
    pubEnc: enc
  });
};

KeyPair.fromPrivate = function fromPrivate(ec, priv, enc) {
  if (priv instanceof KeyPair)
    return priv;

  return new KeyPair(ec, {
    priv: priv,
    privEnc: enc
  });
};

// TODO: should not validate for X25519
KeyPair.prototype.validate = function validate() {
  var pub = this.getPublic();

  if (pub.isInfinity())
    return { result: false, reason: 'Invalid public key' };
  if (!pub.validate())
    return { result: false, reason: 'Public key is not a point' };
  if (!pub.mul(this.ec.curve.n).isInfinity())
    return { result: false, reason: 'Public key * N != O' };

  return { result: true, reason: null };
};

KeyPair.prototype.getPublic = function getPublic(enc, compact) {
  if (!this.pub)
    this.pub = this.ec.g.mul(this.priv);

  if (!enc)
    return this.pub;

  return this.pub.encode(enc, compact);
};

KeyPair.prototype.getPrivate = function getPrivate(enc) {
  if (enc === 'hex')
    return this.priv.toString(16, 2);
  else
    return this.priv;
};

KeyPair.prototype._importPrivate = function _importPrivate(key, enc) {
  this.priv = new bn(key, enc || 16);

  // For Curve25519/Curve448 we have a specific procedure.
  // TODO Curve448
  if (this.ec.curve.type === 'mont') {
    var one = this.ec.curve.one;
    var mask = one.ushln(255 - 3).sub(one).ushln(3);
    this.priv = this.priv.or(one.ushln(255 - 1));
    this.priv = this.priv.and(mask);
  } else
    // Ensure that the priv won't be bigger than n, otherwise we may fail
    // in fixed multiplication method
    this.priv = this.priv.umod(this.ec.curve.n);
};

KeyPair.prototype._importPublic = function _importPublic(key, enc) {
  if (key.x || key.y) {
    // Montgomery points only have an `x` coordinate.
    // Weierstrass/Edwards points on the other hand have both `x` and
    // `y` coordinates.
    if (this.ec.curve.type === 'mont') {
      assert$5(key.x, 'Need x coordinate');
    } else if (this.ec.curve.type === 'short' ||
               this.ec.curve.type === 'edwards') {
      assert$5(key.x && key.y, 'Need both x and y coordinate');
    }
    this.pub = this.ec.curve.point(key.x, key.y);
    return;
  }
  this.pub = this.ec.curve.decodePoint(key, enc);
};

// ECDH
KeyPair.prototype.derive = function derive(pub) {
  return pub.mul(this.priv).getX();
};

// ECDSA
KeyPair.prototype.sign = function sign(msg, enc, options) {
  return this.ec.sign(msg, this, enc, options);
};

KeyPair.prototype.verify = function verify(msg, signature) {
  return this.ec.verify(msg, signature, this);
};

KeyPair.prototype.inspect = function inspect() {
  return '<Key priv: ' + (this.priv && this.priv.toString(16, 2)) +
         ' pub: ' + (this.pub && this.pub.inspect()) + ' >';
};

var assert$6 = utils_1$1.assert;

function Signature$1(options, enc) {
  if (options instanceof Signature$1)
    return options;

  if (this._importDER(options, enc))
    return;

  assert$6(options.r && options.s, 'Signature without r or s');
  this.r = new bn(options.r, 16);
  this.s = new bn(options.s, 16);
  if (options.recoveryParam === undefined)
    this.recoveryParam = null;
  else
    this.recoveryParam = options.recoveryParam;
}
var signature$1 = Signature$1;

function Position() {
  this.place = 0;
}

function getLength(buf, p) {
  var initial = buf[p.place++];
  if (!(initial & 0x80)) {
    return initial;
  }
  var octetLen = initial & 0xf;
  var val = 0;
  for (var i = 0, off = p.place; i < octetLen; i++, off++) {
    val <<= 8;
    val |= buf[off];
  }
  p.place = off;
  return val;
}

function rmPadding(buf) {
  var i = 0;
  var len = buf.length - 1;
  while (!buf[i] && !(buf[i + 1] & 0x80) && i < len) {
    i++;
  }
  if (i === 0) {
    return buf;
  }
  return buf.slice(i);
}

Signature$1.prototype._importDER = function _importDER(data, enc) {
  data = utils_1$1.toArray(data, enc);
  var p = new Position();
  if (data[p.place++] !== 0x30) {
    return false;
  }
  var len = getLength(data, p);
  if ((len + p.place) !== data.length) {
    return false;
  }
  if (data[p.place++] !== 0x02) {
    return false;
  }
  var rlen = getLength(data, p);
  var r = data.slice(p.place, rlen + p.place);
  p.place += rlen;
  if (data[p.place++] !== 0x02) {
    return false;
  }
  var slen = getLength(data, p);
  if (data.length !== slen + p.place) {
    return false;
  }
  var s = data.slice(p.place, slen + p.place);
  if (r[0] === 0 && (r[1] & 0x80)) {
    r = r.slice(1);
  }
  if (s[0] === 0 && (s[1] & 0x80)) {
    s = s.slice(1);
  }

  this.r = new bn(r);
  this.s = new bn(s);
  this.recoveryParam = null;

  return true;
};

function constructLength(arr, len) {
  if (len < 0x80) {
    arr.push(len);
    return;
  }
  var octets = 1 + (Math.log(len) / Math.LN2 >>> 3);
  arr.push(octets | 0x80);
  while (--octets) {
    arr.push((len >>> (octets << 3)) & 0xff);
  }
  arr.push(len);
}

Signature$1.prototype.toDER = function toDER(enc) {
  var r = this.r.toArray();
  var s = this.s.toArray();

  // Pad values
  if (r[0] & 0x80)
    r = [ 0 ].concat(r);
  // Pad values
  if (s[0] & 0x80)
    s = [ 0 ].concat(s);

  r = rmPadding(r);
  s = rmPadding(s);

  while (!s[0] && !(s[1] & 0x80)) {
    s = s.slice(1);
  }
  var arr = [ 0x02 ];
  constructLength(arr, r.length);
  arr = arr.concat(r);
  arr.push(0x02);
  constructLength(arr, s.length);
  var backHalf = arr.concat(s);
  var res = [ 0x30 ];
  constructLength(res, backHalf.length);
  res = res.concat(backHalf);
  return utils_1$1.encode(res, enc);
};

var assert$7 = utils_1$1.assert;




function EC(options) {
  if (!(this instanceof EC))
    return new EC(options);

  // Shortcut `elliptic.ec(curve-name)`
  if (typeof options === 'string') {
    assert$7(curves_1.hasOwnProperty(options), 'Unknown curve ' + options);

    options = curves_1[options];
  }

  // Shortcut for `elliptic.ec(elliptic.curves.curveName)`
  if (options instanceof curves_1.PresetCurve)
    options = { curve: options };

  this.curve = options.curve.curve;
  this.n = this.curve.n;
  this.nh = this.n.ushrn(1);
  this.g = this.curve.g;

  // Point on curve
  this.g = options.curve.g;
  this.g.precompute(options.curve.n.bitLength() + 1);

  // Hash function for DRBG
  this.hash = options.hash || options.curve.hash;
}
var ec = EC;

EC.prototype.keyPair = function keyPair(options) {
  return new key(this, options);
};

EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) {
  return key.fromPrivate(this, priv, enc);
};

EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) {
  return key.fromPublic(this, pub, enc);
};

EC.prototype.genKeyPair = function genKeyPair(options) {
  if (!options)
    options = {};

  // Instantiate Hmac_DRBG
  var drbg = new hmacDrbg({
    hash: this.hash,
    pers: options.pers,
    persEnc: options.persEnc || 'utf8',
    entropy: options.entropy || brorand(this.hash.hmacStrength),
    entropyEnc: options.entropy && options.entropyEnc || 'utf8',
    nonce: this.n.toArray()
  });

  // Key generation for curve25519 is simpler
  if (this.curve.type === 'mont') {
    var priv = new bn(drbg.generate(32));
    return this.keyFromPrivate(priv);
  }

  var bytes = this.n.byteLength();
  var ns2 = this.n.sub(new bn(2));
  do {
    var priv = new bn(drbg.generate(bytes));
    if (priv.cmp(ns2) > 0)
      continue;

    priv.iaddn(1);
    return this.keyFromPrivate(priv);
  } while (true);
};

EC.prototype._truncateToN = function truncateToN(msg, truncOnly, bitSize) {
  bitSize = bitSize || msg.byteLength() * 8;
  var delta = bitSize - this.n.bitLength();
  if (delta > 0)
    msg = msg.ushrn(delta);
  if (!truncOnly && msg.cmp(this.n) >= 0)
    return msg.sub(this.n);
  else
    return msg;
};

EC.prototype.truncateMsg  = function truncateMSG(msg) {
  // Bit size is only determined correctly for Uint8Arrays and hex strings
  var bitSize;
  if (msg instanceof Uint8Array) {
    bitSize = msg.byteLength * 8;
    msg = this._truncateToN(new bn(msg, 16), false, bitSize);
  } else if (typeof msg === 'string') {
    bitSize = msg.length * 4;
    msg = this._truncateToN(new bn(msg, 16), false, bitSize);
  } else {
    msg = this._truncateToN(new bn(msg, 16));
  }
  return msg;
};

EC.prototype.sign = function sign(msg, key, enc, options) {
  if (typeof enc === 'object') {
    options = enc;
    enc = null;
  }
  if (!options)
    options = {};

  key = this.keyFromPrivate(key, enc);
  msg = this.truncateMsg(msg);

  // Zero-extend key to provide enough entropy
  var bytes = this.n.byteLength();
  var bkey = key.getPrivate().toArray('be', bytes);

  // Zero-extend nonce to have the same byte size as N
  var nonce = msg.toArray('be', bytes);

  // Instantiate Hmac_DRBG
  var drbg = new hmacDrbg({
    hash: this.hash,
    entropy: bkey,
    nonce: nonce,
    pers: options.pers,
    persEnc: options.persEnc || 'utf8'
  });

  // Number of bytes to generate
  var ns1 = this.n.sub(new bn(1));

  for (var iter = 0; true; iter++) {
    var k = options.k ?
        options.k(iter) :
        new bn(drbg.generate(this.n.byteLength()));
    k = this._truncateToN(k, true);
    if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0)
      continue;

    var kp = this.g.mul(k);
    if (kp.isInfinity())
      continue;

    var kpX = kp.getX();
    var r = kpX.umod(this.n);
    if (r.cmpn(0) === 0)
      continue;

    var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg));
    s = s.umod(this.n);
    if (s.cmpn(0) === 0)
      continue;

    var recoveryParam = (kp.getY().isOdd() ? 1 : 0) |
                        (kpX.cmp(r) !== 0 ? 2 : 0);

    // Use complement of `s`, if it is > `n / 2`
    if (options.canonical && s.cmp(this.nh) > 0) {
      s = this.n.sub(s);
      recoveryParam ^= 1;
    }

    return new signature$1({ r: r, s: s, recoveryParam: recoveryParam });
  }
};

EC.prototype.verify = function verify(msg, signature, key, enc) {
  key = this.keyFromPublic(key, enc);
  signature = new signature$1(signature, 'hex');
  // Fallback to the old code
  var ret = this._verify(this.truncateMsg(msg), signature, key) ||
  this._verify(this._truncateToN(new bn(msg, 16)), signature, key);
  return ret;
};

EC.prototype._verify = function _verify(msg, signature, key) {
  // Perform primitive values validation
  var r = signature.r;
  var s = signature.s;
  if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0)
    return false;
  if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0)
    return false;

  // Validate signature
  var sinv = s.invm(this.n);
  var u1 = sinv.mul(msg).umod(this.n);
  var u2 = sinv.mul(r).umod(this.n);

  if (!this.curve._maxwellTrick) {
    var p = this.g.mulAdd(u1, key.getPublic(), u2);
    if (p.isInfinity())
      return false;

    return p.getX().umod(this.n).cmp(r) === 0;
  }

  // NOTE: Greg Maxwell's trick, inspired by:
  // https://git.io/vad3K

  var p = this.g.jmulAdd(u1, key.getPublic(), u2);
  if (p.isInfinity())
    return false;

  // Compare `p.x` of Jacobian point with `r`,
  // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the
  // inverse of `p.z^2`
  return p.eqXToP(r);
};

EC.prototype.recoverPubKey = function(msg, signature, j, enc) {
  assert$7((3 & j) === j, 'The recovery param is more than two bits');
  signature = new signature$1(signature, enc);

  var n = this.n;
  var e = new bn(msg);
  var r = signature.r;
  var s = signature.s;

  // A set LSB signifies that the y-coordinate is odd
  var isYOdd = j & 1;
  var isSecondKey = j >> 1;
  if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey)
    throw new Error('Unable to find sencond key candinate');

  // 1.1. Let x = r + jn.
  if (isSecondKey)
    r = this.curve.pointFromX(r.add(this.curve.n), isYOdd);
  else
    r = this.curve.pointFromX(r, isYOdd);

  var rInv = signature.r.invm(n);
  var s1 = n.sub(e).mul(rInv).umod(n);
  var s2 = s.mul(rInv).umod(n);

  // 1.6.1 Compute Q = r^-1 (sR -  eG)
  //               Q = r^-1 (sR + -eG)
  return this.g.mulAdd(s1, r, s2);
};

EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) {
  signature = new signature$1(signature, enc);
  if (signature.recoveryParam !== null)
    return signature.recoveryParam;

  for (var i = 0; i < 4; i++) {
    var Qprime;
    try {
      Qprime = this.recoverPubKey(e, signature, i);
    } catch (e) {
      continue;
    }

    if (Qprime.eq(Q))
      return i;
  }
  throw new Error('Unable to find valid recovery factor');
};

var assert$8 = utils_1$1.assert;
var parseBytes = utils_1$1.parseBytes;
var cachedProperty = utils_1$1.cachedProperty;

/**
* @param {EDDSA} eddsa - instance
* @param {Object} params - public/private key parameters
*
* @param {Array<Byte>} [params.secret] - secret seed bytes
* @param {Point} [params.pub] - public key point (aka `A` in eddsa terms)
* @param {Array<Byte>} [params.pub] - public key point encoded as bytes
*
*/
function KeyPair$1(eddsa, params) {
  this.eddsa = eddsa;
  if (params.hasOwnProperty('secret'))
    this._secret = parseBytes(params.secret);
  if (eddsa.isPoint(params.pub))
    this._pub = params.pub;
  else {
    this._pubBytes = parseBytes(params.pub);
    if (this._pubBytes && this._pubBytes.length === 33 &&
        this._pubBytes[0] === 0x40)
      this._pubBytes = this._pubBytes.slice(1, 33);
    if (this._pubBytes && this._pubBytes.length !== 32)
      throw new Error('Unknown point compression format');
  }
}

KeyPair$1.fromPublic = function fromPublic(eddsa, pub) {
  if (pub instanceof KeyPair$1)
    return pub;
  return new KeyPair$1(eddsa, { pub: pub });
};

KeyPair$1.fromSecret = function fromSecret(eddsa, secret) {
  if (secret instanceof KeyPair$1)
    return secret;
  return new KeyPair$1(eddsa, { secret: secret });
};

KeyPair$1.prototype.secret = function secret() {
  return this._secret;
};

cachedProperty(KeyPair$1, 'pubBytes', function pubBytes() {
  return this.eddsa.encodePoint(this.pub());
});

cachedProperty(KeyPair$1, 'pub', function pub() {
  if (this._pubBytes)
    return this.eddsa.decodePoint(this._pubBytes);
  return this.eddsa.g.mul(this.priv());
});

cachedProperty(KeyPair$1, 'privBytes', function privBytes() {
  var eddsa = this.eddsa;
  var hash = this.hash();
  var lastIx = eddsa.encodingLength - 1;

  // https://tools.ietf.org/html/rfc8032#section-5.1.5
  var a = hash.slice(0, eddsa.encodingLength);
  a[0] &= 248;
  a[lastIx] &= 127;
  a[lastIx] |= 64;

  return a;
});

cachedProperty(KeyPair$1, 'priv', function priv() {
  return this.eddsa.decodeInt(this.privBytes());
});

cachedProperty(KeyPair$1, 'hash', function hash() {
  return this.eddsa.hash().update(this.secret()).digest();
});

cachedProperty(KeyPair$1, 'messagePrefix', function messagePrefix() {
  return this.hash().slice(this.eddsa.encodingLength);
});

KeyPair$1.prototype.sign = function sign(message) {
  assert$8(this._secret, 'KeyPair can only verify');
  return this.eddsa.sign(message, this);
};

KeyPair$1.prototype.verify = function verify(message, sig) {
  return this.eddsa.verify(message, sig, this);
};

KeyPair$1.prototype.getSecret = function getSecret(enc) {
  assert$8(this._secret, 'KeyPair is public only');
  return utils_1$1.encode(this.secret(), enc);
};

KeyPair$1.prototype.getPublic = function getPublic(enc, compact) {
  return utils_1$1.encode((compact ? [ 0x40 ] : []).concat(this.pubBytes()), enc);
};

var key$1 = KeyPair$1;

var assert$9 = utils_1$1.assert;
var cachedProperty$1 = utils_1$1.cachedProperty;
var parseBytes$1 = utils_1$1.parseBytes;

/**
* @param {EDDSA} eddsa - eddsa instance
* @param {Array<Bytes>|Object} sig -
* @param {Array<Bytes>|Point} [sig.R] - R point as Point or bytes
* @param {Array<Bytes>|bn} [sig.S] - S scalar as bn or bytes
* @param {Array<Bytes>} [sig.Rencoded] - R point encoded
* @param {Array<Bytes>} [sig.Sencoded] - S scalar encoded
*/
function Signature$2(eddsa, sig) {
  this.eddsa = eddsa;

  if (typeof sig !== 'object')
    sig = parseBytes$1(sig);

  if (Array.isArray(sig)) {
    sig = {
      R: sig.slice(0, eddsa.encodingLength),
      S: sig.slice(eddsa.encodingLength)
    };
  }

  assert$9(sig.R && sig.S, 'Signature without R or S');

  if (eddsa.isPoint(sig.R))
    this._R = sig.R;
  if (sig.S instanceof bn)
    this._S = sig.S;

  this._Rencoded = Array.isArray(sig.R) ? sig.R : sig.Rencoded;
  this._Sencoded = Array.isArray(sig.S) ? sig.S : sig.Sencoded;
}

cachedProperty$1(Signature$2, 'S', function S() {
  return this.eddsa.decodeInt(this.Sencoded());
});

cachedProperty$1(Signature$2, 'R', function R() {
  return this.eddsa.decodePoint(this.Rencoded());
});

cachedProperty$1(Signature$2, 'Rencoded', function Rencoded() {
  return this.eddsa.encodePoint(this.R());
});

cachedProperty$1(Signature$2, 'Sencoded', function Sencoded() {
  return this.eddsa.encodeInt(this.S());
});

Signature$2.prototype.toBytes = function toBytes() {
  return this.Rencoded().concat(this.Sencoded());
};

Signature$2.prototype.toHex = function toHex() {
  return utils_1$1.encode(this.toBytes(), 'hex').toUpperCase();
};

var signature$2 = Signature$2;

var assert$a = utils_1$1.assert;
var parseBytes$2 = utils_1$1.parseBytes;



function EDDSA(curve) {
  assert$a(curve === 'ed25519', 'only tested with ed25519 so far');

  if (!(this instanceof EDDSA))
    return new EDDSA(curve);

  var curve = curves_1[curve].curve;
  this.curve = curve;
  this.g = curve.g;
  this.g.precompute(curve.n.bitLength() + 1);

  this.pointClass = curve.point().constructor;
  this.encodingLength = Math.ceil(curve.n.bitLength() / 8);
  this.hash = hash_1.sha512;
}

var eddsa$1 = EDDSA;

/**
* @param {Array|String} message - message bytes
* @param {Array|String|KeyPair} secret - secret bytes or a keypair
* @returns {Signature} - signature
*/
EDDSA.prototype.sign = function sign(message, secret) {
  message = parseBytes$2(message);
  var key = this.keyFromSecret(secret);
  var r = this.hashInt(key.messagePrefix(), message);
  var R = this.g.mul(r);
  var Rencoded = this.encodePoint(R);
  var s_ = this.hashInt(Rencoded, key.pubBytes(), message)
               .mul(key.priv());
  var S = r.add(s_).umod(this.curve.n);
  return this.makeSignature({ R: R, S: S, Rencoded: Rencoded });
};

/**
* @param {Array} message - message bytes
* @param {Array|String|Signature} sig - sig bytes
* @param {Array|String|Point|KeyPair} pub - public key
* @returns {Boolean} - true if public key matches sig of message
*/
EDDSA.prototype.verify = function verify(message, sig, pub) {
  message = parseBytes$2(message);
  sig = this.makeSignature(sig);
  var key = this.keyFromPublic(pub);
  var h = this.hashInt(sig.Rencoded(), key.pubBytes(), message);
  var SG = this.g.mul(sig.S());
  var RplusAh = sig.R().add(key.pub().mul(h));
  return RplusAh.eq(SG);
};

EDDSA.prototype.hashInt = function hashInt() {
  var hash = this.hash();
  for (var i = 0; i < arguments.length; i++)
    hash.update(arguments[i]);
  return utils_1$1.intFromLE(hash.digest()).umod(this.curve.n);
};

EDDSA.prototype.keyPair = function keyPair(options) {
  return new key$1(this, options);
};

EDDSA.prototype.keyFromPublic = function keyFromPublic(pub) {
  return key$1.fromPublic(this, pub);
};

EDDSA.prototype.keyFromSecret = function keyFromSecret(secret) {
  return key$1.fromSecret(this, secret);
};

EDDSA.prototype.genKeyPair = function genKeyPair(options) {
  if (!options)
    options = {};

  // Instantiate Hmac_DRBG
  var drbg = new hmacDrbg({
    hash: this.hash,
    pers: options.pers,
    persEnc: options.persEnc || 'utf8',
    entropy: options.entropy || brorand(this.hash.hmacStrength),
    entropyEnc: options.entropy && options.entropyEnc || 'utf8',
    nonce: this.curve.n.toArray()
  });

  return this.keyFromSecret(drbg.generate(32));
};

EDDSA.prototype.makeSignature = function makeSignature(sig) {
  if (sig instanceof signature$2)
    return sig;
  return new signature$2(this, sig);
};

/**
* * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-5.2
*
* EDDSA defines methods for encoding and decoding points and integers. These are
* helper convenience methods, that pass along to utility functions implied
* parameters.
*
*/
EDDSA.prototype.encodePoint = function encodePoint(point) {
  var enc = point.getY().toArray('le', this.encodingLength);
  enc[this.encodingLength - 1] |= point.getX().isOdd() ? 0x80 : 0;
  return enc;
};

EDDSA.prototype.decodePoint = function decodePoint(bytes) {
  bytes = utils_1$1.parseBytes(bytes);

  var lastIx = bytes.length - 1;
  var normed = bytes.slice(0, lastIx).concat(bytes[lastIx] & ~0x80);
  var xIsOdd = (bytes[lastIx] & 0x80) !== 0;

  var y = utils_1$1.intFromLE(normed);
  return this.curve.pointFromY(y, xIsOdd);
};

EDDSA.prototype.encodeInt = function encodeInt(num) {
  return num.toArray('le', this.encodingLength);
};

EDDSA.prototype.decodeInt = function decodeInt(bytes) {
  return utils_1$1.intFromLE(bytes);
};

EDDSA.prototype.isPoint = function isPoint(val) {
  return val instanceof this.pointClass;
};

var elliptic_1 = createCommonjsModule(function (module, exports) {

var elliptic = exports;

elliptic.utils = utils_1$1;
elliptic.rand = brorand;
elliptic.curve = curve_1;
elliptic.curves = curves_1;

// Protocols
elliptic.ec = ec;
elliptic.eddsa = eddsa$1;
});

var elliptic$1 = /*#__PURE__*/Object.freeze({
  __proto__: null,
  'default': elliptic_1,
  __moduleExports: elliptic_1
});

exports.AEADEncryptedDataPacket = AEADEncryptedDataPacket;
exports.CleartextMessage = CleartextMessage;
exports.CompressedDataPacket = CompressedDataPacket;
exports.LiteralDataPacket = LiteralDataPacket;
exports.MarkerPacket = MarkerPacket;
exports.Message = Message;
exports.OnePassSignaturePacket = OnePassSignaturePacket;
exports.PacketList = PacketList;
exports.PrivateKey = PrivateKey;
exports.PublicKey = PublicKey;
exports.PublicKeyEncryptedSessionKeyPacket = PublicKeyEncryptedSessionKeyPacket;
exports.PublicKeyPacket = PublicKeyPacket;
exports.PublicSubkeyPacket = PublicSubkeyPacket;
exports.SecretKeyPacket = SecretKeyPacket;
exports.SecretSubkeyPacket = SecretSubkeyPacket;
exports.Signature = Signature;
exports.SignaturePacket = SignaturePacket;
exports.Subkey = Subkey;
exports.SymEncryptedIntegrityProtectedDataPacket = SymEncryptedIntegrityProtectedDataPacket;
exports.SymEncryptedSessionKeyPacket = SymEncryptedSessionKeyPacket;
exports.SymmetricallyEncryptedDataPacket = SymmetricallyEncryptedDataPacket;
exports.TrustPacket = TrustPacket;
exports.UnparseablePacket = UnparseablePacket;
exports.UserAttributePacket = UserAttributePacket;
exports.UserIDPacket = UserIDPacket;
exports.armor = armor;
exports.config = config;
exports.createCleartextMessage = createCleartextMessage;
exports.createMessage = createMessage;
exports.decrypt = decrypt$5;
exports.decryptKey = decryptKey;
exports.decryptSessionKeys = decryptSessionKeys;
exports.encrypt = encrypt$5;
exports.encryptKey = encryptKey;
exports.encryptSessionKey = encryptSessionKey;
exports.enums = enums;
exports.generateKey = generateKey;
exports.generateSessionKey = generateSessionKey$1;
exports.readCleartextMessage = readCleartextMessage;
exports.readKey = readKey;
exports.readKeys = readKeys;
exports.readMessage = readMessage;
exports.readPrivateKey = readPrivateKey;
exports.readPrivateKeys = readPrivateKeys;
exports.readSignature = readSignature;
exports.reformatKey = reformatKey;
exports.revokeKey = revokeKey;
exports.sign = sign$6;
exports.unarmor = unarmor;
exports.verify = verify$6;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               /*! OpenPGP.js v5.11.2 - 2024-06-19 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
var openpgp = (function (exports) {
  'use strict';

  const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

  const doneWritingPromise = Symbol('doneWritingPromise');
  const doneWritingResolve = Symbol('doneWritingResolve');
  const doneWritingReject = Symbol('doneWritingReject');

  const readingIndex = Symbol('readingIndex');

  class ArrayStream extends Array {
    constructor() {
      super();
      this[doneWritingPromise] = new Promise((resolve, reject) => {
        this[doneWritingResolve] = resolve;
        this[doneWritingReject] = reject;
      });
      this[doneWritingPromise].catch(() => {});
    }
  }

  ArrayStream.prototype.getReader = function() {
    if (this[readingIndex] === undefined) {
      this[readingIndex] = 0;
    }
    return {
      read: async () => {
        await this[doneWritingPromise];
        if (this[readingIndex] === this.length) {
          return { value: undefined, done: true };
        }
        return { value: this[this[readingIndex]++], done: false };
      }
    };
  };

  ArrayStream.prototype.readToEnd = async function(join) {
    await this[doneWritingPromise];
    const result = join(this.slice(this[readingIndex]));
    this.length = 0;
    return result;
  };

  ArrayStream.prototype.clone = function() {
    const clone = new ArrayStream();
    clone[doneWritingPromise] = this[doneWritingPromise].then(() => {
      clone.push(...this);
    });
    return clone;
  };

  /**
   * Check whether data is an ArrayStream
   * @param {Any} input  data to check
   * @returns {boolean}
   */
  function isArrayStream(input) {
    return input && input.getReader && Array.isArray(input);
  }

  /**
   * A wrapper class over the native WritableStreamDefaultWriter.
   * It also lets you "write data to" array streams instead of streams.
   * @class
   */
  function Writer(input) {
    if (!isArrayStream(input)) {
      const writer = input.getWriter();
      const releaseLock = writer.releaseLock;
      writer.releaseLock = () => {
        writer.closed.catch(function() {});
        releaseLock.call(writer);
      };
      return writer;
    }
    this.stream = input;
  }

  /**
   * Write a chunk of data.
   * @returns {Promise<undefined>}
   * @async
   */
  Writer.prototype.write = async function(chunk) {
    this.stream.push(chunk);
  };

  /**
   * Close the stream.
   * @returns {Promise<undefined>}
   * @async
   */
  Writer.prototype.close = async function() {
    this.stream[doneWritingResolve]();
  };

  /**
   * Error the stream.
   * @returns {Promise<Object>}
   * @async
   */
  Writer.prototype.abort = async function(reason) {
    this.stream[doneWritingReject](reason);
    return reason;
  };

  /**
   * Release the writer's lock.
   * @returns {undefined}
   * @async
   */
  Writer.prototype.releaseLock = function() {};

  const isNode = typeof globalThis.process === 'object' &&
    typeof globalThis.process.versions === 'object';

  const NodeReadableStream = isNode && void('stream').Readable;

  /**
   * Check whether data is a Stream, and if so of which type
   * @param {Any} input  data to check
   * @returns {'web'|'ponyfill'|'node'|'array'|'web-like'|false}
   */
  function isStream(input) {
    if (isArrayStream(input)) {
      return 'array';
    }
    if (globalThis.ReadableStream && globalThis.ReadableStream.prototype.isPrototypeOf(input)) {
      return 'web';
    }
    if (ReadableStream && ReadableStream.prototype.isPrototypeOf(input)) {
      return 'ponyfill';
    }
    if (NodeReadableStream && NodeReadableStream.prototype.isPrototypeOf(input)) {
      return 'node';
    }
    if (input && input.getReader) {
      return 'web-like';
    }
    return false;
  }

  /**
   * Check whether data is a Uint8Array
   * @param {Any} input  data to check
   * @returns {Boolean}
   */
  function isUint8Array(input) {
    return Uint8Array.prototype.isPrototypeOf(input);
  }

  /**
   * Concat Uint8Arrays
   * @param {Array<Uint8array>} Array of Uint8Arrays to concatenate
   * @returns {Uint8array} Concatenated array
   */
  function concatUint8Array(arrays) {
    if (arrays.length === 1) return arrays[0];

    let totalLength = 0;
    for (let i = 0; i < arrays.length; i++) {
      if (!isUint8Array(arrays[i])) {
        throw new Error('concatUint8Array: Data must be in the form of a Uint8Array');
      }

      totalLength += arrays[i].length;
    }

    const result = new Uint8Array(totalLength);
    let pos = 0;
    arrays.forEach(function (element) {
      result.set(element, pos);
      pos += element.length;
    });

    return result;
  }

  const NodeBuffer = isNode && void('buffer').Buffer;
  const NodeReadableStream$1 = isNode && void('stream').Readable;

  /**
   * Web / node stream conversion functions
   * From https://github.com/gwicke/node-web-streams
   */

  let nodeToWeb;
  let webToNode;

  if (NodeReadableStream$1) {

    /**
     * Convert a Node Readable Stream to a Web ReadableStream
     * @param {Readable} nodeStream
     * @returns {ReadableStream}
     */
    nodeToWeb = function(nodeStream) {
      let canceled = false;
      return new ReadableStream({
        start(controller) {
          nodeStream.pause();
          nodeStream.on('data', chunk => {
            if (canceled) {
              return;
            }
            if (NodeBuffer.isBuffer(chunk)) {
              chunk = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
            }
            controller.enqueue(chunk);
            nodeStream.pause();
          });
          nodeStream.on('end', () => {
            if (canceled) {
              return;
            }
            controller.close();
          });
          nodeStream.on('error', e => controller.error(e));
        },
        pull() {
          nodeStream.resume();
        },
        cancel(reason) {
          canceled = true;
          nodeStream.destroy(reason);
        }
      });
    };


    class NodeReadable extends NodeReadableStream$1 {
      constructor(webStream, options) {
        super(options);
        this._reader = getReader(webStream);
      }

      async _read(size) {
        try {
          while (true) {
            const { done, value } = await this._reader.read();
            if (done) {
              this.push(null);
              break;
            }
            if (!this.push(value)) {
              break;
            }
          }
        } catch (e) {
          this.destroy(e);
        }
      }

      async _destroy(error, callback) {
        this._reader.cancel(error).then(callback, callback);
      }
    }

    /**
     * Convert a Web ReadableStream to a Node Readable Stream
     * @param {ReadableStream} webStream
     * @param {Object} options
     * @returns {Readable}
     */
    webToNode = function(webStream, options) {
      return new NodeReadable(webStream, options);
    };

  }

  const doneReadingSet = new WeakSet();
  const externalBuffer = Symbol('externalBuffer');

  /**
   * A wrapper class over the native ReadableStreamDefaultReader.
   * This additionally implements pushing back data on the stream, which
   * lets us implement peeking and a host of convenience functions.
   * It also lets you read data other than streams, such as a Uint8Array.
   * @class
   */
  function Reader(input) {
    this.stream = input;
    if (input[externalBuffer]) {
      this[externalBuffer] = input[externalBuffer].slice();
    }
    if (isArrayStream(input)) {
      const reader = input.getReader();
      this._read = reader.read.bind(reader);
      this._releaseLock = () => {};
      this._cancel = async () => {};
      return;
    }
    let streamType = isStream(input);
    if (streamType === 'node') {
      input = nodeToWeb(input);
    }
    if (streamType) {
      const reader = input.getReader();
      this._read = reader.read.bind(reader);
      this._releaseLock = () => {
        reader.closed.catch(function() {});
        reader.releaseLock();
      };
      this._cancel = reader.cancel.bind(reader);
      return;
    }
    let doneReading = false;
    this._read = async () => {
      if (doneReading || doneReadingSet.has(input)) {
        return { value: undefined, done: true };
      }
      doneReading = true;
      return { value: input, done: false };
    };
    this._releaseLock = () => {
      if (doneReading) {
        try {
          doneReadingSet.add(input);
        } catch(e) {}
      }
    };
  }

  /**
   * Read a chunk of data.
   * @returns {Promise<Object>} Either { done: false, value: Uint8Array | String } or { done: true, value: undefined }
   * @async
   */
  Reader.prototype.read = async function() {
    if (this[externalBuffer] && this[externalBuffer].length) {
      const value = this[externalBuffer].shift();
      return { done: false, value };
    }
    return this._read();
  };

  /**
   * Allow others to read the stream.
   */
  Reader.prototype.releaseLock = function() {
    if (this[externalBuffer]) {
      this.stream[externalBuffer] = this[externalBuffer];
    }
    this._releaseLock();
  };

  /**
   * Cancel the stream.
   */
  Reader.prototype.cancel = function(reason) {
    return this._cancel(reason);
  };

  /**
   * Read up to and including the first \n character.
   * @returns {Promise<String|Undefined>}
   * @async
   */
  Reader.prototype.readLine = async function() {
    let buffer = [];
    let returnVal;
    while (!returnVal) {
      let { done, value } = await this.read();
      value += '';
      if (done) {
        if (buffer.length) return concat(buffer);
        return;
      }
      const lineEndIndex = value.indexOf('\n') + 1;
      if (lineEndIndex) {
        returnVal = concat(buffer.concat(value.substr(0, lineEndIndex)));
        buffer = [];
      }
      if (lineEndIndex !== value.length) {
        buffer.push(value.substr(lineEndIndex));
      }
    }
    this.unshift(...buffer);
    return returnVal;
  };

  /**
   * Read a single byte/character.
   * @returns {Promise<Number|String|Undefined>}
   * @async
   */
  Reader.prototype.readByte = async function() {
    const { done, value } = await this.read();
    if (done) return;
    const byte = value[0];
    this.unshift(slice(value, 1));
    return byte;
  };

  /**
   * Read a specific amount of bytes/characters, unless the stream ends before that amount.
   * @returns {Promise<Uint8Array|String|Undefined>}
   * @async
   */
  Reader.prototype.readBytes = async function(length) {
    const buffer = [];
    let bufferLength = 0;
    while (true) {
      const { done, value } = await this.read();
      if (done) {
        if (buffer.length) return concat(buffer);
        return;
      }
      buffer.push(value);
      bufferLength += value.length;
      if (bufferLength >= length) {
        const bufferConcat = concat(buffer);
        this.unshift(slice(bufferConcat, length));
        return slice(bufferConcat, 0, length);
      }
    }
  };

  /**
   * Peek (look ahead) a specific amount of bytes/characters, unless the stream ends before that amount.
   * @returns {Promise<Uint8Array|String|Undefined>}
   * @async
   */
  Reader.prototype.peekBytes = async function(length) {
    const bytes = await this.readBytes(length);
    this.unshift(bytes);
    return bytes;
  };

  /**
   * Push data to the front of the stream.
   * Data must have been read in the last call to read*.
   * @param {...(Uint8Array|String|Undefined)} values
   */
  Reader.prototype.unshift = function(...values) {
    if (!this[externalBuffer]) {
      this[externalBuffer] = [];
    }
    if (
      values.length === 1 && isUint8Array(values[0]) &&
      this[externalBuffer].length && values[0].length &&
      this[externalBuffer][0].byteOffset >= values[0].length
    ) {
      this[externalBuffer][0] = new Uint8Array(
        this[externalBuffer][0].buffer,
        this[externalBuffer][0].byteOffset - values[0].length,
        this[externalBuffer][0].byteLength + values[0].length
      );
      return;
    }
    this[externalBuffer].unshift(...values.filter(value => value && value.length));
  };

  /**
   * Read the stream to the end and return its contents, concatenated by the join function (defaults to streams.concat).
   * @param {Function} join
   * @returns {Promise<Uint8array|String|Any>} the return value of join()
   * @async
   */
  Reader.prototype.readToEnd = async function(join=concat) {
    const result = [];
    while (true) {
      const { done, value } = await this.read();
      if (done) break;
      result.push(value);
    }
    return join(result);
  };

  let { ReadableStream, WritableStream, TransformStream } = globalThis;

  let toPonyfillReadable, toNativeReadable;

  async function loadStreamsPonyfill() {
    if (TransformStream) {
      return;
    }

    const [ponyfill, adapter] = await Promise.all([
      Promise.resolve().then(function () { return ponyfill_es6; }),
      Promise.resolve().then(function () { return webStreamsAdapter; })
    ]);

    ({ ReadableStream, WritableStream, TransformStream } = ponyfill);

    const { createReadableStreamWrapper } = adapter;

    if (globalThis.ReadableStream && ReadableStream !== globalThis.ReadableStream) {
      toPonyfillReadable = createReadableStreamWrapper(ReadableStream);
      toNativeReadable = createReadableStreamWrapper(globalThis.ReadableStream);
    }
  }

  const NodeBuffer$1 = isNode && void('buffer').Buffer;

  /**
   * Convert data to Stream
   * @param {ReadableStream|Uint8array|String} input  data to convert
   * @returns {ReadableStream} Converted data
   */
  function toStream(input) {
    let streamType = isStream(input);
    if (streamType === 'node') {
      return nodeToWeb(input);
    }
    if (streamType === 'web' && toPonyfillReadable) {
      return toPonyfillReadable(input);
    }
    if (streamType) {
      return input;
    }
    return new ReadableStream({
      start(controller) {
        controller.enqueue(input);
        controller.close();
      }
    });
  }

  /**
   * Convert data to ArrayStream
   * @param {Object} input  data to convert
   * @returns {ArrayStream} Converted data
   */
  function toArrayStream(input) {
    if (isStream(input)) {
      return input;
    }
    const stream = new ArrayStream();
    (async () => {
      const writer = getWriter(stream);
      await writer.write(input);
      await writer.close();
    })();
    return stream;
  }

  /**
   * Concat a list of Uint8Arrays, Strings or Streams
   * The caller should not mix Uint8Arrays with Strings, but may mix Streams with non-Streams.
   * @param {Array<Uint8array|String|ReadableStream>} Array of Uint8Arrays/Strings/Streams to concatenate
   * @returns {Uint8array|String|ReadableStream} Concatenated array
   */
  function concat(list) {
    if (list.some(stream => isStream(stream) && !isArrayStream(stream))) {
      return concatStream(list);
    }
    if (list.some(stream => isArrayStream(stream))) {
      return concatArrayStream(list);
    }
    if (typeof list[0] === 'string') {
      return list.join('');
    }
    if (NodeBuffer$1 && NodeBuffer$1.isBuffer(list[0])) {
      return NodeBuffer$1.concat(list);
    }
    return concatUint8Array(list);
  }

  /**
   * Concat a list of Streams
   * @param {Array<ReadableStream|Uint8array|String>} list  Array of Uint8Arrays/Strings/Streams to concatenate
   * @returns {ReadableStream} Concatenated list
   */
  function concatStream(list) {
    list = list.map(toStream);
    const transform = transformWithCancel(async function(reason) {
      await Promise.all(transforms.map(stream => cancel(stream, reason)));
    });
    let prev = Promise.resolve();
    const transforms = list.map((stream, i) => transformPair(stream, (readable, writable) => {
      prev = prev.then(() => pipe(readable, transform.writable, {
        preventClose: i !== list.length - 1
      }));
      return prev;
    }));
    return transform.readable;
  }

  /**
   * Concat a list of ArrayStreams
   * @param {Array<ArrayStream|Uint8array|String>} list  Array of Uint8Arrays/Strings/ArrayStreams to concatenate
   * @returns {ArrayStream} Concatenated streams
   */
  function concatArrayStream(list) {
    const result = new ArrayStream();
    let prev = Promise.resolve();
    list.forEach((stream, i) => {
      prev = prev.then(() => pipe(stream, result, {
        preventClose: i !== list.length - 1
      }));
      return prev;
    });
    return result;
  }

  /**
   * Get a Reader
   * @param {ReadableStream|Uint8array|String} input
   * @returns {Reader}
   */
  function getReader(input) {
    return new Reader(input);
  }

  /**
   * Get a Writer
   * @param {WritableStream} input
   * @returns {Writer}
   */
  function getWriter(input) {
    return new Writer(input);
  }

  /**
   * Pipe a readable stream to a writable stream. Don't throw on input stream errors, but forward them to the output stream.
   * @param {ReadableStream|Uint8array|String} input
   * @param {WritableStream} target
   * @param {Object} (optional) options
   * @returns {Promise<undefined>} Promise indicating when piping has finished (input stream closed or errored)
   * @async
   */
  async function pipe(input, target, {
    preventClose = false,
    preventAbort = false,
    preventCancel = false
  } = {}) {
    if (isStream(input) && !isArrayStream(input)) {
      input = toStream(input);
      try {
        if (input[externalBuffer]) {
          const writer = getWriter(target);
          for (let i = 0; i < input[externalBuffer].length; i++) {
            await writer.ready;
            await writer.write(input[externalBuffer][i]);
          }
          writer.releaseLock();
        }
        await input.pipeTo(target, {
          preventClose,
          preventAbort,
          preventCancel
        });
      } catch(e) {}
      return;
    }
    input = toArrayStream(input);
    const reader = getReader(input);
    const writer = getWriter(target);
    try {
      while (true) {
        await writer.ready;
        const { done, value } = await reader.read();
        if (done) {
          if (!preventClose) await writer.close();
          break;
        }
        await writer.write(value);
      }
    } catch (e) {
      if (!preventAbort) await writer.abort(e);
    } finally {
      reader.releaseLock();
      writer.releaseLock();
    }
  }

  /**
   * Pipe a readable stream through a transform stream.
   * @param {ReadableStream|Uint8array|String} input
   * @param {Object} (optional) options
   * @returns {ReadableStream} transformed stream
   */
  function transformRaw(input, options) {
    const transformStream = new TransformStream(options);
    pipe(input, transformStream.writable);
    return transformStream.readable;
  }

  /**
   * Create a cancelable TransformStream.
   * @param {Function} cancel
   * @returns {TransformStream}
   */
  function transformWithCancel(customCancel) {
    let pulled = false;
    let cancelled = false;
    let backpressureChangePromiseResolve, backpressureChangePromiseReject;
    let outputController;
    return {
      readable: new ReadableStream({
        start(controller) {
          outputController = controller;
        },
        pull() {
          if (backpressureChangePromiseResolve) {
            backpressureChangePromiseResolve();
          } else {
            pulled = true;
          }
        },
        async cancel(reason) {
          cancelled = true;
          if (customCancel) {
            await customCancel(reason);
          }
          if (backpressureChangePromiseReject) {
            backpressureChangePromiseReject(reason);
          }
        }
      }, {highWaterMark: 0}),
      writable: new WritableStream({
        write: async function(chunk) {
          if (cancelled) {
            throw new Error('Stream is cancelled');
          }
          outputController.enqueue(chunk);
          if (!pulled) {
            await new Promise((resolve, reject) => {
              backpressureChangePromiseResolve = resolve;
              backpressureChangePromiseReject = reject;
            });
            backpressureChangePromiseResolve = null;
            backpressureChangePromiseReject = null;
          } else {
            pulled = false;
          }
        },
        close: outputController.close.bind(outputController),
        abort: outputController.error.bind(outputController)
      })
    };
  }

  /**
   * Transform a stream using helper functions which are called on each chunk, and on stream close, respectively.
   * @param {ReadableStream|Uint8array|String} input
   * @param {Function} process
   * @param {Function} finish
   * @returns {ReadableStream|Uint8array|String}
   */
  function transform(input, process = () => undefined, finish = () => undefined) {
    if (isArrayStream(input)) {
      const output = new ArrayStream();
      (async () => {
        const writer = getWriter(output);
        try {
          const data = await readToEnd(input);
          const result1 = process(data);
          const result2 = finish();
          let result;
          if (result1 !== undefined && result2 !== undefined) result = concat([result1, result2]);
          else result = result1 !== undefined ? result1 : result2;
          await writer.write(result);
          await writer.close();
        } catch (e) {
          await writer.abort(e);
        }
      })();
      return output;
    }
    if (isStream(input)) {
      return transformRaw(input, {
        async transform(value, controller) {
          try {
            const result = await process(value);
            if (result !== undefined) controller.enqueue(result);
          } catch(e) {
            controller.error(e);
          }
        },
        async flush(controller) {
          try {
            const result = await finish();
            if (result !== undefined) controller.enqueue(result);
          } catch(e) {
            controller.error(e);
          }
        }
      });
    }
    const result1 = process(input);
    const result2 = finish();
    if (result1 !== undefined && result2 !== undefined) return concat([result1, result2]);
    return result1 !== undefined ? result1 : result2;
  }

  /**
   * Transform a stream using a helper function which is passed a readable and a writable stream.
   *   This function also maintains the possibility to cancel the input stream,
   *   and does so on cancelation of the output stream, despite cancelation
   *   normally being impossible when the input stream is being read from.
   * @param {ReadableStream|Uint8array|String} input
   * @param {Function} fn
   * @returns {ReadableStream}
   */
  function transformPair(input, fn) {
    if (isStream(input) && !isArrayStream(input)) {
      let incomingTransformController;
      const incoming = new TransformStream({
        start(controller) {
          incomingTransformController = controller;
        }
      });

      const pipeDonePromise = pipe(input, incoming.writable);

      const outgoing = transformWithCancel(async function(reason) {
        incomingTransformController.error(reason);
        await pipeDonePromise;
        await new Promise(setTimeout);
      });
      fn(incoming.readable, outgoing.writable);
      return outgoing.readable;
    }
    input = toArrayStream(input);
    const output = new ArrayStream();
    fn(input, output);
    return output;
  }

  /**
   * Parse a stream using a helper function which is passed a Reader.
   *   The reader additionally has a remainder() method which returns a
   *   stream pointing to the remainder of input, and is linked to input
   *   for cancelation.
   * @param {ReadableStream|Uint8array|String} input
   * @param {Function} fn
   * @returns {Any} the return value of fn()
   */
  function parse(input, fn) {
    let returnValue;
    const transformed = transformPair(input, (readable, writable) => {
      const reader = getReader(readable);
      reader.remainder = () => {
        reader.releaseLock();
        pipe(readable, writable);
        return transformed;
      };
      returnValue = fn(reader);
    });
    return returnValue;
  }

  /**
   * Tee a Stream for reading it twice. The input stream can no longer be read after tee()ing.
   *   Reading either of the two returned streams will pull from the input stream.
   *   The input stream will only be canceled if both of the returned streams are canceled.
   * @param {ReadableStream|Uint8array|String} input
   * @returns {Array<ReadableStream|Uint8array|String>} array containing two copies of input
   */
  function tee(input) {
    if (isArrayStream(input)) {
      throw new Error('ArrayStream cannot be tee()d, use clone() instead');
    }
    if (isStream(input)) {
      const teed = toStream(input).tee();
      teed[0][externalBuffer] = teed[1][externalBuffer] = input[externalBuffer];
      return teed;
    }
    return [slice(input), slice(input)];
  }

  /**
   * Clone a Stream for reading it twice. The input stream can still be read after clone()ing.
   *   Reading from the clone will pull from the input stream.
   *   The input stream will only be canceled if both the clone and the input stream are canceled.
   * @param {ReadableStream|Uint8array|String} input
   * @returns {ReadableStream|Uint8array|String} cloned input
   */
  function clone(input) {
    if (isArrayStream(input)) {
      return input.clone();
    }
    if (isStream(input)) {
      const teed = tee(input);
      overwrite(input, teed[0]);
      return teed[1];
    }
    return slice(input);
  }

  /**
   * Clone a Stream for reading it twice. Data will arrive at the same rate as the input stream is being read.
   *   Reading from the clone will NOT pull from the input stream. Data only arrives when reading the input stream.
   *   The input stream will NOT be canceled if the clone is canceled, only if the input stream are canceled.
   *   If the input stream is canceled, the clone will be errored.
   * @param {ReadableStream|Uint8array|String} input
   * @returns {ReadableStream|Uint8array|String} cloned input
   */
  function passiveClone(input) {
    if (isArrayStream(input)) {
      return clone(input);
    }
    if (isStream(input)) {
      return new ReadableStream({
        start(controller) {
          const transformed = transformPair(input, async (readable, writable) => {
            const reader = getReader(readable);
            const writer = getWriter(writable);
            try {
              while (true) {
                await writer.ready;
                const { done, value } = await reader.read();
                if (done) {
                  try { controller.close(); } catch(e) {}
                  await writer.close();
                  return;
                }
                try { controller.enqueue(value); } catch(e) {}
                await writer.write(value);
              }
            } catch(e) {
              controller.error(e);
              await writer.abort(e);
            }
          });
          overwrite(input, transformed);
        }
      });
    }
    return slice(input);
  }

  /**
   * Modify a stream object to point to a different stream object.
   *   This is used internally by clone() and passiveClone() to provide an abstraction over tee().
   * @param {ReadableStream} input
   * @param {ReadableStream} clone
   */
  function overwrite(input, clone) {
    // Overwrite input.getReader, input.locked, etc to point to clone
    Object.entries(Object.getOwnPropertyDescriptors(input.constructor.prototype)).forEach(([name, descriptor]) => {
      if (name === 'constructor') {
        return;
      }
      if (descriptor.value) {
        descriptor.value = descriptor.value.bind(clone);
      } else {
        descriptor.get = descriptor.get.bind(clone);
      }
      Object.defineProperty(input, name, descriptor);
    });
  }

  /**
   * Return a stream pointing to a part of the input stream.
   * @param {ReadableStream|Uint8array|String} input
   * @returns {ReadableStream|Uint8array|String} clone
   */
  function slice(input, begin=0, end=Infinity) {
    if (isArrayStream(input)) {
      throw new Error('Not implemented');
    }
    if (isStream(input)) {
      if (begin >= 0 && end >= 0) {
        let bytesRead = 0;
        return transformRaw(input, {
          transform(value, controller) {
            if (bytesRead < end) {
              if (bytesRead + value.length >= begin) {
                controller.enqueue(slice(value, Math.max(begin - bytesRead, 0), end - bytesRead));
              }
              bytesRead += value.length;
            } else {
              controller.terminate();
            }
          }
        });
      }
      if (begin < 0 && (end < 0 || end === Infinity)) {
        let lastBytes = [];
        return transform(input, value => {
          if (value.length >= -begin) lastBytes = [value];
          else lastBytes.push(value);
        }, () => slice(concat(lastBytes), begin, end));
      }
      if (begin === 0 && end < 0) {
        let lastBytes;
        return transform(input, value => {
          const returnValue = lastBytes ? concat([lastBytes, value]) : value;
          if (returnValue.length >= -end) {
            lastBytes = slice(returnValue, end);
            return slice(returnValue, begin, end);
          } else {
            lastBytes = returnValue;
          }
        });
      }
      console.warn(`stream.slice(input, ${begin}, ${end}) not implemented efficiently.`);
      return fromAsync(async () => slice(await readToEnd(input), begin, end));
    }
    if (input[externalBuffer]) {
      input = concat(input[externalBuffer].concat([input]));
    }
    if (isUint8Array(input) && !(NodeBuffer$1 && NodeBuffer$1.isBuffer(input))) {
      if (end === Infinity) end = input.length;
      return input.subarray(begin, end);
    }
    return input.slice(begin, end);
  }

  /**
   * Read a stream to the end and return its contents, concatenated by the join function (defaults to concat).
   * @param {ReadableStream|Uint8array|String} input
   * @param {Function} join
   * @returns {Promise<Uint8array|String|Any>} the return value of join()
   * @async
   */
  async function readToEnd(input, join=concat) {
    if (isArrayStream(input)) {
      return input.readToEnd(join);
    }
    if (isStream(input)) {
      return getReader(input).readToEnd(join);
    }
    return input;
  }

  /**
   * Cancel a stream.
   * @param {ReadableStream|Uint8array|String} input
   * @param {Any} reason
   * @returns {Promise<Any>} indicates when the stream has been canceled
   * @async
   */
  async function cancel(input, reason) {
    if (isStream(input)) {
      if (input.cancel) {
        return input.cancel(reason);
      }
      if (input.destroy) {
        input.destroy(reason);
        await new Promise(setTimeout);
        return reason;
      }
    }
  }

  /**
   * Convert an async function to an ArrayStream. When the function returns, its return value is written to the stream.
   * @param {Function} fn
   * @returns {ArrayStream}
   */
  function fromAsync(fn) {
    const arrayStream = new ArrayStream();
    (async () => {
      const writer = getWriter(arrayStream);
      try {
        await writer.write(await fn());
        await writer.close();
      } catch (e) {
        await writer.abort(e);
      }
    })();
    return arrayStream;
  }

  /* eslint-disable new-cap */

  /**
   * @fileoverview
   * BigInteger implementation of basic operations
   * that wraps the native BigInt library.
   * Operations are not constant time,
   * but we try and limit timing leakage where we can
   * @module biginteger/native
   * @private
   */

  /**
   * @private
   */
  class BigInteger {
    /**
     * Get a BigInteger (input must be big endian for strings and arrays)
     * @param {Number|String|Uint8Array} n - Value to convert
     * @throws {Error} on null or undefined input
     */
    constructor(n) {
      if (n === undefined) {
        throw new Error('Invalid BigInteger input');
      }

      if (n instanceof Uint8Array) {
        const bytes = n;
        const hex = new Array(bytes.length);
        for (let i = 0; i < bytes.length; i++) {
          const hexByte = bytes[i].toString(16);
          hex[i] = (bytes[i] <= 0xF) ? ('0' + hexByte) : hexByte;
        }
        this.value = BigInt('0x0' + hex.join(''));
      } else {
        this.value = BigInt(n);
      }
    }

    clone() {
      return new BigInteger(this.value);
    }

    /**
     * BigInteger increment in place
     */
    iinc() {
      this.value++;
      return this;
    }

    /**
     * BigInteger increment
     * @returns {BigInteger} this + 1.
     */
    inc() {
      return this.clone().iinc();
    }

    /**
     * BigInteger decrement in place
     */
    idec() {
      this.value--;
      return this;
    }

    /**
     * BigInteger decrement
     * @returns {BigInteger} this - 1.
     */
    dec() {
      return this.clone().idec();
    }

    /**
     * BigInteger addition in place
     * @param {BigInteger} x - Value to add
     */
    iadd(x) {
      this.value += x.value;
      return this;
    }

    /**
     * BigInteger addition
     * @param {BigInteger} x - Value to add
     * @returns {BigInteger} this + x.
     */
    add(x) {
      return this.clone().iadd(x);
    }

    /**
     * BigInteger subtraction in place
     * @param {BigInteger} x - Value to subtract
     */
    isub(x) {
      this.value -= x.value;
      return this;
    }

    /**
     * BigInteger subtraction
     * @param {BigInteger} x - Value to subtract
     * @returns {BigInteger} this - x.
     */
    sub(x) {
      return this.clone().isub(x);
    }

    /**
     * BigInteger multiplication in place
     * @param {BigInteger} x - Value to multiply
     */
    imul(x) {
      this.value *= x.value;
      return this;
    }

    /**
     * BigInteger multiplication
     * @param {BigInteger} x - Value to multiply
     * @returns {BigInteger} this * x.
     */
    mul(x) {
      return this.clone().imul(x);
    }

    /**
     * Compute value modulo m, in place
     * @param {BigInteger} m - Modulo
     */
    imod(m) {
      this.value %= m.value;
      if (this.isNegative()) {
        this.iadd(m);
      }
      return this;
    }

    /**
     * Compute value modulo m
     * @param {BigInteger} m - Modulo
     * @returns {BigInteger} this mod m.
     */
    mod(m) {
      return this.clone().imod(m);
    }

    /**
     * Compute modular exponentiation using square and multiply
     * @param {BigInteger} e - Exponent
     * @param {BigInteger} n - Modulo
     * @returns {BigInteger} this ** e mod n.
     */
    modExp(e, n) {
      if (n.isZero()) throw Error('Modulo cannot be zero');
      if (n.isOne()) return new BigInteger(0);
      if (e.isNegative()) throw Error('Unsopported negative exponent');

      let exp = e.value;
      let x = this.value;

      x %= n.value;
      let r = BigInt(1);
      while (exp > BigInt(0)) {
        const lsb = exp & BigInt(1);
        exp >>= BigInt(1); // e / 2
        // Always compute multiplication step, to reduce timing leakage
        const rx = (r * x) % n.value;
        // Update r only if lsb is 1 (odd exponent)
        r = lsb ? rx : r;
        x = (x * x) % n.value; // Square
      }
      return new BigInteger(r);
    }


    /**
     * Compute the inverse of this value modulo n
     * Note: this and and n must be relatively prime
     * @param {BigInteger} n - Modulo
     * @returns {BigInteger} x such that this*x = 1 mod n
     * @throws {Error} if the inverse does not exist
     */
    modInv(n) {
      const { gcd, x } = this._egcd(n);
      if (!gcd.isOne()) {
        throw new Error('Inverse does not exist');
      }
      return x.add(n).mod(n);
    }

    /**
     * Extended Eucleadian algorithm (http://anh.cs.luc.edu/331/notes/xgcd.pdf)
     * Given a = this and b, compute (x, y) such that ax + by = gdc(a, b)
     * @param {BigInteger} b - Second operand
     * @returns {{ gcd, x, y: BigInteger }}
     */
    _egcd(b) {
      let x = BigInt(0);
      let y = BigInt(1);
      let xPrev = BigInt(1);
      let yPrev = BigInt(0);

      let a = this.value;
      b = b.value;

      while (b !== BigInt(0)) {
        const q = a / b;
        let tmp = x;
        x = xPrev - q * x;
        xPrev = tmp;

        tmp = y;
        y = yPrev - q * y;
        yPrev = tmp;

        tmp = b;
        b = a % b;
        a = tmp;
      }

      return {
        x: new BigInteger(xPrev),
        y: new BigInteger(yPrev),
        gcd: new BigInteger(a)
      };
    }

    /**
     * Compute greatest common divisor between this and n
     * @param {BigInteger} b - Operand
     * @returns {BigInteger} gcd
     */
    gcd(b) {
      let a = this.value;
      b = b.value;
      while (b !== BigInt(0)) {
        const tmp = b;
        b = a % b;
        a = tmp;
      }
      return new BigInteger(a);
    }

    /**
     * Shift this to the left by x, in place
     * @param {BigInteger} x - Shift value
     */
    ileftShift(x) {
      this.value <<= x.value;
      return this;
    }

    /**
     * Shift this to the left by x
     * @param {BigInteger} x - Shift value
     * @returns {BigInteger} this << x.
     */
    leftShift(x) {
      return this.clone().ileftShift(x);
    }

    /**
     * Shift this to the right by x, in place
     * @param {BigInteger} x - Shift value
     */
    irightShift(x) {
      this.value >>= x.value;
      return this;
    }

    /**
     * Shift this to the right by x
     * @param {BigInteger} x - Shift value
     * @returns {BigInteger} this >> x.
     */
    rightShift(x) {
      return this.clone().irightShift(x);
    }

    /**
     * Whether this value is equal to x
     * @param {BigInteger} x
     * @returns {Boolean}
     */
    equal(x) {
      return this.value === x.value;
    }

    /**
     * Whether this value is less than x
     * @param {BigInteger} x
     * @returns {Boolean}
     */
    lt(x) {
      return this.value < x.value;
    }

    /**
     * Whether this value is less than or equal to x
     * @param {BigInteger} x
     * @returns {Boolean}
     */
    lte(x) {
      return this.value <= x.value;
    }

    /**
     * Whether this value is greater than x
     * @param {BigInteger} x
     * @returns {Boolean}
     */
    gt(x) {
      return this.value > x.value;
    }

    /**
     * Whether this value is greater than or equal to x
     * @param {BigInteger} x
     * @returns {Boolean}
     */
    gte(x) {
      return this.value >= x.value;
    }

    isZero() {
      return this.value === BigInt(0);
    }

    isOne() {
      return this.value === BigInt(1);
    }

    isNegative() {
      return this.value < BigInt(0);
    }

    isEven() {
      return !(this.value & BigInt(1));
    }

    abs() {
      const res = this.clone();
      if (this.isNegative()) {
        res.value = -res.value;
      }
      return res;
    }

    /**
     * Get this value as a string
     * @returns {String} this value.
     */
    toString() {
      return this.value.toString();
    }

    /**
     * Get this value as an exact Number (max 53 bits)
     * Fails if this value is too large
     * @returns {Number}
     */
    toNumber() {
      const number = Number(this.value);
      if (number > Number.MAX_SAFE_INTEGER) {
        // We throw and error to conform with the bn.js implementation
        throw new Error('Number can only safely store up to 53 bits');
      }
      return number;
    }

    /**
     * Get value of i-th bit
     * @param {Number} i - Bit index
     * @returns {Number} Bit value.
     */
    getBit(i) {
      const bit = (this.value >> BigInt(i)) & BigInt(1);
      return (bit === BigInt(0)) ? 0 : 1;
    }

    /**
     * Compute bit length
     * @returns {Number} Bit length.
     */
    bitLength() {
      const zero = new BigInteger(0);
      const one = new BigInteger(1);
      const negOne = new BigInteger(-1);

      // -1n >> -1n is -1n
      // 1n >> 1n is 0n
      const target = this.isNegative() ? negOne : zero;
      let bitlen = 1;
      const tmp = this.clone();
      while (!tmp.irightShift(one).equal(target)) {
        bitlen++;
      }
      return bitlen;
    }

    /**
     * Compute byte length
     * @returns {Number} Byte length.
     */
    byteLength() {
      const zero = new BigInteger(0);
      const negOne = new BigInteger(-1);

      const target = this.isNegative() ? negOne : zero;
      const eight = new BigInteger(8);
      let len = 1;
      const tmp = this.clone();
      while (!tmp.irightShift(eight).equal(target)) {
        len++;
      }
      return len;
    }

    /**
     * Get Uint8Array representation of this number
     * @param {String} endian - Endianess of output array (defaults to 'be')
     * @param {Number} length - Of output array
     * @returns {Uint8Array}
     */
    toUint8Array(endian = 'be', length) {
      // we get and parse the hex string (https://coolaj86.com/articles/convert-js-bigints-to-typedarrays/)
      // this is faster than shift+mod iterations
      let hex = this.value.toString(16);
      if (hex.length % 2 === 1) {
        hex = '0' + hex;
      }

      const rawLength = hex.length / 2;
      const bytes = new Uint8Array(length || rawLength);
      // parse hex
      const offset = length ? (length - rawLength) : 0;
      let i = 0;
      while (i < rawLength) {
        bytes[i + offset] = parseInt(hex.slice(2 * i, 2 * i + 2), 16);
        i++;
      }

      if (endian !== 'be') {
        bytes.reverse();
      }

      return bytes;
    }
  }

  const detectBigInt = () => typeof BigInt !== 'undefined';

  async function getBigInteger() {
    if (detectBigInt()) {
      return BigInteger;
    } else {
      const { default: BigInteger } = await Promise.resolve().then(function () { return bn_interface; });
      return BigInteger;
    }
  }

  /**
   * @module enums
   */

  const byValue = Symbol('byValue');

  var enums = {

    /** Maps curve names under various standards to one
     * @see {@link https://wiki.gnupg.org/ECC|ECC - GnuPG wiki}
     * @enum {String}
     * @readonly
     */
    curve: {
      /** NIST P-256 Curve */
      'p256':                'p256',
      'P-256':               'p256',
      'secp256r1':           'p256',
      'prime256v1':          'p256',
      '1.2.840.10045.3.1.7': 'p256',
      '2a8648ce3d030107':    'p256',
      '2A8648CE3D030107':    'p256',

      /** NIST P-384 Curve */
      'p384':         'p384',
      'P-384':        'p384',
      'secp384r1':    'p384',
      '1.3.132.0.34': 'p384',
      '2b81040022':   'p384',
      '2B81040022':   'p384',

      /** NIST P-521 Curve */
      'p521':         'p521',
      'P-521':        'p521',
      'secp521r1':    'p521',
      '1.3.132.0.35': 'p521',
      '2b81040023':   'p521',
      '2B81040023':   'p521',

      /** SECG SECP256k1 Curve */
      'secp256k1':    'secp256k1',
      '1.3.132.0.10': 'secp256k1',
      '2b8104000a':   'secp256k1',
      '2B8104000A':   'secp256k1',

      /** Ed25519 - deprecated by crypto-refresh (replaced by standaone Ed25519 algo) */
      'ed25519Legacy':          'ed25519',
      'ED25519':                'ed25519',
      /** @deprecated use `ed25519Legacy` instead */
      'ed25519':                'ed25519',
      'Ed25519':                'ed25519',
      '1.3.6.1.4.1.11591.15.1': 'ed25519',
      '2b06010401da470f01':     'ed25519',
      '2B06010401DA470F01':     'ed25519',

      /** Curve25519 - deprecated by crypto-refresh (replaced by standaone X25519 algo) */
      'curve25519Legacy':       'curve25519',
      'X25519':                 'curve25519',
      'cv25519':                'curve25519',
      /** @deprecated use `curve25519Legacy` instead */
      'curve25519':             'curve25519',
      'Curve25519':             'curve25519',
      '1.3.6.1.4.1.3029.1.5.1': 'curve25519',
      '2b060104019755010501':   'curve25519',
      '2B060104019755010501':   'curve25519',

      /** BrainpoolP256r1 Curve */
      'brainpoolP256r1':       'brainpoolP256r1',
      '1.3.36.3.3.2.8.1.1.7':  'brainpoolP256r1',
      '2b2403030208010107':    'brainpoolP256r1',
      '2B2403030208010107':    'brainpoolP256r1',

      /** BrainpoolP384r1 Curve */
      'brainpoolP384r1':       'brainpoolP384r1',
      '1.3.36.3.3.2.8.1.1.11': 'brainpoolP384r1',
      '2b240303020801010b':    'brainpoolP384r1',
      '2B240303020801010B':    'brainpoolP384r1',

      /** BrainpoolP512r1 Curve */
      'brainpoolP512r1':       'brainpoolP512r1',
      '1.3.36.3.3.2.8.1.1.13': 'brainpoolP512r1',
      '2b240303020801010d':    'brainpoolP512r1',
      '2B240303020801010D':    'brainpoolP512r1'
    },

    /** A string to key specifier type
     * @enum {Integer}
     * @readonly
     */
    s2k: {
      simple: 0,
      salted: 1,
      iterated: 3,
      gnu: 101
    },

    /** {@link https://tools.ietf.org/html/draft-ietf-openpgp-crypto-refresh-08.html#section-9.1|crypto-refresh RFC, section 9.1}
     * @enum {Integer}
     * @readonly
     */
    publicKey: {
      /** RSA (Encrypt or Sign) [HAC] */
      rsaEncryptSign: 1,
      /** RSA (Encrypt only) [HAC] */
      rsaEncrypt: 2,
      /** RSA (Sign only) [HAC] */
      rsaSign: 3,
      /** Elgamal (Encrypt only) [ELGAMAL] [HAC] */
      elgamal: 16,
      /** DSA (Sign only) [FIPS186] [HAC] */
      dsa: 17,
      /** ECDH (Encrypt only) [RFC6637] */
      ecdh: 18,
      /** ECDSA (Sign only) [RFC6637] */
      ecdsa: 19,
      /** EdDSA (Sign only) - deprecated by crypto-refresh (replaced by `ed25519` identifier below)
       * [{@link https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04|Draft RFC}] */
      eddsaLegacy: 22, // NB: this is declared before `eddsa` to translate 22 to 'eddsa' for backwards compatibility
      /** @deprecated use `eddsaLegacy` instead */
      ed25519Legacy: 22,
      /** @deprecated use `eddsaLegacy` instead */
      eddsa: 22,
      /** Reserved for AEDH */
      aedh: 23,
      /** Reserved for AEDSA */
      aedsa: 24,
      /** X25519 (Encrypt only) */
      x25519: 25,
      /** X448 (Encrypt only) */
      x448: 26,
      /** Ed25519 (Sign only) */
      ed25519: 27,
      /** Ed448 (Sign only) */
      ed448: 28
    },

    /** {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC4880, section 9.2}
     * @enum {Integer}
     * @readonly
     */
    symmetric: {
      plaintext: 0,
      /** Not implemented! */
      idea: 1,
      tripledes: 2,
      cast5: 3,
      blowfish: 4,
      aes128: 7,
      aes192: 8,
      aes256: 9,
      twofish: 10
    },

    /** {@link https://tools.ietf.org/html/rfc4880#section-9.3|RFC4880, section 9.3}
     * @enum {Integer}
     * @readonly
     */
    compression: {
      uncompressed: 0,
      /** RFC1951 */
      zip: 1,
      /** RFC1950 */
      zlib: 2,
      bzip2: 3
    },

    /** {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC4880, section 9.4}
     * @enum {Integer}
     * @readonly
     */
    hash: {
      md5: 1,
      sha1: 2,
      ripemd: 3,
      sha256: 8,
      sha384: 9,
      sha512: 10,
      sha224: 11
    },

    /** A list of hash names as accepted by webCrypto functions.
     * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest|Parameters, algo}
     * @enum {String}
     */
    webHash: {
      'SHA-1': 2,
      'SHA-256': 8,
      'SHA-384': 9,
      'SHA-512': 10
    },

    /** {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-9.6|RFC4880bis-04, section 9.6}
     * @enum {Integer}
     * @readonly
     */
    aead: {
      eax: 1,
      ocb: 2,
      experimentalGCM: 100 // Private algorithm
    },

    /** A list of packet types and numeric tags associated with them.
     * @enum {Integer}
     * @readonly
     */
    packet: {
      publicKeyEncryptedSessionKey: 1,
      signature: 2,
      symEncryptedSessionKey: 3,
      onePassSignature: 4,
      secretKey: 5,
      publicKey: 6,
      secretSubkey: 7,
      compressedData: 8,
      symmetricallyEncryptedData: 9,
      marker: 10,
      literalData: 11,
      trust: 12,
      userID: 13,
      publicSubkey: 14,
      userAttribute: 17,
      symEncryptedIntegrityProtectedData: 18,
      modificationDetectionCode: 19,
      aeadEncryptedData: 20 // see IETF draft: https://tools.ietf.org/html/draft-ford-openpgp-format-00#section-2.1
    },

    /** Data types in the literal packet
     * @enum {Integer}
     * @readonly
     */
    literal: {
      /** Binary data 'b' */
      binary: 'b'.charCodeAt(),
      /** Text data 't' */
      text: 't'.charCodeAt(),
      /** Utf8 data 'u' */
      utf8: 'u'.charCodeAt(),
      /** MIME message body part 'm' */
      mime: 'm'.charCodeAt()
    },


    /** One pass signature packet type
     * @enum {Integer}
     * @readonly
     */
    signature: {
      /** 0x00: Signature of a binary document. */
      binary: 0,
      /** 0x01: Signature of a canonical text document.
       *
       * Canonicalyzing the document by converting line endings. */
      text: 1,
      /** 0x02: Standalone signature.
       *
       * This signature is a signature of only its own subpacket contents.
       * It is calculated identically to a signature over a zero-lengh
       * binary document.  Note that it doesn't make sense to have a V3
       * standalone signature. */
      standalone: 2,
      /** 0x10: Generic certification of a User ID and Public-Key packet.
       *
       * The issuer of this certification does not make any particular
       * assertion as to how well the certifier has checked that the owner
       * of the key is in fact the person described by the User ID. */
      certGeneric: 16,
      /** 0x11: Persona certification of a User ID and Public-Key packet.
       *
       * The issuer of this certification has not done any verification of
       * the claim that the owner of this key is the User ID specified. */
      certPersona: 17,
      /** 0x12: Casual certification of a User ID and Public-Key packet.
       *
       * The issuer of this certification has done some casual
       * verification of the claim of identity. */
      certCasual: 18,
      /** 0x13: Positive certification of a User ID and Public-Key packet.
       *
       * The issuer of this certification has done substantial
       * verification of the claim of identity.
       *
       * Most OpenPGP implementations make their "key signatures" as 0x10
       * certifications.  Some implementations can issue 0x11-0x13
       * certifications, but few differentiate between the types. */
      certPositive: 19,
      /** 0x30: Certification revocation signature
       *
       * This signature revokes an earlier User ID certification signature
       * (signature class 0x10 through 0x13) or direct-key signature
       * (0x1F).  It should be issued by the same key that issued the
       * revoked signature or an authorized revocation key.  The signature
       * is computed over the same data as the certificate that it
       * revokes, and should have a later creation date than that
       * certificate. */
      certRevocation: 48,
      /** 0x18: Subkey Binding Signature
       *
       * This signature is a statement by the top-level signing key that
       * indicates that it owns the subkey.  This signature is calculated
       * directly on the primary key and subkey, and not on any User ID or
       * other packets.  A signature that binds a signing subkey MUST have
       * an Embedded Signature subpacket in this binding signature that
       * contains a 0x19 signature made by the signing subkey on the
       * primary key and subkey. */
      subkeyBinding: 24,
      /** 0x19: Primary Key Binding Signature
       *
       * This signature is a statement by a signing subkey, indicating
       * that it is owned by the primary key and subkey.  This signature
       * is calculated the same way as a 0x18 signature: directly on the
       * primary key and subkey, and not on any User ID or other packets.
       *
       * When a signature is made over a key, the hash data starts with the
       * octet 0x99, followed by a two-octet length of the key, and then body
       * of the key packet.  (Note that this is an old-style packet header for
       * a key packet with two-octet length.)  A subkey binding signature
       * (type 0x18) or primary key binding signature (type 0x19) then hashes
       * the subkey using the same format as the main key (also using 0x99 as
       * the first octet). */
      keyBinding: 25,
      /** 0x1F: Signature directly on a key
       *
       * This signature is calculated directly on a key.  It binds the
       * information in the Signature subpackets to the key, and is
       * appropriate to be used for subpackets that provide information
       * about the key, such as the Revocation Key subpacket.  It is also
       * appropriate for statements that non-self certifiers want to make
       * about the key itself, rather than the binding between a key and a
       * name. */
      key: 31,
      /** 0x20: Key revocation signature
       *
       * The signature is calculated directly on the key being revoked.  A
       * revoked key is not to be used.  Only revocation signatures by the
       * key being revoked, or by an authorized revocation key, should be
       * considered valid revocation signatures.a */
      keyRevocation: 32,
      /** 0x28: Subkey revocation signature
       *
       * The signature is calculated directly on the subkey being revoked.
       * A revoked subkey is not to be used.  Only revocation signatures
       * by the top-level signature key that is bound to this subkey, or
       * by an authorized revocation key, should be considered valid
       * revocation signatures.
       *
       * Key revocation signatures (types 0x20 and 0x28)
       * hash only the key being revoked. */
      subkeyRevocation: 40,
      /** 0x40: Timestamp signature.
       * This signature is only meaningful for the timestamp contained in
       * it. */
      timestamp: 64,
      /** 0x50: Third-Party Confirmation signature.
       *
       * This signature is a signature over some other OpenPGP Signature
       * packet(s).  It is analogous to a notary seal on the signed data.
       * A third-party signature SHOULD include Signature Target
       * subpacket(s) to give easy identification.  Note that we really do
       * mean SHOULD.  There are plausible uses for this (such as a blind
       * party that only sees the signature, not the key or source
       * document) that cannot include a target subpacket. */
      thirdParty: 80
    },

    /** Signature subpacket type
     * @enum {Integer}
     * @readonly
     */
    signatureSubpacket: {
      signatureCreationTime: 2,
      signatureExpirationTime: 3,
      exportableCertification: 4,
      trustSignature: 5,
      regularExpression: 6,
      revocable: 7,
      keyExpirationTime: 9,
      placeholderBackwardsCompatibility: 10,
      preferredSymmetricAlgorithms: 11,
      revocationKey: 12,
      issuer: 16,
      notationData: 20,
      preferredHashAlgorithms: 21,
      preferredCompressionAlgorithms: 22,
      keyServerPreferences: 23,
      preferredKeyServer: 24,
      primaryUserID: 25,
      policyURI: 26,
      keyFlags: 27,
      signersUserID: 28,
      reasonForRevocation: 29,
      features: 30,
      signatureTarget: 31,
      embeddedSignature: 32,
      issuerFingerprint: 33,
      preferredAEADAlgorithms: 34
    },

    /** Key flags
     * @enum {Integer}
     * @readonly
     */
    keyFlags: {
      /** 0x01 - This key may be used to certify other keys. */
      certifyKeys: 1,
      /** 0x02 - This key may be used to sign data. */
      signData: 2,
      /** 0x04 - This key may be used to encrypt communications. */
      encryptCommunication: 4,
      /** 0x08 - This key may be used to encrypt storage. */
      encryptStorage: 8,
      /** 0x10 - The private component of this key may have been split
       *        by a secret-sharing mechanism. */
      splitPrivateKey: 16,
      /** 0x20 - This key may be used for authentication. */
      authentication: 32,
      /** 0x80 - The private component of this key may be in the
       *        possession of more than one person. */
      sharedPrivateKey: 128
    },

    /** Armor type
     * @enum {Integer}
     * @readonly
     */
    armor: {
      multipartSection: 0,
      multipartLast: 1,
      signed: 2,
      message: 3,
      publicKey: 4,
      privateKey: 5,
      signature: 6
    },

    /** {@link https://tools.ietf.org/html/rfc4880#section-5.2.3.23|RFC4880, section 5.2.3.23}
     * @enum {Integer}
     * @readonly
     */
    reasonForRevocation: {
      /** No reason specified (key revocations or cert revocations) */
      noReason: 0,
      /** Key is superseded (key revocations) */
      keySuperseded: 1,
      /** Key material has been compromised (key revocations) */
      keyCompromised: 2,
      /** Key is retired and no longer used (key revocations) */
      keyRetired: 3,
      /** User ID information is no longer valid (cert revocations) */
      userIDInvalid: 32
    },

    /** {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-5.2.3.25|RFC4880bis-04, section 5.2.3.25}
     * @enum {Integer}
     * @readonly
     */
    features: {
      /** 0x01 - Modification Detection (packets 18 and 19) */
      modificationDetection: 1,
      /** 0x02 - AEAD Encrypted Data Packet (packet 20) and version 5
       *         Symmetric-Key Encrypted Session Key Packets (packet 3) */
      aead: 2,
      /** 0x04 - Version 5 Public-Key Packet format and corresponding new
        *        fingerprint format */
      v5Keys: 4
    },

    /**
     * Asserts validity of given value and converts from string/integer to integer.
     * @param {Object} type target enum type
     * @param {String|Integer} e value to check and/or convert
     * @returns {Integer} enum value if it exists
     * @throws {Error} if the value is invalid
     */
    write: function(type, e) {
      if (typeof e === 'number') {
        e = this.read(type, e);
      }

      if (type[e] !== undefined) {
        return type[e];
      }

      throw new Error('Invalid enum value.');
    },

    /**
     * Converts enum integer value to the corresponding string, if it exists.
     * @param {Object} type target enum type
     * @param {Integer} e value to convert
     * @returns {String} name of enum value if it exists
     * @throws {Error} if the value is invalid
     */
    read: function(type, e) {
      if (!type[byValue]) {
        type[byValue] = [];
        Object.entries(type).forEach(([key, value]) => {
          type[byValue][value] = key;
        });
      }

      if (type[byValue][e] !== undefined) {
        return type[byValue][e];
      }

      throw new Error('Invalid enum value.');
    }
  };

  // GPG4Browsers - An OpenPGP implementation in javascript

  const debugMode = (() => {
    try {
      return process.env.NODE_ENV === 'development'; // eslint-disable-line no-process-env
    } catch (e) {}
    return false;
  })();

  const util = {
    isString: function(data) {
      return typeof data === 'string' || data instanceof String;
    },

    isArray: function(data) {
      return data instanceof Array;
    },

    isUint8Array: isUint8Array,

    isStream: isStream,

    readNumber: function (bytes) {
      let n = 0;
      for (let i = 0; i < bytes.length; i++) {
        n += (256 ** i) * bytes[bytes.length - 1 - i];
      }
      return n;
    },

    writeNumber: function (n, bytes) {
      const b = new Uint8Array(bytes);
      for (let i = 0; i < bytes; i++) {
        b[i] = (n >> (8 * (bytes - i - 1))) & 0xFF;
      }

      return b;
    },

    readDate: function (bytes) {
      const n = util.readNumber(bytes);
      const d = new Date(n * 1000);
      return d;
    },

    writeDate: function (time) {
      const numeric = Math.floor(time.getTime() / 1000);

      return util.writeNumber(numeric, 4);
    },

    normalizeDate: function (time = Date.now()) {
      return time === null || time === Infinity ? time : new Date(Math.floor(+time / 1000) * 1000);
    },

    /**
     * Read one MPI from bytes in input
     * @param {Uint8Array} bytes - Input data to parse
     * @returns {Uint8Array} Parsed MPI.
     */
    readMPI: function (bytes) {
      const bits = (bytes[0] << 8) | bytes[1];
      const bytelen = (bits + 7) >>> 3;
      return bytes.subarray(2, 2 + bytelen);
    },

    /**
     * Left-pad Uint8Array to length by adding 0x0 bytes
     * @param {Uint8Array} bytes - Data to pad
     * @param {Number} length - Padded length
     * @returns {Uint8Array} Padded bytes.
     */
    leftPad(bytes, length) {
      const padded = new Uint8Array(length);
      const offset = length - bytes.length;
      padded.set(bytes, offset);
      return padded;
    },

    /**
     * Convert a Uint8Array to an MPI-formatted Uint8Array.
     * @param {Uint8Array} bin - An array of 8-bit integers to convert
     * @returns {Uint8Array} MPI-formatted Uint8Array.
     */
    uint8ArrayToMPI: function (bin) {
      const bitSize = util.uint8ArrayBitLength(bin);
      if (bitSize === 0) {
        throw new Error('Zero MPI');
      }
      const stripped = bin.subarray(bin.length - Math.ceil(bitSize / 8));
      const prefix = new Uint8Array([(bitSize & 0xFF00) >> 8, bitSize & 0xFF]);
      return util.concatUint8Array([prefix, stripped]);
    },

    /**
     * Return bit length of the input data
     * @param {Uint8Array} bin input data (big endian)
     * @returns bit length
     */
    uint8ArrayBitLength: function (bin) {
      let i; // index of leading non-zero byte
      for (i = 0; i < bin.length; i++) if (bin[i] !== 0) break;
      if (i === bin.length) {
        return 0;
      }
      const stripped = bin.subarray(i);
      return (stripped.length - 1) * 8 + util.nbits(stripped[0]);
    },

    /**
     * Convert a hex string to an array of 8-bit integers
     * @param {String} hex - A hex string to convert
     * @returns {Uint8Array} An array of 8-bit integers.
     */
    hexToUint8Array: function (hex) {
      const result = new Uint8Array(hex.length >> 1);
      for (let k = 0; k < hex.length >> 1; k++) {
        result[k] = parseInt(hex.substr(k << 1, 2), 16);
      }
      return result;
    },

    /**
     * Convert an array of 8-bit integers to a hex string
     * @param {Uint8Array} bytes - Array of 8-bit integers to convert
     * @returns {String} Hexadecimal representation of the array.
     */
    uint8ArrayToHex: function (bytes) {
      const r = [];
      const e = bytes.length;
      let c = 0;
      let h;
      while (c < e) {
        h = bytes[c++].toString(16);
        while (h.length < 2) {
          h = '0' + h;
        }
        r.push('' + h);
      }
      return r.join('');
    },

    /**
     * Convert a string to an array of 8-bit integers
     * @param {String} str - String to convert
     * @returns {Uint8Array} An array of 8-bit integers.
     */
    stringToUint8Array: function (str) {
      return transform(str, str => {
        if (!util.isString(str)) {
          throw new Error('stringToUint8Array: Data must be in the form of a string');
        }

        const result = new Uint8Array(str.length);
        for (let i = 0; i < str.length; i++) {
          result[i] = str.charCodeAt(i);
        }
        return result;
      });
    },

    /**
     * Convert an array of 8-bit integers to a string
     * @param {Uint8Array} bytes - An array of 8-bit integers to convert
     * @returns {String} String representation of the array.
     */
    uint8ArrayToString: function (bytes) {
      bytes = new Uint8Array(bytes);
      const result = [];
      const bs = 1 << 14;
      const j = bytes.length;

      for (let i = 0; i < j; i += bs) {
        result.push(String.fromCharCode.apply(String, bytes.subarray(i, i + bs < j ? i + bs : j)));
      }
      return result.join('');
    },

    /**
     * Convert a native javascript string to a Uint8Array of utf8 bytes
     * @param {String|ReadableStream} str - The string to convert
     * @returns {Uint8Array|ReadableStream} A valid squence of utf8 bytes.
     */
    encodeUTF8: function (str) {
      const encoder = new TextEncoder('utf-8');
      // eslint-disable-next-line no-inner-declarations
      function process(value, lastChunk = false) {
        return encoder.encode(value, { stream: !lastChunk });
      }
      return transform(str, process, () => process('', true));
    },

    /**
     * Convert a Uint8Array of utf8 bytes to a native javascript string
     * @param {Uint8Array|ReadableStream} utf8 - A valid squence of utf8 bytes
     * @returns {String|ReadableStream} A native javascript string.
     */
    decodeUTF8: function (utf8) {
      const decoder = new TextDecoder('utf-8');
      // eslint-disable-next-line no-inner-declarations
      function process(value, lastChunk = false) {
        return decoder.decode(value, { stream: !lastChunk });
      }
      return transform(utf8, process, () => process(new Uint8Array(), true));
    },

    /**
     * Concat a list of Uint8Arrays, Strings or Streams
     * The caller must not mix Uint8Arrays with Strings, but may mix Streams with non-Streams.
     * @param {Array<Uint8Array|String|ReadableStream>} Array - Of Uint8Arrays/Strings/Streams to concatenate
     * @returns {Uint8Array|String|ReadableStream} Concatenated array.
     */
    concat: concat,

    /**
     * Concat Uint8Arrays
     * @param {Array<Uint8Array>} Array - Of Uint8Arrays to concatenate
     * @returns {Uint8Array} Concatenated array.
     */
    concatUint8Array: concatUint8Array,

    /**
     * Check Uint8Array equality
     * @param {Uint8Array} array1 - First array
     * @param {Uint8Array} array2 - Second array
     * @returns {Boolean} Equality.
     */
    equalsUint8Array: function (array1, array2) {
      if (!util.isUint8Array(array1) || !util.isUint8Array(array2)) {
        throw new Error('Data must be in the form of a Uint8Array');
      }

      if (array1.length !== array2.length) {
        return false;
      }

      for (let i = 0; i < array1.length; i++) {
        if (array1[i] !== array2[i]) {
          return false;
        }
      }
      return true;
    },

    /**
     * Calculates a 16bit sum of a Uint8Array by adding each character
     * codes modulus 65535
     * @param {Uint8Array} Uint8Array - To create a sum of
     * @returns {Uint8Array} 2 bytes containing the sum of all charcodes % 65535.
     */
    writeChecksum: function (text) {
      let s = 0;
      for (let i = 0; i < text.length; i++) {
        s = (s + text[i]) & 0xFFFF;
      }
      return util.writeNumber(s, 2);
    },

    /**
     * Helper function to print a debug message. Debug
     * messages are only printed if
     * @param {String} str - String of the debug message
     */
    printDebug: function (str) {
      if (debugMode) {
        console.log('[OpenPGP.js debug]', str);
      }
    },

    /**
     * Helper function to print a debug error. Debug
     * messages are only printed if
     * @param {String} str - String of the debug message
     */
    printDebugError: function (error) {
      if (debugMode) {
        console.error('[OpenPGP.js debug]', error);
      }
    },

    // returns bit length of the integer x
    nbits: function (x) {
      let r = 1;
      let t = x >>> 16;
      if (t !== 0) {
        x = t;
        r += 16;
      }
      t = x >> 8;
      if (t !== 0) {
        x = t;
        r += 8;
      }
      t = x >> 4;
      if (t !== 0) {
        x = t;
        r += 4;
      }
      t = x >> 2;
      if (t !== 0) {
        x = t;
        r += 2;
      }
      t = x >> 1;
      if (t !== 0) {
        x = t;
        r += 1;
      }
      return r;
    },

    /**
     * If S[1] == 0, then double(S) == (S[2..128] || 0);
     * otherwise, double(S) == (S[2..128] || 0) xor
     * (zeros(120) || 10000111).
     *
     * Both OCB and EAX (through CMAC) require this function to be constant-time.
     *
     * @param {Uint8Array} data
     */
    double: function(data) {
      const doubleVar = new Uint8Array(data.length);
      const last = data.length - 1;
      for (let i = 0; i < last; i++) {
        doubleVar[i] = (data[i] << 1) ^ (data[i + 1] >> 7);
      }
      doubleVar[last] = (data[last] << 1) ^ ((data[0] >> 7) * 0x87);
      return doubleVar;
    },

    /**
     * Shift a Uint8Array to the right by n bits
     * @param {Uint8Array} array - The array to shift
     * @param {Integer} bits - Amount of bits to shift (MUST be smaller
     * than 8)
     * @returns {String} Resulting array.
     */
    shiftRight: function (array, bits) {
      if (bits) {
        for (let i = array.length - 1; i >= 0; i--) {
          array[i] >>= bits;
          if (i > 0) {
            array[i] |= (array[i - 1] << (8 - bits));
          }
        }
      }
      return array;
    },

    /**
     * Get native Web Cryptography api, only the current version of the spec.
     * @returns {Object} The SubtleCrypto api or 'undefined'.
     */
    getWebCrypto: function() {
      return typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle;
    },

    /**
     * Get BigInteger class
     * It wraps the native BigInt type if it's available
     * Otherwise it relies on bn.js
     * @returns {BigInteger}
     * @async
     */
    getBigInteger,

    /**
     * Get native Node.js crypto api.
     * @returns {Object} The crypto module or 'undefined'.
     */
    getNodeCrypto: function() {
      return void('crypto');
    },

    getNodeZlib: function() {
      return void('zlib');
    },

    /**
     * Get native Node.js Buffer constructor. This should be used since
     * Buffer is not available under browserify.
     * @returns {Function} The Buffer constructor or 'undefined'.
     */
    getNodeBuffer: function() {
      return ({}).Buffer;
    },

    getHardwareConcurrency: function() {
      if (typeof navigator !== 'undefined') {
        return navigator.hardwareConcurrency || 1;
      }

      const os = void('os'); // Assume we're on Node.js.
      return os.cpus().length;
    },

    isEmailAddress: function(data) {
      if (!util.isString(data)) {
        return false;
      }
      const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+([a-zA-Z]{2,}[0-9]*|xn--[a-zA-Z\-0-9]+)))$/;
      return re.test(data);
    },

    /**
     * Normalize line endings to <CR><LF>
     * Support any encoding where CR=0x0D, LF=0x0A
     */
    canonicalizeEOL: function(data) {
      const CR = 13;
      const LF = 10;
      let carryOverCR = false;

      return transform(data, bytes => {
        if (carryOverCR) {
          bytes = util.concatUint8Array([new Uint8Array([CR]), bytes]);
        }

        if (bytes[bytes.length - 1] === CR) {
          carryOverCR = true;
          bytes = bytes.subarray(0, -1);
        } else {
          carryOverCR = false;
        }

        let index;
        const indices = [];
        for (let i = 0; ; i = index) {
          index = bytes.indexOf(LF, i) + 1;
          if (index) {
            if (bytes[index - 2] !== CR) indices.push(index);
          } else {
            break;
          }
        }
        if (!indices.length) {
          return bytes;
        }

        const normalized = new Uint8Array(bytes.length + indices.length);
        let j = 0;
        for (let i = 0; i < indices.length; i++) {
          const sub = bytes.subarray(indices[i - 1] || 0, indices[i]);
          normalized.set(sub, j);
          j += sub.length;
          normalized[j - 1] = CR;
          normalized[j] = LF;
          j++;
        }
        normalized.set(bytes.subarray(indices[indices.length - 1] || 0), j);
        return normalized;
      }, () => (carryOverCR ? new Uint8Array([CR]) : undefined));
    },

    /**
     * Convert line endings from canonicalized <CR><LF> to native <LF>
     * Support any encoding where CR=0x0D, LF=0x0A
     */
    nativeEOL: function(data) {
      const CR = 13;
      const LF = 10;
      let carryOverCR = false;

      return transform(data, bytes => {
        if (carryOverCR && bytes[0] !== LF) {
          bytes = util.concatUint8Array([new Uint8Array([CR]), bytes]);
        } else {
          bytes = new Uint8Array(bytes); // Don't mutate passed bytes
        }

        if (bytes[bytes.length - 1] === CR) {
          carryOverCR = true;
          bytes = bytes.subarray(0, -1);
        } else {
          carryOverCR = false;
        }

        let index;
        let j = 0;
        for (let i = 0; i !== bytes.length; i = index) {
          index = bytes.indexOf(CR, i) + 1;
          if (!index) index = bytes.length;
          const last = index - (bytes[index] === LF ? 1 : 0);
          if (i) bytes.copyWithin(j, i, last);
          j += last - i;
        }
        return bytes.subarray(0, j);
      }, () => (carryOverCR ? new Uint8Array([CR]) : undefined));
    },

    /**
     * Remove trailing spaces, carriage returns and tabs from each line
     */
    removeTrailingSpaces: function(text) {
      return text.split('\n').map(line => {
        let i = line.length - 1;
        for (; i >= 0 && (line[i] === ' ' || line[i] === '\t' || line[i] === '\r'); i--);
        return line.substr(0, i + 1);
      }).join('\n');
    },

    wrapError: function(message, error) {
      if (!error) {
        return new Error(message);
      }

      // update error message
      try {
        error.message = message + ': ' + error.message;
      } catch (e) {}

      return error;
    },

    /**
     * Map allowed packet tags to corresponding classes
     * Meant to be used to format `allowedPacket` for Packetlist.read
     * @param {Array<Object>} allowedClasses
     * @returns {Object} map from enum.packet to corresponding *Packet class
     */
    constructAllowedPackets: function(allowedClasses) {
      const map = {};
      allowedClasses.forEach(PacketClass => {
        if (!PacketClass.tag) {
          throw new Error('Invalid input: expected a packet class');
        }
        map[PacketClass.tag] = PacketClass;
      });
      return map;
    },

    /**
     * Return a Promise that will resolve as soon as one of the promises in input resolves
     * or will reject if all input promises all rejected
     * (similar to Promise.any, but with slightly different error handling)
     * @param {Array<Promise>} promises
     * @return {Promise<Any>} Promise resolving to the result of the fastest fulfilled promise
     *                          or rejected with the Error of the last resolved Promise (if all promises are rejected)
     */
    anyPromise: function(promises) {
      // eslint-disable-next-line no-async-promise-executor
      return new Promise(async (resolve, reject) => {
        let exception;
        await Promise.all(promises.map(async promise => {
          try {
            resolve(await promise);
          } catch (e) {
            exception = e;
          }
        }));
        reject(exception);
      });
    },

    /**
     * Return either `a` or `b` based on `cond`, in algorithmic constant time.
     * @param {Boolean} cond
     * @param {Uint8Array} a
     * @param {Uint8Array} b
     * @returns `a` if `cond` is true, `b` otherwise
     */
    selectUint8Array: function(cond, a, b) {
      const length = Math.max(a.length, b.length);
      const result = new Uint8Array(length);
      let end = 0;
      for (let i = 0; i < result.length; i++) {
        result[i] = (a[i] & (256 - cond)) | (b[i] & (255 + cond));
        end += (cond & i < a.length) | ((1 - cond) & i < b.length);
      }
      return result.subarray(0, end);
    },
    /**
     * Return either `a` or `b` based on `cond`, in algorithmic constant time.
     * NB: it only supports `a, b` with values between 0-255.
     * @param {Boolean} cond
     * @param {Uint8} a
     * @param {Uint8} b
     * @returns `a` if `cond` is true, `b` otherwise
     */
    selectUint8: function(cond, a, b) {
      return (a & (256 - cond)) | (b & (255 + cond));
    },
    /**
     * @param {module:enums.symmetric} cipherAlgo
     */
    isAES: function(cipherAlgo) {
      return cipherAlgo === enums.symmetric.aes128 || cipherAlgo === enums.symmetric.aes192 || cipherAlgo === enums.symmetric.aes256;
    }
  };

  /* OpenPGP radix-64/base64 string encoding/decoding
   * Copyright 2005 Herbert Hanewinkel, www.haneWIN.de
   * version 1.0, check www.haneWIN.de for the latest version
   *
   * This software is provided as-is, without express or implied warranty.
   * Permission to use, copy, modify, distribute or sell this software, with or
   * without fee, for any purpose and by any individual or organization, is hereby
   * granted, provided that the above copyright notice and this paragraph appear
   * in all copies. Distribution as a part of an application or binary must
   * include the above copyright notice in the documentation and/or other materials
   * provided with the application or distribution.
   */

  const Buffer = util.getNodeBuffer();

  let encodeChunk;
  let decodeChunk;
  if (Buffer) {
    encodeChunk = buf => Buffer.from(buf).toString('base64');
    decodeChunk = str => {
      const b = Buffer.from(str, 'base64');
      return new Uint8Array(b.buffer, b.byteOffset, b.byteLength);
    };
  } else {
    encodeChunk = buf => btoa(util.uint8ArrayToString(buf));
    decodeChunk = str => util.stringToUint8Array(atob(str));
  }

  /**
   * Convert binary array to radix-64
   * @param {Uint8Array | ReadableStream<Uint8Array>} data - Uint8Array to convert
   * @returns {String | ReadableStream<String>} Radix-64 version of input string.
   * @static
   */
  function encode(data) {
    let buf = new Uint8Array();
    return transform(data, value => {
      buf = util.concatUint8Array([buf, value]);
      const r = [];
      const bytesPerLine = 45; // 60 chars per line * (3 bytes / 4 chars of base64).
      const lines = Math.floor(buf.length / bytesPerLine);
      const bytes = lines * bytesPerLine;
      const encoded = encodeChunk(buf.subarray(0, bytes));
      for (let i = 0; i < lines; i++) {
        r.push(encoded.substr(i * 60, 60));
        r.push('\n');
      }
      buf = buf.subarray(bytes);
      return r.join('');
    }, () => (buf.length ? encodeChunk(buf) + '\n' : ''));
  }

  /**
   * Convert radix-64 to binary array
   * @param {String | ReadableStream<String>} data - Radix-64 string to convert
   * @returns {Uint8Array | ReadableStream<Uint8Array>} Binary array version of input string.
   * @static
   */
  function decode(data) {
    let buf = '';
    return transform(data, value => {
      buf += value;

      // Count how many whitespace characters there are in buf
      let spaces = 0;
      const spacechars = [' ', '\t', '\r', '\n'];
      for (let i = 0; i < spacechars.length; i++) {
        const spacechar = spacechars[i];
        for (let pos = buf.indexOf(spacechar); pos !== -1; pos = buf.indexOf(spacechar, pos + 1)) {
          spaces++;
        }
      }

      // Backtrack until we have 4n non-whitespace characters
      // that we can safely base64-decode
      let length = buf.length;
      for (; length > 0 && (length - spaces) % 4 !== 0; length--) {
        if (spacechars.includes(buf[length])) spaces--;
      }

      const decoded = decodeChunk(buf.substr(0, length));
      buf = buf.substr(length);
      return decoded;
    }, () => decodeChunk(buf));
  }

  /**
   * Convert a Base-64 encoded string an array of 8-bit integer
   *
   * Note: accepts both Radix-64 and URL-safe strings
   * @param {String} base64 - Base-64 encoded string to convert
   * @returns {Uint8Array} An array of 8-bit integers.
   */
  function b64ToUint8Array(base64) {
    return decode(base64.replace(/-/g, '+').replace(/_/g, '/'));
  }

  /**
   * Convert an array of 8-bit integer to a Base-64 encoded string
   * @param {Uint8Array} bytes - An array of 8-bit integers to convert
   * @param {bool} url - If true, output is URL-safe
   * @returns {String} Base-64 encoded string.
   */
  function uint8ArrayToB64(bytes, url) {
    let encoded = encode(bytes).replace(/[\r\n]/g, '');
    if (url) {
      encoded = encoded.replace(/[+]/g, '-').replace(/[/]/g, '_').replace(/[=]/g, '');
    }
    return encoded;
  }

  // GPG4Browsers - An OpenPGP implementation in javascript

  var config = {
    /**
     * @memberof module:config
     * @property {Integer} preferredHashAlgorithm Default hash algorithm {@link module:enums.hash}
     */
    preferredHashAlgorithm: enums.hash.sha256,
    /**
     * @memberof module:config
     * @property {Integer} preferredSymmetricAlgorithm Default encryption cipher {@link module:enums.symmetric}
     */
    preferredSymmetricAlgorithm: enums.symmetric.aes256,
    /**
     * @memberof module:config
     * @property {Integer} compression Default compression algorithm {@link module:enums.compression}
     */
    preferredCompressionAlgorithm: enums.compression.uncompressed,
    /**
     * @memberof module:config
     * @property {Integer} deflateLevel Default zip/zlib compression level, between 1 and 9
     */
    deflateLevel: 6,

    /**
     * Use Authenticated Encryption with Additional Data (AEAD) protection for symmetric encryption.
     * Note: not all OpenPGP implementations are compatible with this option.
     * **FUTURE OPENPGP.JS VERSIONS MAY BREAK COMPATIBILITY WHEN USING THIS OPTION**
     * @see {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07|RFC4880bis-07}
     * @memberof module:config
     * @property {Boolean} aeadProtect
     */
    aeadProtect: false,
    /**
     * Default Authenticated Encryption with Additional Data (AEAD) encryption mode
     * Only has an effect when aeadProtect is set to true.
     * @memberof module:config
     * @property {Integer} preferredAEADAlgorithm Default AEAD mode {@link module:enums.aead}
     */
    preferredAEADAlgorithm: enums.aead.eax,
    /**
     * Chunk Size Byte for Authenticated Encryption with Additional Data (AEAD) mode
     * Only has an effect when aeadProtect is set to true.
     * Must be an integer value from 0 to 56.
     * @memberof module:config
     * @property {Integer} aeadChunkSizeByte
     */
    aeadChunkSizeByte: 12,
    /**
     * Use V5 keys.
     * Note: not all OpenPGP implementations are compatible with this option.
     * **FUTURE OPENPGP.JS VERSIONS MAY BREAK COMPATIBILITY WHEN USING THIS OPTION**
     * @memberof module:config
     * @property {Boolean} v5Keys
     */
    v5Keys: false,
    /**
     * {@link https://tools.ietf.org/html/rfc4880#section-3.7.1.3|RFC4880 3.7.1.3}:
     * Iteration Count Byte for S2K (String to Key)
     * @memberof module:config
     * @property {Integer} s2kIterationCountByte
     */
    s2kIterationCountByte: 224,
    /**
     * Allow decryption of messages without integrity protection.
     * This is an **insecure** setting:
     *  - message modifications cannot be detected, thus processing the decrypted data is potentially unsafe.
     *  - it enables downgrade attacks against integrity-protected messages.
     * @memberof module:config
     * @property {Boolean} allowUnauthenticatedMessages
     */
    allowUnauthenticatedMessages: false,
    /**
     * Allow streaming unauthenticated data before its integrity has been checked. This would allow the application to
     * process large streams while limiting memory usage by releasing the decrypted chunks as soon as possible
     * and deferring checking their integrity until the decrypted stream has been read in full.
     *
     * This setting is **insecure** if the partially decrypted message is processed further or displayed to the user.
     * @memberof module:config
     * @property {Boolean} allowUnauthenticatedStream
     */
    allowUnauthenticatedStream: false,
    /**
     * @memberof module:config
     * @property {Boolean} checksumRequired Do not throw error when armor is missing a checksum
     */
    checksumRequired: false,
    /**
     * Minimum RSA key size allowed for key generation and message signing, verification and encryption.
     * The default is 2047 since due to a bug, previous versions of OpenPGP.js could generate 2047-bit keys instead of 2048-bit ones.
     * @memberof module:config
     * @property {Number} minRSABits
     */
    minRSABits: 2047,
    /**
     * Work-around for rare GPG decryption bug when encrypting with multiple passwords.
     * **Slower and slightly less secure**
     * @memberof module:config
     * @property {Boolean} passwordCollisionCheck
     */
    passwordCollisionCheck: false,
    /**
     * @memberof module:config
     * @property {Boolean} revocationsExpire If true, expired revocation signatures are ignored
     */
    revocationsExpire: false,
    /**
     * Allow decryption using RSA keys without `encrypt` flag.
     * This setting is potentially insecure, but it is needed to get around an old openpgpjs bug
     * where key flags were ignored when selecting a key for encryption.
     * @memberof module:config
     * @property {Boolean} allowInsecureDecryptionWithSigningKeys
     */
    allowInsecureDecryptionWithSigningKeys: false,
    /**
     * Allow verification of message signatures with keys whose validity at the time of signing cannot be determined.
     * Instead, a verification key will also be consider valid as long as it is valid at the current time.
     * This setting is potentially insecure, but it is needed to verify messages signed with keys that were later reformatted,
     * and have self-signature's creation date that does not match the primary key creation date.
     * @memberof module:config
     * @property {Boolean} allowInsecureDecryptionWithSigningKeys
     */
    allowInsecureVerificationWithReformattedKeys: false,

    /**
     * Enable constant-time decryption of RSA- and ElGamal-encrypted session keys, to hinder Bleichenbacher-like attacks (https://link.springer.com/chapter/10.1007/BFb0055716).
     * This setting has measurable performance impact and it is only helpful in application scenarios where both of the following conditions apply:
     * - new/incoming messages are automatically decrypted (without user interaction);
     * - an attacker can determine how long it takes to decrypt each message (e.g. due to decryption errors being logged remotely).
     * See also `constantTimePKCS1DecryptionSupportedSymmetricAlgorithms`.
     * @memberof module:config
     * @property {Boolean} constantTimePKCS1Decryption
     */
    constantTimePKCS1Decryption: false,
    /**
     * This setting is only meaningful if `constantTimePKCS1Decryption` is enabled.
     * Decryption of RSA- and ElGamal-encrypted session keys of symmetric algorithms different from the ones specified here will fail.
     * However, the more algorithms are added, the slower the decryption procedure becomes.
     * @memberof module:config
     * @property {Set<Integer>} constantTimePKCS1DecryptionSupportedSymmetricAlgorithms {@link module:enums.symmetric}
     */
    constantTimePKCS1DecryptionSupportedSymmetricAlgorithms: new Set([enums.symmetric.aes128, enums.symmetric.aes192, enums.symmetric.aes256]),

    /**
     * @memberof module:config
     * @property {Integer} minBytesForWebCrypto The minimum amount of bytes for which to use native WebCrypto APIs when available
     */
    minBytesForWebCrypto: 1000,
    /**
     * @memberof module:config
     * @property {Boolean} ignoreUnsupportedPackets Ignore unsupported/unrecognizable packets on parsing instead of throwing an error
     */
    ignoreUnsupportedPackets: true,
    /**
     * @memberof module:config
     * @property {Boolean} ignoreMalformedPackets Ignore malformed packets on parsing instead of throwing an error
     */
    ignoreMalformedPackets: false,
    /**
     * Parsing of packets is normally restricted to a predefined set of packets. For example a Sym. Encrypted Integrity Protected Data Packet can only
     * contain a certain set of packets including LiteralDataPacket. With this setting we can allow additional packets, which is probably not advisable
     * as a global config setting, but can be used for specific function calls (e.g. decrypt method of Message).
     * @memberof module:config
     * @property {Array} additionalAllowedPackets Allow additional packets on parsing. Defined as array of packet classes, e.g. [PublicKeyPacket]
     */
    additionalAllowedPackets: [],
    /**
     * @memberof module:config
     * @property {Boolean} showVersion Whether to include {@link module:config/config.versionString} in armored messages
     */
    showVersion: false,
    /**
     * @memberof module:config
     * @property {Boolean} showComment Whether to include {@link module:config/config.commentString} in armored messages
     */
    showComment: false,
    /**
     * @memberof module:config
     * @property {String} versionString A version string to be included in armored messages
     */
    versionString: 'OpenPGP.js 5.11.2',
    /**
     * @memberof module:config
     * @property {String} commentString A comment string to be included in armored messages
     */
    commentString: 'https://openpgpjs.org',

    /**
     * Max userID string length (used for parsing)
     * @memberof module:config
     * @property {Integer} maxUserIDLength
     */
    maxUserIDLength: 1024 * 5,
    /**
     * Contains notatations that are considered "known". Known notations do not trigger
     * validation error when the notation is marked as critical.
     * @memberof module:config
     * @property {Array} knownNotations
     */
    knownNotations: [],
    /**
     * Whether to use the indutny/elliptic library for curves (other than Curve25519) that are not supported by the available native crypto API.
     * When false, certain standard curves will not be supported (depending on the platform).
     * Note: the indutny/elliptic curve library is not designed to be constant time.
     * @memberof module:config
     * @property {Boolean} useIndutnyElliptic
     */
    useIndutnyElliptic: true,
    /**
     * Reject insecure hash algorithms
     * @memberof module:config
     * @property {Set<Integer>} rejectHashAlgorithms {@link module:enums.hash}
     */
    rejectHashAlgorithms: new Set([enums.hash.md5, enums.hash.ripemd]),
    /**
     * Reject insecure message hash algorithms
     * @memberof module:config
     * @property {Set<Integer>} rejectMessageHashAlgorithms {@link module:enums.hash}
     */
    rejectMessageHashAlgorithms: new Set([enums.hash.md5, enums.hash.ripemd, enums.hash.sha1]),
    /**
     * Reject insecure public key algorithms for key generation and message encryption, signing or verification
     * @memberof module:config
     * @property {Set<Integer>} rejectPublicKeyAlgorithms {@link module:enums.publicKey}
     */
    rejectPublicKeyAlgorithms: new Set([enums.publicKey.elgamal, enums.publicKey.dsa]),
    /**
     * Reject non-standard curves for key generation, message encryption, signing or verification
     * @memberof module:config
     * @property {Set<String>} rejectCurves {@link module:enums.curve}
     */
    rejectCurves: new Set([enums.curve.secp256k1])
  };

  /**
   * @fileoverview This object contains global configuration values.
   * @see module:config/config
   * @module config
   */

  // GPG4Browsers - An OpenPGP implementation in javascript

  /**
   * Finds out which Ascii Armoring type is used. Throws error if unknown type.
   * @param {String} text - ascii armored text
   * @returns {Integer} 0 = MESSAGE PART n of m.
   *         1 = MESSAGE PART n
   *         2 = SIGNED MESSAGE
   *         3 = PGP MESSAGE
   *         4 = PUBLIC KEY BLOCK
   *         5 = PRIVATE KEY BLOCK
   *         6 = SIGNATURE
   * @private
   */
  function getType(text) {
    const reHeader = /^-----BEGIN PGP (MESSAGE, PART \d+\/\d+|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$/m;

    const header = text.match(reHeader);

    if (!header) {
      throw new Error('Unknown ASCII armor type');
    }

    // BEGIN PGP MESSAGE, PART X/Y
    // Used for multi-part messages, where the armor is split amongst Y
    // parts, and this is the Xth part out of Y.
    if (/MESSAGE, PART \d+\/\d+/.test(header[1])) {
      return enums.armor.multipartSection;
    } else
    // BEGIN PGP MESSAGE, PART X
    // Used for multi-part messages, where this is the Xth part of an
    // unspecified number of parts. Requires the MESSAGE-ID Armor
    // Header to be used.
    if (/MESSAGE, PART \d+/.test(header[1])) {
      return enums.armor.multipartLast;
    } else
    // BEGIN PGP SIGNED MESSAGE
    if (/SIGNED MESSAGE/.test(header[1])) {
      return enums.armor.signed;
    } else
    // BEGIN PGP MESSAGE
    // Used for signed, encrypted, or compressed files.
    if (/MESSAGE/.test(header[1])) {
      return enums.armor.message;
    } else
    // BEGIN PGP PUBLIC KEY BLOCK
    // Used for armoring public keys.
    if (/PUBLIC KEY BLOCK/.test(header[1])) {
      return enums.armor.publicKey;
    } else
    // BEGIN PGP PRIVATE KEY BLOCK
    // Used for armoring private keys.
    if (/PRIVATE KEY BLOCK/.test(header[1])) {
      return enums.armor.privateKey;
    } else
    // BEGIN PGP SIGNATURE
    // Used for detached signatures, OpenPGP/MIME signatures, and
    // cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE
    // for detached signatures.
    if (/SIGNATURE/.test(header[1])) {
      return enums.armor.signature;
    }
  }

  /**
   * Add additional information to the armor version of an OpenPGP binary
   * packet block.
   * @author  Alex
   * @version 2011-12-16
   * @param {String} [customComment] - Additional comment to add to the armored string
   * @returns {String} The header information.
   * @private
   */
  function addheader(customComment, config) {
    let result = '';
    if (config.showVersion) {
      result += 'Version: ' + config.versionString + '\n';
    }
    if (config.showComment) {
      result += 'Comment: ' + config.commentString + '\n';
    }
    if (customComment) {
      result += 'Comment: ' + customComment + '\n';
    }
    result += '\n';
    return result;
  }


  /**
   * Calculates a checksum over the given data and returns it base64 encoded
   * @param {String | ReadableStream<String>} data - Data to create a CRC-24 checksum for
   * @returns {String | ReadableStream<String>} Base64 encoded checksum.
   * @private
   */
  function getCheckSum(data) {
    const crc = createcrc24(data);
    return encode(crc);
  }

  // https://create.stephan-brumme.com/crc32/#slicing-by-8-overview

  const crc_table = [
    new Array(0xFF),
    new Array(0xFF),
    new Array(0xFF),
    new Array(0xFF)
  ];

  for (let i = 0; i <= 0xFF; i++) {
    let crc = i << 16;
    for (let j = 0; j < 8; j++) {
      crc = (crc << 1) ^ ((crc & 0x800000) !== 0 ? 0x864CFB : 0);
    }
    crc_table[0][i] =
      ((crc & 0xFF0000) >> 16) |
      (crc & 0x00FF00) |
      ((crc & 0x0000FF) << 16);
  }
  for (let i = 0; i <= 0xFF; i++) {
    crc_table[1][i] = (crc_table[0][i] >> 8) ^ crc_table[0][crc_table[0][i] & 0xFF];
  }
  for (let i = 0; i <= 0xFF; i++) {
    crc_table[2][i] = (crc_table[1][i] >> 8) ^ crc_table[0][crc_table[1][i] & 0xFF];
  }
  for (let i = 0; i <= 0xFF; i++) {
    crc_table[3][i] = (crc_table[2][i] >> 8) ^ crc_table[0][crc_table[2][i] & 0xFF];
  }

  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness
  const isLittleEndian = (function() {
    const buffer = new ArrayBuffer(2);
    new DataView(buffer).setInt16(0, 0xFF, true /* littleEndian */);
    // Int16Array uses the platform's endianness.
    return new Int16Array(buffer)[0] === 0xFF;
  }());

  /**
   * Internal function to calculate a CRC-24 checksum over a given string (data)
   * @param {String | ReadableStream<String>} input - Data to create a CRC-24 checksum for
   * @returns {Uint8Array | ReadableStream<Uint8Array>} The CRC-24 checksum.
   * @private
   */
  function createcrc24(input) {
    let crc = 0xCE04B7;
    return transform(input, value => {
      const len32 = isLittleEndian ? Math.floor(value.length / 4) : 0;
      const arr32 = new Uint32Array(value.buffer, value.byteOffset, len32);
      for (let i = 0; i < len32; i++) {
        crc ^= arr32[i];
        crc =
          crc_table[0][(crc >> 24) & 0xFF] ^
          crc_table[1][(crc >> 16) & 0xFF] ^
          crc_table[2][(crc >> 8) & 0xFF] ^
          crc_table[3][(crc >> 0) & 0xFF];
      }
      for (let i = len32 * 4; i < value.length; i++) {
        crc = (crc >> 8) ^ crc_table[0][(crc & 0xFF) ^ value[i]];
      }
    }, () => new Uint8Array([crc, crc >> 8, crc >> 16]));
  }

  /**
   * Verify armored headers. crypto-refresh-06, section 6.2:
   * "An OpenPGP implementation may consider improperly formatted Armor
   * Headers to be corruption of the ASCII Armor, but SHOULD make an
   * effort to recover."
   * @private
   * @param {Array<String>} headers - Armor headers
   */
  function verifyHeaders(headers) {
    for (let i = 0; i < headers.length; i++) {
      if (!/^([^\s:]|[^\s:][^:]*[^\s:]): .+$/.test(headers[i])) {
        util.printDebugError(new Error('Improperly formatted armor header: ' + headers[i]));
      }
      if (!/^(Version|Comment|MessageID|Hash|Charset): .+$/.test(headers[i])) {
        util.printDebugError(new Error('Unknown header: ' + headers[i]));
      }
    }
  }

  /**
   * Splits a message into two parts, the body and the checksum. This is an internal function
   * @param {String} text - OpenPGP armored message part
   * @returns {Object} An object with attribute "body" containing the body.
   * and an attribute "checksum" containing the checksum.
   * @private
   */
  function splitChecksum(text) {
    let body = text;
    let checksum = '';

    const lastEquals = text.lastIndexOf('=');

    if (lastEquals >= 0 && lastEquals !== text.length - 1) { // '=' as the last char means no checksum
      body = text.slice(0, lastEquals);
      checksum = text.slice(lastEquals + 1).substr(0, 4);
    }

    return { body: body, checksum: checksum };
  }

  /**
   * Dearmor an OpenPGP armored message; verify the checksum and return
   * the encoded bytes
   * @param {String} input - OpenPGP armored message
   * @returns {Promise<Object>} An object with attribute "text" containing the message text,
   * an attribute "data" containing a stream of bytes and "type" for the ASCII armor type
   * @async
   * @static
   */
  function unarmor(input, config$1 = config) {
    // eslint-disable-next-line no-async-promise-executor
    return new Promise(async (resolve, reject) => {
      try {
        const reSplit = /^-----[^-]+-----$/m;
        const reEmptyLine = /^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*$/;

        let type;
        const headers = [];
        let lastHeaders = headers;
        let headersDone;
        let text = [];
        let textDone;
        let checksum;
        let data = decode(transformPair(input, async (readable, writable) => {
          const reader = getReader(readable);
          try {
            while (true) {
              let line = await reader.readLine();
              if (line === undefined) {
                throw new Error('Misformed armored text');
              }
              // remove trailing whitespace at end of lines
              line = util.removeTrailingSpaces(line.replace(/[\r\n]/g, ''));
              if (!type) {
                if (reSplit.test(line)) {
                  type = getType(line);
                }
              } else if (!headersDone) {
                if (reSplit.test(line)) {
                  reject(new Error('Mandatory blank line missing between armor headers and armor data'));
                }
                if (!reEmptyLine.test(line)) {
                  lastHeaders.push(line);
                } else {
                  verifyHeaders(lastHeaders);
                  headersDone = true;
                  if (textDone || type !== 2) {
                    resolve({ text, data, headers, type });
                    break;
                  }
                }
              } else if (!textDone && type === 2) {
                if (!reSplit.test(line)) {
                  // Reverse dash-escaping for msg
                  text.push(line.replace(/^- /, ''));
                } else {
                  text = text.join('\r\n');
                  textDone = true;
                  verifyHeaders(lastHeaders);
                  lastHeaders = [];
                  headersDone = false;
                }
              }
            }
          } catch (e) {
            reject(e);
            return;
          }
          const writer = getWriter(writable);
          try {
            while (true) {
              await writer.ready;
              const { done, value } = await reader.read();
              if (done) {
                throw new Error('Misformed armored text');
              }
              const line = value + '';
              if (line.indexOf('=') === -1 && line.indexOf('-') === -1) {
                await writer.write(line);
              } else {
                let remainder = await reader.readToEnd();
                if (!remainder.length) remainder = '';
                remainder = line + remainder;
                remainder = util.removeTrailingSpaces(remainder.replace(/\r/g, ''));
                const parts = remainder.split(reSplit);
                if (parts.length === 1) {
                  throw new Error('Misformed armored text');
                }
                const split = splitChecksum(parts[0].slice(0, -1));
                checksum = split.checksum;
                await writer.write(split.body);
                break;
              }
            }
            await writer.ready;
            await writer.close();
          } catch (e) {
            await writer.abort(e);
          }
        }));
        data = transformPair(data, async (readable, writable) => {
          const checksumVerified = readToEnd(getCheckSum(passiveClone(readable)));
          checksumVerified.catch(() => {});
          await pipe(readable, writable, {
            preventClose: true
          });
          const writer = getWriter(writable);
          try {
            const checksumVerifiedString = (await checksumVerified).replace('\n', '');
            if (checksum !== checksumVerifiedString && (checksum || config$1.checksumRequired)) {
              throw new Error('Ascii armor integrity check failed');
            }
            await writer.ready;
            await writer.close();
          } catch (e) {
            await writer.abort(e);
          }
        });
      } catch (e) {
        reject(e);
      }
    }).then(async result => {
      if (isArrayStream(result.data)) {
        result.data = await readToEnd(result.data);
      }
      return result;
    });
  }


  /**
   * Armor an OpenPGP binary packet block
   * @param {module:enums.armor} messageType - Type of the message
   * @param {Uint8Array | ReadableStream<Uint8Array>} body - The message body to armor
   * @param {Integer} [partIndex]
   * @param {Integer} [partTotal]
   * @param {String} [customComment] - Additional comment to add to the armored string
   * @returns {String | ReadableStream<String>} Armored text.
   * @static
   */
  function armor(messageType, body, partIndex, partTotal, customComment, config$1 = config) {
    let text;
    let hash;
    if (messageType === enums.armor.signed) {
      text = body.text;
      hash = body.hash;
      body = body.data;
    }
    const bodyClone = passiveClone(body);
    const result = [];
    switch (messageType) {
      case enums.armor.multipartSection:
        result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n');
        result.push(addheader(customComment, config$1));
        result.push(encode(body));
        result.push('=', getCheckSum(bodyClone));
        result.push('-----END PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n');
        break;
      case enums.armor.multipartLast:
        result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '-----\n');
        result.push(addheader(customComment, config$1));
        result.push(encode(body));
        result.push('=', getCheckSum(bodyClone));
        result.push('-----END PGP MESSAGE, PART ' + partIndex + '-----\n');
        break;
      case enums.armor.signed:
        result.push('-----BEGIN PGP SIGNED MESSAGE-----\n');
        result.push('Hash: ' + hash + '\n\n');
        result.push(text.replace(/^-/mg, '- -'));
        result.push('\n-----BEGIN PGP SIGNATURE-----\n');
        result.push(addheader(customComment, config$1));
        result.push(encode(body));
        result.push('=', getCheckSum(bodyClone));
        result.push('-----END PGP SIGNATURE-----\n');
        break;
      case enums.armor.message:
        result.push('-----BEGIN PGP MESSAGE-----\n');
        result.push(addheader(customComment, config$1));
        result.push(encode(body));
        result.push('=', getCheckSum(bodyClone));
        result.push('-----END PGP MESSAGE-----\n');
        break;
      case enums.armor.publicKey:
        result.push('-----BEGIN PGP PUBLIC KEY BLOCK-----\n');
        result.push(addheader(customComment, config$1));
        result.push(encode(body));
        result.push('=', getCheckSum(bodyClone));
        result.push('-----END PGP PUBLIC KEY BLOCK-----\n');
        break;
      case enums.armor.privateKey:
        result.push('-----BEGIN PGP PRIVATE KEY BLOCK-----\n');
        result.push(addheader(customComment, config$1));
        result.push(encode(body));
        result.push('=', getCheckSum(bodyClone));
        result.push('-----END PGP PRIVATE KEY BLOCK-----\n');
        break;
      case enums.armor.signature:
        result.push('-----BEGIN PGP SIGNATURE-----\n');
        result.push(addheader(customComment, config$1));
        result.push(encode(body));
        result.push('=', getCheckSum(bodyClone));
        result.push('-----END PGP SIGNATURE-----\n');
        break;
    }

    return util.concat(result);
  }

  // GPG4Browsers - An OpenPGP implementation in javascript

  /**
   * Implementation of type key id
   *
   * {@link https://tools.ietf.org/html/rfc4880#section-3.3|RFC4880 3.3}:
   * A Key ID is an eight-octet scalar that identifies a key.
   * Implementations SHOULD NOT assume that Key IDs are unique.  The
   * section "Enhanced Key Formats" below describes how Key IDs are
   * formed.
   */
  class KeyID {
    constructor() {
      this.bytes = '';
    }

    /**
     * Parsing method for a key id
     * @param {Uint8Array} bytes - Input to read the key id from
     */
    read(bytes) {
      this.bytes = util.uint8ArrayToString(bytes.subarray(0, 8));
      return this.bytes.length;
    }

    /**
     * Serializes the Key ID
     * @returns {Uint8Array} Key ID as a Uint8Array.
     */
    write() {
      return util.stringToUint8Array(this.bytes);
    }

    /**
     * Returns the Key ID represented as a hexadecimal string
     * @returns {String} Key ID as a hexadecimal string.
     */
    toHex() {
      return util.uint8ArrayToHex(util.stringToUint8Array(this.bytes));
    }

    /**
     * Checks equality of Key ID's
     * @param {KeyID} keyID
     * @param {Boolean} matchWildcard - Indicates whether to check if either keyID is a wildcard
     */
    equals(keyID, matchWildcard = false) {
      return (matchWildcard && (keyID.isWildcard() || this.isWildcard())) || this.bytes === keyID.bytes;
    }

    /**
     * Checks to see if the Key ID is unset
     * @returns {Boolean} True if the Key ID is null.
     */
    isNull() {
      return this.bytes === '';
    }

    /**
     * Checks to see if the Key ID is a "wildcard" Key ID (all zeros)
     * @returns {Boolean} True if this is a wildcard Key ID.
     */
    isWildcard() {
      return /^0+$/.test(this.toHex());
    }

    static mapToHex(keyID) {
      return keyID.toHex();
    }

    static fromID(hex) {
      const keyID = new KeyID();
      keyID.read(util.hexToUint8Array(hex));
      return keyID;
    }

    static wildcard() {
      const keyID = new KeyID();
      keyID.read(new Uint8Array(8));
      return keyID;
    }
  }

  /**
   * @file {@link http://asmjs.org Asm.js} implementation of the {@link https://en.wikipedia.org/wiki/Advanced_Encryption_Standard Advanced Encryption Standard}.
   * @author Artem S Vybornov <vybornov@gmail.com>
   * @license MIT
   */
  var AES_asm = function () {

    /**
     * Galois Field stuff init flag
     */
    var ginit_done = false;

    /**
     * Galois Field exponentiation and logarithm tables for 3 (the generator)
     */
    var gexp3, glog3;

    /**
     * Init Galois Field tables
     */
    function ginit() {
      gexp3 = [],
        glog3 = [];

      var a = 1, c, d;
      for (c = 0; c < 255; c++) {
        gexp3[c] = a;

        // Multiply by three
        d = a & 0x80, a <<= 1, a &= 255;
        if (d === 0x80) a ^= 0x1b;
        a ^= gexp3[c];

        // Set the log table value
        glog3[gexp3[c]] = c;
      }
      gexp3[255] = gexp3[0];
      glog3[0] = 0;

      ginit_done = true;
    }

    /**
     * Galois Field multiplication
     * @param {number} a
     * @param {number} b
     * @return {number}
     */
    function gmul(a, b) {
      var c = gexp3[(glog3[a] + glog3[b]) % 255];
      if (a === 0 || b === 0) c = 0;
      return c;
    }

    /**
     * Galois Field reciprocal
     * @param {number} a
     * @return {number}
     */
    function ginv(a) {
      var i = gexp3[255 - glog3[a]];
      if (a === 0) i = 0;
      return i;
    }

    /**
     * AES stuff init flag
     */
    var aes_init_done = false;

    /**
     * Encryption, Decryption, S-Box and KeyTransform tables
     *
     * @type {number[]}
     */
    var aes_sbox;

    /**
     * @type {number[]}
     */
    var aes_sinv;

    /**
     * @type {number[][]}
     */
    var aes_enc;

    /**
     * @type {number[][]}
     */
    var aes_dec;

    /**
     * Init AES tables
     */
    function aes_init() {
      if (!ginit_done) ginit();

      // Calculates AES S-Box value
      function _s(a) {
        var c, s, x;
        s = x = ginv(a);
        for (c = 0; c < 4; c++) {
          s = ((s << 1) | (s >>> 7)) & 255;
          x ^= s;
        }
        x ^= 99;
        return x;
      }

      // Tables
      aes_sbox = [],
        aes_sinv = [],
        aes_enc = [[], [], [], []],
        aes_dec = [[], [], [], []];

      for (var i = 0; i < 256; i++) {
        var s = _s(i);

        // S-Box and its inverse
        aes_sbox[i] = s;
        aes_sinv[s] = i;

        // Ecryption and Decryption tables
        aes_enc[0][i] = (gmul(2, s) << 24) | (s << 16) | (s << 8) | gmul(3, s);
        aes_dec[0][s] = (gmul(14, i) << 24) | (gmul(9, i) << 16) | (gmul(13, i) << 8) | gmul(11, i);
        // Rotate tables
        for (var t = 1; t < 4; t++) {
          aes_enc[t][i] = (aes_enc[t - 1][i] >>> 8) | (aes_enc[t - 1][i] << 24);
          aes_dec[t][s] = (aes_dec[t - 1][s] >>> 8) | (aes_dec[t - 1][s] << 24);
        }
      }

      aes_init_done = true;
    }

    /**
     * Asm.js module constructor.
     *
     * <p>
     * Heap buffer layout by offset:
     * <pre>
     * 0x0000   encryption key schedule
     * 0x0400   decryption key schedule
     * 0x0800   sbox
     * 0x0c00   inv sbox
     * 0x1000   encryption tables
     * 0x2000   decryption tables
     * 0x3000   reserved (future GCM multiplication lookup table)
     * 0x4000   data
     * </pre>
     * Don't touch anything before <code>0x400</code>.
     * </p>
     *
     * @alias AES_asm
     * @class
     * @param foreign - <i>ignored</i>
     * @param buffer - heap buffer to link with
     */
    var wrapper = function (foreign, buffer) {
      // Init AES stuff for the first time
      if (!aes_init_done) aes_init();

      // Fill up AES tables
      var heap = new Uint32Array(buffer);
      heap.set(aes_sbox, 0x0800 >> 2);
      heap.set(aes_sinv, 0x0c00 >> 2);
      for (var i = 0; i < 4; i++) {
        heap.set(aes_enc[i], (0x1000 + 0x400 * i) >> 2);
        heap.set(aes_dec[i], (0x2000 + 0x400 * i) >> 2);
      }

      /**
       * Calculate AES key schedules.
       * @instance
       * @memberof AES_asm
       * @param {number} ks - key size, 4/6/8 (for 128/192/256-bit key correspondingly)
       * @param {number} k0 - key vector components
       * @param {number} k1 - key vector components
       * @param {number} k2 - key vector components
       * @param {number} k3 - key vector components
       * @param {number} k4 - key vector components
       * @param {number} k5 - key vector components
       * @param {number} k6 - key vector components
       * @param {number} k7 - key vector components
       */
      function set_key(ks, k0, k1, k2, k3, k4, k5, k6, k7) {
        var ekeys = heap.subarray(0x000, 60),
          dkeys = heap.subarray(0x100, 0x100 + 60);

        // Encryption key schedule
        ekeys.set([k0, k1, k2, k3, k4, k5, k6, k7]);
        for (var i = ks, rcon = 1; i < 4 * ks + 28; i++) {
          var k = ekeys[i - 1];
          if ((i % ks === 0) || (ks === 8 && i % ks === 4)) {
            k = aes_sbox[k >>> 24] << 24 ^ aes_sbox[k >>> 16 & 255] << 16 ^ aes_sbox[k >>> 8 & 255] << 8 ^ aes_sbox[k & 255];
          }
          if (i % ks === 0) {
            k = (k << 8) ^ (k >>> 24) ^ (rcon << 24);
            rcon = (rcon << 1) ^ ((rcon & 0x80) ? 0x1b : 0);
          }
          ekeys[i] = ekeys[i - ks] ^ k;
        }

        // Decryption key schedule
        for (var j = 0; j < i; j += 4) {
          for (var jj = 0; jj < 4; jj++) {
            var k = ekeys[i - (4 + j) + (4 - jj) % 4];
            if (j < 4 || j >= i - 4) {
              dkeys[j + jj] = k;
            } else {
              dkeys[j + jj] = aes_dec[0][aes_sbox[k >>> 24]]
                ^ aes_dec[1][aes_sbox[k >>> 16 & 255]]
                ^ aes_dec[2][aes_sbox[k >>> 8 & 255]]
                ^ aes_dec[3][aes_sbox[k & 255]];
            }
          }
        }

        // Set rounds number
        asm.set_rounds(ks + 5);
      }

      // create library object with necessary properties
      var stdlib = {Uint8Array: Uint8Array, Uint32Array: Uint32Array};

      var asm = function (stdlib, foreign, buffer) {
        "use asm";

        var S0 = 0, S1 = 0, S2 = 0, S3 = 0,
          I0 = 0, I1 = 0, I2 = 0, I3 = 0,
          N0 = 0, N1 = 0, N2 = 0, N3 = 0,
          M0 = 0, M1 = 0, M2 = 0, M3 = 0,
          H0 = 0, H1 = 0, H2 = 0, H3 = 0,
          R = 0;

        var HEAP = new stdlib.Uint32Array(buffer),
          DATA = new stdlib.Uint8Array(buffer);

        /**
         * AES core
         * @param {number} k - precomputed key schedule offset
         * @param {number} s - precomputed sbox table offset
         * @param {number} t - precomputed round table offset
         * @param {number} r - number of inner rounds to perform
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _core(k, s, t, r, x0, x1, x2, x3) {
          k = k | 0;
          s = s | 0;
          t = t | 0;
          r = r | 0;
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          var t1 = 0, t2 = 0, t3 = 0,
            y0 = 0, y1 = 0, y2 = 0, y3 = 0,
            i = 0;

          t1 = t | 0x400, t2 = t | 0x800, t3 = t | 0xc00;

          // round 0
          x0 = x0 ^ HEAP[(k | 0) >> 2],
            x1 = x1 ^ HEAP[(k | 4) >> 2],
            x2 = x2 ^ HEAP[(k | 8) >> 2],
            x3 = x3 ^ HEAP[(k | 12) >> 2];

          // round 1..r
          for (i = 16; (i | 0) <= (r << 4); i = (i + 16) | 0) {
            y0 = HEAP[(t | x0 >> 22 & 1020) >> 2] ^ HEAP[(t1 | x1 >> 14 & 1020) >> 2] ^ HEAP[(t2 | x2 >> 6 & 1020) >> 2] ^ HEAP[(t3 | x3 << 2 & 1020) >> 2] ^ HEAP[(k | i | 0) >> 2],
              y1 = HEAP[(t | x1 >> 22 & 1020) >> 2] ^ HEAP[(t1 | x2 >> 14 & 1020) >> 2] ^ HEAP[(t2 | x3 >> 6 & 1020) >> 2] ^ HEAP[(t3 | x0 << 2 & 1020) >> 2] ^ HEAP[(k | i | 4) >> 2],
              y2 = HEAP[(t | x2 >> 22 & 1020) >> 2] ^ HEAP[(t1 | x3 >> 14 & 1020) >> 2] ^ HEAP[(t2 | x0 >> 6 & 1020) >> 2] ^ HEAP[(t3 | x1 << 2 & 1020) >> 2] ^ HEAP[(k | i | 8) >> 2],
              y3 = HEAP[(t | x3 >> 22 & 1020) >> 2] ^ HEAP[(t1 | x0 >> 14 & 1020) >> 2] ^ HEAP[(t2 | x1 >> 6 & 1020) >> 2] ^ HEAP[(t3 | x2 << 2 & 1020) >> 2] ^ HEAP[(k | i | 12) >> 2];
            x0 = y0, x1 = y1, x2 = y2, x3 = y3;
          }

          // final round
          S0 = HEAP[(s | x0 >> 22 & 1020) >> 2] << 24 ^ HEAP[(s | x1 >> 14 & 1020) >> 2] << 16 ^ HEAP[(s | x2 >> 6 & 1020) >> 2] << 8 ^ HEAP[(s | x3 << 2 & 1020) >> 2] ^ HEAP[(k | i | 0) >> 2],
            S1 = HEAP[(s | x1 >> 22 & 1020) >> 2] << 24 ^ HEAP[(s | x2 >> 14 & 1020) >> 2] << 16 ^ HEAP[(s | x3 >> 6 & 1020) >> 2] << 8 ^ HEAP[(s | x0 << 2 & 1020) >> 2] ^ HEAP[(k | i | 4) >> 2],
            S2 = HEAP[(s | x2 >> 22 & 1020) >> 2] << 24 ^ HEAP[(s | x3 >> 14 & 1020) >> 2] << 16 ^ HEAP[(s | x0 >> 6 & 1020) >> 2] << 8 ^ HEAP[(s | x1 << 2 & 1020) >> 2] ^ HEAP[(k | i | 8) >> 2],
            S3 = HEAP[(s | x3 >> 22 & 1020) >> 2] << 24 ^ HEAP[(s | x0 >> 14 & 1020) >> 2] << 16 ^ HEAP[(s | x1 >> 6 & 1020) >> 2] << 8 ^ HEAP[(s | x2 << 2 & 1020) >> 2] ^ HEAP[(k | i | 12) >> 2];
        }

        /**
         * ECB mode encryption
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _ecb_enc(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          _core(
            0x0000, 0x0800, 0x1000,
            R,
            x0,
            x1,
            x2,
            x3
          );
        }

        /**
         * ECB mode decryption
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _ecb_dec(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          var t = 0;

          _core(
            0x0400, 0x0c00, 0x2000,
            R,
            x0,
            x3,
            x2,
            x1
          );

          t = S1, S1 = S3, S3 = t;
        }


        /**
         * CBC mode encryption
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _cbc_enc(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          _core(
            0x0000, 0x0800, 0x1000,
            R,
            I0 ^ x0,
            I1 ^ x1,
            I2 ^ x2,
            I3 ^ x3
          );

          I0 = S0,
            I1 = S1,
            I2 = S2,
            I3 = S3;
        }

        /**
         * CBC mode decryption
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _cbc_dec(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          var t = 0;

          _core(
            0x0400, 0x0c00, 0x2000,
            R,
            x0,
            x3,
            x2,
            x1
          );

          t = S1, S1 = S3, S3 = t;

          S0 = S0 ^ I0,
            S1 = S1 ^ I1,
            S2 = S2 ^ I2,
            S3 = S3 ^ I3;

          I0 = x0,
            I1 = x1,
            I2 = x2,
            I3 = x3;
        }

        /**
         * CFB mode encryption
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _cfb_enc(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          _core(
            0x0000, 0x0800, 0x1000,
            R,
            I0,
            I1,
            I2,
            I3
          );

          I0 = S0 = S0 ^ x0,
            I1 = S1 = S1 ^ x1,
            I2 = S2 = S2 ^ x2,
            I3 = S3 = S3 ^ x3;
        }


        /**
         * CFB mode decryption
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _cfb_dec(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          _core(
            0x0000, 0x0800, 0x1000,
            R,
            I0,
            I1,
            I2,
            I3
          );

          S0 = S0 ^ x0,
            S1 = S1 ^ x1,
            S2 = S2 ^ x2,
            S3 = S3 ^ x3;

          I0 = x0,
            I1 = x1,
            I2 = x2,
            I3 = x3;
        }

        /**
         * OFB mode encryption / decryption
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _ofb(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          _core(
            0x0000, 0x0800, 0x1000,
            R,
            I0,
            I1,
            I2,
            I3
          );

          I0 = S0,
            I1 = S1,
            I2 = S2,
            I3 = S3;

          S0 = S0 ^ x0,
            S1 = S1 ^ x1,
            S2 = S2 ^ x2,
            S3 = S3 ^ x3;
        }

        /**
         * CTR mode encryption / decryption
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _ctr(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          _core(
            0x0000, 0x0800, 0x1000,
            R,
            N0,
            N1,
            N2,
            N3
          );

          N3 = (~M3 & N3) | M3 & (N3 + 1);
            N2 = (~M2 & N2) | M2 & (N2 + ((N3 | 0) == 0));
            N1 = (~M1 & N1) | M1 & (N1 + ((N2 | 0) == 0));
            N0 = (~M0 & N0) | M0 & (N0 + ((N1 | 0) == 0));

          S0 = S0 ^ x0;
            S1 = S1 ^ x1;
            S2 = S2 ^ x2;
            S3 = S3 ^ x3;
        }

        /**
         * GCM mode MAC calculation
         * @param {number} x0 - 128-bit input block vector
         * @param {number} x1 - 128-bit input block vector
         * @param {number} x2 - 128-bit input block vector
         * @param {number} x3 - 128-bit input block vector
         */
        function _gcm_mac(x0, x1, x2, x3) {
          x0 = x0 | 0;
          x1 = x1 | 0;
          x2 = x2 | 0;
          x3 = x3 | 0;

          var y0 = 0, y1 = 0, y2 = 0, y3 = 0,
            z0 = 0, z1 = 0, z2 = 0, z3 = 0,
            i = 0, c = 0;

          x0 = x0 ^ I0,
            x1 = x1 ^ I1,
            x2 = x2 ^ I2,
            x3 = x3 ^ I3;

          y0 = H0 | 0,
            y1 = H1 | 0,
            y2 = H2 | 0,
            y3 = H3 | 0;

          for (; (i | 0) < 128; i = (i + 1) | 0) {
            if (y0 >>> 31) {
              z0 = z0 ^ x0,
                z1 = z1 ^ x1,
                z2 = z2 ^ x2,
                z3 = z3 ^ x3;
            }

            y0 = (y0 << 1) | (y1 >>> 31),
              y1 = (y1 << 1) | (y2 >>> 31),
              y2 = (y2 << 1) | (y3 >>> 31),
              y3 = (y3 << 1);

            c = x3 & 1;

            x3 = (x3 >>> 1) | (x2 << 31),
              x2 = (x2 >>> 1) | (x1 << 31),
              x1 = (x1 >>> 1) | (x0 << 31),
              x0 = (x0 >>> 1);

            if (c) x0 = x0 ^ 0xe1000000;
          }

          I0 = z0,
            I1 = z1,
            I2 = z2,
            I3 = z3;
        }

        /**
         * Set the internal rounds number.
         * @instance
         * @memberof AES_asm
         * @param {number} r - number if inner AES rounds
         */
        function set_rounds(r) {
          r = r | 0;
          R = r;
        }

        /**
         * Populate the internal state of the module.
         * @instance
         * @memberof AES_asm
         * @param {number} s0 - state vector
         * @param {number} s1 - state vector
         * @param {number} s2 - state vector
         * @param {number} s3 - state vector
         */
        function set_state(s0, s1, s2, s3) {
          s0 = s0 | 0;
          s1 = s1 | 0;
          s2 = s2 | 0;
          s3 = s3 | 0;

          S0 = s0,
            S1 = s1,
            S2 = s2,
            S3 = s3;
        }

        /**
         * Populate the internal iv of the module.
         * @instance
         * @memberof AES_asm
         * @param {number} i0 - iv vector
         * @param {number} i1 - iv vector
         * @param {number} i2 - iv vector
         * @param {number} i3 - iv vector
         */
        function set_iv(i0, i1, i2, i3) {
          i0 = i0 | 0;
          i1 = i1 | 0;
          i2 = i2 | 0;
          i3 = i3 | 0;

          I0 = i0,
            I1 = i1,
            I2 = i2,
            I3 = i3;
        }

        /**
         * Set nonce for CTR-family modes.
         * @instance
         * @memberof AES_asm
         * @param {number} n0 - nonce vector
         * @param {number} n1 - nonce vector
         * @param {number} n2 - nonce vector
         * @param {number} n3 - nonce vector
         */
        function set_nonce(n0, n1, n2, n3) {
          n0 = n0 | 0;
          n1 = n1 | 0;
          n2 = n2 | 0;
          n3 = n3 | 0;

          N0 = n0,
            N1 = n1,
            N2 = n2,
            N3 = n3;
        }

        /**
         * Set counter mask for CTR-family modes.
         * @instance
         * @memberof AES_asm
         * @param {number} m0 - counter mask vector
         * @param {number} m1 - counter mask vector
         * @param {number} m2 - counter mask vector
         * @param {number} m3 - counter mask vector
         */
        function set_mask(m0, m1, m2, m3) {
          m0 = m0 | 0;
          m1 = m1 | 0;
          m2 = m2 | 0;
          m3 = m3 | 0;

          M0 = m0,
            M1 = m1,
            M2 = m2,
            M3 = m3;
        }

        /**
         * Set counter for CTR-family modes.
         * @instance
         * @memberof AES_asm
         * @param {number} c0 - counter vector
         * @param {number} c1 - counter vector
         * @param {number} c2 - counter vector
         * @param {number} c3 - counter vector
         */
        function set_counter(c0, c1, c2, c3) {
          c0 = c0 | 0;
          c1 = c1 | 0;
          c2 = c2 | 0;
          c3 = c3 | 0;

          N3 = (~M3 & N3) | M3 & c3,
            N2 = (~M2 & N2) | M2 & c2,
            N1 = (~M1 & N1) | M1 & c1,
            N0 = (~M0 & N0) | M0 & c0;
        }

        /**
         * Store the internal state vector into the heap.
         * @instance
         * @memberof AES_asm
         * @param {number} pos - offset where to put the data
         * @return {number} The number of bytes have been written into the heap, always 16.
         */
        function get_state(pos) {
          pos = pos | 0;

          if (pos & 15) return -1;

          DATA[pos | 0] = S0 >>> 24,
            DATA[pos | 1] = S0 >>> 16 & 255,
            DATA[pos | 2] = S0 >>> 8 & 255,
            DATA[pos | 3] = S0 & 255,
            DATA[pos | 4] = S1 >>> 24,
            DATA[pos | 5] = S1 >>> 16 & 255,
            DATA[pos | 6] = S1 >>> 8 & 255,
            DATA[pos | 7] = S1 & 255,
            DATA[pos | 8] = S2 >>> 24,
            DATA[pos | 9] = S2 >>> 16 & 255,
            DATA[pos | 10] = S2 >>> 8 & 255,
            DATA[pos | 11] = S2 & 255,
            DATA[pos | 12] = S3 >>> 24,
            DATA[pos | 13] = S3 >>> 16 & 255,
            DATA[pos | 14] = S3 >>> 8 & 255,
            DATA[pos | 15] = S3 & 255;

          return 16;
        }

        /**
         * Store the internal iv vector into the heap.
         * @instance
         * @memberof AES_asm
         * @param {number} pos - offset where to put the data
         * @return {number} The number of bytes have been written into the heap, always 16.
         */
        function get_iv(pos) {
          pos = pos | 0;

          if (pos & 15) return -1;

          DATA[pos | 0] = I0 >>> 24,
            DATA[pos | 1] = I0 >>> 16 & 255,
            DATA[pos | 2] = I0 >>> 8 & 255,
            DATA[pos | 3] = I0 & 255,
            DATA[pos | 4] = I1 >>> 24,
            DATA[pos | 5] = I1 >>> 16 & 255,
            DATA[pos | 6] = I1 >>> 8 & 255,
            DATA[pos | 7] = I1 & 255,
            DATA[pos | 8] = I2 >>> 24,
            DATA[pos | 9] = I2 >>> 16 & 255,
            DATA[pos | 10] = I2 >>> 8 & 255,
            DATA[pos | 11] = I2 & 255,
            DATA[pos | 12] = I3 >>> 24,
            DATA[pos | 13] = I3 >>> 16 & 255,
            DATA[pos | 14] = I3 >>> 8 & 255,
            DATA[pos | 15] = I3 & 255;

          return 16;
        }

        /**
         * GCM initialization.
         * @instance
         * @memberof AES_asm
         */
        function gcm_init() {
          _ecb_enc(0, 0, 0, 0);
          H0 = S0,
            H1 = S1,
            H2 = S2,
            H3 = S3;
        }

        /**
         * Perform ciphering operation on the supplied data.
         * @instance
         * @memberof AES_asm
         * @param {number} mode - block cipher mode (see {@link AES_asm} mode constants)
         * @param {number} pos - offset of the data being processed
         * @param {number} len - length of the data being processed
         * @return {number} Actual amount of data have been processed.
         */
        function cipher(mode, pos, len) {
          mode = mode | 0;
          pos = pos | 0;
          len = len | 0;

          var ret = 0;

          if (pos & 15) return -1;

          while ((len | 0) >= 16) {
            _cipher_modes[mode & 7](
              DATA[pos | 0] << 24 | DATA[pos | 1] << 16 | DATA[pos | 2] << 8 | DATA[pos | 3],
              DATA[pos | 4] << 24 | DATA[pos | 5] << 16 | DATA[pos | 6] << 8 | DATA[pos | 7],
              DATA[pos | 8] << 24 | DATA[pos | 9] << 16 | DATA[pos | 10] << 8 | DATA[pos | 11],
              DATA[pos | 12] << 24 | DATA[pos | 13] << 16 | DATA[pos | 14] << 8 | DATA[pos | 15]
            );

            DATA[pos | 0] = S0 >>> 24,
              DATA[pos | 1] = S0 >>> 16 & 255,
              DATA[pos | 2] = S0 >>> 8 & 255,
              DATA[pos | 3] = S0 & 255,
              DATA[pos | 4] = S1 >>> 24,
              DATA[pos | 5] = S1 >>> 16 & 255,
              DATA[pos | 6] = S1 >>> 8 & 255,
              DATA[pos | 7] = S1 & 255,
              DATA[pos | 8] = S2 >>> 24,
              DATA[pos | 9] = S2 >>> 16 & 255,
              DATA[pos | 10] = S2 >>> 8 & 255,
              DATA[pos | 11] = S2 & 255,
              DATA[pos | 12] = S3 >>> 24,
              DATA[pos | 13] = S3 >>> 16 & 255,
              DATA[pos | 14] = S3 >>> 8 & 255,
              DATA[pos | 15] = S3 & 255;

            ret = (ret + 16) | 0,
              pos = (pos + 16) | 0,
              len = (len - 16) | 0;
          }

          return ret | 0;
        }

        /**
         * Calculates MAC of the supplied data.
         * @instance
         * @memberof AES_asm
         * @param {number} mode - block cipher mode (see {@link AES_asm} mode constants)
         * @param {number} pos - offset of the data being processed
         * @param {number} len - length of the data being processed
         * @return {number} Actual amount of data have been processed.
         */
        function mac(mode, pos, len) {
          mode = mode | 0;
          pos = pos | 0;
          len = len | 0;

          var ret = 0;

          if (pos & 15) return -1;

          while ((len | 0) >= 16) {
            _mac_modes[mode & 1](
              DATA[pos | 0] << 24 | DATA[pos | 1] << 16 | DATA[pos | 2] << 8 | DATA[pos | 3],
              DATA[pos | 4] << 24 | DATA[pos | 5] << 16 | DATA[pos | 6] << 8 | DATA[pos | 7],
              DATA[pos | 8] << 24 | DATA[pos | 9] << 16 | DATA[pos | 10] << 8 | DATA[pos | 11],
              DATA[pos | 12] << 24 | DATA[pos | 13] << 16 | DATA[pos | 14] << 8 | DATA[pos | 15]
            );

            ret = (ret + 16) | 0,
              pos = (pos + 16) | 0,
              len = (len - 16) | 0;
          }

          return ret | 0;
        }

        /**
         * AES cipher modes table (virual methods)
         */
        var _cipher_modes = [_ecb_enc, _ecb_dec, _cbc_enc, _cbc_dec, _cfb_enc, _cfb_dec, _ofb, _ctr];

        /**
         * AES MAC modes table (virual methods)
         */
        var _mac_modes = [_cbc_enc, _gcm_mac];

        /**
         * Asm.js module exports
         */
        return {
          set_rounds: set_rounds,
          set_state: set_state,
          set_iv: set_iv,
          set_nonce: set_nonce,
          set_mask: set_mask,
          set_counter: set_counter,
          get_state: get_state,
          get_iv: get_iv,
          gcm_init: gcm_init,
          cipher: cipher,
          mac: mac,
        };
      }(stdlib, foreign, buffer);

      asm.set_key = set_key;

      return asm;
    };

    /**
     * AES enciphering mode constants
     * @enum {number}
     * @const
     */
    wrapper.ENC = {
      ECB: 0,
      CBC: 2,
      CFB: 4,
      OFB: 6,
      CTR: 7,
    },

      /**
       * AES deciphering mode constants
       * @enum {number}
       * @const
       */
      wrapper.DEC = {
        ECB: 1,
        CBC: 3,
        CFB: 5,
        OFB: 6,
        CTR: 7,
      },

      /**
       * AES MAC mode constants
       * @enum {number}
       * @const
       */
      wrapper.MAC = {
        CBC: 0,
        GCM: 1,
      };

    /**
     * Heap data offset
     * @type {number}
     * @const
     */
    wrapper.HEAP_DATA = 0x4000;

    return wrapper;
  }();

  function is_bytes(a) {
      return a instanceof Uint8Array;
  }
  function _heap_init(heap, heapSize) {
      const size = heap ? heap.byteLength : heapSize || 65536;
      if (size & 0xfff || size <= 0)
          throw new Error('heap size must be a positive integer and a multiple of 4096');
      heap = heap || new Uint8Array(new ArrayBuffer(size));
      return heap;
  }
  function _heap_write(heap, hpos, data, dpos, dlen) {
      const hlen = heap.length - hpos;
      const wlen = hlen < dlen ? hlen : dlen;
      heap.set(data.subarray(dpos, dpos + wlen), hpos);
      return wlen;
  }
  function joinBytes(...arg) {
      const totalLenght = arg.reduce((sum, curr) => sum + curr.length, 0);
      const ret = new Uint8Array(totalLenght);
      let cursor = 0;
      for (let i = 0; i < arg.length; i++) {
          ret.set(arg[i], cursor);
          cursor += arg[i].length;
      }
      return ret;
  }

  class IllegalStateError extends Error {
      constructor(...args) {
          super(...args);
      }
  }
  class IllegalArgumentError extends Error {
      constructor(...args) {
          super(...args);
      }
  }
  class SecurityError extends Error {
      constructor(...args) {
          super(...args);
      }
  }

  const heap_pool = [];
  const asm_pool = [];
  class AES {
      constructor(key, iv, padding = true, mode, heap, asm) {
          this.pos = 0;
          this.len = 0;
          this.mode = mode;
          // The AES object state
          this.pos = 0;
          this.len = 0;
          this.key = key;
          this.iv = iv;
          this.padding = padding;
          // The AES "worker"
          this.acquire_asm(heap, asm);
      }
      acquire_asm(heap, asm) {
          if (this.heap === undefined || this.asm === undefined) {
              this.heap = heap || heap_pool.pop() || _heap_init().subarray(AES_asm.HEAP_DATA);
              this.asm = asm || asm_pool.pop() || new AES_asm(null, this.heap.buffer);
              this.reset(this.key, this.iv);
          }
          return { heap: this.heap, asm: this.asm };
      }
      release_asm() {
          if (this.heap !== undefined && this.asm !== undefined) {
              heap_pool.push(this.heap);
              asm_pool.push(this.asm);
          }
          this.heap = undefined;
          this.asm = undefined;
      }
      reset(key, iv) {
          const { asm } = this.acquire_asm();
          // Key
          const keylen = key.length;
          if (keylen !== 16 && keylen !== 24 && keylen !== 32)
              throw new IllegalArgumentError('illegal key size');
          const keyview = new DataView(key.buffer, key.byteOffset, key.byteLength);
          asm.set_key(keylen >> 2, keyview.getUint32(0), keyview.getUint32(4), keyview.getUint32(8), keyview.getUint32(12), keylen > 16 ? keyview.getUint32(16) : 0, keylen > 16 ? keyview.getUint32(20) : 0, keylen > 24 ? keyview.getUint32(24) : 0, keylen > 24 ? keyview.getUint32(28) : 0);
          // IV
          if (iv !== undefined) {
              if (iv.length !== 16)
                  throw new IllegalArgumentError('illegal iv size');
              let ivview = new DataView(iv.buffer, iv.byteOffset, iv.byteLength);
              asm.set_iv(ivview.getUint32(0), ivview.getUint32(4), ivview.getUint32(8), ivview.getUint32(12));
          }
          else {
              asm.set_iv(0, 0, 0, 0);
          }
      }
      AES_Encrypt_process(data) {
          if (!is_bytes(data))
              throw new TypeError("data isn't of expected type");
          let { heap, asm } = this.acquire_asm();
          let amode = AES_asm.ENC[this.mode];
          let hpos = AES_asm.HEAP_DATA;
          let pos = this.pos;
          let len = this.len;
          let dpos = 0;
          let dlen = data.length || 0;
          let rpos = 0;
          let rlen = (len + dlen) & -16;
          let wlen = 0;
          let result = new Uint8Array(rlen);
          while (dlen > 0) {
              wlen = _heap_write(heap, pos + len, data, dpos, dlen);
              len += wlen;
              dpos += wlen;
              dlen -= wlen;
              wlen = asm.cipher(amode, hpos + pos, len);
              if (wlen)
                  result.set(heap.subarray(pos, pos + wlen), rpos);
              rpos += wlen;
              if (wlen < len) {
                  pos += wlen;
                  len -= wlen;
              }
              else {
                  pos = 0;
                  len = 0;
              }
          }
          this.pos = pos;
          this.len = len;
          return result;
      }
      AES_Encrypt_finish() {
          let { heap, asm } = this.acquire_asm();
          let amode = AES_asm.ENC[this.mode];
          let hpos = AES_asm.HEAP_DATA;
          let pos = this.pos;
          let len = this.len;
          let plen = 16 - (len % 16);
          let rlen = len;
          if (this.hasOwnProperty('padding')) {
              if (this.padding) {
                  for (let p = 0; p < plen; ++p) {
                      heap[pos + len + p] = plen;
                  }
                  len += plen;
                  rlen = len;
              }
              else if (len % 16) {
                  throw new IllegalArgumentError('data length must be a multiple of the block size');
              }
          }
          else {
              len += plen;
          }
          const result = new Uint8Array(rlen);
          if (len)
              asm.cipher(amode, hpos + pos, len);
          if (rlen)
              result.set(heap.subarray(pos, pos + rlen));
          this.pos = 0;
          this.len = 0;
          this.release_asm();
          return result;
      }
      AES_Decrypt_process(data) {
          if (!is_bytes(data))
              throw new TypeError("data isn't of expected type");
          let { heap, asm } = this.acquire_asm();
          let amode = AES_asm.DEC[this.mode];
          let hpos = AES_asm.HEAP_DATA;
          let pos = this.pos;
          let len = this.len;
          let dpos = 0;
          let dlen = data.length || 0;
          let rpos = 0;
          let rlen = (len + dlen) & -16;
          let plen = 0;
          let wlen = 0;
          if (this.padding) {
              plen = len + dlen - rlen || 16;
              rlen -= plen;
          }
          const result = new Uint8Array(rlen);
          while (dlen > 0) {
              wlen = _heap_write(heap, pos + len, data, dpos, dlen);
              len += wlen;
              dpos += wlen;
              dlen -= wlen;
              wlen = asm.cipher(amode, hpos + pos, len - (!dlen ? plen : 0));
              if (wlen)
                  result.set(heap.subarray(pos, pos + wlen), rpos);
              rpos += wlen;
              if (wlen < len) {
                  pos += wlen;
                  len -= wlen;
              }
              else {
                  pos = 0;
                  len = 0;
              }
          }
          this.pos = pos;
          this.len = len;
          return result;
      }
      AES_Decrypt_finish() {
          let { heap, asm } = this.acquire_asm();
          let amode = AES_asm.DEC[this.mode];
          let hpos = AES_asm.HEAP_DATA;
          let pos = this.pos;
          let len = this.len;
          let rlen = len;
          if (len > 0) {
              if (len % 16) {
                  if (this.hasOwnProperty('padding')) {
                      throw new IllegalArgumentError('data length must be a multiple of the block size');
                  }
                  else {
                      len += 16 - (len % 16);
                  }
              }
              asm.cipher(amode, hpos + pos, len);
              if (this.hasOwnProperty('padding') && this.padding) {
                  let pad = heap[pos + rlen - 1];
                  if (pad < 1 || pad > 16 || pad > rlen)
                      throw new SecurityError('bad padding');
                  let pcheck = 0;
                  for (let i = pad; i > 1; i--)
                      pcheck |= pad ^ heap[pos + rlen - i];
                  if (pcheck)
                      throw new SecurityError('bad padding');
                  rlen -= pad;
              }
          }
          const result = new Uint8Array(rlen);
          if (rlen > 0) {
              result.set(heap.subarray(pos, pos + rlen));
          }
          this.pos = 0;
          this.len = 0;
          this.release_asm();
          return result;
      }
  }

  class AES_ECB {
      static encrypt(data, key, padding = false) {
          return new AES_ECB(key, padding).encrypt(data);
      }
      static decrypt(data, key, padding = false) {
          return new AES_ECB(key, padding).decrypt(data);
      }
      constructor(key, padding = false, aes) {
          this.aes = aes ? aes : new AES(key, undefined, padding, 'ECB');
      }
      encrypt(data) {
          const r1 = this.aes.AES_Encrypt_process(data);
          const r2 = this.aes.AES_Encrypt_finish();
          return joinBytes(r1, r2);
      }
      decrypt(data) {
          const r1 = this.aes.AES_Decrypt_process(data);
          const r2 = this.aes.AES_Decrypt_finish();
          return joinBytes(r1, r2);
      }
  }

  /**
   * Javascript AES implementation.
   * This is used as fallback if the native Crypto APIs are not available.
   */
  function aes(length) {
    const C = function(key) {
      const aesECB = new AES_ECB(key);

      this.encrypt = function(block) {
        return aesECB.encrypt(block);
      };

      this.decrypt = function(block) {
        return aesECB.decrypt(block);
      };
    };

    C.blockSize = C.prototype.blockSize = 16;
    C.keySize = C.prototype.keySize = length / 8;

    return C;
  }

  //Paul Tero, July 2001
  //http://www.tero.co.uk/des/
  //
  //Optimised for performance with large blocks by Michael Hayworth, November 2001
  //http://www.netdealing.com
  //
  // Modified by Recurity Labs GmbH

  //THIS SOFTWARE IS PROVIDED "AS IS" AND
  //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  //IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  //ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  //FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  //DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  //OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  //HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  //OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  //SUCH DAMAGE.

  //des
  //this takes the key, the message, and whether to encrypt or decrypt

  function des(keys, message, encrypt, mode, iv, padding) {
    //declaring this locally speeds things up a bit
    const spfunction1 = [
      0x1010400, 0, 0x10000, 0x1010404, 0x1010004, 0x10404, 0x4, 0x10000, 0x400, 0x1010400,
      0x1010404, 0x400, 0x1000404, 0x1010004, 0x1000000, 0x4, 0x404, 0x1000400, 0x1000400, 0x10400, 0x10400, 0x1010000,
      0x1010000, 0x1000404, 0x10004, 0x1000004, 0x1000004, 0x10004, 0, 0x404, 0x10404, 0x1000000, 0x10000, 0x1010404, 0x4,
      0x1010000, 0x1010400, 0x1000000, 0x1000000, 0x400, 0x1010004, 0x10000, 0x10400, 0x1000004, 0x400, 0x4, 0x1000404,
      0x10404, 0x1010404, 0x10004, 0x1010000, 0x1000404, 0x1000004, 0x404, 0x10404, 0x1010400, 0x404, 0x1000400,
      0x1000400, 0, 0x10004, 0x10400, 0, 0x1010004
    ];
    const spfunction2 = [
      -0x7fef7fe0, -0x7fff8000, 0x8000, 0x108020, 0x100000, 0x20, -0x7fefffe0, -0x7fff7fe0,
      -0x7fffffe0, -0x7fef7fe0, -0x7fef8000, -0x80000000, -0x7fff8000, 0x100000, 0x20, -0x7fefffe0, 0x108000, 0x100020,
      -0x7fff7fe0, 0, -0x80000000, 0x8000, 0x108020, -0x7ff00000, 0x100020, -0x7fffffe0, 0, 0x108000, 0x8020, -0x7fef8000,
      -0x7ff00000, 0x8020, 0, 0x108020, -0x7fefffe0, 0x100000, -0x7fff7fe0, -0x7ff00000, -0x7fef8000, 0x8000, -0x7ff00000,
      -0x7fff8000, 0x20, -0x7fef7fe0, 0x108020, 0x20, 0x8000, -0x80000000, 0x8020, -0x7fef8000, 0x100000, -0x7fffffe0,
      0x100020, -0x7fff7fe0, -0x7fffffe0, 0x100020, 0x108000, 0, -0x7fff8000, 0x8020, -0x80000000, -0x7fefffe0,
      -0x7fef7fe0, 0x108000
    ];
    const spfunction3 = [
      0x208, 0x8020200, 0, 0x8020008, 0x8000200, 0, 0x20208, 0x8000200, 0x20008, 0x8000008,
      0x8000008, 0x20000, 0x8020208, 0x20008, 0x8020000, 0x208, 0x8000000, 0x8, 0x8020200, 0x200, 0x20200, 0x8020000,
      0x8020008, 0x20208, 0x8000208, 0x20200, 0x20000, 0x8000208, 0x8, 0x8020208, 0x200, 0x8000000, 0x8020200, 0x8000000,
      0x20008, 0x208, 0x20000, 0x8020200, 0x8000200, 0, 0x200, 0x20008, 0x8020208, 0x8000200, 0x8000008, 0x200, 0,
      0x8020008, 0x8000208, 0x20000, 0x8000000, 0x8020208, 0x8, 0x20208, 0x20200, 0x8000008, 0x8020000, 0x8000208, 0x208,
      0x8020000, 0x20208, 0x8, 0x8020008, 0x20200
    ];
    const spfunction4 = [
      0x802001, 0x2081, 0x2081, 0x80, 0x802080, 0x800081, 0x800001, 0x2001, 0, 0x802000,
      0x802000, 0x802081, 0x81, 0, 0x800080, 0x800001, 0x1, 0x2000, 0x800000, 0x802001, 0x80, 0x800000, 0x2001, 0x2080,
      0x800081, 0x1, 0x2080, 0x800080, 0x2000, 0x802080, 0x802081, 0x81, 0x800080, 0x800001, 0x802000, 0x802081, 0x81, 0,
      0, 0x802000, 0x2080, 0x800080, 0x800081, 0x1, 0x802001, 0x2081, 0x2081, 0x80, 0x802081, 0x81, 0x1, 0x2000, 0x800001,
      0x2001, 0x802080, 0x800081, 0x2001, 0x2080, 0x800000, 0x802001, 0x80, 0x800000, 0x2000, 0x802080
    ];
    const spfunction5 = [
      0x100, 0x2080100, 0x2080000, 0x42000100, 0x80000, 0x100, 0x40000000, 0x2080000,
      0x40080100, 0x80000, 0x2000100, 0x40080100, 0x42000100, 0x42080000, 0x80100, 0x40000000, 0x2000000, 0x40080000,
      0x40080000, 0, 0x40000100, 0x42080100, 0x42080100, 0x2000100, 0x42080000, 0x40000100, 0, 0x42000000, 0x2080100,
      0x2000000, 0x42000000, 0x80100, 0x80000, 0x42000100, 0x100, 0x2000000, 0x40000000, 0x2080000, 0x42000100,
      0x40080100, 0x2000100, 0x40000000, 0x42080000, 0x2080100, 0x40080100, 0x100, 0x2000000, 0x42080000, 0x42080100,
      0x80100, 0x42000000, 0x42080100, 0x2080000, 0, 0x40080000, 0x42000000, 0x80100, 0x2000100, 0x40000100, 0x80000, 0,
      0x40080000, 0x2080100, 0x40000100
    ];
    const spfunction6 = [
      0x20000010, 0x20400000, 0x4000, 0x20404010, 0x20400000, 0x10, 0x20404010, 0x400000,
      0x20004000, 0x404010, 0x400000, 0x20000010, 0x400010, 0x20004000, 0x20000000, 0x4010, 0, 0x400010, 0x20004010,
      0x4000, 0x404000, 0x20004010, 0x10, 0x20400010, 0x20400010, 0, 0x404010, 0x20404000, 0x4010, 0x404000, 0x20404000,
      0x20000000, 0x20004000, 0x10, 0x20400010, 0x404000, 0x20404010, 0x400000, 0x4010, 0x20000010, 0x400000, 0x20004000,
      0x20000000, 0x4010, 0x20000010, 0x20404010, 0x404000, 0x20400000, 0x404010, 0x20404000, 0, 0x20400010, 0x10, 0x4000,
      0x20400000, 0x404010, 0x4000, 0x400010, 0x20004010, 0, 0x20404000, 0x20000000, 0x400010, 0x20004010
    ];
    const spfunction7 = [
      0x200000, 0x4200002, 0x4000802, 0, 0x800, 0x4000802, 0x200802, 0x4200800, 0x4200802,
      0x200000, 0, 0x4000002, 0x2, 0x4000000, 0x4200002, 0x802, 0x4000800, 0x200802, 0x200002, 0x4000800, 0x4000002,
      0x4200000, 0x4200800, 0x200002, 0x4200000, 0x800, 0x802, 0x4200802, 0x200800, 0x2, 0x4000000, 0x200800, 0x4000000,
      0x200800, 0x200000, 0x4000802, 0x4000802, 0x4200002, 0x4200002, 0x2, 0x200002, 0x4000000, 0x4000800, 0x200000,
      0x4200800, 0x802, 0x200802, 0x4200800, 0x802, 0x4000002, 0x4200802, 0x4200000, 0x200800, 0, 0x2, 0x4200802, 0,
      0x200802, 0x4200000, 0x800, 0x4000002, 0x4000800, 0x800, 0x200002
    ];
    const spfunction8 = [
      0x10001040, 0x1000, 0x40000, 0x10041040, 0x10000000, 0x10001040, 0x40, 0x10000000,
      0x40040, 0x10040000, 0x10041040, 0x41000, 0x10041000, 0x41040, 0x1000, 0x40, 0x10040000, 0x10000040, 0x10001000,
      0x1040, 0x41000, 0x40040, 0x10040040, 0x10041000, 0x1040, 0, 0, 0x10040040, 0x10000040, 0x10001000, 0x41040,
      0x40000, 0x41040, 0x40000, 0x10041000, 0x1000, 0x40, 0x10040040, 0x1000, 0x41040, 0x10001000, 0x40, 0x10000040,
      0x10040000, 0x10040040, 0x10000000, 0x40000, 0x10001040, 0, 0x10041040, 0x40040, 0x10000040, 0x10040000, 0x10001000,
      0x10001040, 0, 0x10041040, 0x41000, 0x41000, 0x1040, 0x1040, 0x40040, 0x10000000, 0x10041000
    ];

    //create the 16 or 48 subkeys we will need
    let m = 0;
    let i;
    let j;
    let temp;
    let right1;
    let right2;
    let left;
    let right;
    let looping;
    let cbcleft;
    let cbcleft2;
    let cbcright;
    let cbcright2;
    let endloop;
    let loopinc;
    let len = message.length;

    //set up the loops for single and triple des
    const iterations = keys.length === 32 ? 3 : 9; //single or triple des
    if (iterations === 3) {
      looping = encrypt ? [0, 32, 2] : [30, -2, -2];
    } else {
      looping = encrypt ? [0, 32, 2, 62, 30, -2, 64, 96, 2] : [94, 62, -2, 32, 64, 2, 30, -2, -2];
    }

    //pad the message depending on the padding parameter
    //only add padding if encrypting - note that you need to use the same padding option for both encrypt and decrypt
    if (encrypt) {
      message = desAddPadding(message, padding);
      len = message.length;
    }

    //store the result here
    let result = new Uint8Array(len);
    let k = 0;

    if (mode === 1) { //CBC mode
      cbcleft = (iv[m++] << 24) | (iv[m++] << 16) | (iv[m++] << 8) | iv[m++];
      cbcright = (iv[m++] << 24) | (iv[m++] << 16) | (iv[m++] << 8) | iv[m++];
      m = 0;
    }

    //loop through each 64 bit chunk of the message
    while (m < len) {
      left = (message[m++] << 24) | (message[m++] << 16) | (message[m++] << 8) | message[m++];
      right = (message[m++] << 24) | (message[m++] << 16) | (message[m++] << 8) | message[m++];

      //for Cipher Block Chaining mode, xor the message with the previous result
      if (mode === 1) {
        if (encrypt) {
          left ^= cbcleft;
          right ^= cbcright;
        } else {
          cbcleft2 = cbcleft;
          cbcright2 = cbcright;
          cbcleft = left;
          cbcright = right;
        }
      }

      //first each 64 but chunk of the message must be permuted according to IP
      temp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
      right ^= temp;
      left ^= (temp << 4);
      temp = ((left >>> 16) ^ right) & 0x0000ffff;
      right ^= temp;
      left ^= (temp << 16);
      temp = ((right >>> 2) ^ left) & 0x33333333;
      left ^= temp;
      right ^= (temp << 2);
      temp = ((right >>> 8) ^ left) & 0x00ff00ff;
      left ^= temp;
      right ^= (temp << 8);
      temp = ((left >>> 1) ^ right) & 0x55555555;
      right ^= temp;
      left ^= (temp << 1);

      left = ((left << 1) | (left >>> 31));
      right = ((right << 1) | (right >>> 31));

      //do this either 1 or 3 times for each chunk of the message
      for (j = 0; j < iterations; j += 3) {
        endloop = looping[j + 1];
        loopinc = looping[j + 2];
        //now go through and perform the encryption or decryption
        for (i = looping[j]; i !== endloop; i += loopinc) { //for efficiency
          right1 = right ^ keys[i];
          right2 = ((right >>> 4) | (right << 28)) ^ keys[i + 1];
          //the result is attained by passing these bytes through the S selection functions
          temp = left;
          left = right;
          right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f] | spfunction6[(right1 >>>
            8) & 0x3f] | spfunction8[right1 & 0x3f] | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) &
            0x3f] | spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]);
        }
        temp = left;
        left = right;
        right = temp; //unreverse left and right
      } //for either 1 or 3 iterations

      //move then each one bit to the right
      left = ((left >>> 1) | (left << 31));
      right = ((right >>> 1) | (right << 31));

      //now perform IP-1, which is IP in the opposite direction
      temp = ((left >>> 1) ^ right) & 0x55555555;
      right ^= temp;
      left ^= (temp << 1);
      temp = ((right >>> 8) ^ left) & 0x00ff00ff;
      left ^= temp;
      right ^= (temp << 8);
      temp = ((right >>> 2) ^ left) & 0x33333333;
      left ^= temp;
      right ^= (temp << 2);
      temp = ((left >>> 16) ^ right) & 0x0000ffff;
      right ^= temp;
      left ^= (temp << 16);
      temp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
      right ^= temp;
      left ^= (temp << 4);

      //for Cipher Block Chaining mode, xor the message with the previous result
      if (mode === 1) {
        if (encrypt) {
          cbcleft = left;
          cbcright = right;
        } else {
          left ^= cbcleft2;
          right ^= cbcright2;
        }
      }

      result[k++] = (left >>> 24);
      result[k++] = ((left >>> 16) & 0xff);
      result[k++] = ((left >>> 8) & 0xff);
      result[k++] = (left & 0xff);
      result[k++] = (right >>> 24);
      result[k++] = ((right >>> 16) & 0xff);
      result[k++] = ((right >>> 8) & 0xff);
      result[k++] = (right & 0xff);
    } //for every 8 characters, or 64 bits in the message

    //only remove padding if decrypting - note that you need to use the same padding option for both encrypt and decrypt
    if (!encrypt) {
      result = desRemovePadding(result, padding);
    }

    return result;
  } //end of des


  //desCreateKeys
  //this takes as input a 64 bit key (even though only 56 bits are used)
  //as an array of 2 integers, and returns 16 48 bit keys

  function desCreateKeys(key) {
    //declaring this locally speeds things up a bit
    const pc2bytes0 = [
      0, 0x4, 0x20000000, 0x20000004, 0x10000, 0x10004, 0x20010000, 0x20010004, 0x200, 0x204,
      0x20000200, 0x20000204, 0x10200, 0x10204, 0x20010200, 0x20010204
    ];
    const pc2bytes1 = [
      0, 0x1, 0x100000, 0x100001, 0x4000000, 0x4000001, 0x4100000, 0x4100001, 0x100, 0x101, 0x100100,
      0x100101, 0x4000100, 0x4000101, 0x4100100, 0x4100101
    ];
    const pc2bytes2 = [
      0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808, 0, 0x8, 0x800, 0x808,
      0x1000000, 0x1000008, 0x1000800, 0x1000808
    ];
    const pc2bytes3 = [
      0, 0x200000, 0x8000000, 0x8200000, 0x2000, 0x202000, 0x8002000, 0x8202000, 0x20000, 0x220000,
      0x8020000, 0x8220000, 0x22000, 0x222000, 0x8022000, 0x8222000
    ];
    const pc2bytes4 = [
      0, 0x40000, 0x10, 0x40010, 0, 0x40000, 0x10, 0x40010, 0x1000, 0x41000, 0x1010, 0x41010, 0x1000,
      0x41000, 0x1010, 0x41010
    ];
    const pc2bytes5 = [
      0, 0x400, 0x20, 0x420, 0, 0x400, 0x20, 0x420, 0x2000000, 0x2000400, 0x2000020, 0x2000420,
      0x2000000, 0x2000400, 0x2000020, 0x2000420
    ];
    const pc2bytes6 = [
      0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002, 0, 0x10000000,
      0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002
    ];
    const pc2bytes7 = [
      0, 0x10000, 0x800, 0x10800, 0x20000000, 0x20010000, 0x20000800, 0x20010800, 0x20000, 0x30000,
      0x20800, 0x30800, 0x20020000, 0x20030000, 0x20020800, 0x20030800
    ];
    const pc2bytes8 = [
      0, 0x40000, 0, 0x40000, 0x2, 0x40002, 0x2, 0x40002, 0x2000000, 0x2040000, 0x2000000, 0x2040000,
      0x2000002, 0x2040002, 0x2000002, 0x2040002
    ];
    const pc2bytes9 = [
      0, 0x10000000, 0x8, 0x10000008, 0, 0x10000000, 0x8, 0x10000008, 0x400, 0x10000400, 0x408,
      0x10000408, 0x400, 0x10000400, 0x408, 0x10000408
    ];
    const pc2bytes10 = [
      0, 0x20, 0, 0x20, 0x100000, 0x100020, 0x100000, 0x100020, 0x2000, 0x2020, 0x2000, 0x2020,
      0x102000, 0x102020, 0x102000, 0x102020
    ];
    const pc2bytes11 = [
      0, 0x1000000, 0x200, 0x1000200, 0x200000, 0x1200000, 0x200200, 0x1200200, 0x4000000, 0x5000000,
      0x4000200, 0x5000200, 0x4200000, 0x5200000, 0x4200200, 0x5200200
    ];
    const pc2bytes12 = [
      0, 0x1000, 0x8000000, 0x8001000, 0x80000, 0x81000, 0x8080000, 0x8081000, 0x10, 0x1010,
      0x8000010, 0x8001010, 0x80010, 0x81010, 0x8080010, 0x8081010
    ];
    const pc2bytes13 = [0, 0x4, 0x100, 0x104, 0, 0x4, 0x100, 0x104, 0x1, 0x5, 0x101, 0x105, 0x1, 0x5, 0x101, 0x105];

    //how many iterations (1 for des, 3 for triple des)
    const iterations = key.length > 8 ? 3 : 1; //changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys
    //stores the return keys
    const keys = new Array(32 * iterations);
    //now define the left shifts which need to be done
    const shifts = [0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0];
    //other variables
    let lefttemp;
    let righttemp;
    let m = 0;
    let n = 0;
    let temp;

    for (let j = 0; j < iterations; j++) { //either 1 or 3 iterations
      let left = (key[m++] << 24) | (key[m++] << 16) | (key[m++] << 8) | key[m++];
      let right = (key[m++] << 24) | (key[m++] << 16) | (key[m++] << 8) | key[m++];

      temp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
      right ^= temp;
      left ^= (temp << 4);
      temp = ((right >>> -16) ^ left) & 0x0000ffff;
      left ^= temp;
      right ^= (temp << -16);
      temp = ((left >>> 2) ^ right) & 0x33333333;
      right ^= temp;
      left ^= (temp << 2);
      temp = ((right >>> -16) ^ left) & 0x0000ffff;
      left ^= temp;
      right ^= (temp << -16);
      temp = ((left >>> 1) ^ right) & 0x55555555;
      right ^= temp;
      left ^= (temp << 1);
      temp = ((right >>> 8) ^ left) & 0x00ff00ff;
      left ^= temp;
      right ^= (temp << 8);
      temp = ((left >>> 1) ^ right) & 0x55555555;
      right ^= temp;
      left ^= (temp << 1);

      //the right side needs to be shifted and to get the last four bits of the left side
      temp = (left << 8) | ((right >>> 20) & 0x000000f0);
      //left needs to be put upside down
      left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0);
      right = temp;

      //now go through and perform these shifts on the left and right keys
      for (let i = 0; i < shifts.length; i++) {
        //shift the keys either one or two bits to the left
        if (shifts[i]) {
          left = (left << 2) | (left >>> 26);
          right = (right << 2) | (right >>> 26);
        } else {
          left = (left << 1) | (left >>> 27);
          right = (right << 1) | (right >>> 27);
        }
        left &= -0xf;
        right &= -0xf;

        //now apply PC-2, in such a way that E is easier when encrypting or decrypting
        //this conversion will look like PC-2 except only the last 6 bits of each byte are used
        //rather than 48 consecutive bits and the order of lines will be according to
        //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7
        lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf] | pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(
          left >>> 16) & 0xf] | pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf] | pc2bytes6[(left >>> 4) &
          0xf];
        righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf] | pc2bytes9[(right >>> 20) & 0xf] |
          pc2bytes10[(right >>> 16) & 0xf] | pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf] |
          pc2bytes13[(right >>> 4) & 0xf];
        temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff;
        keys[n++] = lefttemp ^ temp;
        keys[n++] = righttemp ^ (temp << 16);
      }
    } //for each iterations
    //return the keys we've created
    return keys;
  } //end of desCreateKeys


  function desAddPadding(message, padding) {
    const padLength = 8 - (message.length % 8);

    let pad;
    if (padding === 2 && (padLength < 8)) { //pad the message with spaces
      pad = ' '.charCodeAt(0);
    } else if (padding === 1) { //PKCS7 padding
      pad = padLength;
    } else if (!padding && (padLength < 8)) { //pad the message out with null bytes
      pad = 0;
    } else if (padLength === 8) {
      return message;
    } else {
      throw new Error('des: invalid padding');
    }

    const paddedMessage = new Uint8Array(message.length + padLength);
    for (let i = 0; i < message.length; i++) {
      paddedMessage[i] = message[i];
    }
    for (let j = 0; j < padLength; j++) {
      paddedMessage[message.length + j] = pad;
    }

    return paddedMessage;
  }

  function desRemovePadding(message, padding) {
    let padLength = null;
    let pad;
    if (padding === 2) { // space padded
      pad = ' '.charCodeAt(0);
    } else if (padding === 1) { // PKCS7
      padLength = message[message.length - 1];
    } else if (!padding) { // null padding
      pad = 0;
    } else {
      throw new Error('des: invalid padding');
    }

    if (!padLength) {
      padLength = 1;
      while (message[message.length - padLength] === pad) {
        padLength++;
      }
      padLength--;
    }

    return message.subarray(0, message.length - padLength);
  }

  // added by Recurity Labs

  function TripleDES(key) {
    this.key = [];

    for (let i = 0; i < 3; i++) {
      this.key.push(new Uint8Array(key.subarray(i * 8, (i * 8) + 8)));
    }

    this.encrypt = function(block) {
      return des(
        desCreateKeys(this.key[2]),
        des(
          desCreateKeys(this.key[1]),
          des(
            desCreateKeys(this.key[0]),
            block, true, 0, null, null
          ),
          false, 0, null, null
        ), true, 0, null, null
      );
    };
  }

  TripleDES.keySize = TripleDES.prototype.keySize = 24;
  TripleDES.blockSize = TripleDES.prototype.blockSize = 8;

  // This is "original" DES

  function DES(key) {
    this.key = key;

    this.encrypt = function(block, padding) {
      const keys = desCreateKeys(this.key);
      return des(keys, block, true, 0, null, padding);
    };

    this.decrypt = function(block, padding) {
      const keys = desCreateKeys(this.key);
      return des(keys, block, false, 0, null, padding);
    };
  }

  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.

  // Copyright 2010 pjacobs@xeekr.com . All rights reserved.

  // Modified by Recurity Labs GmbH

  // fixed/modified by Herbert Hanewinkel, www.haneWIN.de
  // check www.haneWIN.de for the latest version

  // cast5.js is a Javascript implementation of CAST-128, as defined in RFC 2144.
  // CAST-128 is a common OpenPGP cipher.


  // CAST5 constructor

  function OpenPGPSymEncCAST5() {
    this.BlockSize = 8;
    this.KeySize = 16;

    this.setKey = function(key) {
      this.masking = new Array(16);
      this.rotate = new Array(16);

      this.reset();

      if (key.length === this.KeySize) {
        this.keySchedule(key);
      } else {
        throw new Error('CAST-128: keys must be 16 bytes');
      }
      return true;
    };

    this.reset = function() {
      for (let i = 0; i < 16; i++) {
        this.masking[i] = 0;
        this.rotate[i] = 0;
      }
    };

    this.getBlockSize = function() {
      return this.BlockSize;
    };

    this.encrypt = function(src) {
      const dst = new Array(src.length);

      for (let i = 0; i < src.length; i += 8) {
        let l = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
        let r = (src[i + 4] << 24) | (src[i + 5] << 16) | (src[i + 6] << 8) | src[i + 7];
        let t;

        t = r;
        r = l ^ f1(r, this.masking[0], this.rotate[0]);
        l = t;
        t = r;
        r = l ^ f2(r, this.masking[1], this.rotate[1]);
        l = t;
        t = r;
        r = l ^ f3(r, this.masking[2], this.rotate[2]);
        l = t;
        t = r;
        r = l ^ f1(r, this.masking[3], this.rotate[3]);
        l = t;

        t = r;
        r = l ^ f2(r, this.masking[4], this.rotate[4]);
        l = t;
        t = r;
        r = l ^ f3(r, this.masking[5], this.rotate[5]);
        l = t;
        t = r;
        r = l ^ f1(r, this.masking[6], this.rotate[6]);
        l = t;
        t = r;
        r = l ^ f2(r, this.masking[7], this.rotate[7]);
        l = t;

        t = r;
        r = l ^ f3(r, this.masking[8], this.rotate[8]);
        l = t;
        t = r;
        r = l ^ f1(r, this.masking[9], this.rotate[9]);
        l = t;
        t = r;
        r = l ^ f2(r, this.masking[10], this.rotate[10]);
        l = t;
        t = r;
        r = l ^ f3(r, this.masking[11], this.rotate[11]);
        l = t;

        t = r;
        r = l ^ f1(r, this.masking[12], this.rotate[12]);
        l = t;
        t = r;
        r = l ^ f2(r, this.masking[13], this.rotate[13]);
        l = t;
        t = r;
        r = l ^ f3(r, this.masking[14], this.rotate[14]);
        l = t;
        t = r;
        r = l ^ f1(r, this.masking[15], this.rotate[15]);
        l = t;

        dst[i] = (r >>> 24) & 255;
        dst[i + 1] = (r >>> 16) & 255;
        dst[i + 2] = (r >>> 8) & 255;
        dst[i + 3] = r & 255;
        dst[i + 4] = (l >>> 24) & 255;
        dst[i + 5] = (l >>> 16) & 255;
        dst[i + 6] = (l >>> 8) & 255;
        dst[i + 7] = l & 255;
      }

      return dst;
    };

    this.decrypt = function(src) {
      const dst = new Array(src.length);

      for (let i = 0; i < src.length; i += 8) {
        let l = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
        let r = (src[i + 4] << 24) | (src[i + 5] << 16) | (src[i + 6] << 8) | src[i + 7];
        let t;

        t = r;
        r = l ^ f1(r, this.masking[15], this.rotate[15]);
        l = t;
        t = r;
        r = l ^ f3(r, this.masking[14], this.rotate[14]);
        l = t;
        t = r;
        r = l ^ f2(r, this.masking[13], this.rotate[13]);
        l = t;
        t = r;
        r = l ^ f1(r, this.masking[12], this.rotate[12]);
        l = t;

        t = r;
        r = l ^ f3(r, this.masking[11], this.rotate[11]);
        l = t;
        t = r;
        r = l ^ f2(r, this.masking[10], this.rotate[10]);
        l = t;
        t = r;
        r = l ^ f1(r, this.masking[9], this.rotate[9]);
        l = t;
        t = r;
        r = l ^ f3(r, this.masking[8], this.rotate[8]);
        l = t;

        t = r;
        r = l ^ f2(r, this.masking[7], this.rotate[7]);
        l = t;
        t = r;
        r = l ^ f1(r, this.masking[6], this.rotate[6]);
        l = t;
        t = r;
        r = l ^ f3(r, this.masking[5], this.rotate[5]);
        l = t;
        t = r;
        r = l ^ f2(r, this.masking[4], this.rotate[4]);
        l = t;

        t = r;
        r = l ^ f1(r, this.masking[3], this.rotate[3]);
        l = t;
        t = r;
        r = l ^ f3(r, this.masking[2], this.rotate[2]);
        l = t;
        t = r;
        r = l ^ f2(r, this.masking[1], this.rotate[1]);
        l = t;
        t = r;
        r = l ^ f1(r, this.masking[0], this.rotate[0]);
        l = t;

        dst[i] = (r >>> 24) & 255;
        dst[i + 1] = (r >>> 16) & 255;
        dst[i + 2] = (r >>> 8) & 255;
        dst[i + 3] = r & 255;
        dst[i + 4] = (l >>> 24) & 255;
        dst[i + 5] = (l >> 16) & 255;
        dst[i + 6] = (l >> 8) & 255;
        dst[i + 7] = l & 255;
      }

      return dst;
    };
    const scheduleA = new Array(4);

    scheduleA[0] = new Array(4);
    scheduleA[0][0] = [4, 0, 0xd, 0xf, 0xc, 0xe, 0x8];
    scheduleA[0][1] = [5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa];
    scheduleA[0][2] = [6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9];
    scheduleA[0][3] = [7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb];

    scheduleA[1] = new Array(4);
    scheduleA[1][0] = [0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0];
    scheduleA[1][1] = [1, 4, 0, 2, 1, 3, 16 + 2];
    scheduleA[1][2] = [2, 5, 7, 6, 5, 4, 16 + 1];
    scheduleA[1][3] = [3, 7, 0xa, 9, 0xb, 8, 16 + 3];

    scheduleA[2] = new Array(4);
    scheduleA[2][0] = [4, 0, 0xd, 0xf, 0xc, 0xe, 8];
    scheduleA[2][1] = [5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa];
    scheduleA[2][2] = [6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9];
    scheduleA[2][3] = [7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb];


    scheduleA[3] = new Array(4);
    scheduleA[3][0] = [0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0];
    scheduleA[3][1] = [1, 4, 0, 2, 1, 3, 16 + 2];
    scheduleA[3][2] = [2, 5, 7, 6, 5, 4, 16 + 1];
    scheduleA[3][3] = [3, 7, 0xa, 9, 0xb, 8, 16 + 3];

    const scheduleB = new Array(4);

    scheduleB[0] = new Array(4);
    scheduleB[0][0] = [16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2];
    scheduleB[0][1] = [16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6];
    scheduleB[0][2] = [16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9];
    scheduleB[0][3] = [16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc];

    scheduleB[1] = new Array(4);
    scheduleB[1][0] = [3, 2, 0xc, 0xd, 8];
    scheduleB[1][1] = [1, 0, 0xe, 0xf, 0xd];
    scheduleB[1][2] = [7, 6, 8, 9, 3];
    scheduleB[1][3] = [5, 4, 0xa, 0xb, 7];


    scheduleB[2] = new Array(4);
    scheduleB[2][0] = [16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9];
    scheduleB[2][1] = [16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc];
    scheduleB[2][2] = [16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2];
    scheduleB[2][3] = [16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6];


    scheduleB[3] = new Array(4);
    scheduleB[3][0] = [8, 9, 7, 6, 3];
    scheduleB[3][1] = [0xa, 0xb, 5, 4, 7];
    scheduleB[3][2] = [0xc, 0xd, 3, 2, 8];
    scheduleB[3][3] = [0xe, 0xf, 1, 0, 0xd];

    // changed 'in' to 'inn' (in javascript 'in' is a reserved word)
    this.keySchedule = function(inn) {
      const t = new Array(8);
      const k = new Array(32);

      let j;

      for (let i = 0; i < 4; i++) {
        j = i * 4;
        t[i] = (inn[j] << 24) | (inn[j + 1] << 16) | (inn[j + 2] << 8) | inn[j + 3];
      }

      const x = [6, 7, 4, 5];
      let ki = 0;
      let w;

      for (let half = 0; half < 2; half++) {
        for (let round = 0; round < 4; round++) {
          for (j = 0; j < 4; j++) {
            const a = scheduleA[round][j];
            w = t[a[1]];

            w ^= sBox[4][(t[a[2] >>> 2] >>> (24 - 8 * (a[2] & 3))) & 0xff];
            w ^= sBox[5][(t[a[3] >>> 2] >>> (24 - 8 * (a[3] & 3))) & 0xff];
            w ^= sBox[6][(t[a[4] >>> 2] >>> (24 - 8 * (a[4] & 3))) & 0xff];
            w ^= sBox[7][(t[a[5] >>> 2] >>> (24 - 8 * (a[5] & 3))) & 0xff];
            w ^= sBox[x[j]][(t[a[6] >>> 2] >>> (24 - 8 * (a[6] & 3))) & 0xff];
            t[a[0]] = w;
          }

          for (j = 0; j < 4; j++) {
            const b = scheduleB[round][j];
            w = sBox[4][(t[b[0] >>> 2] >>> (24 - 8 * (b[0] & 3))) & 0xff];

            w ^= sBox[5][(t[b[1] >>> 2] >>> (24 - 8 * (b[1] & 3))) & 0xff];
            w ^= sBox[6][(t[b[2] >>> 2] >>> (24 - 8 * (b[2] & 3))) & 0xff];
            w ^= sBox[7][(t[b[3] >>> 2] >>> (24 - 8 * (b[3] & 3))) & 0xff];
            w ^= sBox[4 + j][(t[b[4] >>> 2] >>> (24 - 8 * (b[4] & 3))) & 0xff];
            k[ki] = w;
            ki++;
          }
        }
      }

      for (let i = 0; i < 16; i++) {
        this.masking[i] = k[i];
        this.rotate[i] = k[16 + i] & 0x1f;
      }
    };

    // These are the three 'f' functions. See RFC 2144, section 2.2.

    function f1(d, m, r) {
      const t = m + d;
      const I = (t << r) | (t >>> (32 - r));
      return ((sBox[0][I >>> 24] ^ sBox[1][(I >>> 16) & 255]) - sBox[2][(I >>> 8) & 255]) + sBox[3][I & 255];
    }

    function f2(d, m, r) {
      const t = m ^ d;
      const I = (t << r) | (t >>> (32 - r));
      return ((sBox[0][I >>> 24] - sBox[1][(I >>> 16) & 255]) + sBox[2][(I >>> 8) & 255]) ^ sBox[3][I & 255];
    }

    function f3(d, m, r) {
      const t = m - d;
      const I = (t << r) | (t >>> (32 - r));
      return ((sBox[0][I >>> 24] + sBox[1][(I >>> 16) & 255]) ^ sBox[2][(I >>> 8) & 255]) - sBox[3][I & 255];
    }

    const sBox = new Array(8);
    sBox[0] = [
      0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
      0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
      0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
      0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0,
      0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7,
      0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
      0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d,
      0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
      0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
      0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3,
      0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167,
      0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
      0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779,
      0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2,
      0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
      0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d,
      0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5,
      0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
      0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
      0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,
      0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
      0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96,
      0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,
      0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
      0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
      0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,
      0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
      0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872,
      0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,
      0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
      0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9,
      0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf
    ];

    sBox[1] = [
      0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651,
      0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
      0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
      0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
      0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b,
      0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
      0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b,
      0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c,
      0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
      0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb,
      0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd,
      0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
      0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b,
      0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
      0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
      0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
      0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c,
      0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
      0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f,
      0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
      0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
      0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58,
      0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
      0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
      0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6,
      0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
      0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
      0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f,
      0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
      0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
      0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9,
      0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1
    ];

    sBox[2] = [
      0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90,
      0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5,
      0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
      0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240,
      0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
      0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
      0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71,
      0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
      0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
      0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15,
      0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2,
      0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
      0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148,
      0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc,
      0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
      0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e,
      0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
      0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
      0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a,
      0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b,
      0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
      0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
      0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
      0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
      0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
      0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
      0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
      0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
      0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49,
      0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
      0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a,
      0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783
    ];

    sBox[3] = [
      0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1,
      0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf,
      0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
      0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121,
      0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25,
      0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
      0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb,
      0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5,
      0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
      0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6,
      0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23,
      0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
      0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
      0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119,
      0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
      0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a,
      0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79,
      0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
      0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
      0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab,
      0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
      0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
      0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2,
      0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
      0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
      0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919,
      0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
      0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
      0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab,
      0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
      0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282,
      0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2
    ];

    sBox[4] = [
      0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f,
      0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a,
      0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
      0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02,
      0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a,
      0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
      0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9,
      0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981,
      0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
      0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655,
      0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2,
      0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
      0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1,
      0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da,
      0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
      0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f,
      0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba,
      0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
      0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3,
      0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840,
      0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
      0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2,
      0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7,
      0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
      0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e,
      0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e,
      0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
      0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad,
      0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0,
      0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
      0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8,
      0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4
    ];

    sBox[5] = [
      0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac,
      0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138,
      0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
      0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98,
      0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072,
      0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
      0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd,
      0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8,
      0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
      0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54,
      0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387,
      0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
      0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf,
      0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf,
      0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
      0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289,
      0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950,
      0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
      0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b,
      0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be,
      0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
      0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976,
      0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0,
      0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
      0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da,
      0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc,
      0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
      0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25,
      0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121,
      0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
      0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd,
      0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f
    ];

    sBox[6] = [
      0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f,
      0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de,
      0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
      0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19,
      0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2,
      0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
      0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88,
      0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816,
      0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
      0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a,
      0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264,
      0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
      0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28,
      0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3,
      0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
      0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06,
      0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033,
      0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
      0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566,
      0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509,
      0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
      0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e,
      0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c,
      0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
      0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285,
      0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301,
      0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
      0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767,
      0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647,
      0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
      0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c,
      0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3
    ];

    sBox[7] = [
      0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5,
      0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc,
      0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
      0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d,
      0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2,
      0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
      0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc,
      0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c,
      0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
      0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039,
      0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8,
      0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
      0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5,
      0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472,
      0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
      0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c,
      0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb,
      0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
      0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70,
      0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc,
      0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
      0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3,
      0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4,
      0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
      0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f,
      0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e,
      0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
      0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c,
      0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384,
      0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
      0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82,
      0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e
    ];
  }

  function CAST5(key) {
    this.cast5 = new OpenPGPSymEncCAST5();
    this.cast5.setKey(key);

    this.encrypt = function(block) {
      return this.cast5.encrypt(block);
    };
  }

  CAST5.blockSize = CAST5.prototype.blockSize = 8;
  CAST5.keySize = CAST5.prototype.keySize = 16;

  /* eslint-disable no-mixed-operators, no-fallthrough */


  /* Modified by Recurity Labs GmbH
   *
   * Cipher.js
   * A block-cipher algorithm implementation on JavaScript
   * See Cipher.readme.txt for further information.
   *
   * Copyright(c) 2009 Atsushi Oka [ http://oka.nu/ ]
   * This script file is distributed under the LGPL
   *
   * ACKNOWLEDGMENT
   *
   *     The main subroutines are written by Michiel van Everdingen.
   *
   *     Michiel van Everdingen
   *     http://home.versatel.nl/MAvanEverdingen/index.html
   *
   *     All rights for these routines are reserved to Michiel van Everdingen.
   *
   */

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //Math
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  const MAXINT = 0xFFFFFFFF;

  function rotw(w, n) {
    return (w << n | w >>> (32 - n)) & MAXINT;
  }

  function getW(a, i) {
    return a[i] | a[i + 1] << 8 | a[i + 2] << 16 | a[i + 3] << 24;
  }

  function setW(a, i, w) {
    a.splice(i, 4, w & 0xFF, (w >>> 8) & 0xFF, (w >>> 16) & 0xFF, (w >>> 24) & 0xFF);
  }

  function getB(x, n) {
    return (x >>> (n * 8)) & 0xFF;
  }

  // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // Twofish
  // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  function createTwofish() {
    //
    let keyBytes = null;
    let dataBytes = null;
    let dataOffset = -1;
    // var dataLength = -1;
    // var idx2 = -1;
    //

    let tfsKey = [];
    let tfsM = [
      [],
      [],
      [],
      []
    ];

    function tfsInit(key) {
      keyBytes = key;
      let i;
      let a;
      let b;
      let c;
      let d;
      const meKey = [];
      const moKey = [];
      const inKey = [];
      let kLen;
      const sKey = [];
      let f01;
      let f5b;
      let fef;

      const q0 = [
        [8, 1, 7, 13, 6, 15, 3, 2, 0, 11, 5, 9, 14, 12, 10, 4],
        [2, 8, 11, 13, 15, 7, 6, 14, 3, 1, 9, 4, 0, 10, 12, 5]
      ];
      const q1 = [
        [14, 12, 11, 8, 1, 2, 3, 5, 15, 4, 10, 6, 7, 0, 9, 13],
        [1, 14, 2, 11, 4, 12, 3, 7, 6, 13, 10, 5, 15, 9, 0, 8]
      ];
      const q2 = [
        [11, 10, 5, 14, 6, 13, 9, 0, 12, 8, 15, 3, 2, 4, 7, 1],
        [4, 12, 7, 5, 1, 6, 9, 10, 0, 14, 13, 8, 2, 11, 3, 15]
      ];
      const q3 = [
        [13, 7, 15, 4, 1, 2, 6, 14, 9, 11, 3, 0, 8, 5, 12, 10],
        [11, 9, 5, 1, 12, 3, 13, 14, 6, 4, 7, 15, 2, 0, 8, 10]
      ];
      const ror4 = [0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15];
      const ashx = [0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 5, 14, 7];
      const q = [
        [],
        []
      ];
      const m = [
        [],
        [],
        [],
        []
      ];

      function ffm5b(x) {
        return x ^ (x >> 2) ^ [0, 90, 180, 238][x & 3];
      }

      function ffmEf(x) {
        return x ^ (x >> 1) ^ (x >> 2) ^ [0, 238, 180, 90][x & 3];
      }

      function mdsRem(p, q) {
        let i;
        let t;
        let u;
        for (i = 0; i < 8; i++) {
          t = q >>> 24;
          q = ((q << 8) & MAXINT) | p >>> 24;
          p = (p << 8) & MAXINT;
          u = t << 1;
          if (t & 128) {
            u ^= 333;
          }
          q ^= t ^ (u << 16);
          u ^= t >>> 1;
          if (t & 1) {
            u ^= 166;
          }
          q ^= u << 24 | u << 8;
        }
        return q;
      }

      function qp(n, x) {
        const a = x >> 4;
        const b = x & 15;
        const c = q0[n][a ^ b];
        const d = q1[n][ror4[b] ^ ashx[a]];
        return q3[n][ror4[d] ^ ashx[c]] << 4 | q2[n][c ^ d];
      }

      function hFun(x, key) {
        let a = getB(x, 0);
        let b = getB(x, 1);
        let c = getB(x, 2);
        let d = getB(x, 3);
        switch (kLen) {
          case 4:
            a = q[1][a] ^ getB(key[3], 0);
            b = q[0][b] ^ getB(key[3], 1);
            c = q[0][c] ^ getB(key[3], 2);
            d = q[1][d] ^ getB(key[3], 3);
          case 3:
            a = q[1][a] ^ getB(key[2], 0);
            b = q[1][b] ^ getB(key[2], 1);
            c = q[0][c] ^ getB(key[2], 2);
            d = q[0][d] ^ getB(key[2], 3);
          case 2:
            a = q[0][q[0][a] ^ getB(key[1], 0)] ^ getB(key[0], 0);
            b = q[0][q[1][b] ^ getB(key[1], 1)] ^ getB(key[0], 1);
            c = q[1][q[0][c] ^ getB(key[1], 2)] ^ getB(key[0], 2);
            d = q[1][q[1][d] ^ getB(key[1], 3)] ^ getB(key[0], 3);
        }
        return m[0][a] ^ m[1][b] ^ m[2][c] ^ m[3][d];
      }

      keyBytes = keyBytes.slice(0, 32);
      i = keyBytes.length;
      while (i !== 16 && i !== 24 && i !== 32) {
        keyBytes[i++] = 0;
      }

      for (i = 0; i < keyBytes.length; i += 4) {
        inKey[i >> 2] = getW(keyBytes, i);
      }
      for (i = 0; i < 256; i++) {
        q[0][i] = qp(0, i);
        q[1][i] = qp(1, i);
      }
      for (i = 0; i < 256; i++) {
        f01 = q[1][i];
        f5b = ffm5b(f01);
        fef = ffmEf(f01);
        m[0][i] = f01 + (f5b << 8) + (fef << 16) + (fef << 24);
        m[2][i] = f5b + (fef << 8) + (f01 << 16) + (fef << 24);
        f01 = q[0][i];
        f5b = ffm5b(f01);
        fef = ffmEf(f01);
        m[1][i] = fef + (fef << 8) + (f5b << 16) + (f01 << 24);
        m[3][i] = f5b + (f01 << 8) + (fef << 16) + (f5b << 24);
      }

      kLen = inKey.length / 2;
      for (i = 0; i < kLen; i++) {
        a = inKey[i + i];
        meKey[i] = a;
        b = inKey[i + i + 1];
        moKey[i] = b;
        sKey[kLen - i - 1] = mdsRem(a, b);
      }
      for (i = 0; i < 40; i += 2) {
        a = 0x1010101 * i;
        b = a + 0x1010101;
        a = hFun(a, meKey);
        b = rotw(hFun(b, moKey), 8);
        tfsKey[i] = (a + b) & MAXINT;
        tfsKey[i + 1] = rotw(a + 2 * b, 9);
      }
      for (i = 0; i < 256; i++) {
        a = b = c = d = i;
        switch (kLen) {
          case 4:
            a = q[1][a] ^ getB(sKey[3], 0);
            b = q[0][b] ^ getB(sKey[3], 1);
            c = q[0][c] ^ getB(sKey[3], 2);
            d = q[1][d] ^ getB(sKey[3], 3);
          case 3:
            a = q[1][a] ^ getB(sKey[2], 0);
            b = q[1][b] ^ getB(sKey[2], 1);
            c = q[0][c] ^ getB(sKey[2], 2);
            d = q[0][d] ^ getB(sKey[2], 3);
          case 2:
            tfsM[0][i] = m[0][q[0][q[0][a] ^ getB(sKey[1], 0)] ^ getB(sKey[0], 0)];
            tfsM[1][i] = m[1][q[0][q[1][b] ^ getB(sKey[1], 1)] ^ getB(sKey[0], 1)];
            tfsM[2][i] = m[2][q[1][q[0][c] ^ getB(sKey[1], 2)] ^ getB(sKey[0], 2)];
            tfsM[3][i] = m[3][q[1][q[1][d] ^ getB(sKey[1], 3)] ^ getB(sKey[0], 3)];
        }
      }
    }

    function tfsG0(x) {
      return tfsM[0][getB(x, 0)] ^ tfsM[1][getB(x, 1)] ^ tfsM[2][getB(x, 2)] ^ tfsM[3][getB(x, 3)];
    }

    function tfsG1(x) {
      return tfsM[0][getB(x, 3)] ^ tfsM[1][getB(x, 0)] ^ tfsM[2][getB(x, 1)] ^ tfsM[3][getB(x, 2)];
    }

    function tfsFrnd(r, blk) {
      let a = tfsG0(blk[0]);
      let b = tfsG1(blk[1]);
      blk[2] = rotw(blk[2] ^ (a + b + tfsKey[4 * r + 8]) & MAXINT, 31);
      blk[3] = rotw(blk[3], 1) ^ (a + 2 * b + tfsKey[4 * r + 9]) & MAXINT;
      a = tfsG0(blk[2]);
      b = tfsG1(blk[3]);
      blk[0] = rotw(blk[0] ^ (a + b + tfsKey[4 * r + 10]) & MAXINT, 31);
      blk[1] = rotw(blk[1], 1) ^ (a + 2 * b + tfsKey[4 * r + 11]) & MAXINT;
    }

    function tfsIrnd(i, blk) {
      let a = tfsG0(blk[0]);
      let b = tfsG1(blk[1]);
      blk[2] = rotw(blk[2], 1) ^ (a + b + tfsKey[4 * i + 10]) & MAXINT;
      blk[3] = rotw(blk[3] ^ (a + 2 * b + tfsKey[4 * i + 11]) & MAXINT, 31);
      a = tfsG0(blk[2]);
      b = tfsG1(blk[3]);
      blk[0] = rotw(blk[0], 1) ^ (a + b + tfsKey[4 * i + 8]) & MAXINT;
      blk[1] = rotw(blk[1] ^ (a + 2 * b + tfsKey[4 * i + 9]) & MAXINT, 31);
    }

    function tfsClose() {
      tfsKey = [];
      tfsM = [
        [],
        [],
        [],
        []
      ];
    }

    function tfsEncrypt(data, offset) {
      dataBytes = data;
      dataOffset = offset;
      const blk = [getW(dataBytes, dataOffset) ^ tfsKey[0],
        getW(dataBytes, dataOffset + 4) ^ tfsKey[1],
        getW(dataBytes, dataOffset + 8) ^ tfsKey[2],
        getW(dataBytes, dataOffset + 12) ^ tfsKey[3]];
      for (let j = 0; j < 8; j++) {
        tfsFrnd(j, blk);
      }
      setW(dataBytes, dataOffset, blk[2] ^ tfsKey[4]);
      setW(dataBytes, dataOffset + 4, blk[3] ^ tfsKey[5]);
      setW(dataBytes, dataOffset + 8, blk[0] ^ tfsKey[6]);
      setW(dataBytes, dataOffset + 12, blk[1] ^ tfsKey[7]);
      dataOffset += 16;
      return dataBytes;
    }

    function tfsDecrypt(data, offset) {
      dataBytes = data;
      dataOffset = offset;
      const blk = [getW(dataBytes, dataOffset) ^ tfsKey[4],
        getW(dataBytes, dataOffset + 4) ^ tfsKey[5],
        getW(dataBytes, dataOffset + 8) ^ tfsKey[6],
        getW(dataBytes, dataOffset + 12) ^ tfsKey[7]];
      for (let j = 7; j >= 0; j--) {
        tfsIrnd(j, blk);
      }
      setW(dataBytes, dataOffset, blk[2] ^ tfsKey[0]);
      setW(dataBytes, dataOffset + 4, blk[3] ^ tfsKey[1]);
      setW(dataBytes, dataOffset + 8, blk[0] ^ tfsKey[2]);
      setW(dataBytes, dataOffset + 12, blk[1] ^ tfsKey[3]);
      dataOffset += 16;
    }

    // added by Recurity Labs

    function tfsFinal() {
      return dataBytes;
    }

    return {
      name: 'twofish',
      blocksize: 128 / 8,
      open: tfsInit,
      close: tfsClose,
      encrypt: tfsEncrypt,
      decrypt: tfsDecrypt,
      // added by Recurity Labs
      finalize: tfsFinal
    };
  }

  // added by Recurity Labs

  function TF(key) {
    this.tf = createTwofish();
    this.tf.open(Array.from(key), 0);

    this.encrypt = function(block) {
      return this.tf.encrypt(Array.from(block), 0);
    };
  }

  TF.keySize = TF.prototype.keySize = 32;
  TF.blockSize = TF.prototype.blockSize = 16;

  /* Modified by Recurity Labs GmbH
   *
   * Originally written by nklein software (nklein.com)
   */

  /*
   * Javascript implementation based on Bruce Schneier's reference implementation.
   *
   *
   * The constructor doesn't do much of anything.  It's just here
   * so we can start defining properties and methods and such.
   */
  function Blowfish() {}

  /*
   * Declare the block size so that protocols know what size
   * Initialization Vector (IV) they will need.
   */
  Blowfish.prototype.BLOCKSIZE = 8;

  /*
   * These are the default SBOXES.
   */
  Blowfish.prototype.SBOXES = [
    [
      0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
      0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
      0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
      0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
      0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
      0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
      0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
      0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
      0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
      0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
      0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
      0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
      0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
      0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
      0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
      0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
      0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
      0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
      0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
      0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
      0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
      0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
      0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
      0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
      0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
      0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
      0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
      0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
      0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
      0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
      0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
      0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
      0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
      0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
      0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
      0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
      0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
      0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
      0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
      0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
      0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
      0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
      0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
    ],
    [
      0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
      0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
      0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
      0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
      0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
      0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
      0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
      0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
      0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
      0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
      0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
      0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
      0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
      0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
      0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
      0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
      0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
      0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
      0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
      0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
      0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
      0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
      0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
      0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
      0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
      0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
      0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
      0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
      0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
      0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
      0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
      0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
      0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
      0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
      0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
      0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
      0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
      0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
      0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
      0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
      0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
      0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
      0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
    ],
    [
      0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
      0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
      0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
      0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
      0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
      0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
      0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
      0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
      0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
      0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
      0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
      0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
      0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
      0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
      0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
      0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
      0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
      0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
      0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
      0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
      0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
      0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
      0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
      0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
      0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
      0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
      0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
      0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
      0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
      0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
      0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
      0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
      0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
      0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
      0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
      0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
      0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
      0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
      0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
      0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
      0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
      0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
      0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
    ],
    [
      0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
      0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
      0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
      0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
      0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
      0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
      0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
      0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
      0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
      0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
      0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
      0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
      0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
      0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
      0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
      0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
      0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
      0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
      0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
      0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
      0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
      0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
      0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
      0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
      0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
      0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
      0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
      0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
      0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
      0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
      0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
      0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
      0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
      0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
      0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
      0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
      0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
      0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
      0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
      0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
      0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
      0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
      0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
    ]
  ];

  //*
  //* This is the default PARRAY
  //*
  Blowfish.prototype.PARRAY = [
    0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
    0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
    0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b
  ];

  //*
  //* This is the number of rounds the cipher will go
  //*
  Blowfish.prototype.NN = 16;

  //*
  //* This function is needed to get rid of problems
  //* with the high-bit getting set.  If we don't do
  //* this, then sometimes ( aa & 0x00FFFFFFFF ) is not
  //* equal to ( bb & 0x00FFFFFFFF ) even when they
  //* agree bit-for-bit for the first 32 bits.
  //*
  Blowfish.prototype._clean = function(xx) {
    if (xx < 0) {
      const yy = xx & 0x7FFFFFFF;
      xx = yy + 0x80000000;
    }
    return xx;
  };

  //*
  //* This is the mixing function that uses the sboxes
  //*
  Blowfish.prototype._F = function(xx) {
    let yy;

    const dd = xx & 0x00FF;
    xx >>>= 8;
    const cc = xx & 0x00FF;
    xx >>>= 8;
    const bb = xx & 0x00FF;
    xx >>>= 8;
    const aa = xx & 0x00FF;

    yy = this.sboxes[0][aa] + this.sboxes[1][bb];
    yy ^= this.sboxes[2][cc];
    yy += this.sboxes[3][dd];

    return yy;
  };

  //*
  //* This method takes an array with two values, left and right
  //* and does NN rounds of Blowfish on them.
  //*
  Blowfish.prototype._encryptBlock = function(vals) {
    let dataL = vals[0];
    let dataR = vals[1];

    let ii;

    for (ii = 0; ii < this.NN; ++ii) {
      dataL ^= this.parray[ii];
      dataR = this._F(dataL) ^ dataR;

      const tmp = dataL;
      dataL = dataR;
      dataR = tmp;
    }

    dataL ^= this.parray[this.NN + 0];
    dataR ^= this.parray[this.NN + 1];

    vals[0] = this._clean(dataR);
    vals[1] = this._clean(dataL);
  };

  //*
  //* This method takes a vector of numbers and turns them
  //* into long words so that they can be processed by the
  //* real algorithm.
  //*
  //* Maybe I should make the real algorithm above take a vector
  //* instead.  That will involve more looping, but it won't require
  //* the F() method to deconstruct the vector.
  //*
  Blowfish.prototype.encryptBlock = function(vector) {
    let ii;
    const vals = [0, 0];
    const off = this.BLOCKSIZE / 2;
    for (ii = 0; ii < this.BLOCKSIZE / 2; ++ii) {
      vals[0] = (vals[0] << 8) | (vector[ii + 0] & 0x00FF);
      vals[1] = (vals[1] << 8) | (vector[ii + off] & 0x00FF);
    }

    this._encryptBlock(vals);

    const ret = [];
    for (ii = 0; ii < this.BLOCKSIZE / 2; ++ii) {
      ret[ii + 0] = ((vals[0] >>> (24 - 8 * (ii))) & 0x00FF);
      ret[ii + off] = ((vals[1] >>> (24 - 8 * (ii))) & 0x00FF);
      // vals[ 0 ] = ( vals[ 0 ] >>> 8 );
      // vals[ 1 ] = ( vals[ 1 ] >>> 8 );
    }

    return ret;
  };

  //*
  //* This method takes an array with two values, left and right
  //* and undoes NN rounds of Blowfish on them.
  //*
  Blowfish.prototype._decryptBlock = function(vals) {
    let dataL = vals[0];
    let dataR = vals[1];

    let ii;

    for (ii = this.NN + 1; ii > 1; --ii) {
      dataL ^= this.parray[ii];
      dataR = this._F(dataL) ^ dataR;

      const tmp = dataL;
      dataL = dataR;
      dataR = tmp;
    }

    dataL ^= this.parray[1];
    dataR ^= this.parray[0];

    vals[0] = this._clean(dataR);
    vals[1] = this._clean(dataL);
  };

  //*
  //* This method takes a key array and initializes the
  //* sboxes and parray for this encryption.
  //*
  Blowfish.prototype.init = function(key) {
    let ii;
    let jj = 0;

    this.parray = [];
    for (ii = 0; ii < this.NN + 2; ++ii) {
      let data = 0x00000000;
      for (let kk = 0; kk < 4; ++kk) {
        data = (data << 8) | (key[jj] & 0x00FF);
        if (++jj >= key.length) {
          jj = 0;
        }
      }
      this.parray[ii] = this.PARRAY[ii] ^ data;
    }

    this.sboxes = [];
    for (ii = 0; ii < 4; ++ii) {
      this.sboxes[ii] = [];
      for (jj = 0; jj < 256; ++jj) {
        this.sboxes[ii][jj] = this.SBOXES[ii][jj];
      }
    }

    const vals = [0x00000000, 0x00000000];

    for (ii = 0; ii < this.NN + 2; ii += 2) {
      this._encryptBlock(vals);
      this.parray[ii + 0] = vals[0];
      this.parray[ii + 1] = vals[1];
    }

    for (ii = 0; ii < 4; ++ii) {
      for (jj = 0; jj < 256; jj += 2) {
        this._encryptBlock(vals);
        this.sboxes[ii][jj + 0] = vals[0];
        this.sboxes[ii][jj + 1] = vals[1];
      }
    }
  };

  // added by Recurity Labs
  function BF(key) {
    this.bf = new Blowfish();
    this.bf.init(key);

    this.encrypt = function(block) {
      return this.bf.encryptBlock(block);
    };
  }

  BF.keySize = BF.prototype.keySize = 16;
  BF.blockSize = BF.prototype.blockSize = 8;

  /**
   * @fileoverview Symmetric cryptography functions
   * @module crypto/cipher
   * @private
   */

  /**
   * AES-128 encryption and decryption (ID 7)
   * @function
   * @param {String} key - 128-bit key
   * @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
   * @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
   * @returns {Object}
   */
  const aes128 = aes(128);
  /**
   * AES-128 Block Cipher (ID 8)
   * @function
   * @param {String} key - 192-bit key
   * @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
   * @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
   * @returns {Object}
   */
  const aes192 = aes(192);
  /**
   * AES-128 Block Cipher (ID 9)
   * @function
   * @param {String} key - 256-bit key
   * @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
   * @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
   * @returns {Object}
   */
  const aes256 = aes(256);
  // Not in OpenPGP specifications
  const des$1 = DES;
  /**
   * Triple DES Block Cipher (ID 2)
   * @function
   * @param {String} key - 192-bit key
   * @see {@link https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf|NIST SP 800-67}
   * @returns {Object}
   */
  const tripledes = TripleDES;
  /**
   * CAST-128 Block Cipher (ID 3)
   * @function
   * @param {String} key - 128-bit key
   * @see {@link https://tools.ietf.org/html/rfc2144|The CAST-128 Encryption Algorithm}
   * @returns {Object}
   */
  const cast5 = CAST5;
  /**
   * Twofish Block Cipher (ID 10)
   * @function
   * @param {String} key - 256-bit key
   * @see {@link https://tools.ietf.org/html/rfc4880#ref-TWOFISH|TWOFISH}
   * @returns {Object}
   */
  const twofish = TF;
  /**
   * Blowfish Block Cipher (ID 4)
   * @function
   * @param {String} key - 128-bit key
   * @see {@link https://tools.ietf.org/html/rfc4880#ref-BLOWFISH|BLOWFISH}
   * @returns {Object}
   */
  const blowfish = BF;
  /**
   * Not implemented
   * @function
   * @throws {Error}
   */
  const idea = function() {
    throw new Error('IDEA symmetric-key algorithm not implemented');
  };

  var cipher = /*#__PURE__*/Object.freeze({
    __proto__: null,
    aes128: aes128,
    aes192: aes192,
    aes256: aes256,
    des: des$1,
    tripledes: tripledes,
    cast5: cast5,
    twofish: twofish,
    blowfish: blowfish,
    idea: idea
  });

  var sha1_asm = function ( stdlib, foreign, buffer ) {
      "use asm";

      // SHA256 state
      var H0 = 0, H1 = 0, H2 = 0, H3 = 0, H4 = 0,
          TOTAL0 = 0, TOTAL1 = 0;

      // HMAC state
      var I0 = 0, I1 = 0, I2 = 0, I3 = 0, I4 = 0,
          O0 = 0, O1 = 0, O2 = 0, O3 = 0, O4 = 0;

      // I/O buffer
      var HEAP = new stdlib.Uint8Array(buffer);

      function _core ( w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15 ) {
          w0 = w0|0;
          w1 = w1|0;
          w2 = w2|0;
          w3 = w3|0;
          w4 = w4|0;
          w5 = w5|0;
          w6 = w6|0;
          w7 = w7|0;
          w8 = w8|0;
          w9 = w9|0;
          w10 = w10|0;
          w11 = w11|0;
          w12 = w12|0;
          w13 = w13|0;
          w14 = w14|0;
          w15 = w15|0;

          var a = 0, b = 0, c = 0, d = 0, e = 0, n = 0, t = 0,
              w16 = 0, w17 = 0, w18 = 0, w19 = 0,
              w20 = 0, w21 = 0, w22 = 0, w23 = 0, w24 = 0, w25 = 0, w26 = 0, w27 = 0, w28 = 0, w29 = 0,
              w30 = 0, w31 = 0, w32 = 0, w33 = 0, w34 = 0, w35 = 0, w36 = 0, w37 = 0, w38 = 0, w39 = 0,
              w40 = 0, w41 = 0, w42 = 0, w43 = 0, w44 = 0, w45 = 0, w46 = 0, w47 = 0, w48 = 0, w49 = 0,
              w50 = 0, w51 = 0, w52 = 0, w53 = 0, w54 = 0, w55 = 0, w56 = 0, w57 = 0, w58 = 0, w59 = 0,
              w60 = 0, w61 = 0, w62 = 0, w63 = 0, w64 = 0, w65 = 0, w66 = 0, w67 = 0, w68 = 0, w69 = 0,
              w70 = 0, w71 = 0, w72 = 0, w73 = 0, w74 = 0, w75 = 0, w76 = 0, w77 = 0, w78 = 0, w79 = 0;

          a = H0;
          b = H1;
          c = H2;
          d = H3;
          e = H4;

          // 0
          t = ( w0 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 1
          t = ( w1 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 2
          t = ( w2 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 3
          t = ( w3 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 4
          t = ( w4 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 5
          t = ( w5 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 6
          t = ( w6 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 7
          t = ( w7 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 8
          t = ( w8 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 9
          t = ( w9 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 10
          t = ( w10 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 11
          t = ( w11 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 12
          t = ( w12 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 13
          t = ( w13 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 14
          t = ( w14 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 15
          t = ( w15 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 16
          n = w13 ^ w8 ^ w2 ^ w0;
          w16 = (n << 1) | (n >>> 31);
          t = (w16 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 17
          n = w14 ^ w9 ^ w3 ^ w1;
          w17 = (n << 1) | (n >>> 31);
          t = (w17 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 18
          n = w15 ^ w10 ^ w4 ^ w2;
          w18 = (n << 1) | (n >>> 31);
          t = (w18 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 19
          n = w16 ^ w11 ^ w5 ^ w3;
          w19 = (n << 1) | (n >>> 31);
          t = (w19 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (~b & d)) + 0x5a827999 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 20
          n = w17 ^ w12 ^ w6 ^ w4;
          w20 = (n << 1) | (n >>> 31);
          t = (w20 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 21
          n = w18 ^ w13 ^ w7 ^ w5;
          w21 = (n << 1) | (n >>> 31);
          t = (w21 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 22
          n = w19 ^ w14 ^ w8 ^ w6;
          w22 = (n << 1) | (n >>> 31);
          t = (w22 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 23
          n = w20 ^ w15 ^ w9 ^ w7;
          w23 = (n << 1) | (n >>> 31);
          t = (w23 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 24
          n = w21 ^ w16 ^ w10 ^ w8;
          w24 = (n << 1) | (n >>> 31);
          t = (w24 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 25
          n = w22 ^ w17 ^ w11 ^ w9;
          w25 = (n << 1) | (n >>> 31);
          t = (w25 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 26
          n = w23 ^ w18 ^ w12 ^ w10;
          w26 = (n << 1) | (n >>> 31);
          t = (w26 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 27
          n = w24 ^ w19 ^ w13 ^ w11;
          w27 = (n << 1) | (n >>> 31);
          t = (w27 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 28
          n = w25 ^ w20 ^ w14 ^ w12;
          w28 = (n << 1) | (n >>> 31);
          t = (w28 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 29
          n = w26 ^ w21 ^ w15 ^ w13;
          w29 = (n << 1) | (n >>> 31);
          t = (w29 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 30
          n = w27 ^ w22 ^ w16 ^ w14;
          w30 = (n << 1) | (n >>> 31);
          t = (w30 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 31
          n = w28 ^ w23 ^ w17 ^ w15;
          w31 = (n << 1) | (n >>> 31);
          t = (w31 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 32
          n = w29 ^ w24 ^ w18 ^ w16;
          w32 = (n << 1) | (n >>> 31);
          t = (w32 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 33
          n = w30 ^ w25 ^ w19 ^ w17;
          w33 = (n << 1) | (n >>> 31);
          t = (w33 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 34
          n = w31 ^ w26 ^ w20 ^ w18;
          w34 = (n << 1) | (n >>> 31);
          t = (w34 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 35
          n = w32 ^ w27 ^ w21 ^ w19;
          w35 = (n << 1) | (n >>> 31);
          t = (w35 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 36
          n = w33 ^ w28 ^ w22 ^ w20;
          w36 = (n << 1) | (n >>> 31);
          t = (w36 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 37
          n = w34 ^ w29 ^ w23 ^ w21;
          w37 = (n << 1) | (n >>> 31);
          t = (w37 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 38
          n = w35 ^ w30 ^ w24 ^ w22;
          w38 = (n << 1) | (n >>> 31);
          t = (w38 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 39
          n = w36 ^ w31 ^ w25 ^ w23;
          w39 = (n << 1) | (n >>> 31);
          t = (w39 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) + 0x6ed9eba1 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 40
          n = w37 ^ w32 ^ w26 ^ w24;
          w40 = (n << 1) | (n >>> 31);
          t = (w40 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 41
          n = w38 ^ w33 ^ w27 ^ w25;
          w41 = (n << 1) | (n >>> 31);
          t = (w41 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 42
          n = w39 ^ w34 ^ w28 ^ w26;
          w42 = (n << 1) | (n >>> 31);
          t = (w42 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 43
          n = w40 ^ w35 ^ w29 ^ w27;
          w43 = (n << 1) | (n >>> 31);
          t = (w43 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 44
          n = w41 ^ w36 ^ w30 ^ w28;
          w44 = (n << 1) | (n >>> 31);
          t = (w44 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 45
          n = w42 ^ w37 ^ w31 ^ w29;
          w45 = (n << 1) | (n >>> 31);
          t = (w45 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 46
          n = w43 ^ w38 ^ w32 ^ w30;
          w46 = (n << 1) | (n >>> 31);
          t = (w46 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 47
          n = w44 ^ w39 ^ w33 ^ w31;
          w47 = (n << 1) | (n >>> 31);
          t = (w47 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 48
          n = w45 ^ w40 ^ w34 ^ w32;
          w48 = (n << 1) | (n >>> 31);
          t = (w48 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 49
          n = w46 ^ w41 ^ w35 ^ w33;
          w49 = (n << 1) | (n >>> 31);
          t = (w49 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 50
          n = w47 ^ w42 ^ w36 ^ w34;
          w50 = (n << 1) | (n >>> 31);
          t = (w50 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 51
          n = w48 ^ w43 ^ w37 ^ w35;
          w51 = (n << 1) | (n >>> 31);
          t = (w51 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 52
          n = w49 ^ w44 ^ w38 ^ w36;
          w52 = (n << 1) | (n >>> 31);
          t = (w52 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 53
          n = w50 ^ w45 ^ w39 ^ w37;
          w53 = (n << 1) | (n >>> 31);
          t = (w53 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 54
          n = w51 ^ w46 ^ w40 ^ w38;
          w54 = (n << 1) | (n >>> 31);
          t = (w54 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 55
          n = w52 ^ w47 ^ w41 ^ w39;
          w55 = (n << 1) | (n >>> 31);
          t = (w55 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 56
          n = w53 ^ w48 ^ w42 ^ w40;
          w56 = (n << 1) | (n >>> 31);
          t = (w56 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 57
          n = w54 ^ w49 ^ w43 ^ w41;
          w57 = (n << 1) | (n >>> 31);
          t = (w57 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 58
          n = w55 ^ w50 ^ w44 ^ w42;
          w58 = (n << 1) | (n >>> 31);
          t = (w58 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 59
          n = w56 ^ w51 ^ w45 ^ w43;
          w59 = (n << 1) | (n >>> 31);
          t = (w59 + ((a << 5) | (a >>> 27)) + e + ((b & c) | (b & d) | (c & d)) - 0x70e44324 )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 60
          n = w57 ^ w52 ^ w46 ^ w44;
          w60 = (n << 1) | (n >>> 31);
          t = (w60 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 61
          n = w58 ^ w53 ^ w47 ^ w45;
          w61 = (n << 1) | (n >>> 31);
          t = (w61 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 62
          n = w59 ^ w54 ^ w48 ^ w46;
          w62 = (n << 1) | (n >>> 31);
          t = (w62 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 63
          n = w60 ^ w55 ^ w49 ^ w47;
          w63 = (n << 1) | (n >>> 31);
          t = (w63 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 64
          n = w61 ^ w56 ^ w50 ^ w48;
          w64 = (n << 1) | (n >>> 31);
          t = (w64 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 65
          n = w62 ^ w57 ^ w51 ^ w49;
          w65 = (n << 1) | (n >>> 31);
          t = (w65 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 66
          n = w63 ^ w58 ^ w52 ^ w50;
          w66 = (n << 1) | (n >>> 31);
          t = (w66 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 67
          n = w64 ^ w59 ^ w53 ^ w51;
          w67 = (n << 1) | (n >>> 31);
          t = (w67 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 68
          n = w65 ^ w60 ^ w54 ^ w52;
          w68 = (n << 1) | (n >>> 31);
          t = (w68 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 69
          n = w66 ^ w61 ^ w55 ^ w53;
          w69 = (n << 1) | (n >>> 31);
          t = (w69 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 70
          n = w67 ^ w62 ^ w56 ^ w54;
          w70 = (n << 1) | (n >>> 31);
          t = (w70 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 71
          n = w68 ^ w63 ^ w57 ^ w55;
          w71 = (n << 1) | (n >>> 31);
          t = (w71 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 72
          n = w69 ^ w64 ^ w58 ^ w56;
          w72 = (n << 1) | (n >>> 31);
          t = (w72 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 73
          n = w70 ^ w65 ^ w59 ^ w57;
          w73 = (n << 1) | (n >>> 31);
          t = (w73 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 74
          n = w71 ^ w66 ^ w60 ^ w58;
          w74 = (n << 1) | (n >>> 31);
          t = (w74 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 75
          n = w72 ^ w67 ^ w61 ^ w59;
          w75 = (n << 1) | (n >>> 31);
          t = (w75 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 76
          n = w73 ^ w68 ^ w62 ^ w60;
          w76 = (n << 1) | (n >>> 31);
          t = (w76 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 77
          n = w74 ^ w69 ^ w63 ^ w61;
          w77 = (n << 1) | (n >>> 31);
          t = (w77 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 78
          n = w75 ^ w70 ^ w64 ^ w62;
          w78 = (n << 1) | (n >>> 31);
          t = (w78 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          // 79
          n = w76 ^ w71 ^ w65 ^ w63;
          w79 = (n << 1) | (n >>> 31);
          t = (w79 + ((a << 5) | (a >>> 27)) + e + (b ^ c ^ d) - 0x359d3e2a )|0;
          e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;

          H0 = ( H0 + a )|0;
          H1 = ( H1 + b )|0;
          H2 = ( H2 + c )|0;
          H3 = ( H3 + d )|0;
          H4 = ( H4 + e )|0;

      }

      function _core_heap ( offset ) {
          offset = offset|0;

          _core(
              HEAP[offset|0]<<24 | HEAP[offset|1]<<16 | HEAP[offset|2]<<8 | HEAP[offset|3],
              HEAP[offset|4]<<24 | HEAP[offset|5]<<16 | HEAP[offset|6]<<8 | HEAP[offset|7],
              HEAP[offset|8]<<24 | HEAP[offset|9]<<16 | HEAP[offset|10]<<8 | HEAP[offset|11],
              HEAP[offset|12]<<24 | HEAP[offset|13]<<16 | HEAP[offset|14]<<8 | HEAP[offset|15],
              HEAP[offset|16]<<24 | HEAP[offset|17]<<16 | HEAP[offset|18]<<8 | HEAP[offset|19],
              HEAP[offset|20]<<24 | HEAP[offset|21]<<16 | HEAP[offset|22]<<8 | HEAP[offset|23],
              HEAP[offset|24]<<24 | HEAP[offset|25]<<16 | HEAP[offset|26]<<8 | HEAP[offset|27],
              HEAP[offset|28]<<24 | HEAP[offset|29]<<16 | HEAP[offset|30]<<8 | HEAP[offset|31],
              HEAP[offset|32]<<24 | HEAP[offset|33]<<16 | HEAP[offset|34]<<8 | HEAP[offset|35],
              HEAP[offset|36]<<24 | HEAP[offset|37]<<16 | HEAP[offset|38]<<8 | HEAP[offset|39],
              HEAP[offset|40]<<24 | HEAP[offset|41]<<16 | HEAP[offset|42]<<8 | HEAP[offset|43],
              HEAP[offset|44]<<24 | HEAP[offset|45]<<16 | HEAP[offset|46]<<8 | HEAP[offset|47],
              HEAP[offset|48]<<24 | HEAP[offset|49]<<16 | HEAP[offset|50]<<8 | HEAP[offset|51],
              HEAP[offset|52]<<24 | HEAP[offset|53]<<16 | HEAP[offset|54]<<8 | HEAP[offset|55],
              HEAP[offset|56]<<24 | HEAP[offset|57]<<16 | HEAP[offset|58]<<8 | HEAP[offset|59],
              HEAP[offset|60]<<24 | HEAP[offset|61]<<16 | HEAP[offset|62]<<8 | HEAP[offset|63]
          );
      }

      // offset — multiple of 32
      function _state_to_heap ( output ) {
          output = output|0;

          HEAP[output|0] = H0>>>24;
          HEAP[output|1] = H0>>>16&255;
          HEAP[output|2] = H0>>>8&255;
          HEAP[output|3] = H0&255;
          HEAP[output|4] = H1>>>24;
          HEAP[output|5] = H1>>>16&255;
          HEAP[output|6] = H1>>>8&255;
          HEAP[output|7] = H1&255;
          HEAP[output|8] = H2>>>24;
          HEAP[output|9] = H2>>>16&255;
          HEAP[output|10] = H2>>>8&255;
          HEAP[output|11] = H2&255;
          HEAP[output|12] = H3>>>24;
          HEAP[output|13] = H3>>>16&255;
          HEAP[output|14] = H3>>>8&255;
          HEAP[output|15] = H3&255;
          HEAP[output|16] = H4>>>24;
          HEAP[output|17] = H4>>>16&255;
          HEAP[output|18] = H4>>>8&255;
          HEAP[output|19] = H4&255;
      }

      function reset () {
          H0 = 0x67452301;
          H1 = 0xefcdab89;
          H2 = 0x98badcfe;
          H3 = 0x10325476;
          H4 = 0xc3d2e1f0;
          TOTAL0 = TOTAL1 = 0;
      }

      function init ( h0, h1, h2, h3, h4, total0, total1 ) {
          h0 = h0|0;
          h1 = h1|0;
          h2 = h2|0;
          h3 = h3|0;
          h4 = h4|0;
          total0 = total0|0;
          total1 = total1|0;

          H0 = h0;
          H1 = h1;
          H2 = h2;
          H3 = h3;
          H4 = h4;
          TOTAL0 = total0;
          TOTAL1 = total1;
      }

      // offset — multiple of 64
      function process ( offset, length ) {
          offset = offset|0;
          length = length|0;

          var hashed = 0;

          if ( offset & 63 )
              return -1;

          while ( (length|0) >= 64 ) {
              _core_heap(offset);

              offset = ( offset + 64 )|0;
              length = ( length - 64 )|0;

              hashed = ( hashed + 64 )|0;
          }

          TOTAL0 = ( TOTAL0 + hashed )|0;
          if ( TOTAL0>>>0 < hashed>>>0 ) TOTAL1 = ( TOTAL1 + 1 )|0;

          return hashed|0;
      }

      // offset — multiple of 64
      // output — multiple of 32
      function finish ( offset, length, output ) {
          offset = offset|0;
          length = length|0;
          output = output|0;

          var hashed = 0,
              i = 0;

          if ( offset & 63 )
              return -1;

          if ( ~output )
              if ( output & 31 )
                  return -1;

          if ( (length|0) >= 64 ) {
              hashed = process( offset, length )|0;
              if ( (hashed|0) == -1 )
                  return -1;

              offset = ( offset + hashed )|0;
              length = ( length - hashed )|0;
          }

          hashed = ( hashed + length )|0;
          TOTAL0 = ( TOTAL0 + length )|0;
          if ( TOTAL0>>>0 < length>>>0 ) TOTAL1 = (TOTAL1 + 1)|0;

          HEAP[offset|length] = 0x80;

          if ( (length|0) >= 56 ) {
              for ( i = (length+1)|0; (i|0) < 64; i = (i+1)|0 )
                  HEAP[offset|i] = 0x00;
              _core_heap(offset);

              length = 0;

              HEAP[offset|0] = 0;
          }

          for ( i = (length+1)|0; (i|0) < 59; i = (i+1)|0 )
              HEAP[offset|i] = 0;

          HEAP[offset|56] = TOTAL1>>>21&255;
          HEAP[offset|57] = TOTAL1>>>13&255;
          HEAP[offset|58] = TOTAL1>>>5&255;
          HEAP[offset|59] = TOTAL1<<3&255 | TOTAL0>>>29;
          HEAP[offset|60] = TOTAL0>>>21&255;
          HEAP[offset|61] = TOTAL0>>>13&255;
          HEAP[offset|62] = TOTAL0>>>5&255;
          HEAP[offset|63] = TOTAL0<<3&255;
          _core_heap(offset);

          if ( ~output )
              _state_to_heap(output);

          return hashed|0;
      }

      function hmac_reset () {
          H0 = I0;
          H1 = I1;
          H2 = I2;
          H3 = I3;
          H4 = I4;
          TOTAL0 = 64;
          TOTAL1 = 0;
      }

      function _hmac_opad () {
          H0 = O0;
          H1 = O1;
          H2 = O2;
          H3 = O3;
          H4 = O4;
          TOTAL0 = 64;
          TOTAL1 = 0;
      }

      function hmac_init ( p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ) {
          p0 = p0|0;
          p1 = p1|0;
          p2 = p2|0;
          p3 = p3|0;
          p4 = p4|0;
          p5 = p5|0;
          p6 = p6|0;
          p7 = p7|0;
          p8 = p8|0;
          p9 = p9|0;
          p10 = p10|0;
          p11 = p11|0;
          p12 = p12|0;
          p13 = p13|0;
          p14 = p14|0;
          p15 = p15|0;

          // opad
          reset();
          _core(
              p0 ^ 0x5c5c5c5c,
              p1 ^ 0x5c5c5c5c,
              p2 ^ 0x5c5c5c5c,
              p3 ^ 0x5c5c5c5c,
              p4 ^ 0x5c5c5c5c,
              p5 ^ 0x5c5c5c5c,
              p6 ^ 0x5c5c5c5c,
              p7 ^ 0x5c5c5c5c,
              p8 ^ 0x5c5c5c5c,
              p9 ^ 0x5c5c5c5c,
              p10 ^ 0x5c5c5c5c,
              p11 ^ 0x5c5c5c5c,
              p12 ^ 0x5c5c5c5c,
              p13 ^ 0x5c5c5c5c,
              p14 ^ 0x5c5c5c5c,
              p15 ^ 0x5c5c5c5c
          );
          O0 = H0;
          O1 = H1;
          O2 = H2;
          O3 = H3;
          O4 = H4;

          // ipad
          reset();
          _core(
              p0 ^ 0x36363636,
              p1 ^ 0x36363636,
              p2 ^ 0x36363636,
              p3 ^ 0x36363636,
              p4 ^ 0x36363636,
              p5 ^ 0x36363636,
              p6 ^ 0x36363636,
              p7 ^ 0x36363636,
              p8 ^ 0x36363636,
              p9 ^ 0x36363636,
              p10 ^ 0x36363636,
              p11 ^ 0x36363636,
              p12 ^ 0x36363636,
              p13 ^ 0x36363636,
              p14 ^ 0x36363636,
              p15 ^ 0x36363636
          );
          I0 = H0;
          I1 = H1;
          I2 = H2;
          I3 = H3;
          I4 = H4;

          TOTAL0 = 64;
          TOTAL1 = 0;
      }

      // offset — multiple of 64
      // output — multiple of 32
      function hmac_finish ( offset, length, output ) {
          offset = offset|0;
          length = length|0;
          output = output|0;

          var t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, hashed = 0;

          if ( offset & 63 )
              return -1;

          if ( ~output )
              if ( output & 31 )
                  return -1;

          hashed = finish( offset, length, -1 )|0;
          t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4;

          _hmac_opad();
          _core( t0, t1, t2, t3, t4, 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672 );

          if ( ~output )
              _state_to_heap(output);

          return hashed|0;
      }

      // salt is assumed to be already processed
      // offset — multiple of 64
      // output — multiple of 32
      function pbkdf2_generate_block ( offset, length, block, count, output ) {
          offset = offset|0;
          length = length|0;
          block = block|0;
          count = count|0;
          output = output|0;

          var h0 = 0, h1 = 0, h2 = 0, h3 = 0, h4 = 0,
              t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;

          if ( offset & 63 )
              return -1;

          if ( ~output )
              if ( output & 31 )
                  return -1;

          // pad block number into heap
          // FIXME probable OOB write
          HEAP[(offset+length)|0]   = block>>>24;
          HEAP[(offset+length+1)|0] = block>>>16&255;
          HEAP[(offset+length+2)|0] = block>>>8&255;
          HEAP[(offset+length+3)|0] = block&255;

          // finish first iteration
          hmac_finish( offset, (length+4)|0, -1 )|0;
          h0 = t0 = H0, h1 = t1 = H1, h2 = t2 = H2, h3 = t3 = H3, h4 = t4 = H4;
          count = (count-1)|0;

          // perform the rest iterations
          while ( (count|0) > 0 ) {
              hmac_reset();
              _core( t0, t1, t2, t3, t4, 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672 );
              t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4;

              _hmac_opad();
              _core( t0, t1, t2, t3, t4, 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672 );
              t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4;

              h0 = h0 ^ H0;
              h1 = h1 ^ H1;
              h2 = h2 ^ H2;
              h3 = h3 ^ H3;
              h4 = h4 ^ H4;

              count = (count-1)|0;
          }

          H0 = h0;
          H1 = h1;
          H2 = h2;
          H3 = h3;
          H4 = h4;

          if ( ~output )
              _state_to_heap(output);

          return 0;
      }

      return {
        // SHA1
        reset: reset,
        init: init,
        process: process,
        finish: finish,

        // HMAC-SHA1
        hmac_reset: hmac_reset,
        hmac_init: hmac_init,
        hmac_finish: hmac_finish,

        // PBKDF2-HMAC-SHA1
        pbkdf2_generate_block: pbkdf2_generate_block
      }
  };

  class Hash {
      constructor() {
          this.pos = 0;
          this.len = 0;
      }
      reset() {
          const { asm } = this.acquire_asm();
          this.result = null;
          this.pos = 0;
          this.len = 0;
          asm.reset();
          return this;
      }
      process(data) {
          if (this.result !== null)
              throw new IllegalStateError('state must be reset before processing new data');
          const { asm, heap } = this.acquire_asm();
          let hpos = this.pos;
          let hlen = this.len;
          let dpos = 0;
          let dlen = data.length;
          let wlen = 0;
          while (dlen > 0) {
              wlen = _heap_write(heap, hpos + hlen, data, dpos, dlen);
              hlen += wlen;
              dpos += wlen;
              dlen -= wlen;
              wlen = asm.process(hpos, hlen);
              hpos += wlen;
              hlen -= wlen;
              if (!hlen)
                  hpos = 0;
          }
          this.pos = hpos;
          this.len = hlen;
          return this;
      }
      finish() {
          if (this.result !== null)
              throw new IllegalStateError('state must be reset before processing new data');
          const { asm, heap } = this.acquire_asm();
          asm.finish(this.pos, this.len, 0);
          this.result = new Uint8Array(this.HASH_SIZE);
          this.result.set(heap.subarray(0, this.HASH_SIZE));
          this.pos = 0;
          this.len = 0;
          this.release_asm();
          return this;
      }
  }

  const _sha1_block_size = 64;
  const _sha1_hash_size = 20;
  const heap_pool$1 = [];
  const asm_pool$1 = [];
  class Sha1 extends Hash {
      constructor() {
          super();
          this.NAME = 'sha1';
          this.BLOCK_SIZE = _sha1_block_size;
          this.HASH_SIZE = _sha1_hash_size;
          this.acquire_asm();
      }
      acquire_asm() {
          if (this.heap === undefined || this.asm === undefined) {
              this.heap = heap_pool$1.pop() || _heap_init();
              this.asm = asm_pool$1.pop() || sha1_asm({ Uint8Array: Uint8Array }, null, this.heap.buffer);
              this.reset();
          }
          return { heap: this.heap, asm: this.asm };
      }
      release_asm() {
          if (this.heap !== undefined && this.asm !== undefined) {
              heap_pool$1.push(this.heap);
              asm_pool$1.push(this.asm);
          }
          this.heap = undefined;
          this.asm = undefined;
      }
      static bytes(data) {
          return new Sha1().process(data).finish().result;
      }
  }
  Sha1.NAME = 'sha1';
  Sha1.heap_pool = [];
  Sha1.asm_pool = [];
  Sha1.asm_function = sha1_asm;

  var sha256_asm = function ( stdlib, foreign, buffer ) {
      "use asm";

      // SHA256 state
      var H0 = 0, H1 = 0, H2 = 0, H3 = 0, H4 = 0, H5 = 0, H6 = 0, H7 = 0,
          TOTAL0 = 0, TOTAL1 = 0;

      // HMAC state
      var I0 = 0, I1 = 0, I2 = 0, I3 = 0, I4 = 0, I5 = 0, I6 = 0, I7 = 0,
          O0 = 0, O1 = 0, O2 = 0, O3 = 0, O4 = 0, O5 = 0, O6 = 0, O7 = 0;

      // I/O buffer
      var HEAP = new stdlib.Uint8Array(buffer);

      function _core ( w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15 ) {
          w0 = w0|0;
          w1 = w1|0;
          w2 = w2|0;
          w3 = w3|0;
          w4 = w4|0;
          w5 = w5|0;
          w6 = w6|0;
          w7 = w7|0;
          w8 = w8|0;
          w9 = w9|0;
          w10 = w10|0;
          w11 = w11|0;
          w12 = w12|0;
          w13 = w13|0;
          w14 = w14|0;
          w15 = w15|0;

          var a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0;

          a = H0;
          b = H1;
          c = H2;
          d = H3;
          e = H4;
          f = H5;
          g = H6;
          h = H7;
          
          // 0
          h = ( w0 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) +  ( g ^ e & (f^g) ) + 0x428a2f98 )|0;
          d = ( d + h )|0;
          h = ( h + ( (a & b) ^ ( c & (a ^ b) ) ) + ( a>>>2 ^ a>>>13 ^ a>>>22 ^ a<<30 ^ a<<19 ^ a<<10 ) )|0;

          // 1
          g = ( w1 + g + ( d>>>6 ^ d>>>11 ^ d>>>25 ^ d<<26 ^ d<<21 ^ d<<7 ) +  ( f ^ d & (e^f) ) + 0x71374491 )|0;
          c = ( c + g )|0;
          g = ( g + ( (h & a) ^ ( b & (h ^ a) ) ) + ( h>>>2 ^ h>>>13 ^ h>>>22 ^ h<<30 ^ h<<19 ^ h<<10 ) )|0;

          // 2
          f = ( w2 + f + ( c>>>6 ^ c>>>11 ^ c>>>25 ^ c<<26 ^ c<<21 ^ c<<7 ) +  ( e ^ c & (d^e) ) + 0xb5c0fbcf )|0;
          b = ( b + f )|0;
          f = ( f + ( (g & h) ^ ( a & (g ^ h) ) ) + ( g>>>2 ^ g>>>13 ^ g>>>22 ^ g<<30 ^ g<<19 ^ g<<10 ) )|0;

          // 3
          e = ( w3 + e + ( b>>>6 ^ b>>>11 ^ b>>>25 ^ b<<26 ^ b<<21 ^ b<<7 ) +  ( d ^ b & (c^d) ) + 0xe9b5dba5 )|0;
          a = ( a + e )|0;
          e = ( e + ( (f & g) ^ ( h & (f ^ g) ) ) + ( f>>>2 ^ f>>>13 ^ f>>>22 ^ f<<30 ^ f<<19 ^ f<<10 ) )|0;

          // 4
          d = ( w4 + d + ( a>>>6 ^ a>>>11 ^ a>>>25 ^ a<<26 ^ a<<21 ^ a<<7 ) +  ( c ^ a & (b^c) ) + 0x3956c25b )|0;
          h = ( h + d )|0;
          d = ( d + ( (e & f) ^ ( g & (e ^ f) ) ) + ( e>>>2 ^ e>>>13 ^ e>>>22 ^ e<<30 ^ e<<19 ^ e<<10 ) )|0;

          // 5
          c = ( w5 + c + ( h>>>6 ^ h>>>11 ^ h>>>25 ^ h<<26 ^ h<<21 ^ h<<7 ) +  ( b ^ h & (a^b) ) + 0x59f111f1 )|0;
          g = ( g + c )|0;
          c = ( c + ( (d & e) ^ ( f & (d ^ e) ) ) + ( d>>>2 ^ d>>>13 ^ d>>>22 ^ d<<30 ^ d<<19 ^ d<<10 ) )|0;

          // 6
          b = ( w6 + b + ( g>>>6 ^ g>>>11 ^ g>>>25 ^ g<<26 ^ g<<21 ^ g<<7 ) +  ( a ^ g & (h^a) ) + 0x923f82a4 )|0;
          f = ( f + b )|0;
          b = ( b + ( (c & d) ^ ( e & (c ^ d) ) ) + ( c>>>2 ^ c>>>13 ^ c>>>22 ^ c<<30 ^ c<<19 ^ c<<10 ) )|0;

          // 7
          a = ( w7 + a + ( f>>>6 ^ f>>>11 ^ f>>>25 ^ f<<26 ^ f<<21 ^ f<<7 ) +  ( h ^ f & (g^h) ) + 0xab1c5ed5 )|0;
          e = ( e + a )|0;
          a = ( a + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;

          // 8
          h = ( w8 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) +  ( g ^ e & (f^g) ) + 0xd807aa98 )|0;
          d = ( d + h )|0;
          h = ( h + ( (a & b) ^ ( c & (a ^ b) ) ) + ( a>>>2 ^ a>>>13 ^ a>>>22 ^ a<<30 ^ a<<19 ^ a<<10 ) )|0;

          // 9
          g = ( w9 + g + ( d>>>6 ^ d>>>11 ^ d>>>25 ^ d<<26 ^ d<<21 ^ d<<7 ) +  ( f ^ d & (e^f) ) + 0x12835b01 )|0;
          c = ( c + g )|0;
          g = ( g + ( (h & a) ^ ( b & (h ^ a) ) ) + ( h>>>2 ^ h>>>13 ^ h>>>22 ^ h<<30 ^ h<<19 ^ h<<10 ) )|0;

          // 10
          f = ( w10 + f + ( c>>>6 ^ c>>>11 ^ c>>>25 ^ c<<26 ^ c<<21 ^ c<<7 ) +  ( e ^ c & (d^e) ) + 0x243185be )|0;
          b = ( b + f )|0;
          f = ( f + ( (g & h) ^ ( a & (g ^ h) ) ) + ( g>>>2 ^ g>>>13 ^ g>>>22 ^ g<<30 ^ g<<19 ^ g<<10 ) )|0;

          // 11
          e = ( w11 + e + ( b>>>6 ^ b>>>11 ^ b>>>25 ^ b<<26 ^ b<<21 ^ b<<7 ) +  ( d ^ b & (c^d) ) + 0x550c7dc3 )|0;
          a = ( a + e )|0;
          e = ( e + ( (f & g) ^ ( h & (f ^ g) ) ) + ( f>>>2 ^ f>>>13 ^ f>>>22 ^ f<<30 ^ f<<19 ^ f<<10 ) )|0;

          // 12
          d = ( w12 + d + ( a>>>6 ^ a>>>11 ^ a>>>25 ^ a<<26 ^ a<<21 ^ a<<7 ) +  ( c ^ a & (b^c) ) + 0x72be5d74 )|0;
          h = ( h + d )|0;
          d = ( d + ( (e & f) ^ ( g & (e ^ f) ) ) + ( e>>>2 ^ e>>>13 ^ e>>>22 ^ e<<30 ^ e<<19 ^ e<<10 ) )|0;

          // 13
          c = ( w13 + c + ( h>>>6 ^ h>>>11 ^ h>>>25 ^ h<<26 ^ h<<21 ^ h<<7 ) +  ( b ^ h & (a^b) ) + 0x80deb1fe )|0;
          g = ( g + c )|0;
          c = ( c + ( (d & e) ^ ( f & (d ^ e) ) ) + ( d>>>2 ^ d>>>13 ^ d>>>22 ^ d<<30 ^ d<<19 ^ d<<10 ) )|0;

          // 14
          b = ( w14 + b + ( g>>>6 ^ g>>>11 ^ g>>>25 ^ g<<26 ^ g<<21 ^ g<<7 ) +  ( a ^ g & (h^a) ) + 0x9bdc06a7 )|0;
          f = ( f + b )|0;
          b = ( b + ( (c & d) ^ ( e & (c ^ d) ) ) + ( c>>>2 ^ c>>>13 ^ c>>>22 ^ c<<30 ^ c<<19 ^ c<<10 ) )|0;

          // 15
          a = ( w15 + a + ( f>>>6 ^ f>>>11 ^ f>>>25 ^ f<<26 ^ f<<21 ^ f<<7 ) +  ( h ^ f & (g^h) ) + 0xc19bf174 )|0;
          e = ( e + a )|0;
          a = ( a + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;

          // 16
          w0 = ( ( w1>>>7  ^ w1>>>18 ^ w1>>>3  ^ w1<<25 ^ w1<<14 ) + ( w14>>>17 ^ w14>>>19 ^ w14>>>10 ^ w14<<15 ^ w14<<13 ) + w0 + w9 )|0;
          h = ( w0 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) +  ( g ^ e & (f^g) ) + 0xe49b69c1 )|0;
          d = ( d + h )|0;
          h = ( h + ( (a & b) ^ ( c & (a ^ b) ) ) + ( a>>>2 ^ a>>>13 ^ a>>>22 ^ a<<30 ^ a<<19 ^ a<<10 ) )|0;

          // 17
          w1 = ( ( w2>>>7  ^ w2>>>18 ^ w2>>>3  ^ w2<<25 ^ w2<<14 ) + ( w15>>>17 ^ w15>>>19 ^ w15>>>10 ^ w15<<15 ^ w15<<13 ) + w1 + w10 )|0;
          g = ( w1 + g + ( d>>>6 ^ d>>>11 ^ d>>>25 ^ d<<26 ^ d<<21 ^ d<<7 ) +  ( f ^ d & (e^f) ) + 0xefbe4786 )|0;
          c = ( c + g )|0;
          g = ( g + ( (h & a) ^ ( b & (h ^ a) ) ) + ( h>>>2 ^ h>>>13 ^ h>>>22 ^ h<<30 ^ h<<19 ^ h<<10 ) )|0;

          // 18
          w2 = ( ( w3>>>7  ^ w3>>>18 ^ w3>>>3  ^ w3<<25 ^ w3<<14 ) + ( w0>>>17 ^ w0>>>19 ^ w0>>>10 ^ w0<<15 ^ w0<<13 ) + w2 + w11 )|0;
          f = ( w2 + f + ( c>>>6 ^ c>>>11 ^ c>>>25 ^ c<<26 ^ c<<21 ^ c<<7 ) +  ( e ^ c & (d^e) ) + 0x0fc19dc6 )|0;
          b = ( b + f )|0;
          f = ( f + ( (g & h) ^ ( a & (g ^ h) ) ) + ( g>>>2 ^ g>>>13 ^ g>>>22 ^ g<<30 ^ g<<19 ^ g<<10 ) )|0;

          // 19
          w3 = ( ( w4>>>7  ^ w4>>>18 ^ w4>>>3  ^ w4<<25 ^ w4<<14 ) + ( w1>>>17 ^ w1>>>19 ^ w1>>>10 ^ w1<<15 ^ w1<<13 ) + w3 + w12 )|0;
          e = ( w3 + e + ( b>>>6 ^ b>>>11 ^ b>>>25 ^ b<<26 ^ b<<21 ^ b<<7 ) +  ( d ^ b & (c^d) ) + 0x240ca1cc )|0;
          a = ( a + e )|0;
          e = ( e + ( (f & g) ^ ( h & (f ^ g) ) ) + ( f>>>2 ^ f>>>13 ^ f>>>22 ^ f<<30 ^ f<<19 ^ f<<10 ) )|0;

          // 20
          w4 = ( ( w5>>>7  ^ w5>>>18 ^ w5>>>3  ^ w5<<25 ^ w5<<14 ) + ( w2>>>17 ^ w2>>>19 ^ w2>>>10 ^ w2<<15 ^ w2<<13 ) + w4 + w13 )|0;
          d = ( w4 + d + ( a>>>6 ^ a>>>11 ^ a>>>25 ^ a<<26 ^ a<<21 ^ a<<7 ) +  ( c ^ a & (b^c) ) + 0x2de92c6f )|0;
          h = ( h + d )|0;
          d = ( d + ( (e & f) ^ ( g & (e ^ f) ) ) + ( e>>>2 ^ e>>>13 ^ e>>>22 ^ e<<30 ^ e<<19 ^ e<<10 ) )|0;

          // 21
          w5 = ( ( w6>>>7  ^ w6>>>18 ^ w6>>>3  ^ w6<<25 ^ w6<<14 ) + ( w3>>>17 ^ w3>>>19 ^ w3>>>10 ^ w3<<15 ^ w3<<13 ) + w5 + w14 )|0;
          c = ( w5 + c + ( h>>>6 ^ h>>>11 ^ h>>>25 ^ h<<26 ^ h<<21 ^ h<<7 ) +  ( b ^ h & (a^b) ) + 0x4a7484aa )|0;
          g = ( g + c )|0;
          c = ( c + ( (d & e) ^ ( f & (d ^ e) ) ) + ( d>>>2 ^ d>>>13 ^ d>>>22 ^ d<<30 ^ d<<19 ^ d<<10 ) )|0;

          // 22
          w6 = ( ( w7>>>7  ^ w7>>>18 ^ w7>>>3  ^ w7<<25 ^ w7<<14 ) + ( w4>>>17 ^ w4>>>19 ^ w4>>>10 ^ w4<<15 ^ w4<<13 ) + w6 + w15 )|0;
          b = ( w6 + b + ( g>>>6 ^ g>>>11 ^ g>>>25 ^ g<<26 ^ g<<21 ^ g<<7 ) +  ( a ^ g & (h^a) ) + 0x5cb0a9dc )|0;
          f = ( f + b )|0;
          b = ( b + ( (c & d) ^ ( e & (c ^ d) ) ) + ( c>>>2 ^ c>>>13 ^ c>>>22 ^ c<<30 ^ c<<19 ^ c<<10 ) )|0;

          // 23
          w7 = ( ( w8>>>7  ^ w8>>>18 ^ w8>>>3  ^ w8<<25 ^ w8<<14 ) + ( w5>>>17 ^ w5>>>19 ^ w5>>>10 ^ w5<<15 ^ w5<<13 ) + w7 + w0 )|0;
          a = ( w7 + a + ( f>>>6 ^ f>>>11 ^ f>>>25 ^ f<<26 ^ f<<21 ^ f<<7 ) +  ( h ^ f & (g^h) ) + 0x76f988da )|0;
          e = ( e + a )|0;
          a = ( a + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;

          // 24
          w8 = ( ( w9>>>7  ^ w9>>>18 ^ w9>>>3  ^ w9<<25 ^ w9<<14 ) + ( w6>>>17 ^ w6>>>19 ^ w6>>>10 ^ w6<<15 ^ w6<<13 ) + w8 + w1 )|0;
          h = ( w8 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) +  ( g ^ e & (f^g) ) + 0x983e5152 )|0;
          d = ( d + h )|0;
          h = ( h + ( (a & b) ^ ( c & (a ^ b) ) ) + ( a>>>2 ^ a>>>13 ^ a>>>22 ^ a<<30 ^ a<<19 ^ a<<10 ) )|0;

          // 25
          w9 = ( ( w10>>>7  ^ w10>>>18 ^ w10>>>3  ^ w10<<25 ^ w10<<14 ) + ( w7>>>17 ^ w7>>>19 ^ w7>>>10 ^ w7<<15 ^ w7<<13 ) + w9 + w2 )|0;
          g = ( w9 + g + ( d>>>6 ^ d>>>11 ^ d>>>25 ^ d<<26 ^ d<<21 ^ d<<7 ) +  ( f ^ d & (e^f) ) + 0xa831c66d )|0;
          c = ( c + g )|0;
          g = ( g + ( (h & a) ^ ( b & (h ^ a) ) ) + ( h>>>2 ^ h>>>13 ^ h>>>22 ^ h<<30 ^ h<<19 ^ h<<10 ) )|0;

          // 26
          w10 = ( ( w11>>>7  ^ w11>>>18 ^ w11>>>3  ^ w11<<25 ^ w11<<14 ) + ( w8>>>17 ^ w8>>>19 ^ w8>>>10 ^ w8<<15 ^ w8<<13 ) + w10 + w3 )|0;
          f = ( w10 + f + ( c>>>6 ^ c>>>11 ^ c>>>25 ^ c<<26 ^ c<<21 ^ c<<7 ) +  ( e ^ c & (d^e) ) + 0xb00327c8 )|0;
          b = ( b + f )|0;
          f = ( f + ( (g & h) ^ ( a & (g ^ h) ) ) + ( g>>>2 ^ g>>>13 ^ g>>>22 ^ g<<30 ^ g<<19 ^ g<<10 ) )|0;

          // 27
          w11 = ( ( w12>>>7  ^ w12>>>18 ^ w12>>>3  ^ w12<<25 ^ w12<<14 ) + ( w9>>>17 ^ w9>>>19 ^ w9>>>10 ^ w9<<15 ^ w9<<13 ) + w11 + w4 )|0;
          e = ( w11 + e + ( b>>>6 ^ b>>>11 ^ b>>>25 ^ b<<26 ^ b<<21 ^ b<<7 ) +  ( d ^ b & (c^d) ) + 0xbf597fc7 )|0;
          a = ( a + e )|0;
          e = ( e + ( (f & g) ^ ( h & (f ^ g) ) ) + ( f>>>2 ^ f>>>13 ^ f>>>22 ^ f<<30 ^ f<<19 ^ f<<10 ) )|0;

          // 28
          w12 = ( ( w13>>>7  ^ w13>>>18 ^ w13>>>3  ^ w13<<25 ^ w13<<14 ) + ( w10>>>17 ^ w10>>>19 ^ w10>>>10 ^ w10<<15 ^ w10<<13 ) + w12 + w5 )|0;
          d = ( w12 + d + ( a>>>6 ^ a>>>11 ^ a>>>25 ^ a<<26 ^ a<<21 ^ a<<7 ) +  ( c ^ a & (b^c) ) + 0xc6e00bf3 )|0;
          h = ( h + d )|0;
          d = ( d + ( (e & f) ^ ( g & (e ^ f) ) ) + ( e>>>2 ^ e>>>13 ^ e>>>22 ^ e<<30 ^ e<<19 ^ e<<10 ) )|0;

          // 29
          w13 = ( ( w14>>>7  ^ w14>>>18 ^ w14>>>3  ^ w14<<25 ^ w14<<14 ) + ( w11>>>17 ^ w11>>>19 ^ w11>>>10 ^ w11<<15 ^ w11<<13 ) + w13 + w6 )|0;
          c = ( w13 + c + ( h>>>6 ^ h>>>11 ^ h>>>25 ^ h<<26 ^ h<<21 ^ h<<7 ) +  ( b ^ h & (a^b) ) + 0xd5a79147 )|0;
          g = ( g + c )|0;
          c = ( c + ( (d & e) ^ ( f & (d ^ e) ) ) + ( d>>>2 ^ d>>>13 ^ d>>>22 ^ d<<30 ^ d<<19 ^ d<<10 ) )|0;

          // 30
          w14 = ( ( w15>>>7  ^ w15>>>18 ^ w15>>>3  ^ w15<<25 ^ w15<<14 ) + ( w12>>>17 ^ w12>>>19 ^ w12>>>10 ^ w12<<15 ^ w12<<13 ) + w14 + w7 )|0;
          b = ( w14 + b + ( g>>>6 ^ g>>>11 ^ g>>>25 ^ g<<26 ^ g<<21 ^ g<<7 ) +  ( a ^ g & (h^a) ) + 0x06ca6351 )|0;
          f = ( f + b )|0;
          b = ( b + ( (c & d) ^ ( e & (c ^ d) ) ) + ( c>>>2 ^ c>>>13 ^ c>>>22 ^ c<<30 ^ c<<19 ^ c<<10 ) )|0;

          // 31
          w15 = ( ( w0>>>7  ^ w0>>>18 ^ w0>>>3  ^ w0<<25 ^ w0<<14 ) + ( w13>>>17 ^ w13>>>19 ^ w13>>>10 ^ w13<<15 ^ w13<<13 ) + w15 + w8 )|0;
          a = ( w15 + a + ( f>>>6 ^ f>>>11 ^ f>>>25 ^ f<<26 ^ f<<21 ^ f<<7 ) +  ( h ^ f & (g^h) ) + 0x14292967 )|0;
          e = ( e + a )|0;
          a = ( a + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;

          // 32
          w0 = ( ( w1>>>7  ^ w1>>>18 ^ w1>>>3  ^ w1<<25 ^ w1<<14 ) + ( w14>>>17 ^ w14>>>19 ^ w14>>>10 ^ w14<<15 ^ w14<<13 ) + w0 + w9 )|0;
          h = ( w0 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) +  ( g ^ e & (f^g) ) + 0x27b70a85 )|0;
          d = ( d + h )|0;
          h = ( h + ( (a & b) ^ ( c & (a ^ b) ) ) + ( a>>>2 ^ a>>>13 ^ a>>>22 ^ a<<30 ^ a<<19 ^ a<<10 ) )|0;

          // 33
          w1 = ( ( w2>>>7  ^ w2>>>18 ^ w2>>>3  ^ w2<<25 ^ w2<<14 ) + ( w15>>>17 ^ w15>>>19 ^ w15>>>10 ^ w15<<15 ^ w15<<13 ) + w1 + w10 )|0;
          g = ( w1 + g + ( d>>>6 ^ d>>>11 ^ d>>>25 ^ d<<26 ^ d<<21 ^ d<<7 ) +  ( f ^ d & (e^f) ) + 0x2e1b2138 )|0;
          c = ( c + g )|0;
          g = ( g + ( (h & a) ^ ( b & (h ^ a) ) ) + ( h>>>2 ^ h>>>13 ^ h>>>22 ^ h<<30 ^ h<<19 ^ h<<10 ) )|0;

          // 34
          w2 = ( ( w3>>>7  ^ w3>>>18 ^ w3>>>3  ^ w3<<25 ^ w3<<14 ) + ( w0>>>17 ^ w0>>>19 ^ w0>>>10 ^ w0<<15 ^ w0<<13 ) + w2 + w11 )|0;
          f = ( w2 + f + ( c>>>6 ^ c>>>11 ^ c>>>25 ^ c<<26 ^ c<<21 ^ c<<7 ) +  ( e ^ c & (d^e) ) + 0x4d2c6dfc )|0;
          b = ( b + f )|0;
          f = ( f + ( (g & h) ^ ( a & (g ^ h) ) ) + ( g>>>2 ^ g>>>13 ^ g>>>22 ^ g<<30 ^ g<<19 ^ g<<10 ) )|0;

          // 35
          w3 = ( ( w4>>>7  ^ w4>>>18 ^ w4>>>3  ^ w4<<25 ^ w4<<14 ) + ( w1>>>17 ^ w1>>>19 ^ w1>>>10 ^ w1<<15 ^ w1<<13 ) + w3 + w12 )|0;
          e = ( w3 + e + ( b>>>6 ^ b>>>11 ^ b>>>25 ^ b<<26 ^ b<<21 ^ b<<7 ) +  ( d ^ b & (c^d) ) + 0x53380d13 )|0;
          a = ( a + e )|0;
          e = ( e + ( (f & g) ^ ( h & (f ^ g) ) ) + ( f>>>2 ^ f>>>13 ^ f>>>22 ^ f<<30 ^ f<<19 ^ f<<10 ) )|0;

          // 36
          w4 = ( ( w5>>>7  ^ w5>>>18 ^ w5>>>3  ^ w5<<25 ^ w5<<14 ) + ( w2>>>17 ^ w2>>>19 ^ w2>>>10 ^ w2<<15 ^ w2<<13 ) + w4 + w13 )|0;
          d = ( w4 + d + ( a>>>6 ^ a>>>11 ^ a>>>25 ^ a<<26 ^ a<<21 ^ a<<7 ) +  ( c ^ a & (b^c) ) + 0x650a7354 )|0;
          h = ( h + d )|0;
          d = ( d + ( (e & f) ^ ( g & (e ^ f) ) ) + ( e>>>2 ^ e>>>13 ^ e>>>22 ^ e<<30 ^ e<<19 ^ e<<10 ) )|0;

          // 37
          w5 = ( ( w6>>>7  ^ w6>>>18 ^ w6>>>3  ^ w6<<25 ^ w6<<14 ) + ( w3>>>17 ^ w3>>>19 ^ w3>>>10 ^ w3<<15 ^ w3<<13 ) + w5 + w14 )|0;
          c = ( w5 + c + ( h>>>6 ^ h>>>11 ^ h>>>25 ^ h<<26 ^ h<<21 ^ h<<7 ) +  ( b ^ h & (a^b) ) + 0x766a0abb )|0;
          g = ( g + c )|0;
          c = ( c + ( (d & e) ^ ( f & (d ^ e) ) ) + ( d>>>2 ^ d>>>13 ^ d>>>22 ^ d<<30 ^ d<<19 ^ d<<10 ) )|0;

          // 38
          w6 = ( ( w7>>>7  ^ w7>>>18 ^ w7>>>3  ^ w7<<25 ^ w7<<14 ) + ( w4>>>17 ^ w4>>>19 ^ w4>>>10 ^ w4<<15 ^ w4<<13 ) + w6 + w15 )|0;
          b = ( w6 + b + ( g>>>6 ^ g>>>11 ^ g>>>25 ^ g<<26 ^ g<<21 ^ g<<7 ) +  ( a ^ g & (h^a) ) + 0x81c2c92e )|0;
          f = ( f + b )|0;
          b = ( b + ( (c & d) ^ ( e & (c ^ d) ) ) + ( c>>>2 ^ c>>>13 ^ c>>>22 ^ c<<30 ^ c<<19 ^ c<<10 ) )|0;

          // 39
          w7 = ( ( w8>>>7  ^ w8>>>18 ^ w8>>>3  ^ w8<<25 ^ w8<<14 ) + ( w5>>>17 ^ w5>>>19 ^ w5>>>10 ^ w5<<15 ^ w5<<13 ) + w7 + w0 )|0;
          a = ( w7 + a + ( f>>>6 ^ f>>>11 ^ f>>>25 ^ f<<26 ^ f<<21 ^ f<<7 ) +  ( h ^ f & (g^h) ) + 0x92722c85 )|0;
          e = ( e + a )|0;
          a = ( a + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;

          // 40
          w8 = ( ( w9>>>7  ^ w9>>>18 ^ w9>>>3  ^ w9<<25 ^ w9<<14 ) + ( w6>>>17 ^ w6>>>19 ^ w6>>>10 ^ w6<<15 ^ w6<<13 ) + w8 + w1 )|0;
          h = ( w8 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) +  ( g ^ e & (f^g) ) + 0xa2bfe8a1 )|0;
          d = ( d + h )|0;
          h = ( h + ( (a & b) ^ ( c & (a ^ b) ) ) + ( a>>>2 ^ a>>>13 ^ a>>>22 ^ a<<30 ^ a<<19 ^ a<<10 ) )|0;

          // 41
          w9 = ( ( w10>>>7  ^ w10>>>18 ^ w10>>>3  ^ w10<<25 ^ w10<<14 ) + ( w7>>>17 ^ w7>>>19 ^ w7>>>10 ^ w7<<15 ^ w7<<13 ) + w9 + w2 )|0;
          g = ( w9 + g + ( d>>>6 ^ d>>>11 ^ d>>>25 ^ d<<26 ^ d<<21 ^ d<<7 ) +  ( f ^ d & (e^f) ) + 0xa81a664b )|0;
          c = ( c + g )|0;
          g = ( g + ( (h & a) ^ ( b & (h ^ a) ) ) + ( h>>>2 ^ h>>>13 ^ h>>>22 ^ h<<30 ^ h<<19 ^ h<<10 ) )|0;

          // 42
          w10 = ( ( w11>>>7  ^ w11>>>18 ^ w11>>>3  ^ w11<<25 ^ w11<<14 ) + ( w8>>>17 ^ w8>>>19 ^ w8>>>10 ^ w8<<15 ^ w8<<13 ) + w10 + w3 )|0;
          f = ( w10 + f + ( c>>>6 ^ c>>>11 ^ c>>>25 ^ c<<26 ^ c<<21 ^ c<<7 ) +  ( e ^ c & (d^e) ) + 0xc24b8b70 )|0;
          b = ( b + f )|0;
          f = ( f + ( (g & h) ^ ( a & (g ^ h) ) ) + ( g>>>2 ^ g>>>13 ^ g>>>22 ^ g<<30 ^ g<<19 ^ g<<10 ) )|0;

          // 43
          w11 = ( ( w12>>>7  ^ w12>>>18 ^ w12>>>3  ^ w12<<25 ^ w12<<14 ) + ( w9>>>17 ^ w9>>>19 ^ w9>>>10 ^ w9<<15 ^ w9<<13 ) + w11 + w4 )|0;
          e = ( w11 + e + ( b>>>6 ^ b>>>11 ^ b>>>25 ^ b<<26 ^ b<<21 ^ b<<7 ) +  ( d ^ b & (c^d) ) + 0xc76c51a3 )|0;
          a = ( a + e )|0;
          e = ( e + ( (f & g) ^ ( h & (f ^ g) ) ) + ( f>>>2 ^ f>>>13 ^ f>>>22 ^ f<<30 ^ f<<19 ^ f<<10 ) )|0;

          // 44
          w12 = ( ( w13>>>7  ^ w13>>>18 ^ w13>>>3  ^ w13<<25 ^ w13<<14 ) + ( w10>>>17 ^ w10>>>19 ^ w10>>>10 ^ w10<<15 ^ w10<<13 ) + w12 + w5 )|0;
          d = ( w12 + d + ( a>>>6 ^ a>>>11 ^ a>>>25 ^ a<<26 ^ a<<21 ^ a<<7 ) +  ( c ^ a & (b^c) ) + 0xd192e819 )|0;
          h = ( h + d )|0;
          d = ( d + ( (e & f) ^ ( g & (e ^ f) ) ) + ( e>>>2 ^ e>>>13 ^ e>>>22 ^ e<<30 ^ e<<19 ^ e<<10 ) )|0;

          // 45
          w13 = ( ( w14>>>7  ^ w14>>>18 ^ w14>>>3  ^ w14<<25 ^ w14<<14 ) + ( w11>>>17 ^ w11>>>19 ^ w11>>>10 ^ w11<<15 ^ w11<<13 ) + w13 + w6 )|0;
          c = ( w13 + c + ( h>>>6 ^ h>>>11 ^ h>>>25 ^ h<<26 ^ h<<21 ^ h<<7 ) +  ( b ^ h & (a^b) ) + 0xd6990624 )|0;
          g = ( g + c )|0;
          c = ( c + ( (d & e) ^ ( f & (d ^ e) ) ) + ( d>>>2 ^ d>>>13 ^ d>>>22 ^ d<<30 ^ d<<19 ^ d<<10 ) )|0;

          // 46
          w14 = ( ( w15>>>7  ^ w15>>>18 ^ w15>>>3  ^ w15<<25 ^ w15<<14 ) + ( w12>>>17 ^ w12>>>19 ^ w12>>>10 ^ w12<<15 ^ w12<<13 ) + w14 + w7 )|0;
          b = ( w14 + b + ( g>>>6 ^ g>>>11 ^ g>>>25 ^ g<<26 ^ g<<21 ^ g<<7 ) +  ( a ^ g & (h^a) ) + 0xf40e3585 )|0;
          f = ( f + b )|0;
          b = ( b + ( (c & d) ^ ( e & (c ^ d) ) ) + ( c>>>2 ^ c>>>13 ^ c>>>22 ^ c<<30 ^ c<<19 ^ c<<10 ) )|0;

          // 47
          w15 = ( ( w0>>>7  ^ w0>>>18 ^ w0>>>3  ^ w0<<25 ^ w0<<14 ) + ( w13>>>17 ^ w13>>>19 ^ w13>>>10 ^ w13<<15 ^ w13<<13 ) + w15 + w8 )|0;
          a = ( w15 + a + ( f>>>6 ^ f>>>11 ^ f>>>25 ^ f<<26 ^ f<<21 ^ f<<7 ) +  ( h ^ f & (g^h) ) + 0x106aa070 )|0;
          e = ( e + a )|0;
          a = ( a + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;

          // 48
          w0 = ( ( w1>>>7  ^ w1>>>18 ^ w1>>>3  ^ w1<<25 ^ w1<<14 ) + ( w14>>>17 ^ w14>>>19 ^ w14>>>10 ^ w14<<15 ^ w14<<13 ) + w0 + w9 )|0;
          h = ( w0 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) +  ( g ^ e & (f^g) ) + 0x19a4c116 )|0;
          d = ( d + h )|0;
          h = ( h + ( (a & b) ^ ( c & (a ^ b) ) ) + ( a>>>2 ^ a>>>13 ^ a>>>22 ^ a<<30 ^ a<<19 ^ a<<10 ) )|0;

          // 49
          w1 = ( ( w2>>>7  ^ w2>>>18 ^ w2>>>3  ^ w2<<25 ^ w2<<14 ) + ( w15>>>17 ^ w15>>>19 ^ w15>>>10 ^ w15<<15 ^ w15<<13 ) + w1 + w10 )|0;
          g = ( w1 + g + ( d>>>6 ^ d>>>11 ^ d>>>25 ^ d<<26 ^ d<<21 ^ d<<7 ) +  ( f ^ d & (e^f) ) + 0x1e376c08 )|0;
          c = ( c + g )|0;
          g = ( g + ( (h & a) ^ ( b & (h ^ a) ) ) + ( h>>>2 ^ h>>>13 ^ h>>>22 ^ h<<30 ^ h<<19 ^ h<<10 ) )|0;

          // 50
          w2 = ( ( w3>>>7  ^ w3>>>18 ^ w3>>>3  ^ w3<<25 ^ w3<<14 ) + ( w0>>>17 ^ w0>>>19 ^ w0>>>10 ^ w0<<15 ^ w0<<13 ) + w2 + w11 )|0;
          f = ( w2 + f + ( c>>>6 ^ c>>>11 ^ c>>>25 ^ c<<26 ^ c<<21 ^ c<<7 ) +  ( e ^ c & (d^e) ) + 0x2748774c )|0;
          b = ( b + f )|0;
          f = ( f + ( (g & h) ^ ( a & (g ^ h) ) ) + ( g>>>2 ^ g>>>13 ^ g>>>22 ^ g<<30 ^ g<<19 ^ g<<10 ) )|0;

          // 51
          w3 = ( ( w4>>>7  ^ w4>>>18 ^ w4>>>3  ^ w4<<25 ^ w4<<14 ) + ( w1>>>17 ^ w1>>>19 ^ w1>>>10 ^ w1<<15 ^ w1<<13 ) + w3 + w12 )|0;
          e = ( w3 + e + ( b>>>6 ^ b>>>11 ^ b>>>25 ^ b<<26 ^ b<<21 ^ b<<7 ) +  ( d ^ b & (c^d) ) + 0x34b0bcb5 )|0;
          a = ( a + e )|0;
          e = ( e + ( (f & g) ^ ( h & (f ^ g) ) ) + ( f>>>2 ^ f>>>13 ^ f>>>22 ^ f<<30 ^ f<<19 ^ f<<10 ) )|0;

          // 52
          w4 = ( ( w5>>>7  ^ w5>>>18 ^ w5>>>3  ^ w5<<25 ^ w5<<14 ) + ( w2>>>17 ^ w2>>>19 ^ w2>>>10 ^ w2<<15 ^ w2<<13 ) + w4 + w13 )|0;
          d = ( w4 + d + ( a>>>6 ^ a>>>11 ^ a>>>25 ^ a<<26 ^ a<<21 ^ a<<7 ) +  ( c ^ a & (b^c) ) + 0x391c0cb3 )|0;
          h = ( h + d )|0;
          d = ( d + ( (e & f) ^ ( g & (e ^ f) ) ) + ( e>>>2 ^ e>>>13 ^ e>>>22 ^ e<<30 ^ e<<19 ^ e<<10 ) )|0;

          // 53
          w5 = ( ( w6>>>7  ^ w6>>>18 ^ w6>>>3  ^ w6<<25 ^ w6<<14 ) + ( w3>>>17 ^ w3>>>19 ^ w3>>>10 ^ w3<<15 ^ w3<<13 ) + w5 + w14 )|0;
          c = ( w5 + c + ( h>>>6 ^ h>>>11 ^ h>>>25 ^ h<<26 ^ h<<21 ^ h<<7 ) +  ( b ^ h & (a^b) ) + 0x4ed8aa4a )|0;
          g = ( g + c )|0;
          c = ( c + ( (d & e) ^ ( f & (d ^ e) ) ) + ( d>>>2 ^ d>>>13 ^ d>>>22 ^ d<<30 ^ d<<19 ^ d<<10 ) )|0;

          // 54
          w6 = ( ( w7>>>7  ^ w7>>>18 ^ w7>>>3  ^ w7<<25 ^ w7<<14 ) + ( w4>>>17 ^ w4>>>19 ^ w4>>>10 ^ w4<<15 ^ w4<<13 ) + w6 + w15 )|0;
          b = ( w6 + b + ( g>>>6 ^ g>>>11 ^ g>>>25 ^ g<<26 ^ g<<21 ^ g<<7 ) +  ( a ^ g & (h^a) ) + 0x5b9cca4f )|0;
          f = ( f + b )|0;
          b = ( b + ( (c & d) ^ ( e & (c ^ d) ) ) + ( c>>>2 ^ c>>>13 ^ c>>>22 ^ c<<30 ^ c<<19 ^ c<<10 ) )|0;

          // 55
          w7 = ( ( w8>>>7  ^ w8>>>18 ^ w8>>>3  ^ w8<<25 ^ w8<<14 ) + ( w5>>>17 ^ w5>>>19 ^ w5>>>10 ^ w5<<15 ^ w5<<13 ) + w7 + w0 )|0;
          a = ( w7 + a + ( f>>>6 ^ f>>>11 ^ f>>>25 ^ f<<26 ^ f<<21 ^ f<<7 ) +  ( h ^ f & (g^h) ) + 0x682e6ff3 )|0;
          e = ( e + a )|0;
          a = ( a + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;

          // 56
          w8 = ( ( w9>>>7  ^ w9>>>18 ^ w9>>>3  ^ w9<<25 ^ w9<<14 ) + ( w6>>>17 ^ w6>>>19 ^ w6>>>10 ^ w6<<15 ^ w6<<13 ) + w8 + w1 )|0;
          h = ( w8 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) +  ( g ^ e & (f^g) ) + 0x748f82ee )|0;
          d = ( d + h )|0;
          h = ( h + ( (a & b) ^ ( c & (a ^ b) ) ) + ( a>>>2 ^ a>>>13 ^ a>>>22 ^ a<<30 ^ a<<19 ^ a<<10 ) )|0;

          // 57
          w9 = ( ( w10>>>7  ^ w10>>>18 ^ w10>>>3  ^ w10<<25 ^ w10<<14 ) + ( w7>>>17 ^ w7>>>19 ^ w7>>>10 ^ w7<<15 ^ w7<<13 ) + w9 + w2 )|0;
          g = ( w9 + g + ( d>>>6 ^ d>>>11 ^ d>>>25 ^ d<<26 ^ d<<21 ^ d<<7 ) +  ( f ^ d & (e^f) ) + 0x78a5636f )|0;
          c = ( c + g )|0;
          g = ( g + ( (h & a) ^ ( b & (h ^ a) ) ) + ( h>>>2 ^ h>>>13 ^ h>>>22 ^ h<<30 ^ h<<19 ^ h<<10 ) )|0;

          // 58
          w10 = ( ( w11>>>7  ^ w11>>>18 ^ w11>>>3  ^ w11<<25 ^ w11<<14 ) + ( w8>>>17 ^ w8>>>19 ^ w8>>>10 ^ w8<<15 ^ w8<<13 ) + w10 + w3 )|0;
          f = ( w10 + f + ( c>>>6 ^ c>>>11 ^ c>>>25 ^ c<<26 ^ c<<21 ^ c<<7 ) +  ( e ^ c & (d^e) ) + 0x84c87814 )|0;
          b = ( b + f )|0;
          f = ( f + ( (g & h) ^ ( a & (g ^ h) ) ) + ( g>>>2 ^ g>>>13 ^ g>>>22 ^ g<<30 ^ g<<19 ^ g<<10 ) )|0;

          // 59
          w11 = ( ( w12>>>7  ^ w12>>>18 ^ w12>>>3  ^ w12<<25 ^ w12<<14 ) + ( w9>>>17 ^ w9>>>19 ^ w9>>>10 ^ w9<<15 ^ w9<<13 ) + w11 + w4 )|0;
          e = ( w11 + e + ( b>>>6 ^ b>>>11 ^ b>>>25 ^ b<<26 ^ b<<21 ^ b<<7 ) +  ( d ^ b & (c^d) ) + 0x8cc70208 )|0;
          a = ( a + e )|0;
          e = ( e + ( (f & g) ^ ( h & (f ^ g) ) ) + ( f>>>2 ^ f>>>13 ^ f>>>22 ^ f<<30 ^ f<<19 ^ f<<10 ) )|0;

          // 60
          w12 = ( ( w13>>>7  ^ w13>>>18 ^ w13>>>3  ^ w13<<25 ^ w13<<14 ) + ( w10>>>17 ^ w10>>>19 ^ w10>>>10 ^ w10<<15 ^ w10<<13 ) + w12 + w5 )|0;
          d = ( w12 + d + ( a>>>6 ^ a>>>11 ^ a>>>25 ^ a<<26 ^ a<<21 ^ a<<7 ) +  ( c ^ a & (b^c) ) + 0x90befffa )|0;
          h = ( h + d )|0;
          d = ( d + ( (e & f) ^ ( g & (e ^ f) ) ) + ( e>>>2 ^ e>>>13 ^ e>>>22 ^ e<<30 ^ e<<19 ^ e<<10 ) )|0;

          // 61
          w13 = ( ( w14>>>7  ^ w14>>>18 ^ w14>>>3  ^ w14<<25 ^ w14<<14 ) + ( w11>>>17 ^ w11>>>19 ^ w11>>>10 ^ w11<<15 ^ w11<<13 ) + w13 + w6 )|0;
          c = ( w13 + c + ( h>>>6 ^ h>>>11 ^ h>>>25 ^ h<<26 ^ h<<21 ^ h<<7 ) +  ( b ^ h & (a^b) ) + 0xa4506ceb )|0;
          g = ( g + c )|0;
          c = ( c + ( (d & e) ^ ( f & (d ^ e) ) ) + ( d>>>2 ^ d>>>13 ^ d>>>22 ^ d<<30 ^ d<<19 ^ d<<10 ) )|0;

          // 62
          w14 = ( ( w15>>>7  ^ w15>>>18 ^ w15>>>3  ^ w15<<25 ^ w15<<14 ) + ( w12>>>17 ^ w12>>>19 ^ w12>>>10 ^ w12<<15 ^ w12<<13 ) + w14 + w7 )|0;
          b = ( w14 + b + ( g>>>6 ^ g>>>11 ^ g>>>25 ^ g<<26 ^ g<<21 ^ g<<7 ) +  ( a ^ g & (h^a) ) + 0xbef9a3f7 )|0;
          f = ( f + b )|0;
          b = ( b + ( (c & d) ^ ( e & (c ^ d) ) ) + ( c>>>2 ^ c>>>13 ^ c>>>22 ^ c<<30 ^ c<<19 ^ c<<10 ) )|0;

          // 63
          w15 = ( ( w0>>>7  ^ w0>>>18 ^ w0>>>3  ^ w0<<25 ^ w0<<14 ) + ( w13>>>17 ^ w13>>>19 ^ w13>>>10 ^ w13<<15 ^ w13<<13 ) + w15 + w8 )|0;
          a = ( w15 + a + ( f>>>6 ^ f>>>11 ^ f>>>25 ^ f<<26 ^ f<<21 ^ f<<7 ) +  ( h ^ f & (g^h) ) + 0xc67178f2 )|0;
          e = ( e + a )|0;
          a = ( a + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;

          H0 = ( H0 + a )|0;
          H1 = ( H1 + b )|0;
          H2 = ( H2 + c )|0;
          H3 = ( H3 + d )|0;
          H4 = ( H4 + e )|0;
          H5 = ( H5 + f )|0;
          H6 = ( H6 + g )|0;
          H7 = ( H7 + h )|0;
      }

      function _core_heap ( offset ) {
          offset = offset|0;

          _core(
              HEAP[offset|0]<<24 | HEAP[offset|1]<<16 | HEAP[offset|2]<<8 | HEAP[offset|3],
              HEAP[offset|4]<<24 | HEAP[offset|5]<<16 | HEAP[offset|6]<<8 | HEAP[offset|7],
              HEAP[offset|8]<<24 | HEAP[offset|9]<<16 | HEAP[offset|10]<<8 | HEAP[offset|11],
              HEAP[offset|12]<<24 | HEAP[offset|13]<<16 | HEAP[offset|14]<<8 | HEAP[offset|15],
              HEAP[offset|16]<<24 | HEAP[offset|17]<<16 | HEAP[offset|18]<<8 | HEAP[offset|19],
              HEAP[offset|20]<<24 | HEAP[offset|21]<<16 | HEAP[offset|22]<<8 | HEAP[offset|23],
              HEAP[offset|24]<<24 | HEAP[offset|25]<<16 | HEAP[offset|26]<<8 | HEAP[offset|27],
              HEAP[offset|28]<<24 | HEAP[offset|29]<<16 | HEAP[offset|30]<<8 | HEAP[offset|31],
              HEAP[offset|32]<<24 | HEAP[offset|33]<<16 | HEAP[offset|34]<<8 | HEAP[offset|35],
              HEAP[offset|36]<<24 | HEAP[offset|37]<<16 | HEAP[offset|38]<<8 | HEAP[offset|39],
              HEAP[offset|40]<<24 | HEAP[offset|41]<<16 | HEAP[offset|42]<<8 | HEAP[offset|43],
              HEAP[offset|44]<<24 | HEAP[offset|45]<<16 | HEAP[offset|46]<<8 | HEAP[offset|47],
              HEAP[offset|48]<<24 | HEAP[offset|49]<<16 | HEAP[offset|50]<<8 | HEAP[offset|51],
              HEAP[offset|52]<<24 | HEAP[offset|53]<<16 | HEAP[offset|54]<<8 | HEAP[offset|55],
              HEAP[offset|56]<<24 | HEAP[offset|57]<<16 | HEAP[offset|58]<<8 | HEAP[offset|59],
              HEAP[offset|60]<<24 | HEAP[offset|61]<<16 | HEAP[offset|62]<<8 | HEAP[offset|63]
          );
      }

      // offset — multiple of 32
      function _state_to_heap ( output ) {
          output = output|0;

          HEAP[output|0] = H0>>>24;
          HEAP[output|1] = H0>>>16&255;
          HEAP[output|2] = H0>>>8&255;
          HEAP[output|3] = H0&255;
          HEAP[output|4] = H1>>>24;
          HEAP[output|5] = H1>>>16&255;
          HEAP[output|6] = H1>>>8&255;
          HEAP[output|7] = H1&255;
          HEAP[output|8] = H2>>>24;
          HEAP[output|9] = H2>>>16&255;
          HEAP[output|10] = H2>>>8&255;
          HEAP[output|11] = H2&255;
          HEAP[output|12] = H3>>>24;
          HEAP[output|13] = H3>>>16&255;
          HEAP[output|14] = H3>>>8&255;
          HEAP[output|15] = H3&255;
          HEAP[output|16] = H4>>>24;
          HEAP[output|17] = H4>>>16&255;
          HEAP[output|18] = H4>>>8&255;
          HEAP[output|19] = H4&255;
          HEAP[output|20] = H5>>>24;
          HEAP[output|21] = H5>>>16&255;
          HEAP[output|22] = H5>>>8&255;
          HEAP[output|23] = H5&255;
          HEAP[output|24] = H6>>>24;
          HEAP[output|25] = H6>>>16&255;
          HEAP[output|26] = H6>>>8&255;
          HEAP[output|27] = H6&255;
          HEAP[output|28] = H7>>>24;
          HEAP[output|29] = H7>>>16&255;
          HEAP[output|30] = H7>>>8&255;
          HEAP[output|31] = H7&255;
      }

      function reset () {
          H0 = 0x6a09e667;
          H1 = 0xbb67ae85;
          H2 = 0x3c6ef372;
          H3 = 0xa54ff53a;
          H4 = 0x510e527f;
          H5 = 0x9b05688c;
          H6 = 0x1f83d9ab;
          H7 = 0x5be0cd19;
          TOTAL0 = TOTAL1 = 0;
      }

      function init ( h0, h1, h2, h3, h4, h5, h6, h7, total0, total1 ) {
          h0 = h0|0;
          h1 = h1|0;
          h2 = h2|0;
          h3 = h3|0;
          h4 = h4|0;
          h5 = h5|0;
          h6 = h6|0;
          h7 = h7|0;
          total0 = total0|0;
          total1 = total1|0;

          H0 = h0;
          H1 = h1;
          H2 = h2;
          H3 = h3;
          H4 = h4;
          H5 = h5;
          H6 = h6;
          H7 = h7;
          TOTAL0 = total0;
          TOTAL1 = total1;
      }

      // offset — multiple of 64
      function process ( offset, length ) {
          offset = offset|0;
          length = length|0;

          var hashed = 0;

          if ( offset & 63 )
              return -1;

          while ( (length|0) >= 64 ) {
              _core_heap(offset);

              offset = ( offset + 64 )|0;
              length = ( length - 64 )|0;

              hashed = ( hashed + 64 )|0;
          }

          TOTAL0 = ( TOTAL0 + hashed )|0;
          if ( TOTAL0>>>0 < hashed>>>0 ) TOTAL1 = ( TOTAL1 + 1 )|0;

          return hashed|0;
      }

      // offset — multiple of 64
      // output — multiple of 32
      function finish ( offset, length, output ) {
          offset = offset|0;
          length = length|0;
          output = output|0;

          var hashed = 0,
              i = 0;

          if ( offset & 63 )
              return -1;

          if ( ~output )
              if ( output & 31 )
                  return -1;

          if ( (length|0) >= 64 ) {
              hashed = process( offset, length )|0;
              if ( (hashed|0) == -1 )
                  return -1;

              offset = ( offset + hashed )|0;
              length = ( length - hashed )|0;
          }

          hashed = ( hashed + length )|0;
          TOTAL0 = ( TOTAL0 + length )|0;
          if ( TOTAL0>>>0 < length>>>0 ) TOTAL1 = ( TOTAL1 + 1 )|0;

          HEAP[offset|length] = 0x80;

          if ( (length|0) >= 56 ) {
              for ( i = (length+1)|0; (i|0) < 64; i = (i+1)|0 )
                  HEAP[offset|i] = 0x00;

              _core_heap(offset);

              length = 0;

              HEAP[offset|0] = 0;
          }

          for ( i = (length+1)|0; (i|0) < 59; i = (i+1)|0 )
              HEAP[offset|i] = 0;

          HEAP[offset|56] = TOTAL1>>>21&255;
          HEAP[offset|57] = TOTAL1>>>13&255;
          HEAP[offset|58] = TOTAL1>>>5&255;
          HEAP[offset|59] = TOTAL1<<3&255 | TOTAL0>>>29;
          HEAP[offset|60] = TOTAL0>>>21&255;
          HEAP[offset|61] = TOTAL0>>>13&255;
          HEAP[offset|62] = TOTAL0>>>5&255;
          HEAP[offset|63] = TOTAL0<<3&255;
          _core_heap(offset);

          if ( ~output )
              _state_to_heap(output);

          return hashed|0;
      }

      function hmac_reset () {
          H0 = I0;
          H1 = I1;
          H2 = I2;
          H3 = I3;
          H4 = I4;
          H5 = I5;
          H6 = I6;
          H7 = I7;
          TOTAL0 = 64;
          TOTAL1 = 0;
      }

      function _hmac_opad () {
          H0 = O0;
          H1 = O1;
          H2 = O2;
          H3 = O3;
          H4 = O4;
          H5 = O5;
          H6 = O6;
          H7 = O7;
          TOTAL0 = 64;
          TOTAL1 = 0;
      }

      function hmac_init ( p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ) {
          p0 = p0|0;
          p1 = p1|0;
          p2 = p2|0;
          p3 = p3|0;
          p4 = p4|0;
          p5 = p5|0;
          p6 = p6|0;
          p7 = p7|0;
          p8 = p8|0;
          p9 = p9|0;
          p10 = p10|0;
          p11 = p11|0;
          p12 = p12|0;
          p13 = p13|0;
          p14 = p14|0;
          p15 = p15|0;

          // opad
          reset();
          _core(
              p0 ^ 0x5c5c5c5c,
              p1 ^ 0x5c5c5c5c,
              p2 ^ 0x5c5c5c5c,
              p3 ^ 0x5c5c5c5c,
              p4 ^ 0x5c5c5c5c,
              p5 ^ 0x5c5c5c5c,
              p6 ^ 0x5c5c5c5c,
              p7 ^ 0x5c5c5c5c,
              p8 ^ 0x5c5c5c5c,
              p9 ^ 0x5c5c5c5c,
              p10 ^ 0x5c5c5c5c,
              p11 ^ 0x5c5c5c5c,
              p12 ^ 0x5c5c5c5c,
              p13 ^ 0x5c5c5c5c,
              p14 ^ 0x5c5c5c5c,
              p15 ^ 0x5c5c5c5c
          );
          O0 = H0;
          O1 = H1;
          O2 = H2;
          O3 = H3;
          O4 = H4;
          O5 = H5;
          O6 = H6;
          O7 = H7;

          // ipad
          reset();
          _core(
              p0 ^ 0x36363636,
              p1 ^ 0x36363636,
              p2 ^ 0x36363636,
              p3 ^ 0x36363636,
              p4 ^ 0x36363636,
              p5 ^ 0x36363636,
              p6 ^ 0x36363636,
              p7 ^ 0x36363636,
              p8 ^ 0x36363636,
              p9 ^ 0x36363636,
              p10 ^ 0x36363636,
              p11 ^ 0x36363636,
              p12 ^ 0x36363636,
              p13 ^ 0x36363636,
              p14 ^ 0x36363636,
              p15 ^ 0x36363636
          );
          I0 = H0;
          I1 = H1;
          I2 = H2;
          I3 = H3;
          I4 = H4;
          I5 = H5;
          I6 = H6;
          I7 = H7;

          TOTAL0 = 64;
          TOTAL1 = 0;
      }

      // offset — multiple of 64
      // output — multiple of 32
      function hmac_finish ( offset, length, output ) {
          offset = offset|0;
          length = length|0;
          output = output|0;

          var t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
              hashed = 0;

          if ( offset & 63 )
              return -1;

          if ( ~output )
              if ( output & 31 )
                  return -1;

          hashed = finish( offset, length, -1 )|0;
          t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;

          _hmac_opad();
          _core( t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768 );

          if ( ~output )
              _state_to_heap(output);

          return hashed|0;
      }

      // salt is assumed to be already processed
      // offset — multiple of 64
      // output — multiple of 32
      function pbkdf2_generate_block ( offset, length, block, count, output ) {
          offset = offset|0;
          length = length|0;
          block = block|0;
          count = count|0;
          output = output|0;

          var h0 = 0, h1 = 0, h2 = 0, h3 = 0, h4 = 0, h5 = 0, h6 = 0, h7 = 0,
              t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0;

          if ( offset & 63 )
              return -1;

          if ( ~output )
              if ( output & 31 )
                  return -1;

          // pad block number into heap
          // FIXME probable OOB write
          HEAP[(offset+length)|0]   = block>>>24;
          HEAP[(offset+length+1)|0] = block>>>16&255;
          HEAP[(offset+length+2)|0] = block>>>8&255;
          HEAP[(offset+length+3)|0] = block&255;

          // finish first iteration
          hmac_finish( offset, (length+4)|0, -1 )|0;
          h0 = t0 = H0, h1 = t1 = H1, h2 = t2 = H2, h3 = t3 = H3, h4 = t4 = H4, h5 = t5 = H5, h6 = t6 = H6, h7 = t7 = H7;
          count = (count-1)|0;

          // perform the rest iterations
          while ( (count|0) > 0 ) {
              hmac_reset();
              _core( t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768 );
              t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;

              _hmac_opad();
              _core( t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768 );
              t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;

              h0 = h0 ^ H0;
              h1 = h1 ^ H1;
              h2 = h2 ^ H2;
              h3 = h3 ^ H3;
              h4 = h4 ^ H4;
              h5 = h5 ^ H5;
              h6 = h6 ^ H6;
              h7 = h7 ^ H7;

              count = (count-1)|0;
          }

          H0 = h0;
          H1 = h1;
          H2 = h2;
          H3 = h3;
          H4 = h4;
          H5 = h5;
          H6 = h6;
          H7 = h7;

          if ( ~output )
              _state_to_heap(output);

          return 0;
      }

      return {
        // SHA256
        reset: reset,
        init: init,
        process: process,
        finish: finish,

        // HMAC-SHA256
        hmac_reset: hmac_reset,
        hmac_init: hmac_init,
        hmac_finish: hmac_finish,

        // PBKDF2-HMAC-SHA256
        pbkdf2_generate_block: pbkdf2_generate_block
      }
  };

  const _sha256_block_size = 64;
  const _sha256_hash_size = 32;
  const heap_pool$2 = [];
  const asm_pool$2 = [];
  class Sha256 extends Hash {
      constructor() {
          super();
          this.NAME = 'sha256';
          this.BLOCK_SIZE = _sha256_block_size;
          this.HASH_SIZE = _sha256_hash_size;
          this.acquire_asm();
      }
      acquire_asm() {
          if (this.heap === undefined || this.asm === undefined) {
              this.heap = heap_pool$2.pop() || _heap_init();
              this.asm = asm_pool$2.pop() || sha256_asm({ Uint8Array: Uint8Array }, null, this.heap.buffer);
              this.reset();
          }
          return { heap: this.heap, asm: this.asm };
      }
      release_asm() {
          if (this.heap !== undefined && this.asm !== undefined) {
              heap_pool$2.push(this.heap);
              asm_pool$2.push(this.asm);
          }
          this.heap = undefined;
          this.asm = undefined;
      }
      static bytes(data) {
          return new Sha256().process(data).finish().result;
      }
  }
  Sha256.NAME = 'sha256';

  var minimalisticAssert = assert;

  function assert(val, msg) {
    if (!val)
      throw new Error(msg || 'Assertion failed');
  }

  assert.equal = function assertEqual(l, r, msg) {
    if (l != r)
      throw new Error(msg || ('Assertion failed: ' + l + ' != ' + r));
  };

  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

  function createCommonjsModule(fn, module) {
  	return module = { exports: {} }, fn(module, module.exports), module.exports;
  }

  function commonjsRequire () {
  	throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
  }

  var inherits_browser = createCommonjsModule(function (module) {
  if (typeof Object.create === 'function') {
    // implementation from standard node.js 'util' module
    module.exports = function inherits(ctor, superCtor) {
      ctor.super_ = superCtor;
      ctor.prototype = Object.create(superCtor.prototype, {
        constructor: {
          value: ctor,
          enumerable: false,
          writable: true,
          configurable: true
        }
      });
    };
  } else {
    // old school shim for old browsers
    module.exports = function inherits(ctor, superCtor) {
      ctor.super_ = superCtor;
      var TempCtor = function () {};
      TempCtor.prototype = superCtor.prototype;
      ctor.prototype = new TempCtor();
      ctor.prototype.constructor = ctor;
    };
  }
  });

  var inherits_1 = inherits_browser;

  function toArray(msg, enc) {
    if (Array.isArray(msg))
      return msg.slice();
    if (!msg)
      return [];
    var res = [];
    if (typeof msg === 'string') {
      if (!enc) {
        for (var i = 0; i < msg.length; i++) {
          var c = msg.charCodeAt(i);
          var hi = c >> 8;
          var lo = c & 0xff;
          if (hi)
            res.push(hi, lo);
          else
            res.push(lo);
        }
      } else if (enc === 'hex') {
        msg = msg.replace(/[^a-z0-9]+/ig, '');
        if (msg.length % 2 !== 0)
          msg = '0' + msg;
        for (i = 0; i < msg.length; i += 2)
          res.push(parseInt(msg[i] + msg[i + 1], 16));
      }
    } else {
      for (i = 0; i < msg.length; i++)
        res[i] = msg[i] | 0;
    }
    return res;
  }
  var toArray_1 = toArray;

  function toHex(msg) {
    var res = '';
    for (var i = 0; i < msg.length; i++)
      res += zero2(msg[i].toString(16));
    return res;
  }
  var toHex_1 = toHex;

  function htonl(w) {
    var res = (w >>> 24) |
              ((w >>> 8) & 0xff00) |
              ((w << 8) & 0xff0000) |
              ((w & 0xff) << 24);
    return res >>> 0;
  }
  var htonl_1 = htonl;

  function toHex32(msg, endian) {
    var res = '';
    for (var i = 0; i < msg.length; i++) {
      var w = msg[i];
      if (endian === 'little')
        w = htonl(w);
      res += zero8(w.toString(16));
    }
    return res;
  }
  var toHex32_1 = toHex32;

  function zero2(word) {
    if (word.length === 1)
      return '0' + word;
    else
      return word;
  }
  var zero2_1 = zero2;

  function zero8(word) {
    if (word.length === 7)
      return '0' + word;
    else if (word.length === 6)
      return '00' + word;
    else if (word.length === 5)
      return '000' + word;
    else if (word.length === 4)
      return '0000' + word;
    else if (word.length === 3)
      return '00000' + word;
    else if (word.length === 2)
      return '000000' + word;
    else if (word.length === 1)
      return '0000000' + word;
    else
      return word;
  }
  var zero8_1 = zero8;

  function join32(msg, start, end, endian) {
    var len = end - start;
    minimalisticAssert(len % 4 === 0);
    var res = new Array(len / 4);
    for (var i = 0, k = start; i < res.length; i++, k += 4) {
      var w;
      if (endian === 'big')
        w = (msg[k] << 24) | (msg[k + 1] << 16) | (msg[k + 2] << 8) | msg[k + 3];
      else
        w = (msg[k + 3] << 24) | (msg[k + 2] << 16) | (msg[k + 1] << 8) | msg[k];
      res[i] = w >>> 0;
    }
    return res;
  }
  var join32_1 = join32;

  function split32(msg, endian) {
    var res = new Array(msg.length * 4);
    for (var i = 0, k = 0; i < msg.length; i++, k += 4) {
      var m = msg[i];
      if (endian === 'big') {
        res[k] = m >>> 24;
        res[k + 1] = (m >>> 16) & 0xff;
        res[k + 2] = (m >>> 8) & 0xff;
        res[k + 3] = m & 0xff;
      } else {
        res[k + 3] = m >>> 24;
        res[k + 2] = (m >>> 16) & 0xff;
        res[k + 1] = (m >>> 8) & 0xff;
        res[k] = m & 0xff;
      }
    }
    return res;
  }
  var split32_1 = split32;

  function rotr32(w, b) {
    return (w >>> b) | (w << (32 - b));
  }
  var rotr32_1 = rotr32;

  function rotl32(w, b) {
    return (w << b) | (w >>> (32 - b));
  }
  var rotl32_1 = rotl32;

  function sum32(a, b) {
    return (a + b) >>> 0;
  }
  var sum32_1 = sum32;

  function sum32_3(a, b, c) {
    return (a + b + c) >>> 0;
  }
  var sum32_3_1 = sum32_3;

  function sum32_4(a, b, c, d) {
    return (a + b + c + d) >>> 0;
  }
  var sum32_4_1 = sum32_4;

  function sum32_5(a, b, c, d, e) {
    return (a + b + c + d + e) >>> 0;
  }
  var sum32_5_1 = sum32_5;

  function sum64(buf, pos, ah, al) {
    var bh = buf[pos];
    var bl = buf[pos + 1];

    var lo = (al + bl) >>> 0;
    var hi = (lo < al ? 1 : 0) + ah + bh;
    buf[pos] = hi >>> 0;
    buf[pos + 1] = lo;
  }
  var sum64_1 = sum64;

  function sum64_hi(ah, al, bh, bl) {
    var lo = (al + bl) >>> 0;
    var hi = (lo < al ? 1 : 0) + ah + bh;
    return hi >>> 0;
  }
  var sum64_hi_1 = sum64_hi;

  function sum64_lo(ah, al, bh, bl) {
    var lo = al + bl;
    return lo >>> 0;
  }
  var sum64_lo_1 = sum64_lo;

  function sum64_4_hi(ah, al, bh, bl, ch, cl, dh, dl) {
    var carry = 0;
    var lo = al;
    lo = (lo + bl) >>> 0;
    carry += lo < al ? 1 : 0;
    lo = (lo + cl) >>> 0;
    carry += lo < cl ? 1 : 0;
    lo = (lo + dl) >>> 0;
    carry += lo < dl ? 1 : 0;

    var hi = ah + bh + ch + dh + carry;
    return hi >>> 0;
  }
  var sum64_4_hi_1 = sum64_4_hi;

  function sum64_4_lo(ah, al, bh, bl, ch, cl, dh, dl) {
    var lo = al + bl + cl + dl;
    return lo >>> 0;
  }
  var sum64_4_lo_1 = sum64_4_lo;

  function sum64_5_hi(ah, al, bh, bl, ch, cl, dh, dl, eh, el) {
    var carry = 0;
    var lo = al;
    lo = (lo + bl) >>> 0;
    carry += lo < al ? 1 : 0;
    lo = (lo + cl) >>> 0;
    carry += lo < cl ? 1 : 0;
    lo = (lo + dl) >>> 0;
    carry += lo < dl ? 1 : 0;
    lo = (lo + el) >>> 0;
    carry += lo < el ? 1 : 0;

    var hi = ah + bh + ch + dh + eh + carry;
    return hi >>> 0;
  }
  var sum64_5_hi_1 = sum64_5_hi;

  function sum64_5_lo(ah, al, bh, bl, ch, cl, dh, dl, eh, el) {
    var lo = al + bl + cl + dl + el;

    return lo >>> 0;
  }
  var sum64_5_lo_1 = sum64_5_lo;

  function rotr64_hi(ah, al, num) {
    var r = (al << (32 - num)) | (ah >>> num);
    return r >>> 0;
  }
  var rotr64_hi_1 = rotr64_hi;

  function rotr64_lo(ah, al, num) {
    var r = (ah << (32 - num)) | (al >>> num);
    return r >>> 0;
  }
  var rotr64_lo_1 = rotr64_lo;

  function shr64_hi(ah, al, num) {
    return ah >>> num;
  }
  var shr64_hi_1 = shr64_hi;

  function shr64_lo(ah, al, num) {
    var r = (ah << (32 - num)) | (al >>> num);
    return r >>> 0;
  }
  var shr64_lo_1 = shr64_lo;

  var utils = {
  	inherits: inherits_1,
  	toArray: toArray_1,
  	toHex: toHex_1,
  	htonl: htonl_1,
  	toHex32: toHex32_1,
  	zero2: zero2_1,
  	zero8: zero8_1,
  	join32: join32_1,
  	split32: split32_1,
  	rotr32: rotr32_1,
  	rotl32: rotl32_1,
  	sum32: sum32_1,
  	sum32_3: sum32_3_1,
  	sum32_4: sum32_4_1,
  	sum32_5: sum32_5_1,
  	sum64: sum64_1,
  	sum64_hi: sum64_hi_1,
  	sum64_lo: sum64_lo_1,
  	sum64_4_hi: sum64_4_hi_1,
  	sum64_4_lo: sum64_4_lo_1,
  	sum64_5_hi: sum64_5_hi_1,
  	sum64_5_lo: sum64_5_lo_1,
  	rotr64_hi: rotr64_hi_1,
  	rotr64_lo: rotr64_lo_1,
  	shr64_hi: shr64_hi_1,
  	shr64_lo: shr64_lo_1
  };

  function BlockHash() {
    this.pending = null;
    this.pendingTotal = 0;
    this.blockSize = this.constructor.blockSize;
    this.outSize = this.constructor.outSize;
    this.hmacStrength = this.constructor.hmacStrength;
    this.padLength = this.constructor.padLength / 8;
    this.endian = 'big';

    this._delta8 = this.blockSize / 8;
    this._delta32 = this.blockSize / 32;
  }
  var BlockHash_1 = BlockHash;

  BlockHash.prototype.update = function update(msg, enc) {
    // Convert message to array, pad it, and join into 32bit blocks
    msg = utils.toArray(msg, enc);
    if (!this.pending)
      this.pending = msg;
    else
      this.pending = this.pending.concat(msg);
    this.pendingTotal += msg.length;

    // Enough data, try updating
    if (this.pending.length >= this._delta8) {
      msg = this.pending;

      // Process pending data in blocks
      var r = msg.length % this._delta8;
      this.pending = msg.slice(msg.length - r, msg.length);
      if (this.pending.length === 0)
        this.pending = null;

      msg = utils.join32(msg, 0, msg.length - r, this.endian);
      for (var i = 0; i < msg.length; i += this._delta32)
        this._update(msg, i, i + this._delta32);
    }

    return this;
  };

  BlockHash.prototype.digest = function digest(enc) {
    this.update(this._pad());
    minimalisticAssert(this.pending === null);

    return this._digest(enc);
  };

  BlockHash.prototype._pad = function pad() {
    var len = this.pendingTotal;
    var bytes = this._delta8;
    var k = bytes - ((len + this.padLength) % bytes);
    var res = new Array(k + this.padLength);
    res[0] = 0x80;
    for (var i = 1; i < k; i++)
      res[i] = 0;

    // Append length
    len <<= 3;
    if (this.endian === 'big') {
      for (var t = 8; t < this.padLength; t++)
        res[i++] = 0;

      res[i++] = 0;
      res[i++] = 0;
      res[i++] = 0;
      res[i++] = 0;
      res[i++] = (len >>> 24) & 0xff;
      res[i++] = (len >>> 16) & 0xff;
      res[i++] = (len >>> 8) & 0xff;
      res[i++] = len & 0xff;
    } else {
      res[i++] = len & 0xff;
      res[i++] = (len >>> 8) & 0xff;
      res[i++] = (len >>> 16) & 0xff;
      res[i++] = (len >>> 24) & 0xff;
      res[i++] = 0;
      res[i++] = 0;
      res[i++] = 0;
      res[i++] = 0;

      for (t = 8; t < this.padLength; t++)
        res[i++] = 0;
    }

    return res;
  };

  var common = {
  	BlockHash: BlockHash_1
  };

  var rotr32$1 = utils.rotr32;

  function ft_1(s, x, y, z) {
    if (s === 0)
      return ch32(x, y, z);
    if (s === 1 || s === 3)
      return p32(x, y, z);
    if (s === 2)
      return maj32(x, y, z);
  }
  var ft_1_1 = ft_1;

  function ch32(x, y, z) {
    return (x & y) ^ ((~x) & z);
  }
  var ch32_1 = ch32;

  function maj32(x, y, z) {
    return (x & y) ^ (x & z) ^ (y & z);
  }
  var maj32_1 = maj32;

  function p32(x, y, z) {
    return x ^ y ^ z;
  }
  var p32_1 = p32;

  function s0_256(x) {
    return rotr32$1(x, 2) ^ rotr32$1(x, 13) ^ rotr32$1(x, 22);
  }
  var s0_256_1 = s0_256;

  function s1_256(x) {
    return rotr32$1(x, 6) ^ rotr32$1(x, 11) ^ rotr32$1(x, 25);
  }
  var s1_256_1 = s1_256;

  function g0_256(x) {
    return rotr32$1(x, 7) ^ rotr32$1(x, 18) ^ (x >>> 3);
  }
  var g0_256_1 = g0_256;

  function g1_256(x) {
    return rotr32$1(x, 17) ^ rotr32$1(x, 19) ^ (x >>> 10);
  }
  var g1_256_1 = g1_256;

  var common$1 = {
  	ft_1: ft_1_1,
  	ch32: ch32_1,
  	maj32: maj32_1,
  	p32: p32_1,
  	s0_256: s0_256_1,
  	s1_256: s1_256_1,
  	g0_256: g0_256_1,
  	g1_256: g1_256_1
  };

  var sum32$1 = utils.sum32;
  var sum32_4$1 = utils.sum32_4;
  var sum32_5$1 = utils.sum32_5;
  var ch32$1 = common$1.ch32;
  var maj32$1 = common$1.maj32;
  var s0_256$1 = common$1.s0_256;
  var s1_256$1 = common$1.s1_256;
  var g0_256$1 = common$1.g0_256;
  var g1_256$1 = common$1.g1_256;

  var BlockHash$1 = common.BlockHash;

  var sha256_K = [
    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
  ];

  function SHA256() {
    if (!(this instanceof SHA256))
      return new SHA256();

    BlockHash$1.call(this);
    this.h = [
      0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
      0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
    ];
    this.k = sha256_K;
    this.W = new Array(64);
  }
  utils.inherits(SHA256, BlockHash$1);
  var _256 = SHA256;

  SHA256.blockSize = 512;
  SHA256.outSize = 256;
  SHA256.hmacStrength = 192;
  SHA256.padLength = 64;

  SHA256.prototype._update = function _update(msg, start) {
    var W = this.W;

    for (var i = 0; i < 16; i++)
      W[i] = msg[start + i];
    for (; i < W.length; i++)
      W[i] = sum32_4$1(g1_256$1(W[i - 2]), W[i - 7], g0_256$1(W[i - 15]), W[i - 16]);

    var a = this.h[0];
    var b = this.h[1];
    var c = this.h[2];
    var d = this.h[3];
    var e = this.h[4];
    var f = this.h[5];
    var g = this.h[6];
    var h = this.h[7];

    minimalisticAssert(this.k.length === W.length);
    for (i = 0; i < W.length; i++) {
      var T1 = sum32_5$1(h, s1_256$1(e), ch32$1(e, f, g), this.k[i], W[i]);
      var T2 = sum32$1(s0_256$1(a), maj32$1(a, b, c));
      h = g;
      g = f;
      f = e;
      e = sum32$1(d, T1);
      d = c;
      c = b;
      b = a;
      a = sum32$1(T1, T2);
    }

    this.h[0] = sum32$1(this.h[0], a);
    this.h[1] = sum32$1(this.h[1], b);
    this.h[2] = sum32$1(this.h[2], c);
    this.h[3] = sum32$1(this.h[3], d);
    this.h[4] = sum32$1(this.h[4], e);
    this.h[5] = sum32$1(this.h[5], f);
    this.h[6] = sum32$1(this.h[6], g);
    this.h[7] = sum32$1(this.h[7], h);
  };

  SHA256.prototype._digest = function digest(enc) {
    if (enc === 'hex')
      return utils.toHex32(this.h, 'big');
    else
      return utils.split32(this.h, 'big');
  };

  function SHA224() {
    if (!(this instanceof SHA224))
      return new SHA224();

    _256.call(this);
    this.h = [
      0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
      0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 ];
  }
  utils.inherits(SHA224, _256);
  var _224 = SHA224;

  SHA224.blockSize = 512;
  SHA224.outSize = 224;
  SHA224.hmacStrength = 192;
  SHA224.padLength = 64;

  SHA224.prototype._digest = function digest(enc) {
    // Just truncate output
    if (enc === 'hex')
      return utils.toHex32(this.h.slice(0, 7), 'big');
    else
      return utils.split32(this.h.slice(0, 7), 'big');
  };

  var rotr64_hi$1 = utils.rotr64_hi;
  var rotr64_lo$1 = utils.rotr64_lo;
  var shr64_hi$1 = utils.shr64_hi;
  var shr64_lo$1 = utils.shr64_lo;
  var sum64$1 = utils.sum64;
  var sum64_hi$1 = utils.sum64_hi;
  var sum64_lo$1 = utils.sum64_lo;
  var sum64_4_hi$1 = utils.sum64_4_hi;
  var sum64_4_lo$1 = utils.sum64_4_lo;
  var sum64_5_hi$1 = utils.sum64_5_hi;
  var sum64_5_lo$1 = utils.sum64_5_lo;

  var BlockHash$2 = common.BlockHash;

  var sha512_K = [
    0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd,
    0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,
    0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019,
    0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,
    0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe,
    0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,
    0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1,
    0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,
    0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3,
    0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,
    0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483,
    0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,
    0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210,
    0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,
    0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725,
    0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,
    0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926,
    0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,
    0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8,
    0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,
    0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001,
    0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,
    0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910,
    0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,
    0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53,
    0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,
    0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb,
    0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,
    0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60,
    0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,
    0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9,
    0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,
    0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207,
    0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,
    0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6,
    0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,
    0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493,
    0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,
    0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a,
    0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817
  ];

  function SHA512() {
    if (!(this instanceof SHA512))
      return new SHA512();

    BlockHash$2.call(this);
    this.h = [
      0x6a09e667, 0xf3bcc908,
      0xbb67ae85, 0x84caa73b,
      0x3c6ef372, 0xfe94f82b,
      0xa54ff53a, 0x5f1d36f1,
      0x510e527f, 0xade682d1,
      0x9b05688c, 0x2b3e6c1f,
      0x1f83d9ab, 0xfb41bd6b,
      0x5be0cd19, 0x137e2179 ];
    this.k = sha512_K;
    this.W = new Array(160);
  }
  utils.inherits(SHA512, BlockHash$2);
  var _512 = SHA512;

  SHA512.blockSize = 1024;
  SHA512.outSize = 512;
  SHA512.hmacStrength = 192;
  SHA512.padLength = 128;

  SHA512.prototype._prepareBlock = function _prepareBlock(msg, start) {
    var W = this.W;

    // 32 x 32bit words
    for (var i = 0; i < 32; i++)
      W[i] = msg[start + i];
    for (; i < W.length; i += 2) {
      var c0_hi = g1_512_hi(W[i - 4], W[i - 3]);  // i - 2
      var c0_lo = g1_512_lo(W[i - 4], W[i - 3]);
      var c1_hi = W[i - 14];  // i - 7
      var c1_lo = W[i - 13];
      var c2_hi = g0_512_hi(W[i - 30], W[i - 29]);  // i - 15
      var c2_lo = g0_512_lo(W[i - 30], W[i - 29]);
      var c3_hi = W[i - 32];  // i - 16
      var c3_lo = W[i - 31];

      W[i] = sum64_4_hi$1(
        c0_hi, c0_lo,
        c1_hi, c1_lo,
        c2_hi, c2_lo,
        c3_hi, c3_lo);
      W[i + 1] = sum64_4_lo$1(
        c0_hi, c0_lo,
        c1_hi, c1_lo,
        c2_hi, c2_lo,
        c3_hi, c3_lo);
    }
  };

  SHA512.prototype._update = function _update(msg, start) {
    this._prepareBlock(msg, start);

    var W = this.W;

    var ah = this.h[0];
    var al = this.h[1];
    var bh = this.h[2];
    var bl = this.h[3];
    var ch = this.h[4];
    var cl = this.h[5];
    var dh = this.h[6];
    var dl = this.h[7];
    var eh = this.h[8];
    var el = this.h[9];
    var fh = this.h[10];
    var fl = this.h[11];
    var gh = this.h[12];
    var gl = this.h[13];
    var hh = this.h[14];
    var hl = this.h[15];

    minimalisticAssert(this.k.length === W.length);
    for (var i = 0; i < W.length; i += 2) {
      var c0_hi = hh;
      var c0_lo = hl;
      var c1_hi = s1_512_hi(eh, el);
      var c1_lo = s1_512_lo(eh, el);
      var c2_hi = ch64_hi(eh, el, fh, fl, gh);
      var c2_lo = ch64_lo(eh, el, fh, fl, gh, gl);
      var c3_hi = this.k[i];
      var c3_lo = this.k[i + 1];
      var c4_hi = W[i];
      var c4_lo = W[i + 1];

      var T1_hi = sum64_5_hi$1(
        c0_hi, c0_lo,
        c1_hi, c1_lo,
        c2_hi, c2_lo,
        c3_hi, c3_lo,
        c4_hi, c4_lo);
      var T1_lo = sum64_5_lo$1(
        c0_hi, c0_lo,
        c1_hi, c1_lo,
        c2_hi, c2_lo,
        c3_hi, c3_lo,
        c4_hi, c4_lo);

      c0_hi = s0_512_hi(ah, al);
      c0_lo = s0_512_lo(ah, al);
      c1_hi = maj64_hi(ah, al, bh, bl, ch);
      c1_lo = maj64_lo(ah, al, bh, bl, ch, cl);

      var T2_hi = sum64_hi$1(c0_hi, c0_lo, c1_hi, c1_lo);
      var T2_lo = sum64_lo$1(c0_hi, c0_lo, c1_hi, c1_lo);

      hh = gh;
      hl = gl;

      gh = fh;
      gl = fl;

      fh = eh;
      fl = el;

      eh = sum64_hi$1(dh, dl, T1_hi, T1_lo);
      el = sum64_lo$1(dl, dl, T1_hi, T1_lo);

      dh = ch;
      dl = cl;

      ch = bh;
      cl = bl;

      bh = ah;
      bl = al;

      ah = sum64_hi$1(T1_hi, T1_lo, T2_hi, T2_lo);
      al = sum64_lo$1(T1_hi, T1_lo, T2_hi, T2_lo);
    }

    sum64$1(this.h, 0, ah, al);
    sum64$1(this.h, 2, bh, bl);
    sum64$1(this.h, 4, ch, cl);
    sum64$1(this.h, 6, dh, dl);
    sum64$1(this.h, 8, eh, el);
    sum64$1(this.h, 10, fh, fl);
    sum64$1(this.h, 12, gh, gl);
    sum64$1(this.h, 14, hh, hl);
  };

  SHA512.prototype._digest = function digest(enc) {
    if (enc === 'hex')
      return utils.toHex32(this.h, 'big');
    else
      return utils.split32(this.h, 'big');
  };

  function ch64_hi(xh, xl, yh, yl, zh) {
    var r = (xh & yh) ^ ((~xh) & zh);
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function ch64_lo(xh, xl, yh, yl, zh, zl) {
    var r = (xl & yl) ^ ((~xl) & zl);
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function maj64_hi(xh, xl, yh, yl, zh) {
    var r = (xh & yh) ^ (xh & zh) ^ (yh & zh);
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function maj64_lo(xh, xl, yh, yl, zh, zl) {
    var r = (xl & yl) ^ (xl & zl) ^ (yl & zl);
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function s0_512_hi(xh, xl) {
    var c0_hi = rotr64_hi$1(xh, xl, 28);
    var c1_hi = rotr64_hi$1(xl, xh, 2);  // 34
    var c2_hi = rotr64_hi$1(xl, xh, 7);  // 39

    var r = c0_hi ^ c1_hi ^ c2_hi;
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function s0_512_lo(xh, xl) {
    var c0_lo = rotr64_lo$1(xh, xl, 28);
    var c1_lo = rotr64_lo$1(xl, xh, 2);  // 34
    var c2_lo = rotr64_lo$1(xl, xh, 7);  // 39

    var r = c0_lo ^ c1_lo ^ c2_lo;
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function s1_512_hi(xh, xl) {
    var c0_hi = rotr64_hi$1(xh, xl, 14);
    var c1_hi = rotr64_hi$1(xh, xl, 18);
    var c2_hi = rotr64_hi$1(xl, xh, 9);  // 41

    var r = c0_hi ^ c1_hi ^ c2_hi;
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function s1_512_lo(xh, xl) {
    var c0_lo = rotr64_lo$1(xh, xl, 14);
    var c1_lo = rotr64_lo$1(xh, xl, 18);
    var c2_lo = rotr64_lo$1(xl, xh, 9);  // 41

    var r = c0_lo ^ c1_lo ^ c2_lo;
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function g0_512_hi(xh, xl) {
    var c0_hi = rotr64_hi$1(xh, xl, 1);
    var c1_hi = rotr64_hi$1(xh, xl, 8);
    var c2_hi = shr64_hi$1(xh, xl, 7);

    var r = c0_hi ^ c1_hi ^ c2_hi;
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function g0_512_lo(xh, xl) {
    var c0_lo = rotr64_lo$1(xh, xl, 1);
    var c1_lo = rotr64_lo$1(xh, xl, 8);
    var c2_lo = shr64_lo$1(xh, xl, 7);

    var r = c0_lo ^ c1_lo ^ c2_lo;
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function g1_512_hi(xh, xl) {
    var c0_hi = rotr64_hi$1(xh, xl, 19);
    var c1_hi = rotr64_hi$1(xl, xh, 29);  // 61
    var c2_hi = shr64_hi$1(xh, xl, 6);

    var r = c0_hi ^ c1_hi ^ c2_hi;
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function g1_512_lo(xh, xl) {
    var c0_lo = rotr64_lo$1(xh, xl, 19);
    var c1_lo = rotr64_lo$1(xl, xh, 29);  // 61
    var c2_lo = shr64_lo$1(xh, xl, 6);

    var r = c0_lo ^ c1_lo ^ c2_lo;
    if (r < 0)
      r += 0x100000000;
    return r;
  }

  function SHA384() {
    if (!(this instanceof SHA384))
      return new SHA384();

    _512.call(this);
    this.h = [
      0xcbbb9d5d, 0xc1059ed8,
      0x629a292a, 0x367cd507,
      0x9159015a, 0x3070dd17,
      0x152fecd8, 0xf70e5939,
      0x67332667, 0xffc00b31,
      0x8eb44a87, 0x68581511,
      0xdb0c2e0d, 0x64f98fa7,
      0x47b5481d, 0xbefa4fa4 ];
  }
  utils.inherits(SHA384, _512);
  var _384 = SHA384;

  SHA384.blockSize = 1024;
  SHA384.outSize = 384;
  SHA384.hmacStrength = 192;
  SHA384.padLength = 128;

  SHA384.prototype._digest = function digest(enc) {
    if (enc === 'hex')
      return utils.toHex32(this.h.slice(0, 12), 'big');
    else
      return utils.split32(this.h.slice(0, 12), 'big');
  };

  var rotl32$1 = utils.rotl32;
  var sum32$2 = utils.sum32;
  var sum32_3$1 = utils.sum32_3;
  var sum32_4$2 = utils.sum32_4;
  var BlockHash$3 = common.BlockHash;

  function RIPEMD160() {
    if (!(this instanceof RIPEMD160))
      return new RIPEMD160();

    BlockHash$3.call(this);

    this.h = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ];
    this.endian = 'little';
  }
  utils.inherits(RIPEMD160, BlockHash$3);
  var ripemd160 = RIPEMD160;

  RIPEMD160.blockSize = 512;
  RIPEMD160.outSize = 160;
  RIPEMD160.hmacStrength = 192;
  RIPEMD160.padLength = 64;

  RIPEMD160.prototype._update = function update(msg, start) {
    var A = this.h[0];
    var B = this.h[1];
    var C = this.h[2];
    var D = this.h[3];
    var E = this.h[4];
    var Ah = A;
    var Bh = B;
    var Ch = C;
    var Dh = D;
    var Eh = E;
    for (var j = 0; j < 80; j++) {
      var T = sum32$2(
        rotl32$1(
          sum32_4$2(A, f(j, B, C, D), msg[r[j] + start], K(j)),
          s[j]),
        E);
      A = E;
      E = D;
      D = rotl32$1(C, 10);
      C = B;
      B = T;
      T = sum32$2(
        rotl32$1(
          sum32_4$2(Ah, f(79 - j, Bh, Ch, Dh), msg[rh[j] + start], Kh(j)),
          sh[j]),
        Eh);
      Ah = Eh;
      Eh = Dh;
      Dh = rotl32$1(Ch, 10);
      Ch = Bh;
      Bh = T;
    }
    T = sum32_3$1(this.h[1], C, Dh);
    this.h[1] = sum32_3$1(this.h[2], D, Eh);
    this.h[2] = sum32_3$1(this.h[3], E, Ah);
    this.h[3] = sum32_3$1(this.h[4], A, Bh);
    this.h[4] = sum32_3$1(this.h[0], B, Ch);
    this.h[0] = T;
  };

  RIPEMD160.prototype._digest = function digest(enc) {
    if (enc === 'hex')
      return utils.toHex32(this.h, 'little');
    else
      return utils.split32(this.h, 'little');
  };

  function f(j, x, y, z) {
    if (j <= 15)
      return x ^ y ^ z;
    else if (j <= 31)
      return (x & y) | ((~x) & z);
    else if (j <= 47)
      return (x | (~y)) ^ z;
    else if (j <= 63)
      return (x & z) | (y & (~z));
    else
      return x ^ (y | (~z));
  }

  function K(j) {
    if (j <= 15)
      return 0x00000000;
    else if (j <= 31)
      return 0x5a827999;
    else if (j <= 47)
      return 0x6ed9eba1;
    else if (j <= 63)
      return 0x8f1bbcdc;
    else
      return 0xa953fd4e;
  }

  function Kh(j) {
    if (j <= 15)
      return 0x50a28be6;
    else if (j <= 31)
      return 0x5c4dd124;
    else if (j <= 47)
      return 0x6d703ef3;
    else if (j <= 63)
      return 0x7a6d76e9;
    else
      return 0x00000000;
  }

  var r = [
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
    7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
    3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
    1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
    4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
  ];

  var rh = [
    5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
    6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
    15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
    8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
    12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
  ];

  var s = [
    11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
    7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
    11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
    11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
    9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
  ];

  var sh = [
    8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
    9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
    9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
    15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
    8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
  ];

  var ripemd = {
  	ripemd160: ripemd160
  };

  /**
   * A fast MD5 JavaScript implementation
   * Copyright (c) 2012 Joseph Myers
   * http://www.myersdaily.org/joseph/javascript/md5-text.html
   *
   * Permission to use, copy, modify, and distribute this software
   * and its documentation for any purposes and without
   * fee is hereby granted provided that this copyright notice
   * appears in all copies.
   *
   * Of course, this soft is provided "as is" without express or implied
   * warranty of any kind.
   */

  // MD5 Digest
  async function md5(entree) {
    const digest = md51(util.uint8ArrayToString(entree));
    return util.hexToUint8Array(hex(digest));
  }

  function md5cycle(x, k) {
    let a = x[0];
    let b = x[1];
    let c = x[2];
    let d = x[3];

    a = ff(a, b, c, d, k[0], 7, -680876936);
    d = ff(d, a, b, c, k[1], 12, -389564586);
    c = ff(c, d, a, b, k[2], 17, 606105819);
    b = ff(b, c, d, a, k[3], 22, -1044525330);
    a = ff(a, b, c, d, k[4], 7, -176418897);
    d = ff(d, a, b, c, k[5], 12, 1200080426);
    c = ff(c, d, a, b, k[6], 17, -1473231341);
    b = ff(b, c, d, a, k[7], 22, -45705983);
    a = ff(a, b, c, d, k[8], 7, 1770035416);
    d = ff(d, a, b, c, k[9], 12, -1958414417);
    c = ff(c, d, a, b, k[10], 17, -42063);
    b = ff(b, c, d, a, k[11], 22, -1990404162);
    a = ff(a, b, c, d, k[12], 7, 1804603682);
    d = ff(d, a, b, c, k[13], 12, -40341101);
    c = ff(c, d, a, b, k[14], 17, -1502002290);
    b = ff(b, c, d, a, k[15], 22, 1236535329);

    a = gg(a, b, c, d, k[1], 5, -165796510);
    d = gg(d, a, b, c, k[6], 9, -1069501632);
    c = gg(c, d, a, b, k[11], 14, 643717713);
    b = gg(b, c, d, a, k[0], 20, -373897302);
    a = gg(a, b, c, d, k[5], 5, -701558691);
    d = gg(d, a, b, c, k[10], 9, 38016083);
    c = gg(c, d, a, b, k[15], 14, -660478335);
    b = gg(b, c, d, a, k[4], 20, -405537848);
    a = gg(a, b, c, d, k[9], 5, 568446438);
    d = gg(d, a, b, c, k[14], 9, -1019803690);
    c = gg(c, d, a, b, k[3], 14, -187363961);
    b = gg(b, c, d, a, k[8], 20, 1163531501);
    a = gg(a, b, c, d, k[13], 5, -1444681467);
    d = gg(d, a, b, c, k[2], 9, -51403784);
    c = gg(c, d, a, b, k[7], 14, 1735328473);
    b = gg(b, c, d, a, k[12], 20, -1926607734);

    a = hh(a, b, c, d, k[5], 4, -378558);
    d = hh(d, a, b, c, k[8], 11, -2022574463);
    c = hh(c, d, a, b, k[11], 16, 1839030562);
    b = hh(b, c, d, a, k[14], 23, -35309556);
    a = hh(a, b, c, d, k[1], 4, -1530992060);
    d = hh(d, a, b, c, k[4], 11, 1272893353);
    c = hh(c, d, a, b, k[7], 16, -155497632);
    b = hh(b, c, d, a, k[10], 23, -1094730640);
    a = hh(a, b, c, d, k[13], 4, 681279174);
    d = hh(d, a, b, c, k[0], 11, -358537222);
    c = hh(c, d, a, b, k[3], 16, -722521979);
    b = hh(b, c, d, a, k[6], 23, 76029189);
    a = hh(a, b, c, d, k[9], 4, -640364487);
    d = hh(d, a, b, c, k[12], 11, -421815835);
    c = hh(c, d, a, b, k[15], 16, 530742520);
    b = hh(b, c, d, a, k[2], 23, -995338651);

    a = ii(a, b, c, d, k[0], 6, -198630844);
    d = ii(d, a, b, c, k[7], 10, 1126891415);
    c = ii(c, d, a, b, k[14], 15, -1416354905);
    b = ii(b, c, d, a, k[5], 21, -57434055);
    a = ii(a, b, c, d, k[12], 6, 1700485571);
    d = ii(d, a, b, c, k[3], 10, -1894986606);
    c = ii(c, d, a, b, k[10], 15, -1051523);
    b = ii(b, c, d, a, k[1], 21, -2054922799);
    a = ii(a, b, c, d, k[8], 6, 1873313359);
    d = ii(d, a, b, c, k[15], 10, -30611744);
    c = ii(c, d, a, b, k[6], 15, -1560198380);
    b = ii(b, c, d, a, k[13], 21, 1309151649);
    a = ii(a, b, c, d, k[4], 6, -145523070);
    d = ii(d, a, b, c, k[11], 10, -1120210379);
    c = ii(c, d, a, b, k[2], 15, 718787259);
    b = ii(b, c, d, a, k[9], 21, -343485551);

    x[0] = add32(a, x[0]);
    x[1] = add32(b, x[1]);
    x[2] = add32(c, x[2]);
    x[3] = add32(d, x[3]);
  }

  function cmn(q, a, b, x, s, t) {
    a = add32(add32(a, q), add32(x, t));
    return add32((a << s) | (a >>> (32 - s)), b);
  }

  function ff(a, b, c, d, x, s, t) {
    return cmn((b & c) | ((~b) & d), a, b, x, s, t);
  }

  function gg(a, b, c, d, x, s, t) {
    return cmn((b & d) | (c & (~d)), a, b, x, s, t);
  }

  function hh(a, b, c, d, x, s, t) {
    return cmn(b ^ c ^ d, a, b, x, s, t);
  }

  function ii(a, b, c, d, x, s, t) {
    return cmn(c ^ (b | (~d)), a, b, x, s, t);
  }

  function md51(s) {
    const n = s.length;
    const state = [1732584193, -271733879, -1732584194, 271733878];
    let i;
    for (i = 64; i <= s.length; i += 64) {
      md5cycle(state, md5blk(s.substring(i - 64, i)));
    }
    s = s.substring(i - 64);
    const tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for (i = 0; i < s.length; i++) {
      tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
    }
    tail[i >> 2] |= 0x80 << ((i % 4) << 3);
    if (i > 55) {
      md5cycle(state, tail);
      for (i = 0; i < 16; i++) {
        tail[i] = 0;
      }
    }
    tail[14] = n * 8;
    md5cycle(state, tail);
    return state;
  }

  /* there needs to be support for Unicode here,
   * unless we pretend that we can redefine the MD-5
   * algorithm for multi-byte characters (perhaps
   * by adding every four 16-bit characters and
   * shortening the sum to 32 bits). Otherwise
   * I suggest performing MD-5 as if every character
   * was two bytes--e.g., 0040 0025 = @%--but then
   * how will an ordinary MD-5 sum be matched?
   * There is no way to standardize text to something
   * like UTF-8 before transformation; speed cost is
   * utterly prohibitive. The JavaScript standard
   * itself needs to look at this: it should start
   * providing access to strings as preformed UTF-8
   * 8-bit unsigned value arrays.
   */
  function md5blk(s) { /* I figured global was faster.   */
    const md5blks = [];
    let i; /* Andy King said do it this way. */
    for (i = 0; i < 64; i += 4) {
      md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) <<
        24);
    }
    return md5blks;
  }

  const hex_chr = '0123456789abcdef'.split('');

  function rhex(n) {
    let s = '';
    let j = 0;
    for (; j < 4; j++) {
      s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
    }
    return s;
  }

  function hex(x) {
    for (let i = 0; i < x.length; i++) {
      x[i] = rhex(x[i]);
    }
    return x.join('');
  }

  /* this function is much faster,
  so if possible we use it. Some IEs
  are the only ones I know of that
  need the idiotic second function,
  generated by an if clause.  */

  function add32(a, b) {
    return (a + b) & 0xFFFFFFFF;
  }

  /**
   * @fileoverview Provides an interface to hashing functions available in Node.js or external libraries.
   * @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
   * @see {@link https://github.com/indutny/hash.js|hash.js}
   * @module crypto/hash
   * @private
   */

  const webCrypto = util.getWebCrypto();
  const nodeCrypto = util.getNodeCrypto();
  const nodeCryptoHashes = nodeCrypto && nodeCrypto.getHashes();

  function nodeHash(type) {
    if (!nodeCrypto || !nodeCryptoHashes.includes(type)) {
      return;
    }
    return async function (data) {
      const shasum = nodeCrypto.createHash(type);
      return transform(data, value => {
        shasum.update(value);
      }, () => new Uint8Array(shasum.digest()));
    };
  }

  function hashjsHash(hash, webCryptoHash) {
    return async function(data, config$1 = config) {
      if (isArrayStream(data)) {
        data = await readToEnd(data);
      }
      if (!util.isStream(data) && webCrypto && webCryptoHash && data.length >= config$1.minBytesForWebCrypto) {
        return new Uint8Array(await webCrypto.digest(webCryptoHash, data));
      }
      const hashInstance = hash();
      return transform(data, value => {
        hashInstance.update(value);
      }, () => new Uint8Array(hashInstance.digest()));
    };
  }

  function asmcryptoHash(hash, webCryptoHash) {
    return async function(data, config$1 = config) {
      if (isArrayStream(data)) {
        data = await readToEnd(data);
      }
      if (util.isStream(data)) {
        const hashInstance = new hash();
        return transform(data, value => {
          hashInstance.process(value);
        }, () => hashInstance.finish().result);
      } else if (webCrypto && webCryptoHash && data.length >= config$1.minBytesForWebCrypto) {
        return new Uint8Array(await webCrypto.digest(webCryptoHash, data));
      } else {
        return hash.bytes(data);
      }
    };
  }

  const hashFunctions = {
    md5: nodeHash('md5') || md5,
    sha1: nodeHash('sha1') || asmcryptoHash(Sha1, 'SHA-1'),
    sha224: nodeHash('sha224') || hashjsHash(_224),
    sha256: nodeHash('sha256') || asmcryptoHash(Sha256, 'SHA-256'),
    sha384: nodeHash('sha384') || hashjsHash(_384, 'SHA-384'),
    sha512: nodeHash('sha512') || hashjsHash(_512, 'SHA-512'), // asmcrypto sha512 is huge.
    ripemd: nodeHash('ripemd160') || hashjsHash(ripemd160)
  };

  var hash = {

    /** @see module:md5 */
    md5: hashFunctions.md5,
    /** @see asmCrypto */
    sha1: hashFunctions.sha1,
    /** @see hash.js */
    sha224: hashFunctions.sha224,
    /** @see asmCrypto */
    sha256: hashFunctions.sha256,
    /** @see hash.js */
    sha384: hashFunctions.sha384,
    /** @see asmCrypto */
    sha512: hashFunctions.sha512,
    /** @see hash.js */
    ripemd: hashFunctions.ripemd,

    /**
     * Create a hash on the specified data using the specified algorithm
     * @param {module:enums.hash} algo - Hash algorithm type (see {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4})
     * @param {Uint8Array} data - Data to be hashed
     * @returns {Promise<Uint8Array>} Hash value.
     */
    digest: function(algo, data) {
      switch (algo) {
        case enums.hash.md5:
          return this.md5(data);
        case enums.hash.sha1:
          return this.sha1(data);
        case enums.hash.ripemd:
          return this.ripemd(data);
        case enums.hash.sha256:
          return this.sha256(data);
        case enums.hash.sha384:
          return this.sha384(data);
        case enums.hash.sha512:
          return this.sha512(data);
        case enums.hash.sha224:
          return this.sha224(data);
        default:
          throw new Error('Invalid hash function.');
      }
    },

    /**
     * Returns the hash size in bytes of the specified hash algorithm type
     * @param {module:enums.hash} algo - Hash algorithm type (See {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4})
     * @returns {Integer} Size in bytes of the resulting hash.
     */
    getHashByteLength: function(algo) {
      switch (algo) {
        case enums.hash.md5:
          return 16;
        case enums.hash.sha1:
        case enums.hash.ripemd:
          return 20;
        case enums.hash.sha256:
          return 32;
        case enums.hash.sha384:
          return 48;
        case enums.hash.sha512:
          return 64;
        case enums.hash.sha224:
          return 28;
        default:
          throw new Error('Invalid hash algorithm.');
      }
    }
  };

  class AES_CFB {
      static encrypt(data, key, iv) {
          return new AES_CFB(key, iv).encrypt(data);
      }
      static decrypt(data, key, iv) {
          return new AES_CFB(key, iv).decrypt(data);
      }
      constructor(key, iv, aes) {
          this.aes = aes ? aes : new AES(key, iv, true, 'CFB');
          delete this.aes.padding;
      }
      encrypt(data) {
          const r1 = this.aes.AES_Encrypt_process(data);
          const r2 = this.aes.AES_Encrypt_finish();
          return joinBytes(r1, r2);
      }
      decrypt(data) {
          const r1 = this.aes.AES_Decrypt_process(data);
          const r2 = this.aes.AES_Decrypt_finish();
          return joinBytes(r1, r2);
      }
  }

  /**
   * Get implementation of the given cipher
   * @param {enums.symmetric} algo
   * @returns {Object}
   * @throws {Error} on invalid algo
   */
  function getCipher(algo) {
    const algoName = enums.read(enums.symmetric, algo);
    return cipher[algoName];
  }

  // Modified by ProtonTech AG

  const webCrypto$1 = util.getWebCrypto();
  const nodeCrypto$1 = util.getNodeCrypto();

  const knownAlgos = nodeCrypto$1 ? nodeCrypto$1.getCiphers() : [];
  const nodeAlgos = {
    idea: knownAlgos.includes('idea-cfb') ? 'idea-cfb' : undefined, /* Unused, not implemented */
    tripledes: knownAlgos.includes('des-ede3-cfb') ? 'des-ede3-cfb' : undefined,
    cast5: knownAlgos.includes('cast5-cfb') ? 'cast5-cfb' : undefined,
    blowfish: knownAlgos.includes('bf-cfb') ? 'bf-cfb' : undefined,
    aes128: knownAlgos.includes('aes-128-cfb') ? 'aes-128-cfb' : undefined,
    aes192: knownAlgos.includes('aes-192-cfb') ? 'aes-192-cfb' : undefined,
    aes256: knownAlgos.includes('aes-256-cfb') ? 'aes-256-cfb' : undefined
    /* twofish is not implemented in OpenSSL */
  };

  /**
   * CFB encryption
   * @param {enums.symmetric} algo - block cipher algorithm
   * @param {Uint8Array} key
   * @param {MaybeStream<Uint8Array>} plaintext
   * @param {Uint8Array} iv
   * @param {Object} config - full configuration, defaults to openpgp.config
   * @returns MaybeStream<Uint8Array>
   */
  async function encrypt(algo, key, plaintext, iv, config) {
    const algoName = enums.read(enums.symmetric, algo);
    if (util.getNodeCrypto() && nodeAlgos[algoName]) { // Node crypto library.
      return nodeEncrypt(algo, key, plaintext, iv);
    }
    if (util.isAES(algo)) {
      return aesEncrypt(algo, key, plaintext, iv, config);
    }

    const Cipher = getCipher(algo);
    const cipherfn = new Cipher(key);
    const block_size = cipherfn.blockSize;

    const blockc = iv.slice();
    let pt = new Uint8Array();
    const process = chunk => {
      if (chunk) {
        pt = util.concatUint8Array([pt, chunk]);
      }
      const ciphertext = new Uint8Array(pt.length);
      let i;
      let j = 0;
      while (chunk ? pt.length >= block_size : pt.length) {
        const encblock = cipherfn.encrypt(blockc);
        for (i = 0; i < block_size; i++) {
          blockc[i] = pt[i] ^ encblock[i];
          ciphertext[j++] = blockc[i];
        }
        pt = pt.subarray(block_size);
      }
      return ciphertext.subarray(0, j);
    };
    return transform(plaintext, process, process);
  }

  /**
   * CFB decryption
   * @param {enums.symmetric} algo - block cipher algorithm
   * @param {Uint8Array} key
   * @param {MaybeStream<Uint8Array>} ciphertext
   * @param {Uint8Array} iv
   * @returns MaybeStream<Uint8Array>
   */
  async function decrypt(algo, key, ciphertext, iv) {
    const algoName = enums.read(enums.symmetric, algo);
    if (util.getNodeCrypto() && nodeAlgos[algoName]) { // Node crypto library.
      return nodeDecrypt(algo, key, ciphertext, iv);
    }
    if (util.isAES(algo)) {
      return aesDecrypt(algo, key, ciphertext, iv);
    }

    const Cipher = getCipher(algo);
    const cipherfn = new Cipher(key);
    const block_size = cipherfn.blockSize;

    let blockp = iv;
    let ct = new Uint8Array();
    const process = chunk => {
      if (chunk) {
        ct = util.concatUint8Array([ct, chunk]);
      }
      const plaintext = new Uint8Array(ct.length);
      let i;
      let j = 0;
      while (chunk ? ct.length >= block_size : ct.length) {
        const decblock = cipherfn.encrypt(blockp);
        blockp = ct.subarray(0, block_size);
        for (i = 0; i < block_size; i++) {
          plaintext[j++] = blockp[i] ^ decblock[i];
        }
        ct = ct.subarray(block_size);
      }
      return plaintext.subarray(0, j);
    };
    return transform(ciphertext, process, process);
  }

  function aesEncrypt(algo, key, pt, iv, config) {
    if (
      util.getWebCrypto() &&
      key.length !== 24 && // Chrome doesn't support 192 bit keys, see https://www.chromium.org/blink/webcrypto#TOC-AES-support
      !util.isStream(pt) &&
      pt.length >= 3000 * config.minBytesForWebCrypto // Default to a 3MB minimum. Chrome is pretty slow for small messages, see: https://bugs.chromium.org/p/chromium/issues/detail?id=701188#c2
    ) { // Web Crypto
      return webEncrypt(algo, key, pt, iv);
    }
    // asm.js fallback
    const cfb = new AES_CFB(key, iv);
    return transform(pt, value => cfb.aes.AES_Encrypt_process(value), () => cfb.aes.AES_Encrypt_finish());
  }

  function aesDecrypt(algo, key, ct, iv) {
    if (util.isStream(ct)) {
      const cfb = new AES_CFB(key, iv);
      return transform(ct, value => cfb.aes.AES_Decrypt_process(value), () => cfb.aes.AES_Decrypt_finish());
    }
    return AES_CFB.decrypt(ct, key, iv);
  }

  function xorMut(a, b) {
    for (let i = 0; i < a.length; i++) {
      a[i] = a[i] ^ b[i];
    }
  }

  async function webEncrypt(algo, key, pt, iv) {
    const ALGO = 'AES-CBC';
    const _key = await webCrypto$1.importKey('raw', key, { name: ALGO }, false, ['encrypt']);
    const { blockSize } = getCipher(algo);
    const cbc_pt = util.concatUint8Array([new Uint8Array(blockSize), pt]);
    const ct = new Uint8Array(await webCrypto$1.encrypt({ name: ALGO, iv }, _key, cbc_pt)).subarray(0, pt.length);
    xorMut(ct, pt);
    return ct;
  }

  function nodeEncrypt(algo, key, pt, iv) {
    const algoName = enums.read(enums.symmetric, algo);
    const cipherObj = new nodeCrypto$1.createCipheriv(nodeAlgos[algoName], key, iv);
    return transform(pt, value => new Uint8Array(cipherObj.update(value)));
  }

  function nodeDecrypt(algo, key, ct, iv) {
    const algoName = enums.read(enums.symmetric, algo);
    const decipherObj = new nodeCrypto$1.createDecipheriv(nodeAlgos[algoName], key, iv);
    return transform(ct, value => new Uint8Array(decipherObj.update(value)));
  }

  var cfb = /*#__PURE__*/Object.freeze({
    __proto__: null,
    encrypt: encrypt,
    decrypt: decrypt
  });

  class AES_CTR {
      static encrypt(data, key, nonce) {
          return new AES_CTR(key, nonce).encrypt(data);
      }
      static decrypt(data, key, nonce) {
          return new AES_CTR(key, nonce).encrypt(data);
      }
      constructor(key, nonce, aes) {
          this.aes = aes ? aes : new AES(key, undefined, false, 'CTR');
          delete this.aes.padding;
          this.AES_CTR_set_options(nonce);
      }
      encrypt(data) {
          const r1 = this.aes.AES_Encrypt_process(data);
          const r2 = this.aes.AES_Encrypt_finish();
          return joinBytes(r1, r2);
      }
      decrypt(data) {
          const r1 = this.aes.AES_Encrypt_process(data);
          const r2 = this.aes.AES_Encrypt_finish();
          return joinBytes(r1, r2);
      }
      AES_CTR_set_options(nonce, counter, size) {
          let { asm } = this.aes.acquire_asm();
          if (size !== undefined) {
              if (size < 8 || size > 48)
                  throw new IllegalArgumentError('illegal counter size');
              let mask = Math.pow(2, size) - 1;
              asm.set_mask(0, 0, (mask / 0x100000000) | 0, mask | 0);
          }
          else {
              size = 48;
              asm.set_mask(0, 0, 0xffff, 0xffffffff);
          }
          if (nonce !== undefined) {
              let len = nonce.length;
              if (!len || len > 16)
                  throw new IllegalArgumentError('illegal nonce size');
              let view = new DataView(new ArrayBuffer(16));
              new Uint8Array(view.buffer).set(nonce);
              asm.set_nonce(view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12));
          }
          else {
              throw new Error('nonce is required');
          }
          if (counter !== undefined) {
              if (counter < 0 || counter >= Math.pow(2, size))
                  throw new IllegalArgumentError('illegal counter value');
              asm.set_counter(0, 0, (counter / 0x100000000) | 0, counter | 0);
          }
      }
  }

  class AES_CBC {
      static encrypt(data, key, padding = true, iv) {
          return new AES_CBC(key, iv, padding).encrypt(data);
      }
      static decrypt(data, key, padding = true, iv) {
          return new AES_CBC(key, iv, padding).decrypt(data);
      }
      constructor(key, iv, padding = true, aes) {
          this.aes = aes ? aes : new AES(key, iv, padding, 'CBC');
      }
      encrypt(data) {
          const r1 = this.aes.AES_Encrypt_process(data);
          const r2 = this.aes.AES_Encrypt_finish();
          return joinBytes(r1, r2);
      }
      decrypt(data) {
          const r1 = this.aes.AES_Decrypt_process(data);
          const r2 = this.aes.AES_Decrypt_finish();
          return joinBytes(r1, r2);
      }
  }

  /**
   * @fileoverview This module implements AES-CMAC on top of
   * native AES-CBC using either the WebCrypto API or Node.js' crypto API.
   * @module crypto/cmac
   * @private
   */

  const webCrypto$2 = util.getWebCrypto();
  const nodeCrypto$2 = util.getNodeCrypto();


  /**
   * This implementation of CMAC is based on the description of OMAC in
   * http://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf. As per that
   * document:
   *
   * We have made a small modification to the OMAC algorithm as it was
   * originally presented, changing one of its two constants.
   * Specifically, the constant 4 at line 85 was the constant 1/2 (the
   * multiplicative inverse of 2) in the original definition of OMAC [14].
   * The OMAC authors indicate that they will promulgate this modification
   * [15], which slightly simplifies implementations.
   */

  const blockLength = 16;


  /**
   * xor `padding` into the end of `data`. This function implements "the
   * operation xor→ [which] xors the shorter string into the end of longer
   * one". Since data is always as least as long as padding, we can
   * simplify the implementation.
   * @param {Uint8Array} data
   * @param {Uint8Array} padding
   */
  function rightXORMut(data, padding) {
    const offset = data.length - blockLength;
    for (let i = 0; i < blockLength; i++) {
      data[i + offset] ^= padding[i];
    }
    return data;
  }

  function pad(data, padding, padding2) {
    // if |M| in {n, 2n, 3n, ...}
    if (data.length && data.length % blockLength === 0) {
      // then return M xor→ B,
      return rightXORMut(data, padding);
    }
    // else return (M || 10^(n−1−(|M| mod n))) xor→ P
    const padded = new Uint8Array(data.length + (blockLength - (data.length % blockLength)));
    padded.set(data);
    padded[data.length] = 0b10000000;
    return rightXORMut(padded, padding2);
  }

  const zeroBlock = new Uint8Array(blockLength);

  async function CMAC(key) {
    const cbc = await CBC(key);

    // L ← E_K(0^n); B ← 2L; P ← 4L
    const padding = util.double(await cbc(zeroBlock));
    const padding2 = util.double(padding);

    return async function(data) {
      // return CBC_K(pad(M; B, P))
      return (await cbc(pad(data, padding, padding2))).subarray(-blockLength);
    };
  }

  async function CBC(key) {
    if (util.getWebCrypto() && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support
      key = await webCrypto$2.importKey('raw', key, { name: 'AES-CBC', length: key.length * 8 }, false, ['encrypt']);
      return async function(pt) {
        const ct = await webCrypto$2.encrypt({ name: 'AES-CBC', iv: zeroBlock, length: blockLength * 8 }, key, pt);
        return new Uint8Array(ct).subarray(0, ct.byteLength - blockLength);
      };
    }
    if (util.getNodeCrypto()) { // Node crypto library
      return async function(pt) {
        const en = new nodeCrypto$2.createCipheriv('aes-' + (key.length * 8) + '-cbc', key, zeroBlock);
        const ct = en.update(pt);
        return new Uint8Array(ct);
      };
    }
    // asm.js fallback
    return async function(pt) {
      return AES_CBC.encrypt(pt, key, false, zeroBlock);
    };
  }

  // OpenPGP.js - An OpenPGP implementation in javascript

  const webCrypto$3 = util.getWebCrypto();
  const nodeCrypto$3 = util.getNodeCrypto();
  const Buffer$1 = util.getNodeBuffer();


  const blockLength$1 = 16;
  const ivLength = blockLength$1;
  const tagLength = blockLength$1;

  const zero = new Uint8Array(blockLength$1);
  const one = new Uint8Array(blockLength$1); one[blockLength$1 - 1] = 1;
  const two = new Uint8Array(blockLength$1); two[blockLength$1 - 1] = 2;

  async function OMAC(key) {
    const cmac = await CMAC(key);
    return function(t, message) {
      return cmac(util.concatUint8Array([t, message]));
    };
  }

  async function CTR(key) {
    if (
      util.getWebCrypto() &&
      key.length !== 24 // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support
    ) {
      key = await webCrypto$3.importKey('raw', key, { name: 'AES-CTR', length: key.length * 8 }, false, ['encrypt']);
      return async function(pt, iv) {
        const ct = await webCrypto$3.encrypt({ name: 'AES-CTR', counter: iv, length: blockLength$1 * 8 }, key, pt);
        return new Uint8Array(ct);
      };
    }
    if (util.getNodeCrypto()) { // Node crypto library
      return async function(pt, iv) {
        const en = new nodeCrypto$3.createCipheriv('aes-' + (key.length * 8) + '-ctr', key, iv);
        const ct = Buffer$1.concat([en.update(pt), en.final()]);
        return new Uint8Array(ct);
      };
    }
    // asm.js fallback
    return async function(pt, iv) {
      return AES_CTR.encrypt(pt, key, iv);
    };
  }


  /**
   * Class to en/decrypt using EAX mode.
   * @param {enums.symmetric} cipher - The symmetric cipher algorithm to use
   * @param {Uint8Array} key - The encryption key
   */
  async function EAX(cipher, key) {
    if (cipher !== enums.symmetric.aes128 &&
      cipher !== enums.symmetric.aes192 &&
      cipher !== enums.symmetric.aes256) {
      throw new Error('EAX mode supports only AES cipher');
    }

    const [
      omac,
      ctr
    ] = await Promise.all([
      OMAC(key),
      CTR(key)
    ]);

    return {
      /**
       * Encrypt plaintext input.
       * @param {Uint8Array} plaintext - The cleartext input to be encrypted
       * @param {Uint8Array} nonce - The nonce (16 bytes)
       * @param {Uint8Array} adata - Associated data to sign
       * @returns {Promise<Uint8Array>} The ciphertext output.
       */
      encrypt: async function(plaintext, nonce, adata) {
        const [
          omacNonce,
          omacAdata
        ] = await Promise.all([
          omac(zero, nonce),
          omac(one, adata)
        ]);
        const ciphered = await ctr(plaintext, omacNonce);
        const omacCiphered = await omac(two, ciphered);
        const tag = omacCiphered; // Assumes that omac(*).length === tagLength.
        for (let i = 0; i < tagLength; i++) {
          tag[i] ^= omacAdata[i] ^ omacNonce[i];
        }
        return util.concatUint8Array([ciphered, tag]);
      },

      /**
       * Decrypt ciphertext input.
       * @param {Uint8Array} ciphertext - The ciphertext input to be decrypted
       * @param {Uint8Array} nonce - The nonce (16 bytes)
       * @param {Uint8Array} adata - Associated data to verify
       * @returns {Promise<Uint8Array>} The plaintext output.
       */
      decrypt: async function(ciphertext, nonce, adata) {
        if (ciphertext.length < tagLength) throw new Error('Invalid EAX ciphertext');
        const ciphered = ciphertext.subarray(0, -tagLength);
        const ctTag = ciphertext.subarray(-tagLength);
        const [
          omacNonce,
          omacAdata,
          omacCiphered
        ] = await Promise.all([
          omac(zero, nonce),
          omac(one, adata),
          omac(two, ciphered)
        ]);
        const tag = omacCiphered; // Assumes that omac(*).length === tagLength.
        for (let i = 0; i < tagLength; i++) {
          tag[i] ^= omacAdata[i] ^ omacNonce[i];
        }
        if (!util.equalsUint8Array(ctTag, tag)) throw new Error('Authentication tag mismatch');
        const plaintext = await ctr(ciphered, omacNonce);
        return plaintext;
      }
    };
  }


  /**
   * Get EAX nonce as defined by {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-5.16.1|RFC4880bis-04, section 5.16.1}.
   * @param {Uint8Array} iv - The initialization vector (16 bytes)
   * @param {Uint8Array} chunkIndex - The chunk index (8 bytes)
   */
  EAX.getNonce = function(iv, chunkIndex) {
    const nonce = iv.slice();
    for (let i = 0; i < chunkIndex.length; i++) {
      nonce[8 + i] ^= chunkIndex[i];
    }
    return nonce;
  };

  EAX.blockLength = blockLength$1;
  EAX.ivLength = ivLength;
  EAX.tagLength = tagLength;

  // OpenPGP.js - An OpenPGP implementation in javascript

  const blockLength$2 = 16;
  const ivLength$1 = 15;

  // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-5.16.2:
  // While OCB [RFC7253] allows the authentication tag length to be of any
  // number up to 128 bits long, this document requires a fixed
  // authentication tag length of 128 bits (16 octets) for simplicity.
  const tagLength$1 = 16;


  function ntz(n) {
    let ntz = 0;
    for (let i = 1; (n & i) === 0; i <<= 1) {
      ntz++;
    }
    return ntz;
  }

  function xorMut$1(S, T) {
    for (let i = 0; i < S.length; i++) {
      S[i] ^= T[i];
    }
    return S;
  }

  function xor(S, T) {
    return xorMut$1(S.slice(), T);
  }

  const zeroBlock$1 = new Uint8Array(blockLength$2);
  const one$1 = new Uint8Array([1]);

  /**
   * Class to en/decrypt using OCB mode.
   * @param {enums.symmetric} cipher - The symmetric cipher algorithm to use
   * @param {Uint8Array} key - The encryption key
   */
  async function OCB(cipher$1, key) {

    let maxNtz = 0;
    let encipher;
    let decipher;
    let mask;

    constructKeyVariables(cipher$1, key);

    function constructKeyVariables(cipher$1, key) {
      const cipherName = enums.read(enums.symmetric, cipher$1);
      const aes = new cipher[cipherName](key);
      encipher = aes.encrypt.bind(aes);
      decipher = aes.decrypt.bind(aes);

      const mask_x = encipher(zeroBlock$1);
      const mask_$ = util.double(mask_x);
      mask = [];
      mask[0] = util.double(mask_$);


      mask.x = mask_x;
      mask.$ = mask_$;
    }

    function extendKeyVariables(text, adata) {
      const newMaxNtz = util.nbits(Math.max(text.length, adata.length) / blockLength$2 | 0) - 1;
      for (let i = maxNtz + 1; i <= newMaxNtz; i++) {
        mask[i] = util.double(mask[i - 1]);
      }
      maxNtz = newMaxNtz;
    }

    function hash(adata) {
      if (!adata.length) {
        // Fast path
        return zeroBlock$1;
      }

      //
      // Consider A as a sequence of 128-bit blocks
      //
      const m = adata.length / blockLength$2 | 0;

      const offset = new Uint8Array(blockLength$2);
      const sum = new Uint8Array(blockLength$2);
      for (let i = 0; i < m; i++) {
        xorMut$1(offset, mask[ntz(i + 1)]);
        xorMut$1(sum, encipher(xor(offset, adata)));
        adata = adata.subarray(blockLength$2);
      }

      //
      // Process any final partial block; compute final hash value
      //
      if (adata.length) {
        xorMut$1(offset, mask.x);

        const cipherInput = new Uint8Array(blockLength$2);
        cipherInput.set(adata, 0);
        cipherInput[adata.length] = 0b10000000;
        xorMut$1(cipherInput, offset);

        xorMut$1(sum, encipher(cipherInput));
      }

      return sum;
    }

    /**
     * Encrypt/decrypt data.
     * @param {encipher|decipher} fn - Encryption/decryption block cipher function
     * @param {Uint8Array} text - The cleartext or ciphertext (without tag) input
     * @param {Uint8Array} nonce - The nonce (15 bytes)
     * @param {Uint8Array} adata - Associated data to sign
     * @returns {Promise<Uint8Array>} The ciphertext or plaintext output, with tag appended in both cases.
     */
    function crypt(fn, text, nonce, adata) {
      //
      // Consider P as a sequence of 128-bit blocks
      //
      const m = text.length / blockLength$2 | 0;

      //
      // Key-dependent variables
      //
      extendKeyVariables(text, adata);

      //
      // Nonce-dependent and per-encryption variables
      //
      //    Nonce = num2str(TAGLEN mod 128,7) || zeros(120-bitlen(N)) || 1 || N
      // Note: We assume here that tagLength mod 16 == 0.
      const paddedNonce = util.concatUint8Array([zeroBlock$1.subarray(0, ivLength$1 - nonce.length), one$1, nonce]);
      //    bottom = str2num(Nonce[123..128])
      const bottom = paddedNonce[blockLength$2 - 1] & 0b111111;
      //    Ktop = ENCIPHER(K, Nonce[1..122] || zeros(6))
      paddedNonce[blockLength$2 - 1] &= 0b11000000;
      const kTop = encipher(paddedNonce);
      //    Stretch = Ktop || (Ktop[1..64] xor Ktop[9..72])
      const stretched = util.concatUint8Array([kTop, xor(kTop.subarray(0, 8), kTop.subarray(1, 9))]);
      //    Offset_0 = Stretch[1+bottom..128+bottom]
      const offset = util.shiftRight(stretched.subarray(0 + (bottom >> 3), 17 + (bottom >> 3)), 8 - (bottom & 7)).subarray(1);
      //    Checksum_0 = zeros(128)
      const checksum = new Uint8Array(blockLength$2);

      const ct = new Uint8Array(text.length + tagLength$1);

      //
      // Process any whole blocks
      //
      let i;
      let pos = 0;
      for (i = 0; i < m; i++) {
        // Offset_i = Offset_{i-1} xor L_{ntz(i)}
        xorMut$1(offset, mask[ntz(i + 1)]);
        // C_i = Offset_i xor ENCIPHER(K, P_i xor Offset_i)
        // P_i = Offset_i xor DECIPHER(K, C_i xor Offset_i)
        ct.set(xorMut$1(fn(xor(offset, text)), offset), pos);
        // Checksum_i = Checksum_{i-1} xor P_i
        xorMut$1(checksum, fn === encipher ? text : ct.subarray(pos));

        text = text.subarray(blockLength$2);
        pos += blockLength$2;
      }

      //
      // Process any final partial block and compute raw tag
      //
      if (text.length) {
        // Offset_* = Offset_m xor L_*
        xorMut$1(offset, mask.x);
        // Pad = ENCIPHER(K, Offset_*)
        const padding = encipher(offset);
        // C_* = P_* xor Pad[1..bitlen(P_*)]
        ct.set(xor(text, padding), pos);

        // Checksum_* = Checksum_m xor (P_* || 1 || new Uint8Array(127-bitlen(P_*)))
        const xorInput = new Uint8Array(blockLength$2);
        xorInput.set(fn === encipher ? text : ct.subarray(pos, -tagLength$1), 0);
        xorInput[text.length] = 0b10000000;
        xorMut$1(checksum, xorInput);
        pos += text.length;
      }
      // Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A)
      const tag = xorMut$1(encipher(xorMut$1(xorMut$1(checksum, offset), mask.$)), hash(adata));

      //
      // Assemble ciphertext
      //
      // C = C_1 || C_2 || ... || C_m || C_* || Tag[1..TAGLEN]
      ct.set(tag, pos);
      return ct;
    }


    return {
      /**
       * Encrypt plaintext input.
       * @param {Uint8Array} plaintext - The cleartext input to be encrypted
       * @param {Uint8Array} nonce - The nonce (15 bytes)
       * @param {Uint8Array} adata - Associated data to sign
       * @returns {Promise<Uint8Array>} The ciphertext output.
       */
      encrypt: async function(plaintext, nonce, adata) {
        return crypt(encipher, plaintext, nonce, adata);
      },

      /**
       * Decrypt ciphertext input.
       * @param {Uint8Array} ciphertext - The ciphertext input to be decrypted
       * @param {Uint8Array} nonce - The nonce (15 bytes)
       * @param {Uint8Array} adata - Associated data to sign
       * @returns {Promise<Uint8Array>} The ciphertext output.
       */
      decrypt: async function(ciphertext, nonce, adata) {
        if (ciphertext.length < tagLength$1) throw new Error('Invalid OCB ciphertext');

        const tag = ciphertext.subarray(-tagLength$1);
        ciphertext = ciphertext.subarray(0, -tagLength$1);

        const crypted = crypt(decipher, ciphertext, nonce, adata);
        // if (Tag[1..TAGLEN] == T)
        if (util.equalsUint8Array(tag, crypted.subarray(-tagLength$1))) {
          return crypted.subarray(0, -tagLength$1);
        }
        throw new Error('Authentication tag mismatch');
      }
    };
  }


  /**
   * Get OCB nonce as defined by {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-5.16.2|RFC4880bis-04, section 5.16.2}.
   * @param {Uint8Array} iv - The initialization vector (15 bytes)
   * @param {Uint8Array} chunkIndex - The chunk index (8 bytes)
   */
  OCB.getNonce = function(iv, chunkIndex) {
    const nonce = iv.slice();
    for (let i = 0; i < chunkIndex.length; i++) {
      nonce[7 + i] ^= chunkIndex[i];
    }
    return nonce;
  };

  OCB.blockLength = blockLength$2;
  OCB.ivLength = ivLength$1;
  OCB.tagLength = tagLength$1;

  const _AES_GCM_data_maxLength = 68719476704; // 2^36 - 2^5
  class AES_GCM {
      constructor(key, nonce, adata, tagSize = 16, aes) {
          this.tagSize = tagSize;
          this.gamma0 = 0;
          this.counter = 1;
          this.aes = aes ? aes : new AES(key, undefined, false, 'CTR');
          let { asm, heap } = this.aes.acquire_asm();
          // Init GCM
          asm.gcm_init();
          // Tag size
          if (this.tagSize < 4 || this.tagSize > 16)
              throw new IllegalArgumentError('illegal tagSize value');
          // Nonce
          const noncelen = nonce.length || 0;
          const noncebuf = new Uint8Array(16);
          if (noncelen !== 12) {
              this._gcm_mac_process(nonce);
              heap[0] = 0;
              heap[1] = 0;
              heap[2] = 0;
              heap[3] = 0;
              heap[4] = 0;
              heap[5] = 0;
              heap[6] = 0;
              heap[7] = 0;
              heap[8] = 0;
              heap[9] = 0;
              heap[10] = 0;
              heap[11] = noncelen >>> 29;
              heap[12] = (noncelen >>> 21) & 255;
              heap[13] = (noncelen >>> 13) & 255;
              heap[14] = (noncelen >>> 5) & 255;
              heap[15] = (noncelen << 3) & 255;
              asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA, 16);
              asm.get_iv(AES_asm.HEAP_DATA);
              asm.set_iv(0, 0, 0, 0);
              noncebuf.set(heap.subarray(0, 16));
          }
          else {
              noncebuf.set(nonce);
              noncebuf[15] = 1;
          }
          const nonceview = new DataView(noncebuf.buffer);
          this.gamma0 = nonceview.getUint32(12);
          asm.set_nonce(nonceview.getUint32(0), nonceview.getUint32(4), nonceview.getUint32(8), 0);
          asm.set_mask(0, 0, 0, 0xffffffff);
          // Associated data
          if (adata !== undefined) {
              if (adata.length > _AES_GCM_data_maxLength)
                  throw new IllegalArgumentError('illegal adata length');
              if (adata.length) {
                  this.adata = adata;
                  this._gcm_mac_process(adata);
              }
              else {
                  this.adata = undefined;
              }
          }
          else {
              this.adata = undefined;
          }
          // Counter
          if (this.counter < 1 || this.counter > 0xffffffff)
              throw new RangeError('counter must be a positive 32-bit integer');
          asm.set_counter(0, 0, 0, (this.gamma0 + this.counter) | 0);
      }
      static encrypt(cleartext, key, nonce, adata, tagsize) {
          return new AES_GCM(key, nonce, adata, tagsize).encrypt(cleartext);
      }
      static decrypt(ciphertext, key, nonce, adata, tagsize) {
          return new AES_GCM(key, nonce, adata, tagsize).decrypt(ciphertext);
      }
      encrypt(data) {
          return this.AES_GCM_encrypt(data);
      }
      decrypt(data) {
          return this.AES_GCM_decrypt(data);
      }
      AES_GCM_Encrypt_process(data) {
          let dpos = 0;
          let dlen = data.length || 0;
          let { asm, heap } = this.aes.acquire_asm();
          let counter = this.counter;
          let pos = this.aes.pos;
          let len = this.aes.len;
          let rpos = 0;
          let rlen = (len + dlen) & -16;
          let wlen = 0;
          if (((counter - 1) << 4) + len + dlen > _AES_GCM_data_maxLength)
              throw new RangeError('counter overflow');
          const result = new Uint8Array(rlen);
          while (dlen > 0) {
              wlen = _heap_write(heap, pos + len, data, dpos, dlen);
              len += wlen;
              dpos += wlen;
              dlen -= wlen;
              wlen = asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA + pos, len);
              wlen = asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA + pos, wlen);
              if (wlen)
                  result.set(heap.subarray(pos, pos + wlen), rpos);
              counter += wlen >>> 4;
              rpos += wlen;
              if (wlen < len) {
                  pos += wlen;
                  len -= wlen;
              }
              else {
                  pos = 0;
                  len = 0;
              }
          }
          this.counter = counter;
          this.aes.pos = pos;
          this.aes.len = len;
          return result;
      }
      AES_GCM_Encrypt_finish() {
          let { asm, heap } = this.aes.acquire_asm();
          let counter = this.counter;
          let tagSize = this.tagSize;
          let adata = this.adata;
          let pos = this.aes.pos;
          let len = this.aes.len;
          const result = new Uint8Array(len + tagSize);
          asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA + pos, (len + 15) & -16);
          if (len)
              result.set(heap.subarray(pos, pos + len));
          let i = len;
          for (; i & 15; i++)
              heap[pos + i] = 0;
          asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA + pos, i);
          const alen = adata !== undefined ? adata.length : 0;
          const clen = ((counter - 1) << 4) + len;
          heap[0] = 0;
          heap[1] = 0;
          heap[2] = 0;
          heap[3] = alen >>> 29;
          heap[4] = alen >>> 21;
          heap[5] = (alen >>> 13) & 255;
          heap[6] = (alen >>> 5) & 255;
          heap[7] = (alen << 3) & 255;
          heap[8] = heap[9] = heap[10] = 0;
          heap[11] = clen >>> 29;
          heap[12] = (clen >>> 21) & 255;
          heap[13] = (clen >>> 13) & 255;
          heap[14] = (clen >>> 5) & 255;
          heap[15] = (clen << 3) & 255;
          asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA, 16);
          asm.get_iv(AES_asm.HEAP_DATA);
          asm.set_counter(0, 0, 0, this.gamma0);
          asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA, 16);
          result.set(heap.subarray(0, tagSize), len);
          this.counter = 1;
          this.aes.pos = 0;
          this.aes.len = 0;
          return result;
      }
      AES_GCM_Decrypt_process(data) {
          let dpos = 0;
          let dlen = data.length || 0;
          let { asm, heap } = this.aes.acquire_asm();
          let counter = this.counter;
          let tagSize = this.tagSize;
          let pos = this.aes.pos;
          let len = this.aes.len;
          let rpos = 0;
          let rlen = len + dlen > tagSize ? (len + dlen - tagSize) & -16 : 0;
          let tlen = len + dlen - rlen;
          let wlen = 0;
          if (((counter - 1) << 4) + len + dlen > _AES_GCM_data_maxLength)
              throw new RangeError('counter overflow');
          const result = new Uint8Array(rlen);
          while (dlen > tlen) {
              wlen = _heap_write(heap, pos + len, data, dpos, dlen - tlen);
              len += wlen;
              dpos += wlen;
              dlen -= wlen;
              wlen = asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA + pos, wlen);
              wlen = asm.cipher(AES_asm.DEC.CTR, AES_asm.HEAP_DATA + pos, wlen);
              if (wlen)
                  result.set(heap.subarray(pos, pos + wlen), rpos);
              counter += wlen >>> 4;
              rpos += wlen;
              pos = 0;
              len = 0;
          }
          if (dlen > 0) {
              len += _heap_write(heap, 0, data, dpos, dlen);
          }
          this.counter = counter;
          this.aes.pos = pos;
          this.aes.len = len;
          return result;
      }
      AES_GCM_Decrypt_finish() {
          let { asm, heap } = this.aes.acquire_asm();
          let tagSize = this.tagSize;
          let adata = this.adata;
          let counter = this.counter;
          let pos = this.aes.pos;
          let len = this.aes.len;
          let rlen = len - tagSize;
          if (len < tagSize)
              throw new IllegalStateError('authentication tag not found');
          const result = new Uint8Array(rlen);
          const atag = new Uint8Array(heap.subarray(pos + rlen, pos + len));
          let i = rlen;
          for (; i & 15; i++)
              heap[pos + i] = 0;
          asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA + pos, i);
          asm.cipher(AES_asm.DEC.CTR, AES_asm.HEAP_DATA + pos, i);
          if (rlen)
              result.set(heap.subarray(pos, pos + rlen));
          const alen = adata !== undefined ? adata.length : 0;
          const clen = ((counter - 1) << 4) + len - tagSize;
          heap[0] = 0;
          heap[1] = 0;
          heap[2] = 0;
          heap[3] = alen >>> 29;
          heap[4] = alen >>> 21;
          heap[5] = (alen >>> 13) & 255;
          heap[6] = (alen >>> 5) & 255;
          heap[7] = (alen << 3) & 255;
          heap[8] = heap[9] = heap[10] = 0;
          heap[11] = clen >>> 29;
          heap[12] = (clen >>> 21) & 255;
          heap[13] = (clen >>> 13) & 255;
          heap[14] = (clen >>> 5) & 255;
          heap[15] = (clen << 3) & 255;
          asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA, 16);
          asm.get_iv(AES_asm.HEAP_DATA);
          asm.set_counter(0, 0, 0, this.gamma0);
          asm.cipher(AES_asm.ENC.CTR, AES_asm.HEAP_DATA, 16);
          let acheck = 0;
          for (let i = 0; i < tagSize; ++i)
              acheck |= atag[i] ^ heap[i];
          if (acheck)
              throw new SecurityError('data integrity check failed');
          this.counter = 1;
          this.aes.pos = 0;
          this.aes.len = 0;
          return result;
      }
      AES_GCM_decrypt(data) {
          const result1 = this.AES_GCM_Decrypt_process(data);
          const result2 = this.AES_GCM_Decrypt_finish();
          const result = new Uint8Array(result1.length + result2.length);
          if (result1.length)
              result.set(result1);
          if (result2.length)
              result.set(result2, result1.length);
          return result;
      }
      AES_GCM_encrypt(data) {
          const result1 = this.AES_GCM_Encrypt_process(data);
          const result2 = this.AES_GCM_Encrypt_finish();
          const result = new Uint8Array(result1.length + result2.length);
          if (result1.length)
              result.set(result1);
          if (result2.length)
              result.set(result2, result1.length);
          return result;
      }
      _gcm_mac_process(data) {
          let { asm, heap } = this.aes.acquire_asm();
          let dpos = 0;
          let dlen = data.length || 0;
          let wlen = 0;
          while (dlen > 0) {
              wlen = _heap_write(heap, 0, data, dpos, dlen);
              dpos += wlen;
              dlen -= wlen;
              while (wlen & 15)
                  heap[wlen++] = 0;
              asm.mac(AES_asm.MAC.GCM, AES_asm.HEAP_DATA, wlen);
          }
      }
  }

  // OpenPGP.js - An OpenPGP implementation in javascript

  const webCrypto$4 = util.getWebCrypto();
  const nodeCrypto$4 = util.getNodeCrypto();
  const Buffer$2 = util.getNodeBuffer();

  const blockLength$3 = 16;
  const ivLength$2 = 12; // size of the IV in bytes
  const tagLength$2 = 16; // size of the tag in bytes
  const ALGO = 'AES-GCM';

  /**
   * Class to en/decrypt using GCM mode.
   * @param {enums.symmetric} cipher - The symmetric cipher algorithm to use
   * @param {Uint8Array} key - The encryption key
   */
  async function GCM(cipher, key) {
    if (cipher !== enums.symmetric.aes128 &&
      cipher !== enums.symmetric.aes192 &&
      cipher !== enums.symmetric.aes256) {
      throw new Error('GCM mode supports only AES cipher');
    }

    if (util.getNodeCrypto()) { // Node crypto library
      return {
        encrypt: async function(pt, iv, adata = new Uint8Array()) {
          const en = new nodeCrypto$4.createCipheriv('aes-' + (key.length * 8) + '-gcm', key, iv);
          en.setAAD(adata);
          const ct = Buffer$2.concat([en.update(pt), en.final(), en.getAuthTag()]); // append auth tag to ciphertext
          return new Uint8Array(ct);
        },

        decrypt: async function(ct, iv, adata = new Uint8Array()) {
          const de = new nodeCrypto$4.createDecipheriv('aes-' + (key.length * 8) + '-gcm', key, iv);
          de.setAAD(adata);
          de.setAuthTag(ct.slice(ct.length - tagLength$2, ct.length)); // read auth tag at end of ciphertext
          const pt = Buffer$2.concat([de.update(ct.slice(0, ct.length - tagLength$2)), de.final()]);
          return new Uint8Array(pt);
        }
      };
    }

    if (util.getWebCrypto() && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support
      const _key = await webCrypto$4.importKey('raw', key, { name: ALGO }, false, ['encrypt', 'decrypt']);

      return {
        encrypt: async function(pt, iv, adata = new Uint8Array()) {
          if (!pt.length) { // iOS does not support GCM-en/decrypting empty messages
            return AES_GCM.encrypt(pt, key, iv, adata);
          }
          const ct = await webCrypto$4.encrypt({ name: ALGO, iv, additionalData: adata, tagLength: tagLength$2 * 8 }, _key, pt);
          return new Uint8Array(ct);
        },

        decrypt: async function(ct, iv, adata = new Uint8Array()) {
          if (ct.length === tagLength$2) { // iOS does not support GCM-en/decrypting empty messages
            return AES_GCM.decrypt(ct, key, iv, adata);
          }
          const pt = await webCrypto$4.decrypt({ name: ALGO, iv, additionalData: adata, tagLength: tagLength$2 * 8 }, _key, ct);
          return new Uint8Array(pt);
        }
      };
    }

    return {
      encrypt: async function(pt, iv, adata) {
        return AES_GCM.encrypt(pt, key, iv, adata);
      },

      decrypt: async function(ct, iv, adata) {
        return AES_GCM.decrypt(ct, key, iv, adata);
      }
    };
  }


  /**
   * Get GCM nonce. Note: this operation is not defined by the standard.
   * A future version of the standard may define GCM mode differently,
   * hopefully under a different ID (we use Private/Experimental algorithm
   * ID 100) so that we can maintain backwards compatibility.
   * @param {Uint8Array} iv - The initialization vector (12 bytes)
   * @param {Uint8Array} chunkIndex - The chunk index (8 bytes)
   */
  GCM.getNonce = function(iv, chunkIndex) {
    const nonce = iv.slice();
    for (let i = 0; i < chunkIndex.length; i++) {
      nonce[4 + i] ^= chunkIndex[i];
    }
    return nonce;
  };

  GCM.blockLength = blockLength$3;
  GCM.ivLength = ivLength$2;
  GCM.tagLength = tagLength$2;

  /**
   * @fileoverview Cipher modes
   * @module crypto/mode
   * @private
   */

  var mode = {
    /** @see module:crypto/mode/cfb */
    cfb: cfb,
    /** @see module:crypto/mode/gcm */
    gcm: GCM,
    experimentalGCM: GCM,
    /** @see module:crypto/mode/eax */
    eax: EAX,
    /** @see module:crypto/mode/ocb */
    ocb: OCB
  };

  var naclFastLight = createCommonjsModule(function (module) {
  /*jshint bitwise: false*/

  (function(nacl) {

  // Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.
  // Public domain.
  //
  // Implementation derived from TweetNaCl version 20140427.
  // See for details: http://tweetnacl.cr.yp.to/

  var gf = function(init) {
    var i, r = new Float64Array(16);
    if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
    return r;
  };

  //  Pluggable, initialized in high-level API below.
  var randombytes = function(/* x, n */) { throw new Error('no PRNG'); };

  var _9 = new Uint8Array(32); _9[0] = 9;

  var gf0 = gf(),
      gf1 = gf([1]),
      _121665 = gf([0xdb41, 1]),
      D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]),
      D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]),
      X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]),
      Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]),
      I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);

  function vn(x, xi, y, yi, n) {
    var i,d = 0;
    for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i];
    return (1 & ((d - 1) >>> 8)) - 1;
  }

  function crypto_verify_32(x, xi, y, yi) {
    return vn(x,xi,y,yi,32);
  }

  function set25519(r, a) {
    var i;
    for (i = 0; i < 16; i++) r[i] = a[i]|0;
  }

  function car25519(o) {
    var i, v, c = 1;
    for (i = 0; i < 16; i++) {
      v = o[i] + c + 65535;
      c = Math.floor(v / 65536);
      o[i] = v - c * 65536;
    }
    o[0] += c-1 + 37 * (c-1);
  }

  function sel25519(p, q, b) {
    var t, c = ~(b-1);
    for (var i = 0; i < 16; i++) {
      t = c & (p[i] ^ q[i]);
      p[i] ^= t;
      q[i] ^= t;
    }
  }

  function pack25519(o, n) {
    var i, j, b;
    var m = gf(), t = gf();
    for (i = 0; i < 16; i++) t[i] = n[i];
    car25519(t);
    car25519(t);
    car25519(t);
    for (j = 0; j < 2; j++) {
      m[0] = t[0] - 0xffed;
      for (i = 1; i < 15; i++) {
        m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1);
        m[i-1] &= 0xffff;
      }
      m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1);
      b = (m[15]>>16) & 1;
      m[14] &= 0xffff;
      sel25519(t, m, 1-b);
    }
    for (i = 0; i < 16; i++) {
      o[2*i] = t[i] & 0xff;
      o[2*i+1] = t[i]>>8;
    }
  }

  function neq25519(a, b) {
    var c = new Uint8Array(32), d = new Uint8Array(32);
    pack25519(c, a);
    pack25519(d, b);
    return crypto_verify_32(c, 0, d, 0);
  }

  function par25519(a) {
    var d = new Uint8Array(32);
    pack25519(d, a);
    return d[0] & 1;
  }

  function unpack25519(o, n) {
    var i;
    for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8);
    o[15] &= 0x7fff;
  }

  function A(o, a, b) {
    for (var i = 0; i < 16; i++) o[i] = a[i] + b[i];
  }

  function Z(o, a, b) {
    for (var i = 0; i < 16; i++) o[i] = a[i] - b[i];
  }

  function M(o, a, b) {
    var v, c,
       t0 = 0,  t1 = 0,  t2 = 0,  t3 = 0,  t4 = 0,  t5 = 0,  t6 = 0,  t7 = 0,
       t8 = 0,  t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
      t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
      t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0,
      b0 = b[0],
      b1 = b[1],
      b2 = b[2],
      b3 = b[3],
      b4 = b[4],
      b5 = b[5],
      b6 = b[6],
      b7 = b[7],
      b8 = b[8],
      b9 = b[9],
      b10 = b[10],
      b11 = b[11],
      b12 = b[12],
      b13 = b[13],
      b14 = b[14],
      b15 = b[15];

    v = a[0];
    t0 += v * b0;
    t1 += v * b1;
    t2 += v * b2;
    t3 += v * b3;
    t4 += v * b4;
    t5 += v * b5;
    t6 += v * b6;
    t7 += v * b7;
    t8 += v * b8;
    t9 += v * b9;
    t10 += v * b10;
    t11 += v * b11;
    t12 += v * b12;
    t13 += v * b13;
    t14 += v * b14;
    t15 += v * b15;
    v = a[1];
    t1 += v * b0;
    t2 += v * b1;
    t3 += v * b2;
    t4 += v * b3;
    t5 += v * b4;
    t6 += v * b5;
    t7 += v * b6;
    t8 += v * b7;
    t9 += v * b8;
    t10 += v * b9;
    t11 += v * b10;
    t12 += v * b11;
    t13 += v * b12;
    t14 += v * b13;
    t15 += v * b14;
    t16 += v * b15;
    v = a[2];
    t2 += v * b0;
    t3 += v * b1;
    t4 += v * b2;
    t5 += v * b3;
    t6 += v * b4;
    t7 += v * b5;
    t8 += v * b6;
    t9 += v * b7;
    t10 += v * b8;
    t11 += v * b9;
    t12 += v * b10;
    t13 += v * b11;
    t14 += v * b12;
    t15 += v * b13;
    t16 += v * b14;
    t17 += v * b15;
    v = a[3];
    t3 += v * b0;
    t4 += v * b1;
    t5 += v * b2;
    t6 += v * b3;
    t7 += v * b4;
    t8 += v * b5;
    t9 += v * b6;
    t10 += v * b7;
    t11 += v * b8;
    t12 += v * b9;
    t13 += v * b10;
    t14 += v * b11;
    t15 += v * b12;
    t16 += v * b13;
    t17 += v * b14;
    t18 += v * b15;
    v = a[4];
    t4 += v * b0;
    t5 += v * b1;
    t6 += v * b2;
    t7 += v * b3;
    t8 += v * b4;
    t9 += v * b5;
    t10 += v * b6;
    t11 += v * b7;
    t12 += v * b8;
    t13 += v * b9;
    t14 += v * b10;
    t15 += v * b11;
    t16 += v * b12;
    t17 += v * b13;
    t18 += v * b14;
    t19 += v * b15;
    v = a[5];
    t5 += v * b0;
    t6 += v * b1;
    t7 += v * b2;
    t8 += v * b3;
    t9 += v * b4;
    t10 += v * b5;
    t11 += v * b6;
    t12 += v * b7;
    t13 += v * b8;
    t14 += v * b9;
    t15 += v * b10;
    t16 += v * b11;
    t17 += v * b12;
    t18 += v * b13;
    t19 += v * b14;
    t20 += v * b15;
    v = a[6];
    t6 += v * b0;
    t7 += v * b1;
    t8 += v * b2;
    t9 += v * b3;
    t10 += v * b4;
    t11 += v * b5;
    t12 += v * b6;
    t13 += v * b7;
    t14 += v * b8;
    t15 += v * b9;
    t16 += v * b10;
    t17 += v * b11;
    t18 += v * b12;
    t19 += v * b13;
    t20 += v * b14;
    t21 += v * b15;
    v = a[7];
    t7 += v * b0;
    t8 += v * b1;
    t9 += v * b2;
    t10 += v * b3;
    t11 += v * b4;
    t12 += v * b5;
    t13 += v * b6;
    t14 += v * b7;
    t15 += v * b8;
    t16 += v * b9;
    t17 += v * b10;
    t18 += v * b11;
    t19 += v * b12;
    t20 += v * b13;
    t21 += v * b14;
    t22 += v * b15;
    v = a[8];
    t8 += v * b0;
    t9 += v * b1;
    t10 += v * b2;
    t11 += v * b3;
    t12 += v * b4;
    t13 += v * b5;
    t14 += v * b6;
    t15 += v * b7;
    t16 += v * b8;
    t17 += v * b9;
    t18 += v * b10;
    t19 += v * b11;
    t20 += v * b12;
    t21 += v * b13;
    t22 += v * b14;
    t23 += v * b15;
    v = a[9];
    t9 += v * b0;
    t10 += v * b1;
    t11 += v * b2;
    t12 += v * b3;
    t13 += v * b4;
    t14 += v * b5;
    t15 += v * b6;
    t16 += v * b7;
    t17 += v * b8;
    t18 += v * b9;
    t19 += v * b10;
    t20 += v * b11;
    t21 += v * b12;
    t22 += v * b13;
    t23 += v * b14;
    t24 += v * b15;
    v = a[10];
    t10 += v * b0;
    t11 += v * b1;
    t12 += v * b2;
    t13 += v * b3;
    t14 += v * b4;
    t15 += v * b5;
    t16 += v * b6;
    t17 += v * b7;
    t18 += v * b8;
    t19 += v * b9;
    t20 += v * b10;
    t21 += v * b11;
    t22 += v * b12;
    t23 += v * b13;
    t24 += v * b14;
    t25 += v * b15;
    v = a[11];
    t11 += v * b0;
    t12 += v * b1;
    t13 += v * b2;
    t14 += v * b3;
    t15 += v * b4;
    t16 += v * b5;
    t17 += v * b6;
    t18 += v * b7;
    t19 += v * b8;
    t20 += v * b9;
    t21 += v * b10;
    t22 += v * b11;
    t23 += v * b12;
    t24 += v * b13;
    t25 += v * b14;
    t26 += v * b15;
    v = a[12];
    t12 += v * b0;
    t13 += v * b1;
    t14 += v * b2;
    t15 += v * b3;
    t16 += v * b4;
    t17 += v * b5;
    t18 += v * b6;
    t19 += v * b7;
    t20 += v * b8;
    t21 += v * b9;
    t22 += v * b10;
    t23 += v * b11;
    t24 += v * b12;
    t25 += v * b13;
    t26 += v * b14;
    t27 += v * b15;
    v = a[13];
    t13 += v * b0;
    t14 += v * b1;
    t15 += v * b2;
    t16 += v * b3;
    t17 += v * b4;
    t18 += v * b5;
    t19 += v * b6;
    t20 += v * b7;
    t21 += v * b8;
    t22 += v * b9;
    t23 += v * b10;
    t24 += v * b11;
    t25 += v * b12;
    t26 += v * b13;
    t27 += v * b14;
    t28 += v * b15;
    v = a[14];
    t14 += v * b0;
    t15 += v * b1;
    t16 += v * b2;
    t17 += v * b3;
    t18 += v * b4;
    t19 += v * b5;
    t20 += v * b6;
    t21 += v * b7;
    t22 += v * b8;
    t23 += v * b9;
    t24 += v * b10;
    t25 += v * b11;
    t26 += v * b12;
    t27 += v * b13;
    t28 += v * b14;
    t29 += v * b15;
    v = a[15];
    t15 += v * b0;
    t16 += v * b1;
    t17 += v * b2;
    t18 += v * b3;
    t19 += v * b4;
    t20 += v * b5;
    t21 += v * b6;
    t22 += v * b7;
    t23 += v * b8;
    t24 += v * b9;
    t25 += v * b10;
    t26 += v * b11;
    t27 += v * b12;
    t28 += v * b13;
    t29 += v * b14;
    t30 += v * b15;

    t0  += 38 * t16;
    t1  += 38 * t17;
    t2  += 38 * t18;
    t3  += 38 * t19;
    t4  += 38 * t20;
    t5  += 38 * t21;
    t6  += 38 * t22;
    t7  += 38 * t23;
    t8  += 38 * t24;
    t9  += 38 * t25;
    t10 += 38 * t26;
    t11 += 38 * t27;
    t12 += 38 * t28;
    t13 += 38 * t29;
    t14 += 38 * t30;
    // t15 left as is

    // first car
    c = 1;
    v =  t0 + c + 65535; c = Math.floor(v / 65536);  t0 = v - c * 65536;
    v =  t1 + c + 65535; c = Math.floor(v / 65536);  t1 = v - c * 65536;
    v =  t2 + c + 65535; c = Math.floor(v / 65536);  t2 = v - c * 65536;
    v =  t3 + c + 65535; c = Math.floor(v / 65536);  t3 = v - c * 65536;
    v =  t4 + c + 65535; c = Math.floor(v / 65536);  t4 = v - c * 65536;
    v =  t5 + c + 65535; c = Math.floor(v / 65536);  t5 = v - c * 65536;
    v =  t6 + c + 65535; c = Math.floor(v / 65536);  t6 = v - c * 65536;
    v =  t7 + c + 65535; c = Math.floor(v / 65536);  t7 = v - c * 65536;
    v =  t8 + c + 65535; c = Math.floor(v / 65536);  t8 = v - c * 65536;
    v =  t9 + c + 65535; c = Math.floor(v / 65536);  t9 = v - c * 65536;
    v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
    v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
    v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
    v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
    v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
    v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
    t0 += c-1 + 37 * (c-1);

    // second car
    c = 1;
    v =  t0 + c + 65535; c = Math.floor(v / 65536);  t0 = v - c * 65536;
    v =  t1 + c + 65535; c = Math.floor(v / 65536);  t1 = v - c * 65536;
    v =  t2 + c + 65535; c = Math.floor(v / 65536);  t2 = v - c * 65536;
    v =  t3 + c + 65535; c = Math.floor(v / 65536);  t3 = v - c * 65536;
    v =  t4 + c + 65535; c = Math.floor(v / 65536);  t4 = v - c * 65536;
    v =  t5 + c + 65535; c = Math.floor(v / 65536);  t5 = v - c * 65536;
    v =  t6 + c + 65535; c = Math.floor(v / 65536);  t6 = v - c * 65536;
    v =  t7 + c + 65535; c = Math.floor(v / 65536);  t7 = v - c * 65536;
    v =  t8 + c + 65535; c = Math.floor(v / 65536);  t8 = v - c * 65536;
    v =  t9 + c + 65535; c = Math.floor(v / 65536);  t9 = v - c * 65536;
    v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
    v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
    v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
    v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
    v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
    v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
    t0 += c-1 + 37 * (c-1);

    o[ 0] = t0;
    o[ 1] = t1;
    o[ 2] = t2;
    o[ 3] = t3;
    o[ 4] = t4;
    o[ 5] = t5;
    o[ 6] = t6;
    o[ 7] = t7;
    o[ 8] = t8;
    o[ 9] = t9;
    o[10] = t10;
    o[11] = t11;
    o[12] = t12;
    o[13] = t13;
    o[14] = t14;
    o[15] = t15;
  }

  function S(o, a) {
    M(o, a, a);
  }

  function inv25519(o, i) {
    var c = gf();
    var a;
    for (a = 0; a < 16; a++) c[a] = i[a];
    for (a = 253; a >= 0; a--) {
      S(c, c);
      if(a !== 2 && a !== 4) M(c, c, i);
    }
    for (a = 0; a < 16; a++) o[a] = c[a];
  }

  function pow2523(o, i) {
    var c = gf();
    var a;
    for (a = 0; a < 16; a++) c[a] = i[a];
    for (a = 250; a >= 0; a--) {
        S(c, c);
        if(a !== 1) M(c, c, i);
    }
    for (a = 0; a < 16; a++) o[a] = c[a];
  }

  function crypto_scalarmult(q, n, p) {
    var z = new Uint8Array(32);
    var x = new Float64Array(80), r, i;
    var a = gf(), b = gf(), c = gf(),
        d = gf(), e = gf(), f = gf();
    for (i = 0; i < 31; i++) z[i] = n[i];
    z[31]=(n[31]&127)|64;
    z[0]&=248;
    unpack25519(x,p);
    for (i = 0; i < 16; i++) {
      b[i]=x[i];
      d[i]=a[i]=c[i]=0;
    }
    a[0]=d[0]=1;
    for (i=254; i>=0; --i) {
      r=(z[i>>>3]>>>(i&7))&1;
      sel25519(a,b,r);
      sel25519(c,d,r);
      A(e,a,c);
      Z(a,a,c);
      A(c,b,d);
      Z(b,b,d);
      S(d,e);
      S(f,a);
      M(a,c,a);
      M(c,b,e);
      A(e,a,c);
      Z(a,a,c);
      S(b,a);
      Z(c,d,f);
      M(a,c,_121665);
      A(a,a,d);
      M(c,c,a);
      M(a,d,f);
      M(d,b,x);
      S(b,e);
      sel25519(a,b,r);
      sel25519(c,d,r);
    }
    for (i = 0; i < 16; i++) {
      x[i+16]=a[i];
      x[i+32]=c[i];
      x[i+48]=b[i];
      x[i+64]=d[i];
    }
    var x32 = x.subarray(32);
    var x16 = x.subarray(16);
    inv25519(x32,x32);
    M(x16,x16,x32);
    pack25519(q,x16);
    return 0;
  }

  function crypto_scalarmult_base(q, n) {
    return crypto_scalarmult(q, n, _9);
  }

  function crypto_box_keypair(y, x) {
    randombytes(x, 32);
    return crypto_scalarmult_base(y, x);
  }

  function add(p, q) {
    var a = gf(), b = gf(), c = gf(),
        d = gf(), e = gf(), f = gf(),
        g = gf(), h = gf(), t = gf();

    Z(a, p[1], p[0]);
    Z(t, q[1], q[0]);
    M(a, a, t);
    A(b, p[0], p[1]);
    A(t, q[0], q[1]);
    M(b, b, t);
    M(c, p[3], q[3]);
    M(c, c, D2);
    M(d, p[2], q[2]);
    A(d, d, d);
    Z(e, b, a);
    Z(f, d, c);
    A(g, d, c);
    A(h, b, a);

    M(p[0], e, f);
    M(p[1], h, g);
    M(p[2], g, f);
    M(p[3], e, h);
  }

  function cswap(p, q, b) {
    var i;
    for (i = 0; i < 4; i++) {
      sel25519(p[i], q[i], b);
    }
  }

  function pack(r, p) {
    var tx = gf(), ty = gf(), zi = gf();
    inv25519(zi, p[2]);
    M(tx, p[0], zi);
    M(ty, p[1], zi);
    pack25519(r, ty);
    r[31] ^= par25519(tx) << 7;
  }

  function scalarmult(p, q, s) {
    var b, i;
    set25519(p[0], gf0);
    set25519(p[1], gf1);
    set25519(p[2], gf1);
    set25519(p[3], gf0);
    for (i = 255; i >= 0; --i) {
      b = (s[(i/8)|0] >> (i&7)) & 1;
      cswap(p, q, b);
      add(q, p);
      add(p, p);
      cswap(p, q, b);
    }
  }

  function scalarbase(p, s) {
    var q = [gf(), gf(), gf(), gf()];
    set25519(q[0], X);
    set25519(q[1], Y);
    set25519(q[2], gf1);
    M(q[3], X, Y);
    scalarmult(p, q, s);
  }

  function crypto_sign_keypair(pk, sk, seeded) {
    var d;
    var p = [gf(), gf(), gf(), gf()];
    var i;

    if (!seeded) randombytes(sk, 32);
    d = nacl.hash(sk.subarray(0, 32));
    d[0] &= 248;
    d[31] &= 127;
    d[31] |= 64;

    scalarbase(p, d);
    pack(pk, p);

    for (i = 0; i < 32; i++) sk[i+32] = pk[i];
    return 0;
  }

  var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]);

  function modL(r, x) {
    var carry, i, j, k;
    for (i = 63; i >= 32; --i) {
      carry = 0;
      for (j = i - 32, k = i - 12; j < k; ++j) {
        x[j] += carry - 16 * x[i] * L[j - (i - 32)];
        carry = Math.floor((x[j] + 128) / 256);
        x[j] -= carry * 256;
      }
      x[j] += carry;
      x[i] = 0;
    }
    carry = 0;
    for (j = 0; j < 32; j++) {
      x[j] += carry - (x[31] >> 4) * L[j];
      carry = x[j] >> 8;
      x[j] &= 255;
    }
    for (j = 0; j < 32; j++) x[j] -= carry * L[j];
    for (i = 0; i < 32; i++) {
      x[i+1] += x[i] >> 8;
      r[i] = x[i] & 255;
    }
  }

  function reduce(r) {
    var x = new Float64Array(64), i;
    for (i = 0; i < 64; i++) x[i] = r[i];
    for (i = 0; i < 64; i++) r[i] = 0;
    modL(r, x);
  }

  // Note: difference from C - smlen returned, not passed as argument.
  function crypto_sign(sm, m, n, sk) {
    var d, h, r;
    var i, j, x = new Float64Array(64);
    var p = [gf(), gf(), gf(), gf()];

    d = nacl.hash(sk.subarray(0, 32));
    d[0] &= 248;
    d[31] &= 127;
    d[31] |= 64;

    var smlen = n + 64;
    for (i = 0; i < n; i++) sm[64 + i] = m[i];
    for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i];

    r = nacl.hash(sm.subarray(32, smlen));
    reduce(r);
    scalarbase(p, r);
    pack(sm, p);

    for (i = 32; i < 64; i++) sm[i] = sk[i];
    h = nacl.hash(sm.subarray(0, smlen));
    reduce(h);

    for (i = 0; i < 64; i++) x[i] = 0;
    for (i = 0; i < 32; i++) x[i] = r[i];
    for (i = 0; i < 32; i++) {
      for (j = 0; j < 32; j++) {
        x[i+j] += h[i] * d[j];
      }
    }

    modL(sm.subarray(32), x);
    return smlen;
  }

  function unpackneg(r, p) {
    var t = gf(), chk = gf(), num = gf(),
        den = gf(), den2 = gf(), den4 = gf(),
        den6 = gf();

    set25519(r[2], gf1);
    unpack25519(r[1], p);
    S(num, r[1]);
    M(den, num, D);
    Z(num, num, r[2]);
    A(den, r[2], den);

    S(den2, den);
    S(den4, den2);
    M(den6, den4, den2);
    M(t, den6, num);
    M(t, t, den);

    pow2523(t, t);
    M(t, t, num);
    M(t, t, den);
    M(t, t, den);
    M(r[0], t, den);

    S(chk, r[0]);
    M(chk, chk, den);
    if (neq25519(chk, num)) M(r[0], r[0], I);

    S(chk, r[0]);
    M(chk, chk, den);
    if (neq25519(chk, num)) return -1;

    if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]);

    M(r[3], r[0], r[1]);
    return 0;
  }

  function crypto_sign_open(m, sm, n, pk) {
    var i;
    var t = new Uint8Array(32), h;
    var p = [gf(), gf(), gf(), gf()],
        q = [gf(), gf(), gf(), gf()];

    if (n < 64) return -1;

    if (unpackneg(q, pk)) return -1;

    for (i = 0; i < n; i++) m[i] = sm[i];
    for (i = 0; i < 32; i++) m[i+32] = pk[i];
    h = nacl.hash(m.subarray(0, n));
    reduce(h);
    scalarmult(p, q, h);

    scalarbase(q, sm.subarray(32));
    add(p, q);
    pack(t, p);

    n -= 64;
    if (crypto_verify_32(sm, 0, t, 0)) {
      for (i = 0; i < n; i++) m[i] = 0;
      return -1;
    }

    for (i = 0; i < n; i++) m[i] = sm[i + 64];
    return n;
  }

  var crypto_scalarmult_BYTES = 32,
      crypto_scalarmult_SCALARBYTES = 32,
      crypto_box_PUBLICKEYBYTES = 32,
      crypto_box_SECRETKEYBYTES = 32,
      crypto_sign_BYTES = 64,
      crypto_sign_PUBLICKEYBYTES = 32,
      crypto_sign_SECRETKEYBYTES = 64,
      crypto_sign_SEEDBYTES = 32;

  function checkArrayTypes() {
    for (var i = 0; i < arguments.length; i++) {
      if (!(arguments[i] instanceof Uint8Array))
        throw new TypeError('unexpected type, use Uint8Array');
    }
  }

  function cleanup(arr) {
    for (var i = 0; i < arr.length; i++) arr[i] = 0;
  }

  nacl.scalarMult = function(n, p) {
    checkArrayTypes(n, p);
    if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size');
    if (p.length !== crypto_scalarmult_BYTES) throw new Error('bad p size');
    var q = new Uint8Array(crypto_scalarmult_BYTES);
    crypto_scalarmult(q, n, p);
    return q;
  };

  nacl.box = {};

  nacl.box.keyPair = function() {
    var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES);
    var sk = new Uint8Array(crypto_box_SECRETKEYBYTES);
    crypto_box_keypair(pk, sk);
    return {publicKey: pk, secretKey: sk};
  };

  nacl.box.keyPair.fromSecretKey = function(secretKey) {
    checkArrayTypes(secretKey);
    if (secretKey.length !== crypto_box_SECRETKEYBYTES)
      throw new Error('bad secret key size');
    var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES);
    crypto_scalarmult_base(pk, secretKey);
    return {publicKey: pk, secretKey: new Uint8Array(secretKey)};
  };

  nacl.sign = function(msg, secretKey) {
    checkArrayTypes(msg, secretKey);
    if (secretKey.length !== crypto_sign_SECRETKEYBYTES)
      throw new Error('bad secret key size');
    var signedMsg = new Uint8Array(crypto_sign_BYTES+msg.length);
    crypto_sign(signedMsg, msg, msg.length, secretKey);
    return signedMsg;
  };

  nacl.sign.detached = function(msg, secretKey) {
    var signedMsg = nacl.sign(msg, secretKey);
    var sig = new Uint8Array(crypto_sign_BYTES);
    for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i];
    return sig;
  };

  nacl.sign.detached.verify = function(msg, sig, publicKey) {
    checkArrayTypes(msg, sig, publicKey);
    if (sig.length !== crypto_sign_BYTES)
      throw new Error('bad signature size');
    if (publicKey.length !== crypto_sign_PUBLICKEYBYTES)
      throw new Error('bad public key size');
    var sm = new Uint8Array(crypto_sign_BYTES + msg.length);
    var m = new Uint8Array(crypto_sign_BYTES + msg.length);
    var i;
    for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i];
    for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i];
    return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0);
  };

  nacl.sign.keyPair = function() {
    var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
    var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
    crypto_sign_keypair(pk, sk);
    return {publicKey: pk, secretKey: sk};
  };

  nacl.sign.keyPair.fromSecretKey = function(secretKey) {
    checkArrayTypes(secretKey);
    if (secretKey.length !== crypto_sign_SECRETKEYBYTES)
      throw new Error('bad secret key size');
    var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
    for (var i = 0; i < pk.length; i++) pk[i] = secretKey[32+i];
    return {publicKey: pk, secretKey: new Uint8Array(secretKey)};
  };

  nacl.sign.keyPair.fromSeed = function(seed) {
    checkArrayTypes(seed);
    if (seed.length !== crypto_sign_SEEDBYTES)
      throw new Error('bad seed size');
    var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
    var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
    for (var i = 0; i < 32; i++) sk[i] = seed[i];
    crypto_sign_keypair(pk, sk, true);
    return {publicKey: pk, secretKey: sk};
  };

  nacl.setPRNG = function(fn) {
    randombytes = fn;
  };

  (function() {
    // Initialize PRNG if environment provides CSPRNG.
    // If not, methods calling randombytes will throw.
    var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null;
    if (crypto && crypto.getRandomValues) {
      // Browsers.
      var QUOTA = 65536;
      nacl.setPRNG(function(x, n) {
        var i, v = new Uint8Array(n);
        for (i = 0; i < n; i += QUOTA) {
          crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
        }
        for (i = 0; i < n; i++) x[i] = v[i];
        cleanup(v);
      });
    } else if (typeof commonjsRequire !== 'undefined') {
      // Node.js.
      crypto = void('crypto');
      if (crypto && crypto.randomBytes) {
        nacl.setPRNG(function(x, n) {
          var i, v = crypto.randomBytes(n);
          for (i = 0; i < n; i++) x[i] = v[i];
          cleanup(v);
        });
      }
    }
  })();

  })(module.exports ? module.exports : (self.nacl = self.nacl || {}));
  });

  // GPG4Browsers - An OpenPGP implementation in javascript

  const nodeCrypto$5 = util.getNodeCrypto();

  /**
   * Retrieve secure random byte array of the specified length
   * @param {Integer} length - Length in bytes to generate
   * @returns {Uint8Array} Random byte array.
   */
  function getRandomBytes(length) {
    const buf = new Uint8Array(length);
    if (nodeCrypto$5) {
      const bytes = nodeCrypto$5.randomBytes(buf.length);
      buf.set(bytes);
    } else if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
      crypto.getRandomValues(buf);
    } else {
      throw new Error('No secure random number generator available.');
    }
    return buf;
  }

  /**
   * Create a secure random BigInteger that is greater than or equal to min and less than max.
   * @param {module:BigInteger} min - Lower bound, included
   * @param {module:BigInteger} max - Upper bound, excluded
   * @returns {Promise<module:BigInteger>} Random BigInteger.
   * @async
   */
  async function getRandomBigInteger(min, max) {
    const BigInteger = await util.getBigInteger();

    if (max.lt(min)) {
      throw new Error('Illegal parameter value: max <= min');
    }

    const modulus = max.sub(min);
    const bytes = modulus.byteLength();

    // Using a while loop is necessary to avoid bias introduced by the mod operation.
    // However, we request 64 extra random bits so that the bias is negligible.
    // Section B.1.1 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
    const r = new BigInteger(await getRandomBytes(bytes + 8));
    return r.mod(modulus).add(min);
  }

  var random = /*#__PURE__*/Object.freeze({
    __proto__: null,
    getRandomBytes: getRandomBytes,
    getRandomBigInteger: getRandomBigInteger
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  /**
   * Generate a probably prime random number
   * @param {Integer} bits - Bit length of the prime
   * @param {BigInteger} e - Optional RSA exponent to check against the prime
   * @param {Integer} k - Optional number of iterations of Miller-Rabin test
   * @returns BigInteger
   * @async
   */
  async function randomProbablePrime(bits, e, k) {
    const BigInteger = await util.getBigInteger();
    const one = new BigInteger(1);
    const min = one.leftShift(new BigInteger(bits - 1));
    const thirty = new BigInteger(30);
    /*
     * We can avoid any multiples of 3 and 5 by looking at n mod 30
     * n mod 30 = 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
     * the next possible prime is mod 30:
     *            1  7  7  7  7  7  7 11 11 11 11 13 13 17 17 17 17 19 19 23 23 23 23 29 29 29 29 29 29 1
     */
    const adds = [1, 6, 5, 4, 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2];

    const n = await getRandomBigInteger(min, min.leftShift(one));
    let i = n.mod(thirty).toNumber();

    do {
      n.iadd(new BigInteger(adds[i]));
      i = (i + adds[i]) % adds.length;
      // If reached the maximum, go back to the minimum.
      if (n.bitLength() > bits) {
        n.imod(min.leftShift(one)).iadd(min);
        i = n.mod(thirty).toNumber();
      }
    } while (!await isProbablePrime(n, e, k));
    return n;
  }

  /**
   * Probabilistic primality testing
   * @param {BigInteger} n - Number to test
   * @param {BigInteger} e - Optional RSA exponent to check against the prime
   * @param {Integer} k - Optional number of iterations of Miller-Rabin test
   * @returns {boolean}
   * @async
   */
  async function isProbablePrime(n, e, k) {
    if (e && !n.dec().gcd(e).isOne()) {
      return false;
    }
    if (!await divisionTest(n)) {
      return false;
    }
    if (!await fermat(n)) {
      return false;
    }
    if (!await millerRabin(n, k)) {
      return false;
    }
    // TODO implement the Lucas test
    // See Section C.3.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
    return true;
  }

  /**
   * Tests whether n is probably prime or not using Fermat's test with b = 2.
   * Fails if b^(n-1) mod n != 1.
   * @param {BigInteger} n - Number to test
   * @param {BigInteger} b - Optional Fermat test base
   * @returns {boolean}
   */
  async function fermat(n, b) {
    const BigInteger = await util.getBigInteger();
    b = b || new BigInteger(2);
    return b.modExp(n.dec(), n).isOne();
  }

  async function divisionTest(n) {
    const BigInteger = await util.getBigInteger();
    return smallPrimes.every(m => {
      return n.mod(new BigInteger(m)) !== 0;
    });
  }

  // https://github.com/gpg/libgcrypt/blob/master/cipher/primegen.c
  const smallPrimes = [
    7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
    47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
    103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
    157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
    211, 223, 227, 229, 233, 239, 241, 251, 257, 263,
    269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
    331, 337, 347, 349, 353, 359, 367, 373, 379, 383,
    389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
    449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
    509, 521, 523, 541, 547, 557, 563, 569, 571, 577,
    587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
    643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
    709, 719, 727, 733, 739, 743, 751, 757, 761, 769,
    773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
    853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
    919, 929, 937, 941, 947, 953, 967, 971, 977, 983,
    991, 997, 1009, 1013, 1019, 1021, 1031, 1033,
    1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091,
    1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
    1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
    1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277,
    1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307,
    1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399,
    1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
    1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493,
    1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559,
    1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609,
    1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
    1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
    1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789,
    1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871,
    1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931,
    1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997,
    1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053,
    2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111,
    2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161,
    2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243,
    2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297,
    2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357,
    2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411,
    2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473,
    2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551,
    2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633,
    2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687,
    2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729,
    2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791,
    2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851,
    2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917,
    2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
    3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061,
    3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137,
    3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209,
    3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271,
    3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
    3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391,
    3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467,
    3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533,
    3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583,
    3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643,
    3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709,
    3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779,
    3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851,
    3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917,
    3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989,
    4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049,
    4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111,
    4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177,
    4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243,
    4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
    4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391,
    4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457,
    4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519,
    4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597,
    4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
    4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729,
    4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799,
    4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889,
    4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951,
    4957, 4967, 4969, 4973, 4987, 4993, 4999
  ];


  // Miller-Rabin - Miller Rabin algorithm for primality test
  // Copyright Fedor Indutny, 2014.
  //
  // This software is licensed under the MIT License.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a
  // copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to permit
  // persons to whom the Software is furnished to do so, subject to the
  // following conditions:
  //
  // The above copyright notice and this permission notice shall be included
  // in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  // USE OR OTHER DEALINGS IN THE SOFTWARE.

  // Adapted on Jan 2018 from version 4.0.1 at https://github.com/indutny/miller-rabin

  // Sample syntax for Fixed-Base Miller-Rabin:
  // millerRabin(n, k, () => new BN(small_primes[Math.random() * small_primes.length | 0]))

  /**
   * Tests whether n is probably prime or not using the Miller-Rabin test.
   * See HAC Remark 4.28.
   * @param {BigInteger} n - Number to test
   * @param {Integer} k - Optional number of iterations of Miller-Rabin test
   * @param {Function} rand - Optional function to generate potential witnesses
   * @returns {boolean}
   * @async
   */
  async function millerRabin(n, k, rand) {
    const BigInteger = await util.getBigInteger();
    const len = n.bitLength();

    if (!k) {
      k = Math.max(1, (len / 48) | 0);
    }

    const n1 = n.dec(); // n - 1

    // Find d and s, (n - 1) = (2 ^ s) * d;
    let s = 0;
    while (!n1.getBit(s)) { s++; }
    const d = n.rightShift(new BigInteger(s));

    for (; k > 0; k--) {
      const a = rand ? rand() : await getRandomBigInteger(new BigInteger(2), n1);

      let x = a.modExp(d, n);
      if (x.isOne() || x.equal(n1)) {
        continue;
      }

      let i;
      for (i = 1; i < s; i++) {
        x = x.mul(x).mod(n);

        if (x.isOne()) {
          return false;
        }
        if (x.equal(n1)) {
          break;
        }
      }

      if (i === s) {
        return false;
      }
    }

    return true;
  }

  // GPG4Browsers - An OpenPGP implementation in javascript

  /**
   * ASN1 object identifiers for hashes
   * @see {@link https://tools.ietf.org/html/rfc4880#section-5.2.2}
   */
  const hash_headers = [];
  hash_headers[1] = [0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04,
    0x10];
  hash_headers[2] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14];
  hash_headers[3] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14];
  hash_headers[8] = [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00,
    0x04, 0x20];
  hash_headers[9] = [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00,
    0x04, 0x30];
  hash_headers[10] = [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
    0x00, 0x04, 0x40];
  hash_headers[11] = [0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
    0x00, 0x04, 0x1C];

  /**
   * Create padding with secure random data
   * @private
   * @param {Integer} length - Length of the padding in bytes
   * @returns {Uint8Array} Random padding.
   */
  function getPKCS1Padding(length) {
    const result = new Uint8Array(length);
    let count = 0;
    while (count < length) {
      const randomBytes = getRandomBytes(length - count);
      for (let i = 0; i < randomBytes.length; i++) {
        if (randomBytes[i] !== 0) {
          result[count++] = randomBytes[i];
        }
      }
    }
    return result;
  }

  /**
   * Create a EME-PKCS1-v1_5 padded message
   * @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.1|RFC 4880 13.1.1}
   * @param {Uint8Array} message - Message to be encoded
   * @param {Integer} keyLength - The length in octets of the key modulus
   * @returns {Uint8Array} EME-PKCS1 padded message.
   */
  function emeEncode(message, keyLength) {
    const mLength = message.length;
    // length checking
    if (mLength > keyLength - 11) {
      throw new Error('Message too long');
    }
    // Generate an octet string PS of length k - mLen - 3 consisting of
    // pseudo-randomly generated nonzero octets
    const PS = getPKCS1Padding(keyLength - mLength - 3);
    // Concatenate PS, the message M, and other padding to form an
    // encoded message EM of length k octets as EM = 0x00 || 0x02 || PS || 0x00 || M.
    const encoded = new Uint8Array(keyLength);
    // 0x00 byte
    encoded[1] = 2;
    encoded.set(PS, 2);
    // 0x00 bytes
    encoded.set(message, keyLength - mLength);
    return encoded;
  }

  /**
   * Decode a EME-PKCS1-v1_5 padded message
   * @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.2|RFC 4880 13.1.2}
   * @param {Uint8Array} encoded - Encoded message bytes
   * @param {Uint8Array} randomPayload - Data to return in case of decoding error (needed for constant-time processing)
   * @returns {Uint8Array} decoded data or `randomPayload` (on error, if given)
   * @throws {Error} on decoding failure, unless `randomPayload` is provided
   */
  function emeDecode(encoded, randomPayload) {
    // encoded format: 0x00 0x02 <PS> 0x00 <payload>
    let offset = 2;
    let separatorNotFound = 1;
    for (let j = offset; j < encoded.length; j++) {
      separatorNotFound &= encoded[j] !== 0;
      offset += separatorNotFound;
    }

    const psLen = offset - 2;
    const payload = encoded.subarray(offset + 1); // discard the 0x00 separator
    const isValidPadding = encoded[0] === 0 & encoded[1] === 2 & psLen >= 8 & !separatorNotFound;

    if (randomPayload) {
      return util.selectUint8Array(isValidPadding, payload, randomPayload);
    }

    if (isValidPadding) {
      return payload;
    }

    throw new Error('Decryption error');
  }

  /**
   * Create a EMSA-PKCS1-v1_5 padded message
   * @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.3|RFC 4880 13.1.3}
   * @param {Integer} algo - Hash algorithm type used
   * @param {Uint8Array} hashed - Message to be encoded
   * @param {Integer} emLen - Intended length in octets of the encoded message
   * @returns {Uint8Array} Encoded message.
   */
  async function emsaEncode(algo, hashed, emLen) {
    let i;
    if (hashed.length !== hash.getHashByteLength(algo)) {
      throw new Error('Invalid hash length');
    }
    // produce an ASN.1 DER value for the hash function used.
    // Let T be the full hash prefix
    const hashPrefix = new Uint8Array(hash_headers[algo].length);
    for (i = 0; i < hash_headers[algo].length; i++) {
      hashPrefix[i] = hash_headers[algo][i];
    }
    // and let tLen be the length in octets prefix and hashed data
    const tLen = hashPrefix.length + hashed.length;
    if (emLen < tLen + 11) {
      throw new Error('Intended encoded message length too short');
    }
    // an octet string PS consisting of emLen - tLen - 3 octets with hexadecimal value 0xFF
    // The length of PS will be at least 8 octets
    const PS = new Uint8Array(emLen - tLen - 3).fill(0xff);

    // Concatenate PS, the hash prefix, hashed data, and other padding to form the
    // encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || prefix || hashed
    const EM = new Uint8Array(emLen);
    EM[1] = 0x01;
    EM.set(PS, 2);
    EM.set(hashPrefix, emLen - tLen);
    EM.set(hashed, emLen - hashed.length);
    return EM;
  }

  var pkcs1 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    emeEncode: emeEncode,
    emeDecode: emeDecode,
    emsaEncode: emsaEncode
  });

  // GPG4Browsers - An OpenPGP implementation in javascript

  const webCrypto$5 = util.getWebCrypto();
  const nodeCrypto$6 = util.getNodeCrypto();
  const asn1 = nodeCrypto$6 ? void('asn1.js') : undefined;

  /* eslint-disable no-invalid-this */
  const RSAPrivateKey = nodeCrypto$6 ? asn1.define('RSAPrivateKey', function () {
    this.seq().obj( // used for native NodeJS crypto
      this.key('version').int(), // 0
      this.key('modulus').int(), // n
      this.key('publicExponent').int(), // e
      this.key('privateExponent').int(), // d
      this.key('prime1').int(), // p
      this.key('prime2').int(), // q
      this.key('exponent1').int(), // dp
      this.key('exponent2').int(), // dq
      this.key('coefficient').int() // u
    );
  }) : undefined;

  const RSAPublicKey = nodeCrypto$6 ? asn1.define('RSAPubliceKey', function () {
    this.seq().obj( // used for native NodeJS crypto
      this.key('modulus').int(), // n
      this.key('publicExponent').int() // e
    );
  }) : undefined;
  /* eslint-enable no-invalid-this */

  /** Create signature
   * @param {module:enums.hash} hashAlgo - Hash algorithm
   * @param {Uint8Array} data - Message
   * @param {Uint8Array} n - RSA public modulus
   * @param {Uint8Array} e - RSA public exponent
   * @param {Uint8Array} d - RSA private exponent
   * @param {Uint8Array} p - RSA private prime p
   * @param {Uint8Array} q - RSA private prime q
   * @param {Uint8Array} u - RSA private coefficient
   * @param {Uint8Array} hashed - Hashed message
   * @returns {Promise<Uint8Array>} RSA Signature.
   * @async
   */
  async function sign(hashAlgo, data, n, e, d, p, q, u, hashed) {
    if (data && !util.isStream(data)) {
      if (util.getWebCrypto()) {
        try {
          return await webSign(enums.read(enums.webHash, hashAlgo), data, n, e, d, p, q, u);
        } catch (err) {
          util.printDebugError(err);
        }
      } else if (util.getNodeCrypto()) {
        return nodeSign(hashAlgo, data, n, e, d, p, q, u);
      }
    }
    return bnSign(hashAlgo, n, d, hashed);
  }

  /**
   * Verify signature
   * @param {module:enums.hash} hashAlgo - Hash algorithm
   * @param {Uint8Array} data - Message
   * @param {Uint8Array} s - Signature
   * @param {Uint8Array} n - RSA public modulus
   * @param {Uint8Array} e - RSA public exponent
   * @param {Uint8Array} hashed - Hashed message
   * @returns {Boolean}
   * @async
   */
  async function verify(hashAlgo, data, s, n, e, hashed) {
    if (data && !util.isStream(data)) {
      if (util.getWebCrypto()) {
        try {
          return await webVerify(enums.read(enums.webHash, hashAlgo), data, s, n, e);
        } catch (err) {
          util.printDebugError(err);
        }
      } else if (util.getNodeCrypto()) {
        return nodeVerify(hashAlgo, data, s, n, e);
      }
    }
    return bnVerify(hashAlgo, s, n, e, hashed);
  }

  /**
   * Encrypt message
   * @param {Uint8Array} data - Message
   * @param {Uint8Array} n - RSA public modulus
   * @param {Uint8Array} e - RSA public exponent
   * @returns {Promise<Uint8Array>} RSA Ciphertext.
   * @async
   */
  async function encrypt$1(data, n, e) {
    if (util.getNodeCrypto()) {
      return nodeEncrypt$1(data, n, e);
    }
    return bnEncrypt(data, n, e);
  }

  /**
   * Decrypt RSA message
   * @param {Uint8Array} m - Message
   * @param {Uint8Array} n - RSA public modulus
   * @param {Uint8Array} e - RSA public exponent
   * @param {Uint8Array} d - RSA private exponent
   * @param {Uint8Array} p - RSA private prime p
   * @param {Uint8Array} q - RSA private prime q
   * @param {Uint8Array} u - RSA private coefficient
   * @param {Uint8Array} randomPayload - Data to return on decryption error, instead of throwing
   *                                     (needed for constant-time processing)
   * @returns {Promise<String>} RSA Plaintext.
   * @throws {Error} on decryption error, unless `randomPayload` is given
   * @async
   */
  async function decrypt$1(data, n, e, d, p, q, u, randomPayload) {
    // Node v18.19.1, 20.11.1 and 21.6.2 have disabled support for PKCS#1 decryption,
    // and we want to avoid checking the error type to decide if the random payload
    // should indeed be returned.
    if (util.getNodeCrypto() && !randomPayload) {
      try {
        return await nodeDecrypt$1(data, n, e, d, p, q, u);
      } catch (err) {
        util.printDebugError(err);
      }
    }
    return bnDecrypt(data, n, e, d, p, q, u, randomPayload);
  }

  /**
   * Generate a new random private key B bits long with public exponent E.
   *
   * When possible, webCrypto or nodeCrypto is used. Otherwise, primes are generated using
   * 40 rounds of the Miller-Rabin probabilistic random prime generation algorithm.
   * @see module:crypto/public_key/prime
   * @param {Integer} bits - RSA bit length
   * @param {Integer} e - RSA public exponent
   * @returns {{n, e, d,
   *            p, q ,u: Uint8Array}} RSA public modulus, RSA public exponent, RSA private exponent,
   *                                  RSA private prime p, RSA private prime q, u = p ** -1 mod q
   * @async
   */
  async function generate(bits, e) {
    const BigInteger = await util.getBigInteger();

    e = new BigInteger(e);

    // Native RSA keygen using Web Crypto
    if (util.getWebCrypto()) {
      const keyGenOpt = {
        name: 'RSASSA-PKCS1-v1_5',
        modulusLength: bits, // the specified keysize in bits
        publicExponent: e.toUint8Array(), // take three bytes (max 65537) for exponent
        hash: {
          name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
        }
      };
      const keyPair = await webCrypto$5.generateKey(keyGenOpt, true, ['sign', 'verify']);

      // export the generated keys as JsonWebKey (JWK)
      // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33
      const jwk = await webCrypto$5.exportKey('jwk', keyPair.privateKey);
      // map JWK parameters to corresponding OpenPGP names
      return {
        n: b64ToUint8Array(jwk.n),
        e: e.toUint8Array(),
        d: b64ToUint8Array(jwk.d),
        // switch p and q
        p: b64ToUint8Array(jwk.q),
        q: b64ToUint8Array(jwk.p),
        // Since p and q are switched in places, u is the inverse of jwk.q
        u: b64ToUint8Array(jwk.qi)
      };
    } else if (util.getNodeCrypto() && nodeCrypto$6.generateKeyPair && RSAPrivateKey) {
      const opts = {
        modulusLength: bits,
        publicExponent: e.toNumber(),
        publicKeyEncoding: { type: 'pkcs1', format: 'der' },
        privateKeyEncoding: { type: 'pkcs1', format: 'der' }
      };
      const prv = await new Promise((resolve, reject) => {
        nodeCrypto$6.generateKeyPair('rsa', opts, (err, _, der) => {
          if (err) {
            reject(err);
          } else {
            resolve(RSAPrivateKey.decode(der, 'der'));
          }
        });
      });
      /**
       * OpenPGP spec differs from DER spec, DER: `u = (inverse of q) mod p`, OpenPGP: `u = (inverse of p) mod q`.
       * @link https://tools.ietf.org/html/rfc3447#section-3.2
       * @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1
       */
      return {
        n: prv.modulus.toArrayLike(Uint8Array),
        e: prv.publicExponent.toArrayLike(Uint8Array),
        d: prv.privateExponent.toArrayLike(Uint8Array),
        // switch p and q
        p: prv.prime2.toArrayLike(Uint8Array),
        q: prv.prime1.toArrayLike(Uint8Array),
        // Since p and q are switched in places, we can keep u as defined by DER
        u: prv.coefficient.toArrayLike(Uint8Array)
      };
    }

    // RSA keygen fallback using 40 iterations of the Miller-Rabin test
    // See https://stackoverflow.com/a/6330138 for justification
    // Also see section C.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST
    let p;
    let q;
    let n;
    do {
      q = await randomProbablePrime(bits - (bits >> 1), e, 40);
      p = await randomProbablePrime(bits >> 1, e, 40);
      n = p.mul(q);
    } while (n.bitLength() !== bits);

    const phi = p.dec().imul(q.dec());

    if (q.lt(p)) {
      [p, q] = [q, p];
    }

    return {
      n: n.toUint8Array(),
      e: e.toUint8Array(),
      d: e.modInv(phi).toUint8Array(),
      p: p.toUint8Array(),
      q: q.toUint8Array(),
      // dp: d.mod(p.subn(1)),
      // dq: d.mod(q.subn(1)),
      u: p.modInv(q).toUint8Array()
    };
  }

  /**
   * Validate RSA parameters
   * @param {Uint8Array} n - RSA public modulus
   * @param {Uint8Array} e - RSA public exponent
   * @param {Uint8Array} d - RSA private exponent
   * @param {Uint8Array} p - RSA private prime p
   * @param {Uint8Array} q - RSA private prime q
   * @param {Uint8Array} u - RSA inverse of p w.r.t. q
   * @returns {Promise<Boolean>} Whether params are valid.
   * @async
   */
  async function validateParams(n, e, d, p, q, u) {
    const BigInteger = await util.getBigInteger();
    n = new BigInteger(n);
    p = new BigInteger(p);
    q = new BigInteger(q);

    // expect pq = n
    if (!p.mul(q).equal(n)) {
      return false;
    }

    const two = new BigInteger(2);
    // expect p*u = 1 mod q
    u = new BigInteger(u);
    if (!p.mul(u).mod(q).isOne()) {
      return false;
    }

    e = new BigInteger(e);
    d = new BigInteger(d);
    /**
     * In RSA pkcs#1 the exponents (d, e) are inverses modulo lcm(p-1, q-1)
     * We check that [de = 1 mod (p-1)] and [de = 1 mod (q-1)]
     * By CRT on coprime factors of (p-1, q-1) it follows that [de = 1 mod lcm(p-1, q-1)]
     *
     * We blind the multiplication with r, and check that rde = r mod lcm(p-1, q-1)
     */
    const nSizeOver3 = new BigInteger(Math.floor(n.bitLength() / 3));
    const r = await getRandomBigInteger(two, two.leftShift(nSizeOver3)); // r in [ 2, 2^{|n|/3} ) < p and q
    const rde = r.mul(d).mul(e);

    const areInverses = rde.mod(p.dec()).equal(r) && rde.mod(q.dec()).equal(r);
    if (!areInverses) {
      return false;
    }

    return true;
  }

  async function bnSign(hashAlgo, n, d, hashed) {
    const BigInteger = await util.getBigInteger();
    n = new BigInteger(n);
    const m = new BigInteger(await emsaEncode(hashAlgo, hashed, n.byteLength()));
    d = new BigInteger(d);
    if (m.gte(n)) {
      throw new Error('Message size cannot exceed modulus size');
    }
    return m.modExp(d, n).toUint8Array('be', n.byteLength());
  }

  async function webSign(hashName, data, n, e, d, p, q, u) {
    /** OpenPGP keys require that p < q, and Safari Web Crypto requires that p > q.
     * We swap them in privateToJWK, so it usually works out, but nevertheless,
     * not all OpenPGP keys are compatible with this requirement.
     * OpenPGP.js used to generate RSA keys the wrong way around (p > q), and still
     * does if the underlying Web Crypto does so (though the tested implementations
     * don't do so).
     */
    const jwk = await privateToJWK(n, e, d, p, q, u);
    const algo = {
      name: 'RSASSA-PKCS1-v1_5',
      hash: { name: hashName }
    };
    const key = await webCrypto$5.importKey('jwk', jwk, algo, false, ['sign']);
    return new Uint8Array(await webCrypto$5.sign('RSASSA-PKCS1-v1_5', key, data));
  }

  async function nodeSign(hashAlgo, data, n, e, d, p, q, u) {
    const { default: BN } = await Promise.resolve().then(function () { return bn$1; });
    const pBNum = new BN(p);
    const qBNum = new BN(q);
    const dBNum = new BN(d);
    const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1)
    const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1)
    const sign = nodeCrypto$6.createSign(enums.read(enums.hash, hashAlgo));
    sign.write(data);
    sign.end();
    const keyObject = {
      version: 0,
      modulus: new BN(n),
      publicExponent: new BN(e),
      privateExponent: new BN(d),
      // switch p and q
      prime1: new BN(q),
      prime2: new BN(p),
      // switch dp and dq
      exponent1: dq,
      exponent2: dp,
      coefficient: new BN(u)
    };
    if (typeof nodeCrypto$6.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
      const der = RSAPrivateKey.encode(keyObject, 'der');
      return new Uint8Array(sign.sign({ key: der, format: 'der', type: 'pkcs1' }));
    }
    const pem = RSAPrivateKey.encode(keyObject, 'pem', {
      label: 'RSA PRIVATE KEY'
    });
    return new Uint8Array(sign.sign(pem));
  }

  async function bnVerify(hashAlgo, s, n, e, hashed) {
    const BigInteger = await util.getBigInteger();
    n = new BigInteger(n);
    s = new BigInteger(s);
    e = new BigInteger(e);
    if (s.gte(n)) {
      throw new Error('Signature size cannot exceed modulus size');
    }
    const EM1 = s.modExp(e, n).toUint8Array('be', n.byteLength());
    const EM2 = await emsaEncode(hashAlgo, hashed, n.byteLength());
    return util.equalsUint8Array(EM1, EM2);
  }

  async function webVerify(hashName, data, s, n, e) {
    const jwk = publicToJWK(n, e);
    const key = await webCrypto$5.importKey('jwk', jwk, {
      name: 'RSASSA-PKCS1-v1_5',
      hash: { name:  hashName }
    }, false, ['verify']);
    return webCrypto$5.verify('RSASSA-PKCS1-v1_5', key, s, data);
  }

  async function nodeVerify(hashAlgo, data, s, n, e) {
    const { default: BN } = await Promise.resolve().then(function () { return bn$1; });

    const verify = nodeCrypto$6.createVerify(enums.read(enums.hash, hashAlgo));
    verify.write(data);
    verify.end();
    const keyObject = {
      modulus: new BN(n),
      publicExponent: new BN(e)
    };
    let key;
    if (typeof nodeCrypto$6.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
      const der = RSAPublicKey.encode(keyObject, 'der');
      key = { key: der, format: 'der', type: 'pkcs1' };
    } else {
      key = RSAPublicKey.encode(keyObject, 'pem', {
        label: 'RSA PUBLIC KEY'
      });
    }
    try {
      return await verify.verify(key, s);
    } catch (err) {
      return false;
    }
  }

  async function nodeEncrypt$1(data, n, e) {
    const { default: BN } = await Promise.resolve().then(function () { return bn$1; });

    const keyObject = {
      modulus: new BN(n),
      publicExponent: new BN(e)
    };
    let key;
    if (typeof nodeCrypto$6.createPrivateKey !== 'undefined') {
      const der = RSAPublicKey.encode(keyObject, 'der');
      key = { key: der, format: 'der', type: 'pkcs1', padding: nodeCrypto$6.constants.RSA_PKCS1_PADDING };
    } else {
      const pem = RSAPublicKey.encode(keyObject, 'pem', {
        label: 'RSA PUBLIC KEY'
      });
      key = { key: pem, padding: nodeCrypto$6.constants.RSA_PKCS1_PADDING };
    }
    return new Uint8Array(nodeCrypto$6.publicEncrypt(key, data));
  }

  async function bnEncrypt(data, n, e) {
    const BigInteger = await util.getBigInteger();
    n = new BigInteger(n);
    data = new BigInteger(emeEncode(data, n.byteLength()));
    e = new BigInteger(e);
    if (data.gte(n)) {
      throw new Error('Message size cannot exceed modulus size');
    }
    return data.modExp(e, n).toUint8Array('be', n.byteLength());
  }

  async function nodeDecrypt$1(data, n, e, d, p, q, u) {
    const { default: BN } = await Promise.resolve().then(function () { return bn$1; });

    const pBNum = new BN(p);
    const qBNum = new BN(q);
    const dBNum = new BN(d);
    const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1)
    const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1)
    const keyObject = {
      version: 0,
      modulus: new BN(n),
      publicExponent: new BN(e),
      privateExponent: new BN(d),
      // switch p and q
      prime1: new BN(q),
      prime2: new BN(p),
      // switch dp and dq
      exponent1: dq,
      exponent2: dp,
      coefficient: new BN(u)
    };
    let key;
    if (typeof nodeCrypto$6.createPrivateKey !== 'undefined') {
      const der = RSAPrivateKey.encode(keyObject, 'der');
      key = { key: der, format: 'der' , type: 'pkcs1', padding: nodeCrypto$6.constants.RSA_PKCS1_PADDING };
    } else {
      const pem = RSAPrivateKey.encode(keyObject, 'pem', {
        label: 'RSA PRIVATE KEY'
      });
      key = { key: pem, padding: nodeCrypto$6.constants.RSA_PKCS1_PADDING };
    }
    try {
      return new Uint8Array(nodeCrypto$6.privateDecrypt(key, data));
    } catch (err) {
      throw new Error('Decryption error');
    }
  }

  async function bnDecrypt(data, n, e, d, p, q, u, randomPayload) {
    const BigInteger = await util.getBigInteger();
    data = new BigInteger(data);
    n = new BigInteger(n);
    e = new BigInteger(e);
    d = new BigInteger(d);
    p = new BigInteger(p);
    q = new BigInteger(q);
    u = new BigInteger(u);
    if (data.gte(n)) {
      throw new Error('Data too large.');
    }
    const dq = d.mod(q.dec()); // d mod (q-1)
    const dp = d.mod(p.dec()); // d mod (p-1)

    const unblinder = (await getRandomBigInteger(new BigInteger(2), n)).mod(n);
    const blinder = unblinder.modInv(n).modExp(e, n);
    data = data.mul(blinder).mod(n);


    const mp = data.modExp(dp, p); // data**{d mod (q-1)} mod p
    const mq = data.modExp(dq, q); // data**{d mod (p-1)} mod q
    const h = u.mul(mq.sub(mp)).mod(q); // u * (mq-mp) mod q (operands already < q)

    let result = h.mul(p).add(mp); // result < n due to relations above

    result = result.mul(unblinder).mod(n);


    return emeDecode(result.toUint8Array('be', n.byteLength()), randomPayload);
  }

  /** Convert Openpgp private key params to jwk key according to
   * @link https://tools.ietf.org/html/rfc7517
   * @param {String} hashAlgo
   * @param {Uint8Array} n
   * @param {Uint8Array} e
   * @param {Uint8Array} d
   * @param {Uint8Array} p
   * @param {Uint8Array} q
   * @param {Uint8Array} u
   */
  async function privateToJWK(n, e, d, p, q, u) {
    const BigInteger = await util.getBigInteger();
    const pNum = new BigInteger(p);
    const qNum = new BigInteger(q);
    const dNum = new BigInteger(d);

    let dq = dNum.mod(qNum.dec()); // d mod (q-1)
    let dp = dNum.mod(pNum.dec()); // d mod (p-1)
    dp = dp.toUint8Array();
    dq = dq.toUint8Array();
    return {
      kty: 'RSA',
      n: uint8ArrayToB64(n, true),
      e: uint8ArrayToB64(e, true),
      d: uint8ArrayToB64(d, true),
      // switch p and q
      p: uint8ArrayToB64(q, true),
      q: uint8ArrayToB64(p, true),
      // switch dp and dq
      dp: uint8ArrayToB64(dq, true),
      dq: uint8ArrayToB64(dp, true),
      qi: uint8ArrayToB64(u, true),
      ext: true
    };
  }

  /** Convert Openpgp key public params to jwk key according to
   * @link https://tools.ietf.org/html/rfc7517
   * @param {String} hashAlgo
   * @param {Uint8Array} n
   * @param {Uint8Array} e
   */
  function publicToJWK(n, e) {
    return {
      kty: 'RSA',
      n: uint8ArrayToB64(n, true),
      e: uint8ArrayToB64(e, true),
      ext: true
    };
  }

  var rsa = /*#__PURE__*/Object.freeze({
    __proto__: null,
    sign: sign,
    verify: verify,
    encrypt: encrypt$1,
    decrypt: decrypt$1,
    generate: generate,
    validateParams: validateParams
  });

  // GPG4Browsers - An OpenPGP implementation in javascript

  /**
   * ElGamal Encryption function
   * Note that in OpenPGP, the message needs to be padded with PKCS#1 (same as RSA)
   * @param {Uint8Array} data - To be padded and encrypted
   * @param {Uint8Array} p
   * @param {Uint8Array} g
   * @param {Uint8Array} y
   * @returns {Promise<{ c1: Uint8Array, c2: Uint8Array }>}
   * @async
   */
  async function encrypt$2(data, p, g, y) {
    const BigInteger = await util.getBigInteger();
    p = new BigInteger(p);
    g = new BigInteger(g);
    y = new BigInteger(y);

    const padded = emeEncode(data, p.byteLength());
    const m = new BigInteger(padded);

    // OpenPGP uses a "special" version of ElGamal where g is generator of the full group Z/pZ*
    // hence g has order p-1, and to avoid that k = 0 mod p-1, we need to pick k in [1, p-2]
    const k = await getRandomBigInteger(new BigInteger(1), p.dec());
    return {
      c1: g.modExp(k, p).toUint8Array(),
      c2: y.modExp(k, p).imul(m).imod(p).toUint8Array()
    };
  }

  /**
   * ElGamal Encryption function
   * @param {Uint8Array} c1
   * @param {Uint8Array} c2
   * @param {Uint8Array} p
   * @param {Uint8Array} x
   * @param {Uint8Array} randomPayload - Data to return on unpadding error, instead of throwing
   *                                     (needed for constant-time processing)
   * @returns {Promise<Uint8Array>} Unpadded message.
   * @throws {Error} on decryption error, unless `randomPayload` is given
   * @async
   */
  async function decrypt$2(c1, c2, p, x, randomPayload) {
    const BigInteger = await util.getBigInteger();
    c1 = new BigInteger(c1);
    c2 = new BigInteger(c2);
    p = new BigInteger(p);
    x = new BigInteger(x);

    const padded = c1.modExp(x, p).modInv(p).imul(c2).imod(p);
    return emeDecode(padded.toUint8Array('be', p.byteLength()), randomPayload);
  }

  /**
   * Validate ElGamal parameters
   * @param {Uint8Array} p - ElGamal prime
   * @param {Uint8Array} g - ElGamal group generator
   * @param {Uint8Array} y - ElGamal public key
   * @param {Uint8Array} x - ElGamal private exponent
   * @returns {Promise<Boolean>} Whether params are valid.
   * @async
   */
  async function validateParams$1(p, g, y, x) {
    const BigInteger = await util.getBigInteger();
    p = new BigInteger(p);
    g = new BigInteger(g);
    y = new BigInteger(y);

    const one = new BigInteger(1);
    // Check that 1 < g < p
    if (g.lte(one) || g.gte(p)) {
      return false;
    }

    // Expect p-1 to be large
    const pSize = new BigInteger(p.bitLength());
    const n1023 = new BigInteger(1023);
    if (pSize.lt(n1023)) {
      return false;
    }

    /**
     * g should have order p-1
     * Check that g ** (p-1) = 1 mod p
     */
    if (!g.modExp(p.dec(), p).isOne()) {
      return false;
    }

    /**
     * Since p-1 is not prime, g might have a smaller order that divides p-1
     * We want to make sure that the order is large enough to hinder a small subgroup attack
     *
     * We just check g**i != 1 for all i up to a threshold
     */
    let res = g;
    const i = new BigInteger(1);
    const threshold = new BigInteger(2).leftShift(new BigInteger(17)); // we want order > threshold
    while (i.lt(threshold)) {
      res = res.mul(g).imod(p);
      if (res.isOne()) {
        return false;
      }
      i.iinc();
    }

    /**
     * Re-derive public key y' = g ** x mod p
     * Expect y == y'
     *
     * Blinded exponentiation computes g**{r(p-1) + x} to compare to y
     */
    x = new BigInteger(x);
    const two = new BigInteger(2);
    const r = await getRandomBigInteger(two.leftShift(pSize.dec()), two.leftShift(pSize)); // draw r of same size as p-1
    const rqx = p.dec().imul(r).iadd(x);
    if (!y.equal(g.modExp(rqx, p))) {
      return false;
    }

    return true;
  }

  var elgamal = /*#__PURE__*/Object.freeze({
    __proto__: null,
    encrypt: encrypt$2,
    decrypt: decrypt$2,
    validateParams: validateParams$1
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  class OID {
    constructor(oid) {
      if (oid instanceof OID) {
        this.oid = oid.oid;
      } else if (util.isArray(oid) ||
                 util.isUint8Array(oid)) {
        oid = new Uint8Array(oid);
        if (oid[0] === 0x06) { // DER encoded oid byte array
          if (oid[1] !== oid.length - 2) {
            throw new Error('Length mismatch in DER encoded oid');
          }
          oid = oid.subarray(2);
        }
        this.oid = oid;
      } else {
        this.oid = '';
      }
    }

    /**
     * Method to read an OID object
     * @param {Uint8Array} input - Where to read the OID from
     * @returns {Number} Number of read bytes.
     */
    read(input) {
      if (input.length >= 1) {
        const length = input[0];
        if (input.length >= 1 + length) {
          this.oid = input.subarray(1, 1 + length);
          return 1 + this.oid.length;
        }
      }
      throw new Error('Invalid oid');
    }

    /**
     * Serialize an OID object
     * @returns {Uint8Array} Array with the serialized value the OID.
     */
    write() {
      return util.concatUint8Array([new Uint8Array([this.oid.length]), this.oid]);
    }

    /**
     * Serialize an OID object as a hex string
     * @returns {string} String with the hex value of the OID.
     */
    toHex() {
      return util.uint8ArrayToHex(this.oid);
    }

    /**
     * If a known curve object identifier, return the canonical name of the curve
     * @returns {string} String with the canonical name of the curve.
     */
    getName() {
      const hex = this.toHex();
      if (enums.curve[hex]) {
        return enums.write(enums.curve, hex);
      } else {
        throw new Error('Unknown curve object identifier.');
      }
    }
  }

  // OpenPGP.js - An OpenPGP implementation in javascript

  function keyFromPrivate(indutnyCurve, priv) {
    const keyPair = indutnyCurve.keyPair({ priv: priv });
    return keyPair;
  }

  function keyFromPublic(indutnyCurve, pub) {
    const keyPair = indutnyCurve.keyPair({ pub: pub });
    if (keyPair.validate().result !== true) {
      throw new Error('Invalid elliptic public key');
    }
    return keyPair;
  }

  async function getIndutnyCurve(name) {
    if (!config.useIndutnyElliptic) {
      throw new Error('This curve is only supported in the full build of OpenPGP.js');
    }
    const { default: elliptic } = await Promise.resolve().then(function () { return elliptic$1; });
    return new elliptic.ec(name);
  }

  // GPG4Browsers - An OpenPGP implementation in javascript

  function readSimpleLength(bytes) {
    let len = 0;
    let offset;
    const type = bytes[0];


    if (type < 192) {
      [len] = bytes;
      offset = 1;
    } else if (type < 255) {
      len = ((bytes[0] - 192) << 8) + (bytes[1]) + 192;
      offset = 2;
    } else if (type === 255) {
      len = util.readNumber(bytes.subarray(1, 1 + 4));
      offset = 5;
    }

    return {
      len: len,
      offset: offset
    };
  }

  /**
   * Encodes a given integer of length to the openpgp length specifier to a
   * string
   *
   * @param {Integer} length - The length to encode
   * @returns {Uint8Array} String with openpgp length representation.
   */
  function writeSimpleLength(length) {
    if (length < 192) {
      return new Uint8Array([length]);
    } else if (length > 191 && length < 8384) {
      /*
        * let a = (total data packet length) - 192 let bc = two octet
        * representation of a let d = b + 192
        */
      return new Uint8Array([((length - 192) >> 8) + 192, (length - 192) & 0xFF]);
    }
    return util.concatUint8Array([new Uint8Array([255]), util.writeNumber(length, 4)]);
  }

  function writePartialLength(power) {
    if (power < 0 || power > 30) {
      throw new Error('Partial Length power must be between 1 and 30');
    }
    return new Uint8Array([224 + power]);
  }

  function writeTag(tag_type) {
    /* we're only generating v4 packet headers here */
    return new Uint8Array([0xC0 | tag_type]);
  }

  /**
   * Writes a packet header version 4 with the given tag_type and length to a
   * string
   *
   * @param {Integer} tag_type - Tag type
   * @param {Integer} length - Length of the payload
   * @returns {String} String of the header.
   */
  function writeHeader(tag_type, length) {
    /* we're only generating v4 packet headers here */
    return util.concatUint8Array([writeTag(tag_type), writeSimpleLength(length)]);
  }

  /**
   * Whether the packet type supports partial lengths per RFC4880
   * @param {Integer} tag - Tag type
   * @returns {Boolean} String of the header.
   */
  function supportsStreaming(tag) {
    return [
      enums.packet.literalData,
      enums.packet.compressedData,
      enums.packet.symmetricallyEncryptedData,
      enums.packet.symEncryptedIntegrityProtectedData,
      enums.packet.aeadEncryptedData
    ].includes(tag);
  }

  /**
   * Generic static Packet Parser function
   *
   * @param {Uint8Array | ReadableStream<Uint8Array>} input - Input stream as string
   * @param {Function} callback - Function to call with the parsed packet
   * @returns {Boolean} Returns false if the stream was empty and parsing is done, and true otherwise.
   */
  async function readPackets(input, callback) {
    const reader = getReader(input);
    let writer;
    let callbackReturned;
    try {
      const peekedBytes = await reader.peekBytes(2);
      // some sanity checks
      if (!peekedBytes || peekedBytes.length < 2 || (peekedBytes[0] & 0x80) === 0) {
        throw new Error('Error during parsing. This message / key probably does not conform to a valid OpenPGP format.');
      }
      const headerByte = await reader.readByte();
      let tag = -1;
      let format = -1;
      let packetLength;

      format = 0; // 0 = old format; 1 = new format
      if ((headerByte & 0x40) !== 0) {
        format = 1;
      }

      let packetLengthType;
      if (format) {
        // new format header
        tag = headerByte & 0x3F; // bit 5-0
      } else {
        // old format header
        tag = (headerByte & 0x3F) >> 2; // bit 5-2
        packetLengthType = headerByte & 0x03; // bit 1-0
      }

      const packetSupportsStreaming = supportsStreaming(tag);
      let packet = null;
      if (packetSupportsStreaming) {
        if (util.isStream(input) === 'array') {
          const arrayStream = new ArrayStream();
          writer = getWriter(arrayStream);
          packet = arrayStream;
        } else {
          const transform = new TransformStream();
          writer = getWriter(transform.writable);
          packet = transform.readable;
        }
        // eslint-disable-next-line callback-return
        callbackReturned = callback({ tag, packet });
      } else {
        packet = [];
      }

      let wasPartialLength;
      do {
        if (!format) {
          // 4.2.1. Old Format Packet Lengths
          switch (packetLengthType) {
            case 0:
              // The packet has a one-octet length. The header is 2 octets
              // long.
              packetLength = await reader.readByte();
              break;
            case 1:
              // The packet has a two-octet length. The header is 3 octets
              // long.
              packetLength = (await reader.readByte() << 8) | await reader.readByte();
              break;
            case 2:
              // The packet has a four-octet length. The header is 5
              // octets long.
              packetLength = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
                8) | await reader.readByte();
              break;
            default:
              // 3 - The packet is of indeterminate length. The header is 1
              // octet long, and the implementation must determine how long
              // the packet is. If the packet is in a file, this means that
              // the packet extends until the end of the file. In general,
              // an implementation SHOULD NOT use indeterminate-length
              // packets except where the end of the data will be clear
              // from the context, and even then it is better to use a
              // definite length, or a new format header. The new format
              // headers described below have a mechanism for precisely
              // encoding data of indeterminate length.
              packetLength = Infinity;
              break;
          }
        } else { // 4.2.2. New Format Packet Lengths
          // 4.2.2.1. One-Octet Lengths
          const lengthByte = await reader.readByte();
          wasPartialLength = false;
          if (lengthByte < 192) {
            packetLength = lengthByte;
            // 4.2.2.2. Two-Octet Lengths
          } else if (lengthByte >= 192 && lengthByte < 224) {
            packetLength = ((lengthByte - 192) << 8) + (await reader.readByte()) + 192;
            // 4.2.2.4. Partial Body Lengths
          } else if (lengthByte > 223 && lengthByte < 255) {
            packetLength = 1 << (lengthByte & 0x1F);
            wasPartialLength = true;
            if (!packetSupportsStreaming) {
              throw new TypeError('This packet type does not support partial lengths.');
            }
            // 4.2.2.3. Five-Octet Lengths
          } else {
            packetLength = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
              8) | await reader.readByte();
          }
        }
        if (packetLength > 0) {
          let bytesRead = 0;
          while (true) {
            if (writer) await writer.ready;
            const { done, value } = await reader.read();
            if (done) {
              if (packetLength === Infinity) break;
              throw new Error('Unexpected end of packet');
            }
            const chunk = packetLength === Infinity ? value : value.subarray(0, packetLength - bytesRead);
            if (writer) await writer.write(chunk);
            else packet.push(chunk);
            bytesRead += value.length;
            if (bytesRead >= packetLength) {
              reader.unshift(value.subarray(packetLength - bytesRead + value.length));
              break;
            }
          }
        }
      } while (wasPartialLength);

      // If this was not a packet that "supports streaming", we peek to check
      // whether it is the last packet in the message. We peek 2 bytes instead
      // of 1 because the beginning of this function also peeks 2 bytes, and we
      // want to cut a `subarray` of the correct length into `web-stream-tools`'
      // `externalBuffer` as a tiny optimization here.
      //
      // If it *was* a streaming packet (i.e. the data packets), we peek at the
      // entire remainder of the stream, in order to forward errors in the
      // remainder of the stream to the packet data. (Note that this means we
      // read/peek at all signature packets before closing the literal data
      // packet, for example.) This forwards MDC errors to the literal data
      // stream, for example, so that they don't get lost / forgotten on
      // decryptedMessage.packets.stream, which we never look at.
      //
      // An example of what we do when stream-parsing a message containing
      // [ one-pass signature packet, literal data packet, signature packet ]:
      // 1. Read the one-pass signature packet
      // 2. Peek 2 bytes of the literal data packet
      // 3. Parse the one-pass signature packet
      //
      // 4. Read the literal data packet, simultaneously stream-parsing it
      // 5. Peek until the end of the message
      // 6. Finish parsing the literal data packet
      //
      // 7. Read the signature packet again (we already peeked at it in step 5)
      // 8. Peek at the end of the stream again (`peekBytes` returns undefined)
      // 9. Parse the signature packet
      //
      // Note that this means that if there's an error in the very end of the
      // stream, such as an MDC error, we throw in step 5 instead of in step 8
      // (or never), which is the point of this exercise.
      const nextPacket = await reader.peekBytes(packetSupportsStreaming ? Infinity : 2);
      if (writer) {
        await writer.ready;
        await writer.close();
      } else {
        packet = util.concatUint8Array(packet);
        // eslint-disable-next-line callback-return
        await callback({ tag, packet });
      }
      return !nextPacket || !nextPacket.length;
    } catch (e) {
      if (writer) {
        await writer.abort(e);
        return true;
      } else {
        throw e;
      }
    } finally {
      if (writer) {
        await callbackReturned;
      }
      reader.releaseLock();
    }
  }

  class UnsupportedError extends Error {
    constructor(...params) {
      super(...params);

      if (Error.captureStackTrace) {
        Error.captureStackTrace(this, UnsupportedError);
      }

      this.name = 'UnsupportedError';
    }
  }

  class UnparseablePacket {
    constructor(tag, rawContent) {
      this.tag = tag;
      this.rawContent = rawContent;
    }

    write() {
      return this.rawContent;
    }
  }

  // OpenPGP.js - An OpenPGP implementation in javascript

  const webCrypto$6 = util.getWebCrypto();
  const nodeCrypto$7 = util.getNodeCrypto();

  const webCurves = {
    'p256': 'P-256',
    'p384': 'P-384',
    'p521': 'P-521'
  };
  const knownCurves = nodeCrypto$7 ? nodeCrypto$7.getCurves() : [];
  const nodeCurves = nodeCrypto$7 ? {
    secp256k1: knownCurves.includes('secp256k1') ? 'secp256k1' : undefined,
    p256: knownCurves.includes('prime256v1') ? 'prime256v1' : undefined,
    p384: knownCurves.includes('secp384r1') ? 'secp384r1' : undefined,
    p521: knownCurves.includes('secp521r1') ? 'secp521r1' : undefined,
    ed25519: knownCurves.includes('ED25519') ? 'ED25519' : undefined,
    curve25519: knownCurves.includes('X25519') ? 'X25519' : undefined,
    brainpoolP256r1: knownCurves.includes('brainpoolP256r1') ? 'brainpoolP256r1' : undefined,
    brainpoolP384r1: knownCurves.includes('brainpoolP384r1') ? 'brainpoolP384r1' : undefined,
    brainpoolP512r1: knownCurves.includes('brainpoolP512r1') ? 'brainpoolP512r1' : undefined
  } : {};

  const curves = {
    p256: {
      oid: [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07],
      keyType: enums.publicKey.ecdsa,
      hash: enums.hash.sha256,
      cipher: enums.symmetric.aes128,
      node: nodeCurves.p256,
      web: webCurves.p256,
      payloadSize: 32,
      sharedSize: 256
    },
    p384: {
      oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22],
      keyType: enums.publicKey.ecdsa,
      hash: enums.hash.sha384,
      cipher: enums.symmetric.aes192,
      node: nodeCurves.p384,
      web: webCurves.p384,
      payloadSize: 48,
      sharedSize: 384
    },
    p521: {
      oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23],
      keyType: enums.publicKey.ecdsa,
      hash: enums.hash.sha512,
      cipher: enums.symmetric.aes256,
      node: nodeCurves.p521,
      web: webCurves.p521,
      payloadSize: 66,
      sharedSize: 528
    },
    secp256k1: {
      oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x0A],
      keyType: enums.publicKey.ecdsa,
      hash: enums.hash.sha256,
      cipher: enums.symmetric.aes128,
      node: nodeCurves.secp256k1,
      payloadSize: 32
    },
    ed25519: {
      oid: [0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01],
      keyType: enums.publicKey.eddsaLegacy,
      hash: enums.hash.sha512,
      node: false, // nodeCurves.ed25519 TODO
      payloadSize: 32
    },
    curve25519: {
      oid: [0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01],
      keyType: enums.publicKey.ecdh,
      hash: enums.hash.sha256,
      cipher: enums.symmetric.aes128,
      node: false, // nodeCurves.curve25519 TODO
      payloadSize: 32
    },
    brainpoolP256r1: {
      oid: [0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07],
      keyType: enums.publicKey.ecdsa,
      hash: enums.hash.sha256,
      cipher: enums.symmetric.aes128,
      node: nodeCurves.brainpoolP256r1,
      payloadSize: 32
    },
    brainpoolP384r1: {
      oid: [0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B],
      keyType: enums.publicKey.ecdsa,
      hash: enums.hash.sha384,
      cipher: enums.symmetric.aes192,
      node: nodeCurves.brainpoolP384r1,
      payloadSize: 48
    },
    brainpoolP512r1: {
      oid: [0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D],
      keyType: enums.publicKey.ecdsa,
      hash: enums.hash.sha512,
      cipher: enums.symmetric.aes256,
      node: nodeCurves.brainpoolP512r1,
      payloadSize: 64
    }
  };

  class CurveWithOID {
    constructor(oidOrName, params) {
      try {
        if (util.isArray(oidOrName) ||
            util.isUint8Array(oidOrName)) {
          // by oid byte array
          oidOrName = new OID(oidOrName);
        }
        if (oidOrName instanceof OID) {
          // by curve OID
          oidOrName = oidOrName.getName();
        }
        // by curve name or oid string
        this.name = enums.write(enums.curve, oidOrName);
      } catch (err) {
        throw new UnsupportedError('Unknown curve');
      }
      params = params || curves[this.name];

      this.keyType = params.keyType;

      this.oid = params.oid;
      this.hash = params.hash;
      this.cipher = params.cipher;
      this.node = params.node && curves[this.name];
      this.web = params.web && curves[this.name];
      this.payloadSize = params.payloadSize;
      if (this.web && util.getWebCrypto()) {
        this.type = 'web';
      } else if (this.node && util.getNodeCrypto()) {
        this.type = 'node';
      } else if (this.name === 'curve25519') {
        this.type = 'curve25519';
      } else if (this.name === 'ed25519') {
        this.type = 'ed25519';
      }
    }

    async genKeyPair() {
      let keyPair;
      switch (this.type) {
        case 'web':
          try {
            return await webGenKeyPair(this.name);
          } catch (err) {
            util.printDebugError('Browser did not support generating ec key ' + err.message);
            break;
          }
        case 'node':
          return nodeGenKeyPair(this.name);
        case 'curve25519': {
          const privateKey = getRandomBytes(32);
          privateKey[0] = (privateKey[0] & 127) | 64;
          privateKey[31] &= 248;
          const secretKey = privateKey.slice().reverse();
          keyPair = naclFastLight.box.keyPair.fromSecretKey(secretKey);
          const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]);
          return { publicKey, privateKey };
        }
        case 'ed25519': {
          const privateKey = getRandomBytes(32);
          const keyPair = naclFastLight.sign.keyPair.fromSeed(privateKey);
          const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]);
          return { publicKey, privateKey };
        }
      }
      const indutnyCurve = await getIndutnyCurve(this.name);
      keyPair = await indutnyCurve.genKeyPair({
        entropy: util.uint8ArrayToString(getRandomBytes(32))
      });
      return { publicKey: new Uint8Array(keyPair.getPublic('array', false)), privateKey: keyPair.getPrivate().toArrayLike(Uint8Array) };
    }
  }

  async function generate$1(curve) {
    const BigInteger = await util.getBigInteger();

    curve = new CurveWithOID(curve);
    const keyPair = await curve.genKeyPair();
    const Q = new BigInteger(keyPair.publicKey).toUint8Array();
    const secret = new BigInteger(keyPair.privateKey).toUint8Array('be', curve.payloadSize);
    return {
      oid: curve.oid,
      Q,
      secret,
      hash: curve.hash,
      cipher: curve.cipher
    };
  }

  /**
   * Get preferred hash algo to use with the given curve
   * @param {module:type/oid} oid - curve oid
   * @returns {enums.hash} hash algorithm
   */
  function getPreferredHashAlgo(oid) {
    return curves[enums.write(enums.curve, oid.toHex())].hash;
  }

  /**
   * Validate ECDH and ECDSA parameters
   * Not suitable for EdDSA (different secret key format)
   * @param {module:enums.publicKey} algo - EC algorithm, to filter supported curves
   * @param {module:type/oid} oid - EC object identifier
   * @param {Uint8Array} Q - EC public point
   * @param {Uint8Array} d - EC secret scalar
   * @returns {Promise<Boolean>} Whether params are valid.
   * @async
   */
  async function validateStandardParams(algo, oid, Q, d) {
    const supportedCurves = {
      p256: true,
      p384: true,
      p521: true,
      secp256k1: true,
      curve25519: algo === enums.publicKey.ecdh,
      brainpoolP256r1: true,
      brainpoolP384r1: true,
      brainpoolP512r1: true
    };

    // Check whether the given curve is supported
    const curveName = oid.getName();
    if (!supportedCurves[curveName]) {
      return false;
    }

    if (curveName === 'curve25519') {
      d = d.slice().reverse();
      // Re-derive public point Q'
      const { publicKey } = naclFastLight.box.keyPair.fromSecretKey(d);

      Q = new Uint8Array(Q);
      const dG = new Uint8Array([0x40, ...publicKey]); // Add public key prefix
      if (!util.equalsUint8Array(dG, Q)) {
        return false;
      }

      return true;
    }

    const curve = await getIndutnyCurve(curveName);
    try {
      // Parse Q and check that it is on the curve but not at infinity
      Q = keyFromPublic(curve, Q).getPublic();
    } catch (validationErrors) {
      return false;
    }

    /**
     * Re-derive public point Q' = dG from private key
     * Expect Q == Q'
     */
    const dG = keyFromPrivate(curve, d).getPublic();
    if (!dG.eq(Q)) {
      return false;
    }

    return true;
  }

  //////////////////////////
  //                      //
  //   Helper functions   //
  //                      //
  //////////////////////////


  async function webGenKeyPair(name) {
    // Note: keys generated with ECDSA and ECDH are structurally equivalent
    const webCryptoKey = await webCrypto$6.generateKey({ name: 'ECDSA', namedCurve: webCurves[name] }, true, ['sign', 'verify']);

    const privateKey = await webCrypto$6.exportKey('jwk', webCryptoKey.privateKey);
    const publicKey = await webCrypto$6.exportKey('jwk', webCryptoKey.publicKey);

    return {
      publicKey: jwkToRawPublic(publicKey),
      privateKey: b64ToUint8Array(privateKey.d)
    };
  }

  async function nodeGenKeyPair(name) {
    // Note: ECDSA and ECDH key generation is structurally equivalent
    const ecdh = nodeCrypto$7.createECDH(nodeCurves[name]);
    await ecdh.generateKeys();
    return {
      publicKey: new Uint8Array(ecdh.getPublicKey()),
      privateKey: new Uint8Array(ecdh.getPrivateKey())
    };
  }

  //////////////////////////
  //                      //
  //   Helper functions   //
  //                      //
  //////////////////////////

  /**
   * @param {JsonWebKey} jwk - key for conversion
   *
   * @returns {Uint8Array} Raw public key.
   */
  function jwkToRawPublic(jwk) {
    const bufX = b64ToUint8Array(jwk.x);
    const bufY = b64ToUint8Array(jwk.y);
    const publicKey = new Uint8Array(bufX.length + bufY.length + 1);
    publicKey[0] = 0x04;
    publicKey.set(bufX, 1);
    publicKey.set(bufY, bufX.length + 1);
    return publicKey;
  }

  /**
   * @param {Integer} payloadSize - ec payload size
   * @param {String} name - curve name
   * @param {Uint8Array} publicKey - public key
   *
   * @returns {JsonWebKey} Public key in jwk format.
   */
  function rawPublicToJWK(payloadSize, name, publicKey) {
    const len = payloadSize;
    const bufX = publicKey.slice(1, len + 1);
    const bufY = publicKey.slice(len + 1, len * 2 + 1);
    // https://www.rfc-editor.org/rfc/rfc7518.txt
    const jwk = {
      kty: 'EC',
      crv: name,
      x: uint8ArrayToB64(bufX, true),
      y: uint8ArrayToB64(bufY, true),
      ext: true
    };
    return jwk;
  }

  /**
   * @param {Integer} payloadSize - ec payload size
   * @param {String} name - curve name
   * @param {Uint8Array} publicKey - public key
   * @param {Uint8Array} privateKey - private key
   *
   * @returns {JsonWebKey} Private key in jwk format.
   */
  function privateToJWK$1(payloadSize, name, publicKey, privateKey) {
    const jwk = rawPublicToJWK(payloadSize, name, publicKey);
    jwk.d = uint8ArrayToB64(privateKey, true);
    return jwk;
  }

  // OpenPGP.js - An OpenPGP implementation in javascript

  const webCrypto$7 = util.getWebCrypto();
  const nodeCrypto$8 = util.getNodeCrypto();

  /**
   * Sign a message using the provided key
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {module:enums.hash} hashAlgo - Hash algorithm used to sign
   * @param {Uint8Array} message - Message to sign
   * @param {Uint8Array} publicKey - Public key
   * @param {Uint8Array} privateKey - Private key used to sign the message
   * @param {Uint8Array} hashed - The hashed message
   * @returns {Promise<{
   *   r: Uint8Array,
   *   s: Uint8Array
   * }>} Signature of the message
   * @async
   */
  async function sign$1(oid, hashAlgo, message, publicKey, privateKey, hashed) {
    const curve = new CurveWithOID(oid);
    if (message && !util.isStream(message)) {
      const keyPair = { publicKey, privateKey };
      switch (curve.type) {
        case 'web': {
          // If browser doesn't support a curve, we'll catch it
          try {
            // Need to await to make sure browser succeeds
            return await webSign$1(curve, hashAlgo, message, keyPair);
          } catch (err) {
            // We do not fallback if the error is related to key integrity
            // Unfortunaley Safari does not support p521 and throws a DataError when using it
            // So we need to always fallback for that curve
            if (curve.name !== 'p521' && (err.name === 'DataError' || err.name === 'OperationError')) {
              throw err;
            }
            util.printDebugError('Browser did not support signing: ' + err.message);
          }
          break;
        }
        case 'node': {
          const signature = await nodeSign$1(curve, hashAlgo, message, keyPair);
          return {
            r: signature.r.toArrayLike(Uint8Array),
            s: signature.s.toArrayLike(Uint8Array)
          };
        }
      }
    }
    return ellipticSign(curve, hashed, privateKey);
  }

  /**
   * Verifies if a signature is valid for a message
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {module:enums.hash} hashAlgo - Hash algorithm used in the signature
   * @param  {{r: Uint8Array,
               s: Uint8Array}}   signature Signature to verify
   * @param {Uint8Array} message - Message to verify
   * @param {Uint8Array} publicKey - Public key used to verify the message
   * @param {Uint8Array} hashed - The hashed message
   * @returns {Boolean}
   * @async
   */
  async function verify$1(oid, hashAlgo, signature, message, publicKey, hashed) {
    const curve = new CurveWithOID(oid);
    if (message && !util.isStream(message)) {
      switch (curve.type) {
        case 'web':
          try {
            // Need to await to make sure browser succeeds
            return await webVerify$1(curve, hashAlgo, signature, message, publicKey);
          } catch (err) {
            // We do not fallback if the error is related to key integrity
            // Unfortunately Safari does not support p521 and throws a DataError when using it
            // So we need to always fallback for that curve
            if (curve.name !== 'p521' && (err.name === 'DataError' || err.name === 'OperationError')) {
              throw err;
            }
            util.printDebugError('Browser did not support verifying: ' + err.message);
          }
          break;
        case 'node':
          return nodeVerify$1(curve, hashAlgo, signature, message, publicKey);
      }
    }
    const digest = (typeof hashAlgo === 'undefined') ? message : hashed;
    return ellipticVerify(curve, signature, digest, publicKey);
  }

  /**
   * Validate ECDSA parameters
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {Uint8Array} Q - ECDSA public point
   * @param {Uint8Array} d - ECDSA secret scalar
   * @returns {Promise<Boolean>} Whether params are valid.
   * @async
   */
  async function validateParams$2(oid, Q, d) {
    const curve = new CurveWithOID(oid);
    // Reject curves x25519 and ed25519
    if (curve.keyType !== enums.publicKey.ecdsa) {
      return false;
    }

    // To speed up the validation, we try to use node- or webcrypto when available
    // and sign + verify a random message
    switch (curve.type) {
      case 'web':
      case 'node': {
        const message = getRandomBytes(8);
        const hashAlgo = enums.hash.sha256;
        const hashed = await hash.digest(hashAlgo, message);
        try {
          const signature = await sign$1(oid, hashAlgo, message, Q, d, hashed);
          return await verify$1(oid, hashAlgo, signature, message, Q, hashed);
        } catch (err) {
          return false;
        }
      }
      default:
        return validateStandardParams(enums.publicKey.ecdsa, oid, Q, d);
    }
  }


  //////////////////////////
  //                      //
  //   Helper functions   //
  //                      //
  //////////////////////////

  async function ellipticSign(curve, hashed, privateKey) {
    const indutnyCurve = await getIndutnyCurve(curve.name);
    const key = keyFromPrivate(indutnyCurve, privateKey);
    const signature = key.sign(hashed);
    return {
      r: signature.r.toArrayLike(Uint8Array),
      s: signature.s.toArrayLike(Uint8Array)
    };
  }

  async function ellipticVerify(curve, signature, digest, publicKey) {
    const indutnyCurve = await getIndutnyCurve(curve.name);
    const key = keyFromPublic(indutnyCurve, publicKey);
    return key.verify(digest, signature);
  }

  async function webSign$1(curve, hashAlgo, message, keyPair) {
    const len = curve.payloadSize;
    const jwk = privateToJWK$1(curve.payloadSize, webCurves[curve.name], keyPair.publicKey, keyPair.privateKey);
    const key = await webCrypto$7.importKey(
      'jwk',
      jwk,
      {
        'name': 'ECDSA',
        'namedCurve': webCurves[curve.name],
        'hash': { name: enums.read(enums.webHash, curve.hash) }
      },
      false,
      ['sign']
    );

    const signature = new Uint8Array(await webCrypto$7.sign(
      {
        'name': 'ECDSA',
        'namedCurve': webCurves[curve.name],
        'hash': { name: enums.read(enums.webHash, hashAlgo) }
      },
      key,
      message
    ));

    return {
      r: signature.slice(0, len),
      s: signature.slice(len, len << 1)
    };
  }

  async function webVerify$1(curve, hashAlgo, { r, s }, message, publicKey) {
    const jwk = rawPublicToJWK(curve.payloadSize, webCurves[curve.name], publicKey);
    const key = await webCrypto$7.importKey(
      'jwk',
      jwk,
      {
        'name': 'ECDSA',
        'namedCurve': webCurves[curve.name],
        'hash': { name: enums.read(enums.webHash, curve.hash) }
      },
      false,
      ['verify']
    );

    const signature = util.concatUint8Array([r, s]).buffer;

    return webCrypto$7.verify(
      {
        'name': 'ECDSA',
        'namedCurve': webCurves[curve.name],
        'hash': { name: enums.read(enums.webHash, hashAlgo) }
      },
      key,
      signature,
      message
    );
  }

  async function nodeSign$1(curve, hashAlgo, message, keyPair) {
    const sign = nodeCrypto$8.createSign(enums.read(enums.hash, hashAlgo));
    sign.write(message);
    sign.end();
    const key = ECPrivateKey.encode({
      version: 1,
      parameters: curve.oid,
      privateKey: Array.from(keyPair.privateKey),
      publicKey: { unused: 0, data: Array.from(keyPair.publicKey) }
    }, 'pem', {
      label: 'EC PRIVATE KEY'
    });

    return ECDSASignature.decode(sign.sign(key), 'der');
  }

  async function nodeVerify$1(curve, hashAlgo, { r, s }, message, publicKey) {
    const { default: BN } = await Promise.resolve().then(function () { return bn$1; });

    const verify = nodeCrypto$8.createVerify(enums.read(enums.hash, hashAlgo));
    verify.write(message);
    verify.end();
    const key = SubjectPublicKeyInfo.encode({
      algorithm: {
        algorithm: [1, 2, 840, 10045, 2, 1],
        parameters: curve.oid
      },
      subjectPublicKey: { unused: 0, data: Array.from(publicKey) }
    }, 'pem', {
      label: 'PUBLIC KEY'
    });
    const signature = ECDSASignature.encode({
      r: new BN(r), s: new BN(s)
    }, 'der');

    try {
      return verify.verify(key, signature);
    } catch (err) {
      return false;
    }
  }

  // Originally written by Owen Smith https://github.com/omsmith
  // Adapted on Feb 2018 from https://github.com/Brightspace/node-jwk-to-pem/

  /* eslint-disable no-invalid-this */

  const asn1$1 = nodeCrypto$8 ? void('asn1.js') : undefined;

  const ECDSASignature = nodeCrypto$8 ?
    asn1$1.define('ECDSASignature', function() {
      this.seq().obj(
        this.key('r').int(),
        this.key('s').int()
      );
    }) : undefined;

  const ECPrivateKey = nodeCrypto$8 ?
    asn1$1.define('ECPrivateKey', function() {
      this.seq().obj(
        this.key('version').int(),
        this.key('privateKey').octstr(),
        this.key('parameters').explicit(0).optional().any(),
        this.key('publicKey').explicit(1).optional().bitstr()
      );
    }) : undefined;

  const AlgorithmIdentifier = nodeCrypto$8 ?
    asn1$1.define('AlgorithmIdentifier', function() {
      this.seq().obj(
        this.key('algorithm').objid(),
        this.key('parameters').optional().any()
      );
    }) : undefined;

  const SubjectPublicKeyInfo = nodeCrypto$8 ?
    asn1$1.define('SubjectPublicKeyInfo', function() {
      this.seq().obj(
        this.key('algorithm').use(AlgorithmIdentifier),
        this.key('subjectPublicKey').bitstr()
      );
    }) : undefined;

  var ecdsa = /*#__PURE__*/Object.freeze({
    __proto__: null,
    sign: sign$1,
    verify: verify$1,
    validateParams: validateParams$2
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  naclFastLight.hash = bytes => new Uint8Array(_512().update(bytes).digest());

  /**
   * Sign a message using the provided legacy EdDSA key
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {module:enums.hash} hashAlgo - Hash algorithm used to sign (must be sha256 or stronger)
   * @param {Uint8Array} message - Message to sign
   * @param {Uint8Array} publicKey - Public key
   * @param {Uint8Array} privateKey - Private key used to sign the message
   * @param {Uint8Array} hashed - The hashed message
   * @returns {Promise<{
   *   r: Uint8Array,
   *   s: Uint8Array
   * }>} Signature of the message
   * @async
   */
  async function sign$2(oid, hashAlgo, message, publicKey, privateKey, hashed) {
    if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(enums.hash.sha256)) {
      // see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
      throw new Error('Hash algorithm too weak for EdDSA.');
    }
    const secretKey = util.concatUint8Array([privateKey, publicKey.subarray(1)]);
    const signature = naclFastLight.sign.detached(hashed, secretKey);
    // EdDSA signature params are returned in little-endian format
    return {
      r: signature.subarray(0, 32),
      s: signature.subarray(32)
    };
  }

  /**
   * Verifies if a legacy EdDSA signature is valid for a message
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {module:enums.hash} hashAlgo - Hash algorithm used in the signature
   * @param  {{r: Uint8Array,
               s: Uint8Array}}   signature Signature to verify the message
   * @param {Uint8Array} m - Message to verify
   * @param {Uint8Array} publicKey - Public key used to verify the message
   * @param {Uint8Array} hashed - The hashed message
   * @returns {Boolean}
   * @async
   */
  async function verify$2(oid, hashAlgo, { r, s }, m, publicKey, hashed) {
    if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(enums.hash.sha256)) {
      throw new Error('Hash algorithm too weak for EdDSA.');
    }
    const signature = util.concatUint8Array([r, s]);
    return naclFastLight.sign.detached.verify(hashed, signature, publicKey.subarray(1));
  }
  /**
   * Validate legacy EdDSA parameters
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {Uint8Array} Q - EdDSA public point
   * @param {Uint8Array} k - EdDSA secret seed
   * @returns {Promise<Boolean>} Whether params are valid.
   * @async
   */
  async function validateParams$3(oid, Q, k) {
    // Check whether the given curve is supported
    if (oid.getName() !== 'ed25519') {
      return false;
    }

    /**
     * Derive public point Q' = dG from private key
     * and expect Q == Q'
     */
    const { publicKey } = naclFastLight.sign.keyPair.fromSeed(k);
    const dG = new Uint8Array([0x40, ...publicKey]); // Add public key prefix
    return util.equalsUint8Array(Q, dG);

  }

  var eddsa_legacy = /*#__PURE__*/Object.freeze({
    __proto__: null,
    sign: sign$2,
    verify: verify$2,
    validateParams: validateParams$3
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  naclFastLight.hash = bytes => new Uint8Array(_512().update(bytes).digest());

  /**
   * Generate (non-legacy) EdDSA key
   * @param {module:enums.publicKey} algo - Algorithm identifier
   * @returns {Promise<{ A: Uint8Array, seed: Uint8Array }>}
   */
  async function generate$2(algo) {
    switch (algo) {
      case enums.publicKey.ed25519: {
        const seed = getRandomBytes(32);
        const { publicKey: A } = naclFastLight.sign.keyPair.fromSeed(seed);
        return { A, seed };
      }
      default:
        throw new Error('Unsupported EdDSA algorithm');
    }
  }

  /**
   * Sign a message using the provided key
   * @param {module:enums.publicKey} algo - Algorithm identifier
   * @param {module:enums.hash} hashAlgo - Hash algorithm used to sign (must be sha256 or stronger)
   * @param {Uint8Array} message - Message to sign
   * @param {Uint8Array} publicKey - Public key
   * @param {Uint8Array} privateKey - Private key used to sign the message
   * @param {Uint8Array} hashed - The hashed message
   * @returns {Promise<{
   *   RS: Uint8Array
   * }>} Signature of the message
   * @async
   */
  async function sign$3(algo, hashAlgo, message, publicKey, privateKey, hashed) {
    if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(getPreferredHashAlgo$1(algo))) {
      throw new Error('Hash algorithm too weak for EdDSA.');
    }
    switch (algo) {
      case enums.publicKey.ed25519: {
        const secretKey = util.concatUint8Array([privateKey, publicKey]);
        const signature = naclFastLight.sign.detached(hashed, secretKey);
        return { RS: signature };
      }
      case enums.publicKey.ed448:
      default:
        throw new Error('Unsupported EdDSA algorithm');
    }

  }

  /**
   * Verifies if a signature is valid for a message
   * @param {module:enums.publicKey} algo - Algorithm identifier
   * @param {module:enums.hash} hashAlgo - Hash algorithm used in the signature
   * @param  {{ RS: Uint8Array }} signature Signature to verify the message
   * @param {Uint8Array} m - Message to verify
   * @param {Uint8Array} publicKey - Public key used to verify the message
   * @param {Uint8Array} hashed - The hashed message
   * @returns {Boolean}
   * @async
   */
  async function verify$3(algo, hashAlgo, { RS }, m, publicKey, hashed) {
    if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(getPreferredHashAlgo$1(algo))) {
      throw new Error('Hash algorithm too weak for EdDSA.');
    }
    switch (algo) {
      case enums.publicKey.ed25519: {
        return naclFastLight.sign.detached.verify(hashed, RS, publicKey);
      }
      case enums.publicKey.ed448:
      default:
        throw new Error('Unsupported EdDSA algorithm');
    }
  }
  /**
   * Validate (non-legacy) EdDSA parameters
   * @param {module:enums.publicKey} algo - Algorithm identifier
   * @param {Uint8Array} A - EdDSA public point
   * @param {Uint8Array} seed - EdDSA secret seed
   * @param {Uint8Array} oid - (legacy only) EdDSA OID
   * @returns {Promise<Boolean>} Whether params are valid.
   * @async
   */
  async function validateParams$4(algo, A, seed) {
    switch (algo) {
      case enums.publicKey.ed25519: {
        /**
         * Derive public point A' from private key
         * and expect A == A'
         */
        const { publicKey } = naclFastLight.sign.keyPair.fromSeed(seed);
        return util.equalsUint8Array(A, publicKey);
      }

      case enums.publicKey.ed448: // unsupported
      default:
        return false;
    }
  }

  function getPreferredHashAlgo$1(algo) {
    switch (algo) {
      case enums.publicKey.ed25519:
        return enums.hash.sha256;
      default:
        throw new Error('Unknown EdDSA algo');
    }
  }

  var eddsa = /*#__PURE__*/Object.freeze({
    __proto__: null,
    generate: generate$2,
    sign: sign$3,
    verify: verify$3,
    validateParams: validateParams$4,
    getPreferredHashAlgo: getPreferredHashAlgo$1
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  /**
   * AES key wrap
   * @function
   * @param {Uint8Array} key
   * @param {Uint8Array} data
   * @returns {Uint8Array}
   */
  function wrap(key, data) {
    const aes = new cipher['aes' + (key.length * 8)](key);
    const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
    const P = unpack(data);
    let A = IV;
    const R = P;
    const n = P.length / 2;
    const t = new Uint32Array([0, 0]);
    let B = new Uint32Array(4);
    for (let j = 0; j <= 5; ++j) {
      for (let i = 0; i < n; ++i) {
        t[1] = n * j + (1 + i);
        // B = A
        B[0] = A[0];
        B[1] = A[1];
        // B = A || R[i]
        B[2] = R[2 * i];
        B[3] = R[2 * i + 1];
        // B = AES(K, B)
        B = unpack(aes.encrypt(pack(B)));
        // A = MSB(64, B) ^ t
        A = B.subarray(0, 2);
        A[0] ^= t[0];
        A[1] ^= t[1];
        // R[i] = LSB(64, B)
        R[2 * i] = B[2];
        R[2 * i + 1] = B[3];
      }
    }
    return pack(A, R);
  }

  /**
   * AES key unwrap
   * @function
   * @param {String} key
   * @param {String} data
   * @returns {Uint8Array}
   * @throws {Error}
   */
  function unwrap(key, data) {
    const aes = new cipher['aes' + (key.length * 8)](key);
    const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
    const C = unpack(data);
    let A = C.subarray(0, 2);
    const R = C.subarray(2);
    const n = C.length / 2 - 1;
    const t = new Uint32Array([0, 0]);
    let B = new Uint32Array(4);
    for (let j = 5; j >= 0; --j) {
      for (let i = n - 1; i >= 0; --i) {
        t[1] = n * j + (i + 1);
        // B = A ^ t
        B[0] = A[0] ^ t[0];
        B[1] = A[1] ^ t[1];
        // B = (A ^ t) || R[i]
        B[2] = R[2 * i];
        B[3] = R[2 * i + 1];
        // B = AES-1(B)
        B = unpack(aes.decrypt(pack(B)));
        // A = MSB(64, B)
        A = B.subarray(0, 2);
        // R[i] = LSB(64, B)
        R[2 * i] = B[2];
        R[2 * i + 1] = B[3];
      }
    }
    if (A[0] === IV[0] && A[1] === IV[1]) {
      return pack(R);
    }
    throw new Error('Key Data Integrity failed');
  }

  function createArrayBuffer(data) {
    if (util.isString(data)) {
      const { length } = data;
      const buffer = new ArrayBuffer(length);
      const view = new Uint8Array(buffer);
      for (let j = 0; j < length; ++j) {
        view[j] = data.charCodeAt(j);
      }
      return buffer;
    }
    return new Uint8Array(data).buffer;
  }

  function unpack(data) {
    const { length } = data;
    const buffer = createArrayBuffer(data);
    const view = new DataView(buffer);
    const arr = new Uint32Array(length / 4);
    for (let i = 0; i < length / 4; ++i) {
      arr[i] = view.getUint32(4 * i);
    }
    return arr;
  }

  function pack() {
    let length = 0;
    for (let k = 0; k < arguments.length; ++k) {
      length += 4 * arguments[k].length;
    }
    const buffer = new ArrayBuffer(length);
    const view = new DataView(buffer);
    let offset = 0;
    for (let i = 0; i < arguments.length; ++i) {
      for (let j = 0; j < arguments[i].length; ++j) {
        view.setUint32(offset + 4 * j, arguments[i][j]);
      }
      offset += 4 * arguments[i].length;
    }
    return new Uint8Array(buffer);
  }

  var aesKW = /*#__PURE__*/Object.freeze({
    __proto__: null,
    wrap: wrap,
    unwrap: unwrap
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  /**
   * @fileoverview Functions to add and remove PKCS5 padding
   * @see PublicKeyEncryptedSessionKeyPacket
   * @module crypto/pkcs5
   * @private
   */

  /**
   * Add pkcs5 padding to a message
   * @param {Uint8Array} message - message to pad
   * @returns {Uint8Array} Padded message.
   */
  function encode$1(message) {
    const c = 8 - (message.length % 8);
    const padded = new Uint8Array(message.length + c).fill(c);
    padded.set(message);
    return padded;
  }

  /**
   * Remove pkcs5 padding from a message
   * @param {Uint8Array} message - message to remove padding from
   * @returns {Uint8Array} Message without padding.
   */
  function decode$1(message) {
    const len = message.length;
    if (len > 0) {
      const c = message[len - 1];
      if (c >= 1) {
        const provided = message.subarray(len - c);
        const computed = new Uint8Array(c).fill(c);
        if (util.equalsUint8Array(provided, computed)) {
          return message.subarray(0, len - c);
        }
      }
    }
    throw new Error('Invalid padding');
  }

  var pkcs5 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    encode: encode$1,
    decode: decode$1
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  const webCrypto$8 = util.getWebCrypto();
  const nodeCrypto$9 = util.getNodeCrypto();

  /**
   * Validate ECDH parameters
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {Uint8Array} Q - ECDH public point
   * @param {Uint8Array} d - ECDH secret scalar
   * @returns {Promise<Boolean>} Whether params are valid.
   * @async
   */
  async function validateParams$5(oid, Q, d) {
    return validateStandardParams(enums.publicKey.ecdh, oid, Q, d);
  }

  // Build Param for ECDH algorithm (RFC 6637)
  function buildEcdhParam(public_algo, oid, kdfParams, fingerprint) {
    return util.concatUint8Array([
      oid.write(),
      new Uint8Array([public_algo]),
      kdfParams.write(),
      util.stringToUint8Array('Anonymous Sender    '),
      fingerprint.subarray(0, 20)
    ]);
  }

  // Key Derivation Function (RFC 6637)
  async function kdf(hashAlgo, X, length, param, stripLeading = false, stripTrailing = false) {
    // Note: X is little endian for Curve25519, big-endian for all others.
    // This is not ideal, but the RFC's are unclear
    // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-02#appendix-B
    let i;
    if (stripLeading) {
      // Work around old go crypto bug
      for (i = 0; i < X.length && X[i] === 0; i++);
      X = X.subarray(i);
    }
    if (stripTrailing) {
      // Work around old OpenPGP.js bug
      for (i = X.length - 1; i >= 0 && X[i] === 0; i--);
      X = X.subarray(0, i + 1);
    }
    const digest = await hash.digest(hashAlgo, util.concatUint8Array([
      new Uint8Array([0, 0, 0, 1]),
      X,
      param
    ]));
    return digest.subarray(0, length);
  }

  /**
   * Generate ECDHE ephemeral key and secret from public key
   *
   * @param {CurveWithOID} curve - Elliptic curve object
   * @param {Uint8Array} Q - Recipient public key
   * @returns {Promise<{publicKey: Uint8Array, sharedKey: Uint8Array}>}
   * @async
   */
  async function genPublicEphemeralKey(curve, Q) {
    switch (curve.type) {
      case 'curve25519': {
        const d = getRandomBytes(32);
        const { secretKey, sharedKey } = await genPrivateEphemeralKey(curve, Q, null, d);
        let { publicKey } = naclFastLight.box.keyPair.fromSecretKey(secretKey);
        publicKey = util.concatUint8Array([new Uint8Array([0x40]), publicKey]);
        return { publicKey, sharedKey }; // Note: sharedKey is little-endian here, unlike below
      }
      case 'web':
        if (curve.web && util.getWebCrypto()) {
          try {
            return await webPublicEphemeralKey(curve, Q);
          } catch (err) {
            util.printDebugError(err);
          }
        }
        break;
      case 'node':
        return nodePublicEphemeralKey(curve, Q);
    }
    return ellipticPublicEphemeralKey(curve, Q);
  }

  /**
   * Encrypt and wrap a session key
   *
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {module:type/kdf_params} kdfParams - KDF params including cipher and algorithm to use
   * @param {Uint8Array} data - Unpadded session key data
   * @param {Uint8Array} Q - Recipient public key
   * @param {Uint8Array} fingerprint - Recipient fingerprint
   * @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}
   * @async
   */
  async function encrypt$3(oid, kdfParams, data, Q, fingerprint) {
    const m = encode$1(data);

    const curve = new CurveWithOID(oid);
    const { publicKey, sharedKey } = await genPublicEphemeralKey(curve, Q);
    const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
    const { keySize } = getCipher(kdfParams.cipher);
    const Z = await kdf(kdfParams.hash, sharedKey, keySize, param);
    const wrappedKey = wrap(Z, m);
    return { publicKey, wrappedKey };
  }

  /**
   * Generate ECDHE secret from private key and public part of ephemeral key
   *
   * @param {CurveWithOID} curve - Elliptic curve object
   * @param {Uint8Array} V - Public part of ephemeral key
   * @param {Uint8Array} Q - Recipient public key
   * @param {Uint8Array} d - Recipient private key
   * @returns {Promise<{secretKey: Uint8Array, sharedKey: Uint8Array}>}
   * @async
   */
  async function genPrivateEphemeralKey(curve, V, Q, d) {
    if (d.length !== curve.payloadSize) {
      const privateKey = new Uint8Array(curve.payloadSize);
      privateKey.set(d, curve.payloadSize - d.length);
      d = privateKey;
    }
    switch (curve.type) {
      case 'curve25519': {
        const secretKey = d.slice().reverse();
        const sharedKey = naclFastLight.scalarMult(secretKey, V.subarray(1));
        return { secretKey, sharedKey }; // Note: sharedKey is little-endian here, unlike below
      }
      case 'web':
        if (curve.web && util.getWebCrypto()) {
          try {
            return await webPrivateEphemeralKey(curve, V, Q, d);
          } catch (err) {
            util.printDebugError(err);
          }
        }
        break;
      case 'node':
        return nodePrivateEphemeralKey(curve, V, d);
    }
    return ellipticPrivateEphemeralKey(curve, V, d);
  }

  /**
   * Decrypt and unwrap the value derived from session key
   *
   * @param {module:type/oid} oid - Elliptic curve object identifier
   * @param {module:type/kdf_params} kdfParams - KDF params including cipher and algorithm to use
   * @param {Uint8Array} V - Public part of ephemeral key
   * @param {Uint8Array} C - Encrypted and wrapped value derived from session key
   * @param {Uint8Array} Q - Recipient public key
   * @param {Uint8Array} d - Recipient private key
   * @param {Uint8Array} fingerprint - Recipient fingerprint
   * @returns {Promise<Uint8Array>} Value derived from session key.
   * @async
   */
  async function decrypt$3(oid, kdfParams, V, C, Q, d, fingerprint) {
    const curve = new CurveWithOID(oid);
    const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d);
    const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
    const { keySize } = getCipher(kdfParams.cipher);
    let err;
    for (let i = 0; i < 3; i++) {
      try {
        // Work around old go crypto bug and old OpenPGP.js bug, respectively.
        const Z = await kdf(kdfParams.hash, sharedKey, keySize, param, i === 1, i === 2);
        return decode$1(unwrap(Z, C));
      } catch (e) {
        err = e;
      }
    }
    throw err;
  }

  /**
   * Generate ECDHE secret from private key and public part of ephemeral key using webCrypto
   *
   * @param {CurveWithOID} curve - Elliptic curve object
   * @param {Uint8Array} V - Public part of ephemeral key
   * @param {Uint8Array} Q - Recipient public key
   * @param {Uint8Array} d - Recipient private key
   * @returns {Promise<{secretKey: Uint8Array, sharedKey: Uint8Array}>}
   * @async
   */
  async function webPrivateEphemeralKey(curve, V, Q, d) {
    const recipient = privateToJWK$1(curve.payloadSize, curve.web.web, Q, d);
    let privateKey = webCrypto$8.importKey(
      'jwk',
      recipient,
      {
        name: 'ECDH',
        namedCurve: curve.web.web
      },
      true,
      ['deriveKey', 'deriveBits']
    );
    const jwk = rawPublicToJWK(curve.payloadSize, curve.web.web, V);
    let sender = webCrypto$8.importKey(
      'jwk',
      jwk,
      {
        name: 'ECDH',
        namedCurve: curve.web.web
      },
      true,
      []
    );
    [privateKey, sender] = await Promise.all([privateKey, sender]);
    let S = webCrypto$8.deriveBits(
      {
        name: 'ECDH',
        namedCurve: curve.web.web,
        public: sender
      },
      privateKey,
      curve.web.sharedSize
    );
    let secret = webCrypto$8.exportKey(
      'jwk',
      privateKey
    );
    [S, secret] = await Promise.all([S, secret]);
    const sharedKey = new Uint8Array(S);
    const secretKey = b64ToUint8Array(secret.d);
    return { secretKey, sharedKey };
  }

  /**
   * Generate ECDHE ephemeral key and secret from public key using webCrypto
   *
   * @param {CurveWithOID} curve - Elliptic curve object
   * @param {Uint8Array} Q - Recipient public key
   * @returns {Promise<{publicKey: Uint8Array, sharedKey: Uint8Array}>}
   * @async
   */
  async function webPublicEphemeralKey(curve, Q) {
    const jwk = rawPublicToJWK(curve.payloadSize, curve.web.web, Q);
    let keyPair = webCrypto$8.generateKey(
      {
        name: 'ECDH',
        namedCurve: curve.web.web
      },
      true,
      ['deriveKey', 'deriveBits']
    );
    let recipient = webCrypto$8.importKey(
      'jwk',
      jwk,
      {
        name: 'ECDH',
        namedCurve: curve.web.web
      },
      false,
      []
    );
    [keyPair, recipient] = await Promise.all([keyPair, recipient]);
    let s = webCrypto$8.deriveBits(
      {
        name: 'ECDH',
        namedCurve: curve.web.web,
        public: recipient
      },
      keyPair.privateKey,
      curve.web.sharedSize
    );
    let p = webCrypto$8.exportKey(
      'jwk',
      keyPair.publicKey
    );
    [s, p] = await Promise.all([s, p]);
    const sharedKey = new Uint8Array(s);
    const publicKey = new Uint8Array(jwkToRawPublic(p));
    return { publicKey, sharedKey };
  }

  /**
   * Generate ECDHE secret from private key and public part of ephemeral key using indutny/elliptic
   *
   * @param {CurveWithOID} curve - Elliptic curve object
   * @param {Uint8Array} V - Public part of ephemeral key
   * @param {Uint8Array} d - Recipient private key
   * @returns {Promise<{secretKey: Uint8Array, sharedKey: Uint8Array}>}
   * @async
   */
  async function ellipticPrivateEphemeralKey(curve, V, d) {
    const indutnyCurve = await getIndutnyCurve(curve.name);
    V = keyFromPublic(indutnyCurve, V);
    d = keyFromPrivate(indutnyCurve, d);
    const secretKey = new Uint8Array(d.getPrivate());
    const S = d.derive(V.getPublic());
    const len = indutnyCurve.curve.p.byteLength();
    const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
    return { secretKey, sharedKey };
  }

  /**
   * Generate ECDHE ephemeral key and secret from public key using indutny/elliptic
   *
   * @param {CurveWithOID} curve - Elliptic curve object
   * @param {Uint8Array} Q - Recipient public key
   * @returns {Promise<{publicKey: Uint8Array, sharedKey: Uint8Array}>}
   * @async
   */
  async function ellipticPublicEphemeralKey(curve, Q) {
    const indutnyCurve = await getIndutnyCurve(curve.name);
    const v = await curve.genKeyPair();
    Q = keyFromPublic(indutnyCurve, Q);
    const V = keyFromPrivate(indutnyCurve, v.privateKey);
    const publicKey = v.publicKey;
    const S = V.derive(Q.getPublic());
    const len = indutnyCurve.curve.p.byteLength();
    const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
    return { publicKey, sharedKey };
  }

  /**
   * Generate ECDHE secret from private key and public part of ephemeral key using nodeCrypto
   *
   * @param {CurveWithOID} curve - Elliptic curve object
   * @param {Uint8Array} V - Public part of ephemeral key
   * @param {Uint8Array} d - Recipient private key
   * @returns {Promise<{secretKey: Uint8Array, sharedKey: Uint8Array}>}
   * @async
   */
  async function nodePrivateEphemeralKey(curve, V, d) {
    const recipient = nodeCrypto$9.createECDH(curve.node.node);
    recipient.setPrivateKey(d);
    const sharedKey = new Uint8Array(recipient.computeSecret(V));
    const secretKey = new Uint8Array(recipient.getPrivateKey());
    return { secretKey, sharedKey };
  }

  /**
   * Generate ECDHE ephemeral key and secret from public key using nodeCrypto
   *
   * @param {CurveWithOID} curve - Elliptic curve object
   * @param {Uint8Array} Q - Recipient public key
   * @returns {Promise<{publicKey: Uint8Array, sharedKey: Uint8Array}>}
   * @async
   */
  async function nodePublicEphemeralKey(curve, Q) {
    const sender = nodeCrypto$9.createECDH(curve.node.node);
    sender.generateKeys();
    const sharedKey = new Uint8Array(sender.computeSecret(Q));
    const publicKey = new Uint8Array(sender.getPublicKey());
    return { publicKey, sharedKey };
  }

  var ecdh = /*#__PURE__*/Object.freeze({
    __proto__: null,
    validateParams: validateParams$5,
    encrypt: encrypt$3,
    decrypt: decrypt$3
  });

  /**
   * @fileoverview This module implements HKDF using either the WebCrypto API or Node.js' crypto API.
   * @module crypto/hkdf
   * @private
   */

  const webCrypto$9 = util.getWebCrypto();
  const nodeCrypto$a = util.getNodeCrypto();
  const nodeSubtleCrypto = nodeCrypto$a && nodeCrypto$a.webcrypto && nodeCrypto$a.webcrypto.subtle;

  async function HKDF(hashAlgo, inputKey, salt, info, outLen) {
    const hash = enums.read(enums.webHash, hashAlgo);
    if (!hash) throw new Error('Hash algo not supported with HKDF');

    if (webCrypto$9 || nodeSubtleCrypto) {
      const crypto = webCrypto$9 || nodeSubtleCrypto;
      const importedKey = await crypto.importKey('raw', inputKey, 'HKDF', false, ['deriveBits']);
      const bits = await crypto.deriveBits({ name: 'HKDF', hash, salt, info }, importedKey, outLen * 8);
      return new Uint8Array(bits);
    }

    if (nodeCrypto$a) {
      const hashAlgoName = enums.read(enums.hash, hashAlgo);
      // Node-only HKDF implementation based on https://www.rfc-editor.org/rfc/rfc5869

      const computeHMAC = (hmacKey, hmacMessage) => nodeCrypto$a.createHmac(hashAlgoName, hmacKey).update(hmacMessage).digest();
      // Step 1: Extract
      // PRK = HMAC-Hash(salt, IKM)
      const pseudoRandomKey = computeHMAC(salt, inputKey);

      const hashLen = pseudoRandomKey.length;

      // Step 2: Expand
      // HKDF-Expand(PRK, info, L) -> OKM
      const n = Math.ceil(outLen / hashLen);
      const outputKeyingMaterial = new Uint8Array(n * hashLen);

      // HMAC input buffer updated at each iteration
      const roundInput = new Uint8Array(hashLen + info.length + 1);
      // T_i and last byte are updated at each iteration, but `info` remains constant
      roundInput.set(info, hashLen);

      for (let i = 0; i < n; i++) {
        // T(0) = empty string (zero length)
        // T(i) = HMAC-Hash(PRK, T(i-1) | info | i)
        roundInput[roundInput.length - 1] = i + 1;
        // t = T(i+1)
        const t = computeHMAC(pseudoRandomKey, i > 0 ? roundInput : roundInput.subarray(hashLen));
        roundInput.set(t, 0);

        outputKeyingMaterial.set(t, i * hashLen);
      }

      return outputKeyingMaterial.subarray(0, outLen);
    }

    throw new Error('No HKDF implementation available');
  }

  /**
   * @fileoverview Key encryption and decryption for RFC 6637 ECDH
   * @module crypto/public_key/elliptic/ecdh
   * @private
   */

  const HKDF_INFO = {
    x25519: util.encodeUTF8('OpenPGP X25519')
  };

  /**
   * Generate ECDH key for Montgomery curves
   * @param {module:enums.publicKey} algo - Algorithm identifier
   * @returns {Promise<{ A: Uint8Array, k: Uint8Array }>}
   */
  async function generate$3(algo) {
    switch (algo) {
      case enums.publicKey.x25519: {
        // k stays in little-endian, unlike legacy ECDH over curve25519
        const k = getRandomBytes(32);
        const { publicKey: A } = naclFastLight.box.keyPair.fromSecretKey(k);
        return { A, k };
      }
      default:
        throw new Error('Unsupported ECDH algorithm');
    }
  }

  /**
  * Validate ECDH parameters
  * @param {module:enums.publicKey} algo - Algorithm identifier
  * @param {Uint8Array} A - ECDH public point
  * @param {Uint8Array} k - ECDH secret scalar
  * @returns {Promise<Boolean>} Whether params are valid.
  * @async
  */
  async function validateParams$6(algo, A, k) {
    switch (algo) {
      case enums.publicKey.x25519: {
        /**
         * Derive public point A' from private key
         * and expect A == A'
         */
        const { publicKey } = naclFastLight.box.keyPair.fromSecretKey(k);
        return util.equalsUint8Array(A, publicKey);
      }

      default:
        return false;
    }
  }

  /**
   * Wrap and encrypt a session key
   *
   * @param {module:enums.publicKey} algo - Algorithm identifier
   * @param {Uint8Array} data - session key data to be encrypted
   * @param {Uint8Array} recipientA - Recipient public key (K_B)
   * @returns {Promise<{
   *  ephemeralPublicKey: Uint8Array,
   * wrappedKey: Uint8Array
   * }>} ephemeral public key (K_A) and encrypted key
   * @async
   */
  async function encrypt$4(algo, data, recipientA) {
    switch (algo) {
      case enums.publicKey.x25519: {
        const ephemeralSecretKey = getRandomBytes(32);
        const sharedSecret = naclFastLight.scalarMult(ephemeralSecretKey, recipientA);
        const { publicKey: ephemeralPublicKey } = naclFastLight.box.keyPair.fromSecretKey(ephemeralSecretKey);
        const hkdfInput = util.concatUint8Array([
          ephemeralPublicKey,
          recipientA,
          sharedSecret
        ]);
        const { keySize } = getCipher(enums.symmetric.aes128);
        const encryptionKey = await HKDF(enums.hash.sha256, hkdfInput, new Uint8Array(), HKDF_INFO.x25519, keySize);
        const wrappedKey = wrap(encryptionKey, data);
        return { ephemeralPublicKey, wrappedKey };
      }

      default:
        throw new Error('Unsupported ECDH algorithm');
    }
  }

  /**
   * Decrypt and unwrap the session key
   *
   * @param {module:enums.publicKey} algo - Algorithm identifier
   * @param {Uint8Array} ephemeralPublicKey - (K_A)
   * @param {Uint8Array} wrappedKey,
   * @param {Uint8Array} A - Recipient public key (K_b), needed for KDF
   * @param {Uint8Array} k - Recipient secret key (b)
   * @returns {Promise<Uint8Array>} decrypted session key data
   * @async
   */
  async function decrypt$4(algo, ephemeralPublicKey, wrappedKey, A, k) {
    switch (algo) {
      case enums.publicKey.x25519: {
        const sharedSecret = naclFastLight.scalarMult(k, ephemeralPublicKey);
        const hkdfInput = util.concatUint8Array([
          ephemeralPublicKey,
          A,
          sharedSecret
        ]);
        const { keySize } = getCipher(enums.symmetric.aes128);
        const encryptionKey = await HKDF(enums.hash.sha256, hkdfInput, new Uint8Array(), HKDF_INFO.x25519, keySize);
        return unwrap(encryptionKey, wrappedKey);
      }
      default:
        throw new Error('Unsupported ECDH algorithm');
    }
  }

  var ecdh_x = /*#__PURE__*/Object.freeze({
    __proto__: null,
    generate: generate$3,
    validateParams: validateParams$6,
    encrypt: encrypt$4,
    decrypt: decrypt$4
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  var elliptic = /*#__PURE__*/Object.freeze({
    __proto__: null,
    CurveWithOID: CurveWithOID,
    ecdh: ecdh,
    ecdhX: ecdh_x,
    ecdsa: ecdsa,
    eddsaLegacy: eddsa_legacy,
    eddsa: eddsa,
    generate: generate$1,
    getPreferredHashAlgo: getPreferredHashAlgo
  });

  // GPG4Browsers - An OpenPGP implementation in javascript

  /*
    TODO regarding the hash function, read:
     https://tools.ietf.org/html/rfc4880#section-13.6
     https://tools.ietf.org/html/rfc4880#section-14
  */

  /**
   * DSA Sign function
   * @param {Integer} hashAlgo
   * @param {Uint8Array} hashed
   * @param {Uint8Array} g
   * @param {Uint8Array} p
   * @param {Uint8Array} q
   * @param {Uint8Array} x
   * @returns {Promise<{ r: Uint8Array, s: Uint8Array }>}
   * @async
   */
  async function sign$4(hashAlgo, hashed, g, p, q, x) {
    const BigInteger = await util.getBigInteger();
    const one = new BigInteger(1);
    p = new BigInteger(p);
    q = new BigInteger(q);
    g = new BigInteger(g);
    x = new BigInteger(x);

    let k;
    let r;
    let s;
    let t;
    g = g.mod(p);
    x = x.mod(q);
    // If the output size of the chosen hash is larger than the number of
    // bits of q, the hash result is truncated to fit by taking the number
    // of leftmost bits equal to the number of bits of q.  This (possibly
    // truncated) hash function result is treated as a number and used
    // directly in the DSA signature algorithm.
    const h = new BigInteger(hashed.subarray(0, q.byteLength())).mod(q);
    // FIPS-186-4, section 4.6:
    // The values of r and s shall be checked to determine if r = 0 or s = 0.
    // If either r = 0 or s = 0, a new value of k shall be generated, and the
    // signature shall be recalculated. It is extremely unlikely that r = 0
    // or s = 0 if signatures are generated properly.
    while (true) {
      // See Appendix B here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
      k = await getRandomBigInteger(one, q); // returns in [1, q-1]
      r = g.modExp(k, p).imod(q); // (g**k mod p) mod q
      if (r.isZero()) {
        continue;
      }
      const xr = x.mul(r).imod(q);
      t = h.add(xr).imod(q); // H(m) + x*r mod q
      s = k.modInv(q).imul(t).imod(q); // k**-1 * (H(m) + x*r) mod q
      if (s.isZero()) {
        continue;
      }
      break;
    }
    return {
      r: r.toUint8Array('be', q.byteLength()),
      s: s.toUint8Array('be', q.byteLength())
    };
  }

  /**
   * DSA Verify function
   * @param {Integer} hashAlgo
   * @param {Uint8Array} r
   * @param {Uint8Array} s
   * @param {Uint8Array} hashed
   * @param {Uint8Array} g
   * @param {Uint8Array} p
   * @param {Uint8Array} q
   * @param {Uint8Array} y
   * @returns {boolean}
   * @async
   */
  async function verify$4(hashAlgo, r, s, hashed, g, p, q, y) {
    const BigInteger = await util.getBigInteger();
    const zero = new BigInteger(0);
    r = new BigInteger(r);
    s = new BigInteger(s);

    p = new BigInteger(p);
    q = new BigInteger(q);
    g = new BigInteger(g);
    y = new BigInteger(y);

    if (r.lte(zero) || r.gte(q) ||
        s.lte(zero) || s.gte(q)) {
      util.printDebug('invalid DSA Signature');
      return false;
    }
    const h = new BigInteger(hashed.subarray(0, q.byteLength())).imod(q);
    const w = s.modInv(q); // s**-1 mod q
    if (w.isZero()) {
      util.printDebug('invalid DSA Signature');
      return false;
    }

    g = g.mod(p);
    y = y.mod(p);
    const u1 = h.mul(w).imod(q); // H(m) * w mod q
    const u2 = r.mul(w).imod(q); // r * w mod q
    const t1 = g.modExp(u1, p); // g**u1 mod p
    const t2 = y.modExp(u2, p); // y**u2 mod p
    const v = t1.mul(t2).imod(p).imod(q); // (g**u1 * y**u2 mod p) mod q
    return v.equal(r);
  }

  /**
   * Validate DSA parameters
   * @param {Uint8Array} p - DSA prime
   * @param {Uint8Array} q - DSA group order
   * @param {Uint8Array} g - DSA sub-group generator
   * @param {Uint8Array} y - DSA public key
   * @param {Uint8Array} x - DSA private key
   * @returns {Promise<Boolean>} Whether params are valid.
   * @async
   */
  async function validateParams$7(p, q, g, y, x) {
    const BigInteger = await util.getBigInteger();
    p = new BigInteger(p);
    q = new BigInteger(q);
    g = new BigInteger(g);
    y = new BigInteger(y);
    const one = new BigInteger(1);
    // Check that 1 < g < p
    if (g.lte(one) || g.gte(p)) {
      return false;
    }

    /**
     * Check that subgroup order q divides p-1
     */
    if (!p.dec().mod(q).isZero()) {
      return false;
    }

    /**
     * g has order q
     * Check that g ** q = 1 mod p
     */
    if (!g.modExp(q, p).isOne()) {
      return false;
    }

    /**
     * Check q is large and probably prime (we mainly want to avoid small factors)
     */
    const qSize = new BigInteger(q.bitLength());
    const n150 = new BigInteger(150);
    if (qSize.lt(n150) || !(await isProbablePrime(q, null, 32))) {
      return false;
    }

    /**
     * Re-derive public key y' = g ** x mod p
     * Expect y == y'
     *
     * Blinded exponentiation computes g**{rq + x} to compare to y
     */
    x = new BigInteger(x);
    const two = new BigInteger(2);
    const r = await getRandomBigInteger(two.leftShift(qSize.dec()), two.leftShift(qSize)); // draw r of same size as q
    const rqx = q.mul(r).add(x);
    if (!y.equal(g.modExp(rqx, p))) {
      return false;
    }

    return true;
  }

  var dsa = /*#__PURE__*/Object.freeze({
    __proto__: null,
    sign: sign$4,
    verify: verify$4,
    validateParams: validateParams$7
  });

  /**
   * @fileoverview Asymmetric cryptography functions
   * @module crypto/public_key
   * @private
   */

  var publicKey = {
    /** @see module:crypto/public_key/rsa */
    rsa: rsa,
    /** @see module:crypto/public_key/elgamal */
    elgamal: elgamal,
    /** @see module:crypto/public_key/elliptic */
    elliptic: elliptic,
    /** @see module:crypto/public_key/dsa */
    dsa: dsa,
    /** @see tweetnacl */
    nacl: naclFastLight
  };

  /**
   * @fileoverview Provides functions for asymmetric signing and signature verification
   * @module crypto/signature
   * @private
   */

  /**
   * Parse signature in binary form to get the parameters.
   * The returned values are only padded for EdDSA, since in the other cases their expected length
   * depends on the key params, hence we delegate the padding to the signature verification function.
   * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
   * See {@link https://tools.ietf.org/html/rfc4880#section-5.2.2|RFC 4880 5.2.2.}
   * @param {module:enums.publicKey} algo - Public key algorithm
   * @param {Uint8Array} signature - Data for which the signature was created
   * @returns {Promise<Object>} True if signature is valid.
   * @async
   */
  function parseSignatureParams(algo, signature) {
    let read = 0;
    switch (algo) {
      // Algorithm-Specific Fields for RSA signatures:
      // -  MPI of RSA signature value m**d mod n.
      case enums.publicKey.rsaEncryptSign:
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaSign: {
        const s = util.readMPI(signature.subarray(read));
        // The signature needs to be the same length as the public key modulo n.
        // We pad s on signature verification, where we have access to n.
        return { s };
      }
      // Algorithm-Specific Fields for DSA or ECDSA signatures:
      // -  MPI of DSA or ECDSA value r.
      // -  MPI of DSA or ECDSA value s.
      case enums.publicKey.dsa:
      case enums.publicKey.ecdsa:
      {
        const r = util.readMPI(signature.subarray(read)); read += r.length + 2;
        const s = util.readMPI(signature.subarray(read));
        return { r, s };
      }
      // Algorithm-Specific Fields for legacy EdDSA signatures:
      // -  MPI of an EC point r.
      // -  EdDSA value s, in MPI, in the little endian representation
      case enums.publicKey.eddsaLegacy: {
        // When parsing little-endian MPI data, we always need to left-pad it, as done with big-endian values:
        // https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#section-3.2-9
        let r = util.readMPI(signature.subarray(read)); read += r.length + 2;
        r = util.leftPad(r, 32);
        let s = util.readMPI(signature.subarray(read));
        s = util.leftPad(s, 32);
        return { r, s };
      }
      // Algorithm-Specific Fields for Ed25519 signatures:
      // - 64 octets of the native signature
      case enums.publicKey.ed25519: {
        const RS = signature.subarray(read, read + 64); read += RS.length;
        return { RS };
      }
      default:
        throw new UnsupportedError('Unknown signature algorithm.');
    }
  }

  /**
   * Verifies the signature provided for data using specified algorithms and public key parameters.
   * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
   * and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
   * for public key and hash algorithms.
   * @param {module:enums.publicKey} algo - Public key algorithm
   * @param {module:enums.hash} hashAlgo - Hash algorithm
   * @param {Object} signature - Named algorithm-specific signature parameters
   * @param {Object} publicParams - Algorithm-specific public key parameters
   * @param {Uint8Array} data - Data for which the signature was created
   * @param {Uint8Array} hashed - The hashed data
   * @returns {Promise<Boolean>} True if signature is valid.
   * @async
   */
  async function verify$5(algo, hashAlgo, signature, publicParams, data, hashed) {
    switch (algo) {
      case enums.publicKey.rsaEncryptSign:
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaSign: {
        const { n, e } = publicParams;
        const s = util.leftPad(signature.s, n.length); // padding needed for webcrypto and node crypto
        return publicKey.rsa.verify(hashAlgo, data, s, n, e, hashed);
      }
      case enums.publicKey.dsa: {
        const { g, p, q, y } = publicParams;
        const { r, s } = signature; // no need to pad, since we always handle them as BigIntegers
        return publicKey.dsa.verify(hashAlgo, r, s, hashed, g, p, q, y);
      }
      case enums.publicKey.ecdsa: {
        const { oid, Q } = publicParams;
        const curveSize = new publicKey.elliptic.CurveWithOID(oid).payloadSize;
        // padding needed for webcrypto
        const r = util.leftPad(signature.r, curveSize);
        const s = util.leftPad(signature.s, curveSize);
        return publicKey.elliptic.ecdsa.verify(oid, hashAlgo, { r, s }, data, Q, hashed);
      }
      case enums.publicKey.eddsaLegacy: {
        const { oid, Q } = publicParams;
        // signature already padded on parsing
        return publicKey.elliptic.eddsaLegacy.verify(oid, hashAlgo, signature, data, Q, hashed);
      }
      case enums.publicKey.ed25519: {
        const { A } = publicParams;
        return publicKey.elliptic.eddsa.verify(algo, hashAlgo, signature, data, A, hashed);
      }
      default:
        throw new Error('Unknown signature algorithm.');
    }
  }

  /**
   * Creates a signature on data using specified algorithms and private key parameters.
   * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
   * and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
   * for public key and hash algorithms.
   * @param {module:enums.publicKey} algo - Public key algorithm
   * @param {module:enums.hash} hashAlgo - Hash algorithm
   * @param {Object} publicKeyParams - Algorithm-specific public and private key parameters
   * @param {Object} privateKeyParams - Algorithm-specific public and private key parameters
   * @param {Uint8Array} data - Data to be signed
   * @param {Uint8Array} hashed - The hashed data
   * @returns {Promise<Object>} Signature                      Object containing named signature parameters.
   * @async
   */
  async function sign$5(algo, hashAlgo, publicKeyParams, privateKeyParams, data, hashed) {
    if (!publicKeyParams || !privateKeyParams) {
      throw new Error('Missing key parameters');
    }
    switch (algo) {
      case enums.publicKey.rsaEncryptSign:
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaSign: {
        const { n, e } = publicKeyParams;
        const { d, p, q, u } = privateKeyParams;
        const s = await publicKey.rsa.sign(hashAlgo, data, n, e, d, p, q, u, hashed);
        return { s };
      }
      case enums.publicKey.dsa: {
        const { g, p, q } = publicKeyParams;
        const { x } = privateKeyParams;
        return publicKey.dsa.sign(hashAlgo, hashed, g, p, q, x);
      }
      case enums.publicKey.elgamal: {
        throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.');
      }
      case enums.publicKey.ecdsa: {
        const { oid, Q } = publicKeyParams;
        const { d } = privateKeyParams;
        return publicKey.elliptic.ecdsa.sign(oid, hashAlgo, data, Q, d, hashed);
      }
      case enums.publicKey.eddsaLegacy: {
        const { oid, Q } = publicKeyParams;
        const { seed } = privateKeyParams;
        return publicKey.elliptic.eddsaLegacy.sign(oid, hashAlgo, data, Q, seed, hashed);
      }
      case enums.publicKey.ed25519: {
        const { A } = publicKeyParams;
        const { seed } = privateKeyParams;
        return publicKey.elliptic.eddsa.sign(algo, hashAlgo, data, A, seed, hashed);
      }
      default:
        throw new Error('Unknown signature algorithm.');
    }
  }

  var signature = /*#__PURE__*/Object.freeze({
    __proto__: null,
    parseSignatureParams: parseSignatureParams,
    verify: verify$5,
    sign: sign$5
  });

  // OpenPGP.js - An OpenPGP implementation in javascript

  class ECDHSymmetricKey {
    constructor(data) {
      if (data) {
        this.data = data;
      }
    }

    /**
     * Read an ECDHSymmetricKey from an Uint8Array:
     * - 1 octect for the length `l`
     * - `l` octects of encoded session key data
     * @param {Uint8Array} bytes
     * @returns {Number} Number of read bytes.
     */
    read(bytes) {
      if (bytes.length >= 1) {
        const length = bytes[0];
        if (bytes.length >= 1 + length) {
          this.data = bytes.subarray(1, 1 + length);
          return 1 + this.data.length;
        }
      }
      throw new Error('Invalid symmetric key');
    }

    /**
     * Write an ECDHSymmetricKey as an Uint8Array
     * @returns  {Uint8Array} Serialised data
     */
    write() {
      return util.concatUint8Array([new Uint8Array([this.data.length]), this.data]);
    }
  }

  // OpenPGP.js - An OpenPGP implementation in javascript

  /**
   * Implementation of type KDF parameters
   *
   * {@link https://tools.ietf.org/html/rfc6637#section-7|RFC 6637 7}:
   * A key derivation function (KDF) is necessary to implement the EC
   * encryption.  The Concatenation Key Derivation Function (Approved
   * Alternative 1) [NIST-SP800-56A] with the KDF hash function that is
   * SHA2-256 [FIPS-180-3] or stronger is REQUIRED.
   * @module type/kdf_params
   * @private
   */

  class KDFParams {
    /**
     * @param {enums.hash} hash - Hash algorithm
     * @param {enums.symmetric} cipher - Symmetric algorithm
     */
    constructor(data) {
      if (data) {
        const { hash, cipher } = data;
        this.hash = hash;
        this.cipher = cipher;
      } else {
        this.hash = null;
        this.cipher = null;
      }
    }

    /**
     * Read KDFParams from an Uint8Array
     * @param {Uint8Array} input - Where to read the KDFParams from
     * @returns {Number} Number of read bytes.
     */
    read(input) {
      if (input.length < 4 || input[0] !== 3 || input[1] !== 1) {
        throw new UnsupportedError('Cannot read KDFParams');
      }
      this.hash = input[2];
      this.cipher = input[3];
      return 4;
    }

    /**
     * Write KDFParams to an Uint8Array
     * @returns  {Uint8Array}  Array with the KDFParams value
     */
    write() {
      return new Uint8Array([3, 1, this.hash, this.cipher]);
    }
  }

  /**
   * Encoded symmetric key for x25519 and x448
   * The payload format varies for v3 and v6 PKESK:
   * the former includes an algorithm byte preceeding the encrypted session key.
   *
   * @module type/x25519x448_symkey
   */

  class ECDHXSymmetricKey {
    static fromObject({ wrappedKey, algorithm }) {
      const instance = new ECDHXSymmetricKey();
      instance.wrappedKey = wrappedKey;
      instance.algorithm = algorithm;
      return instance;
    }

    /**
     * - 1 octect for the length `l`
     * - `l` octects of encoded session key data (with optional leading algorithm byte)
     * @param {Uint8Array} bytes
     * @returns {Number} Number of read bytes.
     */
    read(bytes) {
      let read = 0;
      let followLength = bytes[read++];
      this.algorithm = followLength % 2 ? bytes[read++] : null; // session key size is always even
      followLength -= followLength % 2;
      this.wrappedKey = bytes.subarray(read, read + followLength); read += followLength;
    }

    /**
     * Write an MontgomerySymmetricKey as an Uint8Array
     * @returns  {Uint8Array} Serialised data
     */
    write() {
      return util.concatUint8Array([
        this.algorithm ?
          new Uint8Array([this.wrappedKey.length + 1, this.algorithm]) :
          new Uint8Array([this.wrappedKey.length]),
        this.wrappedKey
      ]);
    }
  }

  // GPG4Browsers - An OpenPGP implementation in javascript

  /**
   * Encrypts data using specified algorithm and public key parameters.
   * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
   * @param {module:enums.publicKey} keyAlgo - Public key algorithm
   * @param {module:enums.symmetric} symmetricAlgo - Cipher algorithm
   * @param {Object} publicParams - Algorithm-specific public key parameters
   * @param {Uint8Array} data - Session key data to be encrypted
   * @param {Uint8Array} fingerprint - Recipient fingerprint
   * @returns {Promise<Object>} Encrypted session key parameters.
   * @async
   */
  async function publicKeyEncrypt(keyAlgo, symmetricAlgo, publicParams, data, fingerprint) {
    switch (keyAlgo) {
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaEncryptSign: {
        const { n, e } = publicParams;
        const c = await publicKey.rsa.encrypt(data, n, e);
        return { c };
      }
      case enums.publicKey.elgamal: {
        const { p, g, y } = publicParams;
        return publicKey.elgamal.encrypt(data, p, g, y);
      }
      case enums.publicKey.ecdh: {
        const { oid, Q, kdfParams } = publicParams;
        const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
          oid, kdfParams, data, Q, fingerprint);
        return { V, C: new ECDHSymmetricKey(C) };
      }
      case enums.publicKey.x25519: {
        if (!util.isAES(symmetricAlgo)) {
          // see https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/276
          throw new Error('X25519 keys can only encrypt AES session keys');
        }
        const { A } = publicParams;
        const { ephemeralPublicKey, wrappedKey } = await publicKey.elliptic.ecdhX.encrypt(
          keyAlgo, data, A);
        const C = ECDHXSymmetricKey.fromObject({ algorithm: symmetricAlgo, wrappedKey });
        return { ephemeralPublicKey, C };
      }
      default:
        return [];
    }
  }

  /**
   * Decrypts data using specified algorithm and private key parameters.
   * See {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 5.5.3}
   * @param {module:enums.publicKey} algo - Public key algorithm
   * @param {Object} publicKeyParams - Algorithm-specific public key parameters
   * @param {Object} privateKeyParams - Algorithm-specific private key parameters
   * @param {Object} sessionKeyParams - Encrypted session key parameters
   * @param {Uint8Array} fingerprint - Recipient fingerprint
   * @param {Uint8Array} [randomPayload] - Data to return on decryption error, instead of throwing
   *                                    (needed for constant-time processing in RSA and ElGamal)
   * @returns {Promise<Uint8Array>} Decrypted data.
   * @throws {Error} on sensitive decryption error, unless `randomPayload` is given
   * @async
   */
  async function publicKeyDecrypt(algo, publicKeyParams, privateKeyParams, sessionKeyParams, fingerprint, randomPayload) {
    switch (algo) {
      case enums.publicKey.rsaEncryptSign:
      case enums.publicKey.rsaEncrypt: {
        const { c } = sessionKeyParams;
        const { n, e } = publicKeyParams;
        const { d, p, q, u } = privateKeyParams;
        return publicKey.rsa.decrypt(c, n, e, d, p, q, u, randomPayload);
      }
      case enums.publicKey.elgamal: {
        const { c1, c2 } = sessionKeyParams;
        const p = publicKeyParams.p;
        const x = privateKeyParams.x;
        return publicKey.elgamal.decrypt(c1, c2, p, x, randomPayload);
      }
      case enums.publicKey.ecdh: {
        const { oid, Q, kdfParams } = publicKeyParams;
        const { d } = privateKeyParams;
        const { V, C } = sessionKeyParams;
        return publicKey.elliptic.ecdh.decrypt(
          oid, kdfParams, V, C.data, Q, d, fingerprint);
      }
      case enums.publicKey.x25519: {
        const { A } = publicKeyParams;
        const { k } = privateKeyParams;
        const { ephemeralPublicKey, C } = sessionKeyParams;
        if (!util.isAES(C.algorithm)) {
          throw new Error('AES session key expected');
        }
        return publicKey.elliptic.ecdhX.decrypt(
          algo, ephemeralPublicKey, C.wrappedKey, A, k);
      }
      default:
        throw new Error('Unknown public key encryption algorithm.');
    }
  }

  /**
   * Parse public key material in binary form to get the key parameters
   * @param {module:enums.publicKey} algo - The key algorithm
   * @param {Uint8Array} bytes - The key material to parse
   * @returns {{ read: Number, publicParams: Object }} Number of read bytes plus key parameters referenced by name.
   */
  function parsePublicKeyParams(algo, bytes) {
    let read = 0;
    switch (algo) {
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaEncryptSign:
      case enums.publicKey.rsaSign: {
        const n = util.readMPI(bytes.subarray(read)); read += n.length + 2;
        const e = util.readMPI(bytes.subarray(read)); read += e.length + 2;
        return { read, publicParams: { n, e } };
      }
      case enums.publicKey.dsa: {
        const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
        const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
        const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
        const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
        return { read, publicParams: { p, q, g, y } };
      }
      case enums.publicKey.elgamal: {
        const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
        const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
        const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
        return { read, publicParams: { p, g, y } };
      }
      case enums.publicKey.ecdsa: {
        const oid = new OID(); read += oid.read(bytes);
        checkSupportedCurve(oid);
        const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
        return { read: read, publicParams: { oid, Q } };
      }
      case enums.publicKey.eddsaLegacy: {
        const oid = new OID(); read += oid.read(bytes);
        checkSupportedCurve(oid);
        let Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
        Q = util.leftPad(Q, 33);
        return { read: read, publicParams: { oid, Q } };
      }
      case enums.publicKey.ecdh: {
        const oid = new OID(); read += oid.read(bytes);
        checkSupportedCurve(oid);
        const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
        const kdfParams = new KDFParams(); read += kdfParams.read(bytes.subarray(read));
        return { read: read, publicParams: { oid, Q, kdfParams } };
      }
      case enums.publicKey.ed25519:
      case enums.publicKey.x25519: {
        const A = bytes.subarray(read, read + 32); read += A.length;
        return { read, publicParams: { A } };
      }
      default:
        throw new UnsupportedError('Unknown public key encryption algorithm.');
    }
  }

  /**
   * Parse private key material in binary form to get the key parameters
   * @param {module:enums.publicKey} algo - The key algorithm
   * @param {Uint8Array} bytes - The key material to parse
   * @param {Object} publicParams - (ECC only) public params, needed to format some private params
   * @returns {{ read: Number, privateParams: Object }} Number of read bytes plus the key parameters referenced by name.
   */
  function parsePrivateKeyParams(algo, bytes, publicParams) {
    let read = 0;
    switch (algo) {
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaEncryptSign:
      case enums.publicKey.rsaSign: {
        const d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
        const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
        const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
        const u = util.readMPI(bytes.subarray(read)); read += u.length + 2;
        return { read, privateParams: { d, p, q, u } };
      }
      case enums.publicKey.dsa:
      case enums.publicKey.elgamal: {
        const x = util.readMPI(bytes.subarray(read)); read += x.length + 2;
        return { read, privateParams: { x } };
      }
      case enums.publicKey.ecdsa:
      case enums.publicKey.ecdh: {
        const curve = new CurveWithOID(publicParams.oid);
        let d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
        d = util.leftPad(d, curve.payloadSize);
        return { read, privateParams: { d } };
      }
      case enums.publicKey.eddsaLegacy: {
        const curve = new CurveWithOID(publicParams.oid);
        let seed = util.readMPI(bytes.subarray(read)); read += seed.length + 2;
        seed = util.leftPad(seed, curve.payloadSize);
        return { read, privateParams: { seed } };
      }
      case enums.publicKey.ed25519: {
        const seed = bytes.subarray(read, read + 32); read += seed.length;
        return { read, privateParams: { seed } };
      }
      case enums.publicKey.x25519: {
        const k = bytes.subarray(read, read + 32); read += k.length;
        return { read, privateParams: { k } };
      }
      default:
        throw new UnsupportedError('Unknown public key encryption algorithm.');
    }
  }

  /** Returns the types comprising the encrypted session key of an algorithm
   * @param {module:enums.publicKey} algo - The key algorithm
   * @param {Uint8Array} bytes - The key material to parse
   * @returns {Object} The session key parameters referenced by name.
   */
  function parseEncSessionKeyParams(algo, bytes) {
    let read = 0;
    switch (algo) {
      //   Algorithm-Specific Fields for RSA encrypted session keys:
      //       - MPI of RSA encrypted value m**e mod n.
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaEncryptSign: {
        const c = util.readMPI(bytes.subarray(read));
        return { c };
      }

      //   Algorithm-Specific Fields for Elgamal encrypted session keys:
      //       - MPI of Elgamal value g**k mod p
      //       - MPI of Elgamal value m * y**k mod p
      case enums.publicKey.elgamal: {
        const c1 = util.readMPI(bytes.subarray(read)); read += c1.length + 2;
        const c2 = util.readMPI(bytes.subarray(read));
        return { c1, c2 };
      }
      //   Algorithm-Specific Fields for ECDH encrypted session keys:
      //       - MPI containing the ephemeral key used to establish the shared secret
      //       - ECDH Symmetric Key
      case enums.publicKey.ecdh: {
        const V = util.readMPI(bytes.subarray(read)); read += V.length + 2;
        const C = new ECDHSymmetricKey(); C.read(bytes.subarray(read));
        return { V, C };
      }
      //   Algorithm-Specific Fields for X25519 encrypted session keys:
      //       - 32 octets representing an ephemeral X25519 public key.
      //       - A one-octet size of the following fields.
      //       - The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet).
      //       - The encrypted session key.
      case enums.publicKey.x25519: {
        const ephemeralPublicKey = bytes.subarray(read, read + 32); read += ephemeralPublicKey.length;
        const C = new ECDHXSymmetricKey(); C.read(bytes.subarray(read));
        return { ephemeralPublicKey, C };
      }
      default:
        throw new UnsupportedError('Unknown public key encryption algorithm.');
    }
  }

  /**
   * Convert params to MPI and serializes them in the proper order
   * @param {module:enums.publicKey} algo - The public key algorithm
   * @param {Object} params - The key parameters indexed by name
   * @returns {Uint8Array} The array containing the MPIs.
   */
  function serializeParams(algo, params) {
    // Some algorithms do not rely on MPIs to store the binary params
    const algosWithNativeRepresentation = new Set([enums.publicKey.ed25519, enums.publicKey.x25519]);
    const orderedParams = Object.keys(params).map(name => {
      const param = params[name];
      if (!util.isUint8Array(param)) return param.write();
      return algosWithNativeRepresentation.has(algo) ? param : util.uint8ArrayToMPI(param);
    });
    return util.concatUint8Array(orderedParams);
  }

  /**
   * Generate algorithm-specific key parameters
   * @param {module:enums.publicKey} algo - The public key algorithm
   * @param {Integer} bits - Bit length for RSA keys
   * @param {module:type/oid} oid - Object identifier for ECC keys
   * @returns {Promise<{ publicParams: {Object}, privateParams: {Object} }>} The parameters referenced by name.
   * @async
   */
  function generateParams(algo, bits, oid) {
    switch (algo) {
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaEncryptSign:
      case enums.publicKey.rsaSign: {
        return publicKey.rsa.generate(bits, 65537).then(({ n, e, d, p, q, u }) => ({
          privateParams: { d, p, q, u },
          publicParams: { n, e }
        }));
      }
      case enums.publicKey.ecdsa:
        return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
          privateParams: { d: secret },
          publicParams: { oid: new OID(oid), Q }
        }));
      case enums.publicKey.eddsaLegacy:
        return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
          privateParams: { seed: secret },
          publicParams: { oid: new OID(oid), Q }
        }));
      case enums.publicKey.ecdh:
        return publicKey.elliptic.generate(oid).then(({ oid, Q, secret, hash, cipher }) => ({
          privateParams: { d: secret },
          publicParams: {
            oid: new OID(oid),
            Q,
            kdfParams: new KDFParams({ hash, cipher })
          }
        }));
      case enums.publicKey.ed25519:
        return publicKey.elliptic.eddsa.generate(algo).then(({ A, seed }) => ({
          privateParams: { seed },
          publicParams: { A }
        }));
      case enums.publicKey.x25519:
        return publicKey.elliptic.ecdhX.generate(algo).then(({ A, k }) => ({
          privateParams: { k },
          publicParams: { A }
        }));
      case enums.publicKey.dsa:
      case enums.publicKey.elgamal:
        throw new Error('Unsupported algorithm for key generation.');
      default:
        throw new Error('Unknown public key algorithm.');
    }
  }

  /**
   * Validate algorithm-specific key parameters
   * @param {module:enums.publicKey} algo - The public key algorithm
   * @param {Object} publicParams - Algorithm-specific public key parameters
   * @param {Object} privateParams - Algorithm-specific private key parameters
   * @returns {Promise<Boolean>} Whether the parameters are valid.
   * @async
   */
  async function validateParams$8(algo, publicParams, privateParams) {
    if (!publicParams || !privateParams) {
      throw new Error('Missing key parameters');
    }
    switch (algo) {
      case enums.publicKey.rsaEncrypt:
      case enums.publicKey.rsaEncryptSign:
      case enums.publicKey.rsaSign: {
        const { n, e } = publicParams;
        const { d, p, q, u } = privateParams;
        return publicKey.rsa.validateParams(n, e, d, p, q, u);
      }
      case enums.publicKey.dsa: {
        const { p, q, g, y } = publicParams;
        const { x } = privateParams;
        return publicKey.dsa.validateParams(p, q, g, y, x);
      }
      case enums.publicKey.elgamal: {
        const { p, g, y } = publicParams;
        const { x } = privateParams;
        return publicKey.elgamal.validateParams(p, g, y, x);
      }
      case enums.publicKey.ecdsa:
      case enums.publicKey.ecdh: {
        const algoModule = publicKey.elliptic[enums.read(enums.publicKey, algo)];
        const { oid, Q } = publicParams;
        const { d } = privateParams;
        return algoModule.validateParams(oid, Q, d);
      }
      case enums.publicKey.eddsaLegacy: {
        const { Q, oid } = publicParams;
        const { seed } = privateParams;
        return publicKey.elliptic.eddsaLegacy.validateParams(oid, Q, seed);
      }
      case enums.publicKey.ed25519: {
        const { A } = publicParams;
        const { seed } = privateParams;
        return publicKey.elliptic.eddsa.validateParams(algo, A, seed);
      }
      case enums.publicKey.x25519: {
        const { A } = publicParams;
        const { k } = privateParams;
        return publicKey.elliptic.ecdhX.validateParams(algo, A, k);
      }
      default:
        throw new Error('Unknown public key algorithm.');
    }
  }

  /**
   * Generates a random byte prefix for the specified algorithm
   * See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
   * @param {module:enums.symmetric} algo - Symmetric encryption algorithm
   * @returns {Promise<Uint8Array>} Random bytes with length equal to the block size of the cipher, plus the last two bytes repeated.
   * @async
   */
  async function getPrefixRandom(algo) {
    const { blockSize } = getCipher(algo);
    const prefixrandom = await getRandomBytes(blockSize);
    const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
    return util.concat([prefixrandom, repeat]);
  }

  /**
   * Generating a session key for the specified symmetric algorithm
   * See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
   * @param {module:enums.symmetric} algo - Symmetric encryption algorithm
   * @returns {Uint8Array} Random bytes as a string to be used as a key.
   */
  function generateSessionKey(algo) {
    const { keySize } = getCipher(algo);
    return getRandomBytes(keySize);
  }

  /**
   * Get implementation of the given AEAD mode
   * @param {enums.aead} algo
   * @returns {Object}
   * @throws {Error} on invalid algo
   */
  function getAEADMode(algo) {
    const algoName = enums.read(enums.aead, algo);
    return mode[algoName];
  }

  /**
   * Check whether the given curve OID is supported
   * @param {module:type/oid} oid - EC object identifier
   * @throws {UnsupportedError} if curve is not supported
   */
  function checkSupportedCurve(oid) {
    try {
      oid.getName();
    } catch (e) {
      throw new UnsupportedError('Unknown curve OID');
    }
  }

  /**
   * Get preferred hash algo for a given elliptic algo
   * @param {module:enums.publicKey} algo - alrogithm identifier
   * @param {module:type/oid} [oid] - curve OID if needed by algo
   */
  function getPreferredCurveHashAlgo(algo, oid) {
    switch (algo) {
      case enums.publicKey.ecdsa:
      case enums.publicKey.eddsaLegacy:
        return publicKey.elliptic.getPreferredHashAlgo(oid);
      case enums.publicKey.ed25519:
        return publicKey.elliptic.eddsa.getPreferredHashAlgo(algo);
      default:
        throw new Error('Unknown elliptic signing algo');
    }
  }

  var crypto$1 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    publicKeyEncrypt: publicKeyEncrypt,
    publicKeyDecrypt: publicKeyDecrypt,
    parsePublicKeyParams: parsePublicKeyParams,
    parsePrivateKeyParams: parsePrivateKeyParams,
    parseEncSessionKeyParams: parseEncSessionKeyParams,
    serializeParams: serializeParams,
    generateParams: generateParams,
    validateParams: validateParams$8,
    getPrefixRandom: getPrefixRandom,
    generateSessionKey: generateSessionKey,
    getAEADMode: getAEADMode,
    getCipher: getCipher,
    getPreferredCurveHashAlgo: getPreferredCurveHashAlgo
  });

  /**
   * @fileoverview Provides access to all cryptographic primitives used in OpenPGP.js
   * @see module:crypto/crypto
   * @see module:crypto/signature
   * @see module:crypto/public_key
   * @see module:crypto/cipher
   * @see module:crypto/random
   * @see module:crypto/hash
   * @module crypto
   * @private
   */

  // TODO move cfb and gcm to cipher
  const mod = {
    /** @see module:crypto/cipher */
    cipher: cipher,
    /** @see module:crypto/hash */
    hash: hash,
    /** @see module:crypto/mode */
    mode: mode,
    /** @see module:crypto/public_key */
    publicKey: publicKey,
    /** @see module:crypto/signature */
    signature: signature,
    /** @see module:crypto/random */
    random: random,
    /** @see module:crypto/pkcs1 */
    pkcs1: pkcs1,
    /** @see module:crypto/pkcs5 */
    pkcs5: pkcs5,
    /** @see module:crypto/aes_kw */
    aesKW: aesKW
  };

  Object.assign(mod, crypto$1);

  var TYPED_OK = typeof Uint8Array !== "undefined" &&
    typeof Uint16Array !== "undefined" &&
    typeof Int32Array !== "undefined";


  // reduce buffer size, avoiding mem copy
  function shrinkBuf(buf, size) {
      if (buf.length === size) {
          return buf; 
      }
      if (buf.subarray) {
          return buf.subarray(0, size); 
      }
      buf.length = size;
      return buf;
  }


  const fnTyped = {
      arraySet: function (dest, src, src_offs, len, dest_offs) {
          if (src.subarray && dest.subarray) {
              dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
              return;
          }
          // Fallback to ordinary array
          for (let i = 0; i < len; i++) {
              dest[dest_offs + i] = src[src_offs + i];
          }
      },
      // Join array of chunks to single array.
      flattenChunks: function (chunks) {
          let i, l, len, pos, chunk;

          // calculate data length
          len = 0;
          for (i = 0, l = chunks.length; i < l; i++) {
              len += chunks[i].length;
          }

          // join chunks
          const result = new Uint8Array(len);
          pos = 0;
          for (i = 0, l = chunks.length; i < l; i++) {
              chunk = chunks[i];
              result.set(chunk, pos);
              pos += chunk.length;
          }

          return result;
      }
  };

  const fnUntyped = {
      arraySet: function (dest, src, src_offs, len, dest_offs) {
          for (let i = 0; i < len; i++) {
              dest[dest_offs + i] = src[src_offs + i];
          }
      },
      // Join array of chunks to single array.
      flattenChunks: function (chunks) {
          return [].concat.apply([], chunks);
      }
  };


  // Enable/Disable typed arrays use, for testing
  //

  let Buf8 = TYPED_OK ? Uint8Array : Array;
  let Buf16 = TYPED_OK ? Uint16Array : Array;
  let Buf32 = TYPED_OK ? Int32Array : Array;
  let flattenChunks = TYPED_OK ? fnTyped.flattenChunks : fnUntyped.flattenChunks;
  let arraySet = TYPED_OK ? fnTyped.arraySet : fnUntyped.arraySet;

  // (C) 1995-2013 Jean-loup Gailly and Mark Adler
  // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
  //
  // This software is provided 'as-is', without any express or implied
  // warranty. In no event will the authors be held liable for any damages
  // arising from the use of this software.
  //
  // Permission is granted to anyone to use this software for any purpose,
  // including commercial applications, and to alter it and redistribute it
  // freely, subject to the following restrictions:
  //
  // 1. The origin of this software must not be misrepresented; you must not
  //   claim that you wrote the original software. If you use this software
  //   in a product, an acknowledgment in the product documentation would be
  //   appreciated but is not required.
  // 2. Altered source versions must be plainly marked as such, and must not be
  //   misrepresented as being the original software.
  // 3. This notice may not be removed or altered from any source distribution.

  /* Allowed flush values; see deflate() and inflate() below for details */
  const Z_NO_FLUSH =         0;
  const Z_PARTIAL_FLUSH =    1;
  const Z_SYNC_FLUSH =       2;
  const Z_FULL_FLUSH =       3;
  const Z_FINISH =           4;
  const Z_BLOCK =            5;
  const Z_TREES =            6;

  /* Return codes for the compression/decompression functions. Negative values
   * are errors, positive values are used for special but normal events.
   */
  const Z_OK =               0;
  const Z_STREAM_END =       1;
  const Z_NEED_DICT =        2;
  const Z_STREAM_ERROR =    -2;
  const Z_DATA_ERROR =      -3;
  //export const Z_MEM_ERROR =     -4;
  const Z_BUF_ERROR =       -5;
  const Z_DEFAULT_COMPRESSION =   -1;


  const Z_FILTERED =               1;
  const Z_HUFFMAN_ONLY =           2;
  const Z_RLE =                    3;
  const Z_FIXED =                  4;
  const Z_DEFAULT_STRATEGY =       0;

  /* Possible values of the data_type field (though see inflate()) */
  const Z_BINARY =                 0;
  const Z_TEXT =                   1;
  //export const Z_ASCII =                1; // = Z_TEXT (deprecated)
  const Z_UNKNOWN =                2;

  /* The deflate compression method */
  const Z_DEFLATED =               8;
  //export const Z_NULL =                 null // Use -1 or null inline, depending on var type

  /*============================================================================*/


  function zero$1(buf) {
      let len = buf.length; while (--len >= 0) {
          buf[len] = 0; 
      } 
  }

  // From zutil.h

  const STORED_BLOCK = 0;
  const STATIC_TREES = 1;
  const DYN_TREES    = 2;
  /* The three kinds of block type */

  const MIN_MATCH    = 3;
  const MAX_MATCH    = 258;
  /* The minimum and maximum match lengths */

  // From deflate.h
  /* ===========================================================================
   * Internal compression state.
   */

  const LENGTH_CODES  = 29;
  /* number of length codes, not counting the special END_BLOCK code */

  const LITERALS      = 256;
  /* number of literal bytes 0..255 */

  const L_CODES       = LITERALS + 1 + LENGTH_CODES;
  /* number of Literal or Length codes, including the END_BLOCK code */

  const D_CODES       = 30;
  /* number of distance codes */

  const BL_CODES      = 19;
  /* number of codes used to transfer the bit lengths */

  const HEAP_SIZE     = 2 * L_CODES + 1;
  /* maximum heap size */

  const MAX_BITS      = 15;
  /* All codes must not exceed MAX_BITS bits */

  const Buf_size      = 16;
  /* size of bit buffer in bi_buf */


  /* ===========================================================================
   * Constants
   */

  const MAX_BL_BITS = 7;
  /* Bit length codes must not exceed MAX_BL_BITS bits */

  const END_BLOCK   = 256;
  /* end of block literal code */

  const REP_3_6     = 16;
  /* repeat previous bit length 3-6 times (2 bits of repeat count) */

  const REPZ_3_10   = 17;
  /* repeat a zero length 3-10 times  (3 bits of repeat count) */

  const REPZ_11_138 = 18;
  /* repeat a zero length 11-138 times  (7 bits of repeat count) */

  /* eslint-disable comma-spacing,array-bracket-spacing */
  const extra_lbits =   /* extra bits for each length code */
    [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];

  const extra_dbits =   /* extra bits for each distance code */
    [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];

  const extra_blbits =  /* extra bits for each bit length code */
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];

  const bl_order =
    [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
  /* eslint-enable comma-spacing,array-bracket-spacing */

  /* The lengths of the bit length codes are sent in order of decreasing
   * probability, to avoid transmitting the lengths for unused bit length codes.
   */

  /* ===========================================================================
   * Local data. These are initialized only once.
   */

  // We pre-fill arrays with 0 to avoid uninitialized gaps

  const DIST_CODE_LEN = 512; /* see definition of array dist_code below */

  // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1
  const static_ltree  = new Array((L_CODES + 2) * 2);
  zero$1(static_ltree);
  /* The static literal tree. Since the bit lengths are imposed, there is no
   * need for the L_CODES extra codes used during heap construction. However
   * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
   * below).
   */

  const static_dtree  = new Array(D_CODES * 2);
  zero$1(static_dtree);
  /* The static distance tree. (Actually a trivial tree since all codes use
   * 5 bits.)
   */

  const _dist_code    = new Array(DIST_CODE_LEN);
  zero$1(_dist_code);
  /* Distance codes. The first 256 values correspond to the distances
   * 3 .. 258, the last 256 values correspond to the top 8 bits of
   * the 15 bit distances.
   */

  const _length_code  = new Array(MAX_MATCH - MIN_MATCH + 1);
  zero$1(_length_code);
  /* length code for each normalized match length (0 == MIN_MATCH) */

  const base_length   = new Array(LENGTH_CODES);
  zero$1(base_length);
  /* First normalized length for each code (0 = MIN_MATCH) */

  const base_dist     = new Array(D_CODES);
  zero$1(base_dist);
  /* First normalized distance for each code (0 = distance of 1) */


  function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {

      this.static_tree  = static_tree;  /* static tree or NULL */
      this.extra_bits   = extra_bits;   /* extra bits for each code or NULL */
      this.extra_base   = extra_base;   /* base index for extra_bits */
      this.elems        = elems;        /* max number of elements in the tree */
      this.max_length   = max_length;   /* max bit length for the codes */

      // show if `static_tree` has data or dummy - needed for monomorphic objects
      this.has_stree    = static_tree && static_tree.length;
  }


  let static_l_desc;
  let static_d_desc;
  let static_bl_desc;


  function TreeDesc(dyn_tree, stat_desc) {
      this.dyn_tree = dyn_tree;     /* the dynamic tree */
      this.max_code = 0;            /* largest code with non zero frequency */
      this.stat_desc = stat_desc;   /* the corresponding static tree */
  }



  function d_code(dist) {
      return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
  }


  /* ===========================================================================
   * Output a short LSB first on the stream.
   * IN assertion: there is enough room in pendingBuf.
   */
  function put_short(s, w) {
  //    put_byte(s, (uch)((w) & 0xff));
  //    put_byte(s, (uch)((ush)(w) >> 8));
      s.pending_buf[s.pending++] = w & 0xff;
      s.pending_buf[s.pending++] = w >>> 8 & 0xff;
  }


  /* ===========================================================================
   * Send a value on a given number of bits.
   * IN assertion: length <= 16 and value fits in length bits.
   */
  function send_bits(s, value, length) {
      if (s.bi_valid > Buf_size - length) {
          s.bi_buf |= value << s.bi_valid & 0xffff;
          put_short(s, s.bi_buf);
          s.bi_buf = value >> Buf_size - s.bi_valid;
          s.bi_valid += length - Buf_size;
      } else {
          s.bi_buf |= value << s.bi_valid & 0xffff;
          s.bi_valid += length;
      }
  }


  function send_code(s, c, tree) {
      send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/);
  }


  /* ===========================================================================
   * Reverse the first len bits of a code, using straightforward code (a faster
   * method would use a table)
   * IN assertion: 1 <= len <= 15
   */
  function bi_reverse(code, len) {
      let res = 0;
      do {
          res |= code & 1;
          code >>>= 1;
          res <<= 1;
      } while (--len > 0);
      return res >>> 1;
  }


  /* ===========================================================================
   * Flush the bit buffer, keeping at most 7 bits in it.
   */
  function bi_flush(s) {
      if (s.bi_valid === 16) {
          put_short(s, s.bi_buf);
          s.bi_buf = 0;
          s.bi_valid = 0;

      } else if (s.bi_valid >= 8) {
          s.pending_buf[s.pending++] = s.bi_buf & 0xff;
          s.bi_buf >>= 8;
          s.bi_valid -= 8;
      }
  }


  /* ===========================================================================
   * Compute the optimal bit lengths for a tree and update the total bit length
   * for the current block.
   * IN assertion: the fields freq and dad are set, heap[heap_max] and
   *    above are the tree nodes sorted by increasing frequency.
   * OUT assertions: the field len is set to the optimal bit length, the
   *     array bl_count contains the frequencies for each bit length.
   *     The length opt_len is updated; static_len is also updated if stree is
   *     not null.
   */
  function gen_bitlen(s, desc)
  //    deflate_state *s;
  //    tree_desc *desc;    /* the tree descriptor */
  {
      const tree            = desc.dyn_tree;
      const max_code        = desc.max_code;
      const stree           = desc.stat_desc.static_tree;
      const has_stree       = desc.stat_desc.has_stree;
      const extra           = desc.stat_desc.extra_bits;
      const base            = desc.stat_desc.extra_base;
      const max_length      = desc.stat_desc.max_length;
      let h;              /* heap index */
      let n, m;           /* iterate over the tree elements */
      let bits;           /* bit length */
      let xbits;          /* extra bits */
      let f;              /* frequency */
      let overflow = 0;   /* number of elements with bit length too large */

      for (bits = 0; bits <= MAX_BITS; bits++) {
          s.bl_count[bits] = 0;
      }

      /* In a first pass, compute the optimal bit lengths (which may
     * overflow in the case of the bit length tree).
     */
      tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */

      for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {
          n = s.heap[h];
          bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;
          if (bits > max_length) {
              bits = max_length;
              overflow++;
          }
          tree[n * 2 + 1]/*.Len*/ = bits;
          /* We overwrite tree[n].Dad which is no longer needed */

          if (n > max_code) {
              continue; 
          } /* not a leaf node */

          s.bl_count[bits]++;
          xbits = 0;
          if (n >= base) {
              xbits = extra[n - base];
          }
          f = tree[n * 2]/*.Freq*/;
          s.opt_len += f * (bits + xbits);
          if (has_stree) {
              s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits);
          }
      }
      if (overflow === 0) {
          return; 
      }

      // Trace((stderr,"\nbit length overflow\n"));
      /* This happens for example on obj2 and pic of the Calgary corpus */

      /* Find the first bit length which could increase: */
      do {
          bits = max_length - 1;
          while (s.bl_count[bits] === 0) {
              bits--; 
          }
          s.bl_count[bits]--;      /* move one leaf down the tree */
          s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
          s.bl_count[max_length]--;
          /* The brother of the overflow item also moves one step up,
       * but this does not affect bl_count[max_length]
       */
          overflow -= 2;
      } while (overflow > 0);

      /* Now recompute all bit lengths, scanning in increasing frequency.
     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
     * lengths instead of fixing only the wrong ones. This idea is taken
     * from 'ar' written by Haruhiko Okumura.)
     */
      for (bits = max_length; bits !== 0; bits--) {
          n = s.bl_count[bits];
          while (n !== 0) {
              m = s.heap[--h];
              if (m > max_code) {
                  continue; 
              }
              if (tree[m * 2 + 1]/*.Len*/ !== bits) {
                  // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
                  s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/;
                  tree[m * 2 + 1]/*.Len*/ = bits;
              }
              n--;
          }
      }
  }


  /* ===========================================================================
   * Generate the codes for a given tree and bit counts (which need not be
   * optimal).
   * IN assertion: the array bl_count contains the bit length statistics for
   * the given tree and the field len is set for all tree elements.
   * OUT assertion: the field code is set for all tree elements of non
   *     zero code length.
   */
  function gen_codes(tree, max_code, bl_count)
  //    ct_data *tree;             /* the tree to decorate */
  //    int max_code;              /* largest code with non zero frequency */
  //    ushf *bl_count;            /* number of codes at each bit length */
  {
      const next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */
      let code = 0;              /* running code value */
      let bits;                  /* bit index */
      let n;                     /* code index */

      /* The distribution counts are first used to generate the code values
     * without bit reversal.
     */
      for (bits = 1; bits <= MAX_BITS; bits++) {
          next_code[bits] = code = code + bl_count[bits - 1] << 1;
      }
      /* Check that the bit counts in bl_count are consistent. The last code
     * must be all ones.
     */
      //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
      //        "inconsistent bit counts");
      //Tracev((stderr,"\ngen_codes: max_code %d ", max_code));

      for (n = 0;  n <= max_code; n++) {
          const len = tree[n * 2 + 1]/*.Len*/;
          if (len === 0) {
              continue; 
          }
          /* Now reverse the bits */
          tree[n * 2]/*.Code*/ = bi_reverse(next_code[len]++, len);

      //Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
      //     n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
      }
  }


  /* ===========================================================================
   * Initialize the various 'constant' tables.
   */
  function tr_static_init() {
      let n;        /* iterates over tree elements */
      let bits;     /* bit counter */
      let length;   /* length value */
      let code;     /* code value */
      let dist;     /* distance index */
      const bl_count = new Array(MAX_BITS + 1);
      /* number of codes at each bit length for an optimal tree */

      // do check in _tr_init()
      //if (static_init_done) return;

      /* For some embedded targets, global variables are not initialized: */
      /*#ifdef NO_INIT_GLOBAL_POINTERS
    static_l_desc.static_tree = static_ltree;
    static_l_desc.extra_bits = extra_lbits;
    static_d_desc.static_tree = static_dtree;
    static_d_desc.extra_bits = extra_dbits;
    static_bl_desc.extra_bits = extra_blbits;
  #endif*/

      /* Initialize the mapping length (0..255) -> length code (0..28) */
      length = 0;
      for (code = 0; code < LENGTH_CODES - 1; code++) {
          base_length[code] = length;
          for (n = 0; n < 1 << extra_lbits[code]; n++) {
              _length_code[length++] = code;
          }
      }
      //Assert (length == 256, "tr_static_init: length != 256");
      /* Note that the length 255 (match length 258) can be represented
     * in two different ways: code 284 + 5 bits or code 285, so we
     * overwrite length_code[255] to use the best encoding:
     */
      _length_code[length - 1] = code;

      /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
      dist = 0;
      for (code = 0; code < 16; code++) {
          base_dist[code] = dist;
          for (n = 0; n < 1 << extra_dbits[code]; n++) {
              _dist_code[dist++] = code;
          }
      }
      //Assert (dist == 256, "tr_static_init: dist != 256");
      dist >>= 7; /* from now on, all distances are divided by 128 */
      for (; code < D_CODES; code++) {
          base_dist[code] = dist << 7;
          for (n = 0; n < 1 << extra_dbits[code] - 7; n++) {
              _dist_code[256 + dist++] = code;
          }
      }
      //Assert (dist == 256, "tr_static_init: 256+dist != 512");

      /* Construct the codes of the static literal tree */
      for (bits = 0; bits <= MAX_BITS; bits++) {
          bl_count[bits] = 0;
      }

      n = 0;
      while (n <= 143) {
          static_ltree[n * 2 + 1]/*.Len*/ = 8;
          n++;
          bl_count[8]++;
      }
      while (n <= 255) {
          static_ltree[n * 2 + 1]/*.Len*/ = 9;
          n++;
          bl_count[9]++;
      }
      while (n <= 279) {
          static_ltree[n * 2 + 1]/*.Len*/ = 7;
          n++;
          bl_count[7]++;
      }
      while (n <= 287) {
          static_ltree[n * 2 + 1]/*.Len*/ = 8;
          n++;
          bl_count[8]++;
      }
      /* Codes 286 and 287 do not exist, but we must include them in the
     * tree construction to get a canonical Huffman tree (longest code
     * all ones)
     */
      gen_codes(static_ltree, L_CODES + 1, bl_count);

      /* The static distance tree is trivial: */
      for (n = 0; n < D_CODES; n++) {
          static_dtree[n * 2 + 1]/*.Len*/ = 5;
          static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5);
      }

      // Now data ready and we can init static trees
      static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);
      static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS);
      static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0,         BL_CODES, MAX_BL_BITS);

      //static_init_done = true;
  }


  /* ===========================================================================
   * Initialize a new block.
   */
  function init_block(s) {
      let n; /* iterates over tree elements */

      /* Initialize the trees. */
      for (n = 0; n < L_CODES;  n++) {
          s.dyn_ltree[n * 2]/*.Freq*/ = 0; 
      }
      for (n = 0; n < D_CODES;  n++) {
          s.dyn_dtree[n * 2]/*.Freq*/ = 0; 
      }
      for (n = 0; n < BL_CODES; n++) {
          s.bl_tree[n * 2]/*.Freq*/ = 0; 
      }

      s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1;
      s.opt_len = s.static_len = 0;
      s.last_lit = s.matches = 0;
  }


  /* ===========================================================================
   * Flush the bit buffer and align the output on a byte boundary
   */
  function bi_windup(s) {
      if (s.bi_valid > 8) {
          put_short(s, s.bi_buf);
      } else if (s.bi_valid > 0) {
      //put_byte(s, (Byte)s->bi_buf);
          s.pending_buf[s.pending++] = s.bi_buf;
      }
      s.bi_buf = 0;
      s.bi_valid = 0;
  }

  /* ===========================================================================
   * Copy a stored block, storing first the length and its
   * one's complement if requested.
   */
  function copy_block(s, buf, len, header)
  //DeflateState *s;
  //charf    *buf;    /* the input data */
  //unsigned len;     /* its length */
  //int      header;  /* true if block header must be written */
  {
      bi_windup(s);        /* align on byte boundary */

      if (header) {
          put_short(s, len);
          put_short(s, ~len);
      }
      //  while (len--) {
      //    p