

function mailcloak_encryption() {

  // create a container to prevent variable name conflicts
  var encryptionObj = new Object();

  var bigint = new mailcloak_bigint();


  String.prototype.trim = function() {

  //skip leading and trailing whitespace
  //and return everything in between
    var x=this;
    x=x.replace(/^\s*(.*)/, "$1");
    x=x.replace(/(.*?)\s*$/, "$1");
    return x;
  }


  function cloak(string, privkey) {

    if(string != "" && privkey != "") {

      plus = privkey.indexOf("+");
      n = privkey.substr(0, plus);
      e = privkey.substr(plus+1,privkey.length - plus - 1);

      string = string.replace(/^[\s]+$/g,"");
      return encrypt(string, n, e);

    }

  }


  function uncloak(string, pubkey) {

    if(string != "" && pubkey != "") {

      plus = pubkey.indexOf("+");
      n = pubkey.substr(0, plus);
      d = pubkey.substr(plus+1,pubkey.length - plus - 1);

      return decrypt(string, n, d);

    }

  }


  function encrypt(m, n, e, base, blocklen) {

    // RSA encryption

    // m - message (ascii string)
    // n - public modulus (decimal string)
    // e - public exponent (decimal string)
    // base - base of encrypted string
    // returns encrypted result (decimal string)

    // base of encrypted output
    // must be same for encryption and decryption

    if(!base)base = 16;

    // encrypted output block size
    // usually 8 or 16
    // (8 only works with base 64 or 128-bit and larger keys)

    if(!blocklen)blocklen = 16;

    // how many input characters can we do at a time
    // - this part may need improving
    // (varies with base, n.length, blocklen)

    if(n.length == 64 && base == 64) {
      chunklen = 2; // base = 64, blocklen = 16, 512 bits
    }
    else if(n.length == 64 && base == 16) {
      chunklen = 2; // base = 16, blocklen = 16, 512 bits
    }
    else if(n.length == 64 && base == 10) {
      chunklen = 1; // base = 10, blocklen = 16, 512 bits
    }
    else if(n.length == 32 && base == 64) {
      chunklen = 3; // base = 64, blocklen = 16, 256 bits
    }
    else if(n.length == 32 && base == 16) {
      chunklen = 2; // base = 16, blocklen = 16, 256 bits
    }
    else if(n.length == 32 && base == 10) {
      chunklen = 2; // base = 10, blocklen = 16, 256 bits
    }
    else if(n.length == 16 && base == 64) {
      chunklen = 6; // base = 64, blocklen = 16, 128 bits
    }
    else if(n.length == 16 && base == 16) {
      chunklen = 6; // base = 16, blocklen = 16, 128 bits
    }
    else if(n.length == 16 && base == 10) {
      chunklen = 4; // base = 10, blocklen = 16, 128 bits
    }
    else {
      chunklen = 1;
    }

    var numchunks = Math.ceil(m.length / chunklen);

    var c = "";
    var n=bigint.str2bigInt(n,10,0);
    var y=bigint.str2bigInt(e,10,0);

    for(var chunk=0; chunk<numchunks; chunk++) {

      // get the next chunk to encrypt
      m2 = m.substr(chunk*chunklen,chunklen);

      // right pad the (last) input chunk
      for(ch=m2.length;ch<chunklen;ch++) {
        m2 = m2 + " ";
      }

      // do encryption
      var x=bigint.str2bigInt(m2,256,n.length);
      bigint.powMod(x,y,n);

      // left pad the encrypted output
      c2 = bigint.bigInt2str(x,base);
      for(ch=c2.length;ch<blocklen;ch++) {
        c2 = "0" + c2;
      }

      // add encrypted block to results
      c += c2;

    }

    return c;

  }


  function decrypt(c, n, d, base, blocklen) {

    // RSA decryption

    // c - encrypted message (decimal string)
    // n - public modulus (decimal string)
    // d - private exponent (decimal string)
    // base of encrypted string
    // returns decrypted result (ascii string)

    // base of encrypted input
    // must be same for encryption and decryption

    if(!base)base = 16;

    // encrypted input block size
    // usually 8 or 16
    // (8 only works with base 64 or 128-bit and larger keys)

    if(!blocklen)blocklen = 16;

    var numblocks = Math.ceil(c.length / blocklen);
    var m = "";
    var n=bigint.str2bigInt(n,10,0);
    var y=bigint.str2bigInt(d,10,0);

    for(var block=0; block<numblocks; block++) {

      // get the next block to decrypt
      var c2 = c.substr(block*blocklen,blocklen);
      var x=bigint.str2bigInt(c2,base,n.length);

      // do the decryption
      bigint.powMod(x,y,n);
      m2 = bigint.bigInt2str(x,256);

      // add decrypted chunk to results
      m += m2;

    }

    return m.trim();

  }


  // define functions as methods of object

  encryptionObj.cloak = cloak;
  encryptionObj.uncloak = uncloak;
  encryptionObj.encrypt = encrypt;
  encryptionObj.decrypt = decrypt;

  return encryptionObj;

  // end of outer function

}

// do this here to avoid multiple declarations
var mailcloak_encryption = new mailcloak_encryption();


