﻿// Depends on jsbn.js, UTF8, aesprng.js, entropy.js, aes.js
// 17-08-08 VM UTF8 encryption added
// convert a (hex) string to a bignum object
var prng;
function parseBigInt(str, r) {
    return new BigInteger(str, r);
}
function linebrk(s, n) {
    var ret = "";
    var i = 0;
    while (i + n < s.length) {
        ret += s.substring(i, i + n) + "\n";
        i += n;
    }
    return ret + s.substring(i, s.length);
}
function byte2Hex(b) {
    if (b < 0x10)
        return "0" + b.toString(16);
    else
        return b.toString(16);
}
// "empty" RSA key constructor
function RSAKey() {
    this.n = null;
    this.e = 0;
    this.d = null;
    this.p = null;
    this.q = null;
    this.dmp1 = null;
    this.dmq1 = null;
    this.coeff = null;
}
// Set the public key fields N and e from hex strings
function RSASetPublic(N, E) {
    if (N != null && E != null && N.length > 0 && E.length > 0) {
        this.n = parseBigInt(N, 16);
        this.e = parseInt(E, 16);
    }
    else
        alert("Invalid RSA public key");
}
// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
    return x.modPowInt(this.e, this.n);
}
function BytesToString(a) {
    var s = new String();
    for (var i = 0; i < a.length; i++) { s += String.fromCharCode(a[i]); }
    return s;
}
function StringToBytes(s) {
    var a = new Array();
    for (var i = 0; i < s.length; i++) a[i] = s.charCodeAt(i);
    return a;
}
function RSApadString(s) {
    var i = ((this.n.bitLength() + 7) >> 3) - s.length;
    if (i > 0) return s + BytesToString(getRandomBytes(i));
    return null;
}
function complexRSAEncrypt(text) {
    var plaintext = UTF8encode(text);
    md5_init();
    for (var i = 0; i < plaintext.length; i++) {
        md5_update(plaintext.charCodeAt(i));
    }
    md5_finish();
    var header = String.fromCharCode(1);
    header += String.fromCharCode(plaintext.length >>> 24);
    header += String.fromCharCode(plaintext.length >>> 16);
    header += String.fromCharCode(plaintext.length >>> 8);
    header += String.fromCharCode(plaintext.length & 0xFF);
    header += BytesToString(digestBits);
    /* The format of the actual message is:
    Bytes Content
    0 Byte 1 (to prevent number padding)
    1-4 Length of plaintext, big-endian order
    5-20 MD5 signature of plaintext
    21-end Plaintext
    Note that this message will be padded with random bytes
    either to this.n size for short messages or
    to an integral number of AES blocks (blockSizeInBits / 8).
    This does not include the initial vector for CBC
    encryption, which is added internally by rijndaelEncrypt.
    */
    addEntropyTime(); prng = new AESprng(keyFromEntropy());
    var RSAtext = this.padString(header + plaintext);
    var Aes = (RSAtext == null);
    if (Aes) {
        var key = getRandomBytes(32);
        RSAtext = this.padString(header + BytesToString(key));
    }
    RSAa = this.doPublic(new BigInteger(StringToBytes(RSAtext)));
    RSAh = RSAa.toString(16); if (RSAh.length & 1) RSAh = "0" + RSAh;
    if (Aes) {
        var ct = rijndaelEncrypt(header + plaintext, key, "CBC");
        return RSAh + '/' + byteArrayToHex(ct);
    }
    return RSAh;
}
// protected
RSAKey.prototype.doPublic = RSADoPublic;
RSAKey.prototype.padString = RSApadString;
// public
RSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = complexRSAEncrypt;
function RSACPZsetPublic() {
    this.setPublic("b842718e025d5d9b5fb68c14cb01e584f6187152376a53bd012b793f246a3284\nf998780b3649927e1f8a758cf2dc46c1d8e2c588f2e7b4c74cdb47aa3afef1d0\nbcb4ea4b8f360b88b7d7fb58b8125c2302a556aba8b331297c3602490fc652b1\nbc89f3addaf37848229f8d2a2d1ea43db8788e37be1b5e3f627955af04e293c1", "10001");
}
RSAKey.prototype.CPZsetPublic = RSACPZsetPublic; 
