text: import { RSA } from './rsa';
  import { IllegalArgumentError, IllegalStateError, SecurityError } from '../other/errors';
  import { BigNumber } from '../bignum/bignum';
  import { getRandomValues } from '../other/get-random-values';
  export class RSA_OAEP {
      constructor(key, hash, label) {
          this.rsa = new RSA(key);
          this.hash = hash;
          if (label !== undefined) {
              this.label = label.length > 0 ? label : null;
          }
          else {
              this.label = null;
          }
      }
      encrypt(data, random) {
          const key_size = Math.ceil(this.rsa.key[0].bitLength / 8);
          const hash_size = this.hash.HASH_SIZE;
          const data_length = data.byteLength || data.length || 0;
          const ps_length = key_size - data_length - 2 * hash_size - 2;
          if (data_length > key_size - 2 * this.hash.HASH_SIZE - 2)
              throw new IllegalArgumentError('data too large');
          const message = new Uint8Array(key_size);
          const seed = message.subarray(1, hash_size + 1);
          const data_block = message.subarray(hash_size + 1);
          data_block.set(data, hash_size + ps_length + 1);
          data_block.set(this.hash.process(this.label || new Uint8Array(0)).finish().result, 0);
          data_block[hash_size + ps_length] = 1;
          if (random !== undefined) {
              if (seed.length !== random.length)
                  throw new IllegalArgumentError('random size must equal the hash size');
              seed.set(random);
          }
          else {
              getRandomValues(seed);
          }
          const data_block_mask = this.RSA_MGF1_generate(seed, data_block.length);
          for (let i = 0; i < data_block.length; i++)
              data_block[i] ^= data_block_mask[i];
          const seed_mask = this.RSA_MGF1_generate(data_block, seed.length);
          for (let i = 0; i < seed.length; i++)
              seed[i] ^= seed_mask[i];
          this.rsa.encrypt(new BigNumber(message));
          return new Uint8Array(this.rsa.result);
      }
      decrypt(data) {
          if (!this.rsa.key)
              throw new IllegalStateError('no key is associated with the instance');
          const key_size = Math.ceil(this.rsa.key[0].bitLength / 8);
          const hash_size = this.hash.HASH_SIZE;
          const data_length = data.byteLength || data.length || 0;
          if (data_length !== key_size)
              throw new IllegalArgumentError('bad data');
          this.rsa.decrypt(new BigNumber(data));
          const z = this.rsa.result[0];
          const seed = this.rsa.result.subarray(1, hash_size + 1);
          const data_block = this.rsa.result.subarray(hash_size + 1);
          if (z !== 0)
              throw new SecurityError('decryption failed');
          const seed_mask = this.RSA_MGF1_generate(data_block, seed.length);
          for (let i = 0; i < seed.length; i++)
              seed[i] ^= seed_mask[i];
          const data_block_mask = this.RSA_MGF1_generate(seed, data_block.length);
          for (let i = 0; i < data_block.length; i++)
              data_block[i] ^= data_block_mask[i];
          const lhash = this.hash
              .reset()
              .process(this.label || new Uint8Array(0))
              .finish().result;
          for (let i = 0; i < hash_size; i++) {
              if (lhash[i] !== data_block[i])
                  throw new SecurityError('decryption failed');
          }
          let ps_end = hash_size;
          for (; ps_end < data_block.length; ps_end++) {
              const psz = data_block[ps_end];
              if (psz === 1)
                  break;
              if (psz !== 0)
                  throw new SecurityError('decryption failed');
          }
          if (ps_end === data_block.length)
              throw new SecurityError('decryption failed');
          this.rsa.result = data_block.subarray(ps_end + 1);
          return new Uint8Array(this.rsa.result);
      }
      RSA_MGF1_generate(seed, length = 0) {
          const hash_size = this.hash.HASH_SIZE;
          //    if ( length > (hash_size * 0x100000000) )
          //        throw new IllegalArgumentError("mask length too large");
          const mask = new Uint8Array(length);
          const counter = new Uint8Array(4);
          const chunks = Math.ceil(length / hash_size);
          for (let i = 0; i < chunks; i++) {
              (counter[0] = i >>> 24), (counter[1] = (i >>> 16) & 255), (counter[2] = (i >>> 8) & 255), (counter[3] = i & 255);
              const submask = mask.subarray(i * hash_size);
              let chunk = this.hash
                  .reset()
                  .process(seed)
                  .process(counter)
                  .finish().result;
              if (chunk.length > submask.length)
                  chunk = chunk.subarray(0, submask.length);
              submask.set(chunk);
          }
          return mask;
      }
  }
  export class RSA_PSS {
      constructor(key, hash, saltLength = 4) {
          this.rsa = new RSA(key);
          this.hash = hash;
          this.saltLength = saltLength;
          if (this.saltLength < 0)
              throw new TypeError('saltLength should be a non-negative number');
          if (this.rsa.key !== null &&
              Math.ceil((this.rsa.key[0].bitLength - 1) / 8) < this.hash.HASH_SIZE + this.saltLength + 2)
              throw new SyntaxError('saltLength is too large');
      }
      sign(data, random) {
          const key_bits = this.rsa.key[0].bitLength;
          const hash_size = this.hash.HASH_SIZE;
          const message_length = Math.ceil((key_bits - 1) / 8);
          const salt_length = this.saltLength;
          const ps_length = message_length - salt_length - hash_size - 2;
          const message = new Uint8Array(message_length);
          const h_block = message.subarray(message_length - hash_size - 1, message_length - 1);
          const d_block = message.subarray(0, message_length - hash_size - 1);
          const d_salt = d_block.subarray(ps_length + 1);
          const m_block = new Uint8Array(8 + hash_size + salt_length);
          const m_hash = m_block.subarray(8, 8 + hash_size);
          const m_salt = m_block.subarray(8 + hash_size);
          m_hash.set(this.hash.process(data).finish().result);
          if (salt_length > 0) {
              if (random !== undefined) {
                  if (m_salt.length !== random.length)
                      throw new IllegalArgumentError('random size must equal the salt size');
                  m_salt.set(random);
              }
              else {
                  getRandomValues(m_salt);
              }
          }
          d_block[ps_length] = 1;
          d_salt.set(m_salt);
          h_block.set(this.hash
              .reset()
              .process(m_block)
              .finish().result);
          const d_block_mask = this.RSA_MGF1_generate(h_block, d_block.length);
          for (let i = 0; i < d_block.length; i++)
              d_block[i] ^= d_block_mask[i];
          message[message_length - 1] = 0xbc;
          const zbits = 8 * message_length - key_bits + 1;
          if (zbits % 8)
              message[0] &= 0xff >>> zbits;
          this.rsa.decrypt(new BigNumber(message));
          return this.rsa.result;
      }
      verify(signature, data) {
          const key_bits = this.rsa.key[0].bitLength;
          const hash_size = this.hash.HASH_SIZE;
          const message_length = Math.ceil((key_bits - 1) / 8);
          const salt_length = this.saltLength;
          const ps_length = message_length - salt_length - hash_size - 2;
          this.rsa.encrypt(new BigNumber(signature));
          const message = this.rsa.result;
          if (message[message_length - 1] !== 0xbc)
              throw new SecurityError('bad signature');
          const h_block = message.subarray(message_length - hash_size - 1, message_length - 1);
          const d_block = message.subarray(0, message_length - hash_size - 1);
          const d_salt = d_block.subarray(ps_length + 1);
          const zbits = 8 * message_length - key_bits + 1;
          if (zbits % 8 && message[0] >>> (8 - zbits))
              throw new SecurityError('bad signature');
          const d_block_mask = this.RSA_MGF1_generate(h_block, d_block.length);
          for (let i = 0; i < d_block.length; i++)
              d_block[i] ^= d_block_mask[i];
          if (zbits % 8)
              message[0] &= 0xff >>> zbits;
          for (let i = 0; i < ps_length; i++) {
              if (d_block[i] !== 0)
                  throw new SecurityError('bad signature');
          }
          if (d_block[ps_length] !== 1)
              throw new SecurityError('bad signature');
          const m_block = new Uint8Array(8 + hash_size + salt_length);
          const m_hash = m_block.subarray(8, 8 + hash_size);
          const m_salt = m_block.subarray(8 + hash_size);
          m_hash.set(this.hash
              .reset()
              .process(data)
              .finish().result);
          m_salt.set(d_salt);
          const h_block_verify = this.hash
              .reset()
              .process(m_block)
              .finish().result;
          for (let i = 0; i < hash_size; i++) {
              if (h_block[i] !== h_block_verify[i])
                  throw new SecurityError('bad signature');
          }
      }
      RSA_MGF1_generate(seed, length = 0) {
          const hash_size = this.hash.HASH_SIZE;
          //    if ( length > (hash_size * 0x100000000) )
          //        throw new IllegalArgumentError("mask length too large");
          const mask = new Uint8Array(length);
          const counter = new Uint8Array(4);
          const chunks = Math.ceil(length / hash_size);
          for (let i = 0; i < chunks; i++) {
              (counter[0] = i >>> 24), (counter[1] = (i >>> 16) & 255), (counter[2] = (i >>> 8) & 255), (counter[3] = i & 255);
              const submask = mask.subarray(i * hash_size);
              let chunk = this.hash
                  .reset()
                  .process(seed)
                  .process(counter)
                  .finish().result;
              if (chunk.length > submask.length)
                  chunk = chunk.subarray(0, submask.length);
              submask.set(chunk);
          }
          return mask;
      }
  }
  export class RSA_PKCS1_v1_5 {
      constructor(key, hash) {
          this.rsa = new RSA(key);
          this.hash = hash;
      }
      sign(data) {
          if (!this.rsa.key) {
              throw new IllegalStateError('no key is associated with the instance');
          }
          const prefix = getHashPrefix(this.hash);
          const hash_size = this.hash.HASH_SIZE;
          const t_len = prefix.length + hash_size;
          const k = (this.rsa.key[0].bitLength + 7) >> 3;
          if (k < t_len + 11) {
              throw new Error('Message too long');
          }
          const m_hash = new Uint8Array(hash_size);
          m_hash.set(this.hash.process(data).finish().result);
          // EM = 0x00 || 0x01 || PS || 0x00 || T
          const em = new Uint8Array(k);
          let i = 0;
          em[i++] = 0; // 0x00
          em[i++] = 1; // 0x01
          // PS
          for (i; i < k - t_len - 1; i++) {
              em[i] = 0xff;
          }
          em[i++] = 0;
          em.set(prefix, i); // 0x00
          // T
          em.set(m_hash, em.length - hash_size);
          this.rsa.decrypt(new BigNumber(em));
          return this.rsa.result;
      }
      verify(signature, data) {
          const prefix = getHashPrefix(this.hash);
          const hash_size = this.hash.HASH_SIZE;
          const t_len = prefix.length + hash_size;
          const k = (this.rsa.key[0].bitLength + 7) >> 3;
          if (k < t_len + 11) {
              throw new SecurityError('Bad signature');
          }
          this.rsa.encrypt(new BigNumber(signature));
          const m_hash = new Uint8Array(hash_size);
          m_hash.set(this.hash.process(data).finish().result);
          let res = 1;
          // EM = 0x00 || 0x01 || PS || 0x00 || T
          const decryptedSignature = this.rsa.result;
          let i = 0;
          res &= decryptedSignature[i++] === 0 ? 1 : 0; // 0x00
          res &= decryptedSignature[i++] === 1 ? 1 : 0; // 0x01
          // PS
          for (i; i < k - t_len - 1; i++) {
              res &= decryptedSignature[i] === 0xff ? 1 : 0;
          }
          res &= decryptedSignature[i++] === 0 ? 1 : 0; // 0x00
          // T
          let j = 0;
          let n = i + prefix.length;
          // prefix
          for (i; i < n; i++) {
              res &= decryptedSignature[i] === prefix[j++] ? 1 : 0;
          }
          j = 0;
          n = i + m_hash.length;
          // hash
          for (i; i < n; i++) {
              res &= decryptedSignature[i] === m_hash[j++] ? 1 : 0;
          }
          if (!res) {
              throw new SecurityError('Bad signature');
          }
      }
  }
  const HASH_PREFIXES = {
      sha1: new Uint8Array([0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14]),
      sha256: new Uint8Array([
          0x30,
          0x31,
          0x30,
          0x0d,
          0x06,
          0x09,
          0x60,
          0x86,
          0x48,
          0x01,
          0x65,
          0x03,
          0x04,
          0x02,
          0x01,
          0x05,
          0x00,
          0x04,
          0x20,
      ]),
      sha384: new Uint8Array([
          0x30,
          0x41,
          0x30,
          0x0d,
          0x06,
          0x09,
          0x60,
          0x86,
          0x48,
          0x01,
          0x65,
          0x03,
          0x04,
          0x02,
          0x02,
          0x05,
          0x00,
          0x04,
          0x30,
      ]),
      sha512: new Uint8Array([
          0x30,
          0x51,
          0x30,
          0x0d,
          0x06,
          0x09,
          0x60,
          0x86,
          0x48,
          0x01,
          0x65,
          0x03,
          0x04,
          0x02,
          0x03,
          0x05,
          0x00,
          0x04,
          0x40,
      ]),
  };
  function getHashPrefix(hash) {
      const prefix = HASH_PREFIXES[hash.NAME];
      if (!prefix) {
          throw new Error("Cannot get hash prefix for hash algorithm '" + hash.NAME + "'");
      }
      return prefix;
  }
  