| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- // Depends on jsbn.js and rng.js
- // Version 1.1: support utf-8 encoding in pkcs1pad2
- // convert a (hex) string to a bignum object
- import { BigInteger, nbi, parseBigInt } from "./jsbn";
- import { SecureRandom } from "./rng";
- // 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);
- // }
- function pkcs1pad1(s, n) {
- if (n < s.length + 22) {
- console.error("Message too long for RSA");
- return null;
- }
- var len = n - s.length - 6;
- var filler = "";
- for (var f = 0; f < len; f += 2) {
- filler += "ff";
- }
- var m = "0001" + filler + "00" + s;
- return parseBigInt(m, 16);
- }
- // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
- function pkcs1pad2(s, n) {
- if (n < s.length + 11) { // TODO: fix for utf-8
- console.error("Message too long for RSA");
- return null;
- }
- var ba = [];
- var i = s.length - 1;
- while (i >= 0 && n > 0) {
- var c = s.charCodeAt(i--);
- if (c < 128) { // encode using utf-8
- ba[--n] = c;
- }
- else if ((c > 127) && (c < 2048)) {
- ba[--n] = (c & 63) | 128;
- ba[--n] = (c >> 6) | 192;
- }
- else {
- ba[--n] = (c & 63) | 128;
- ba[--n] = ((c >> 6) & 63) | 128;
- ba[--n] = (c >> 12) | 224;
- }
- }
- ba[--n] = 0;
- var rng = new SecureRandom();
- var x = [];
- while (n > 2) { // random non-zero pad
- x[0] = 0;
- while (x[0] == 0) {
- rng.nextBytes(x);
- }
- ba[--n] = x[0];
- }
- ba[--n] = 2;
- ba[--n] = 0;
- return new BigInteger(ba);
- }
- // "empty" RSA key constructor
- var RSAKey = /** @class */ (function () {
- 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;
- }
- //#region PROTECTED
- // protected
- // RSAKey.prototype.doPublic = RSADoPublic;
- // Perform raw public operation on "x": return x^e (mod n)
- RSAKey.prototype.doPublic = function (x) {
- return x.modPowInt(this.e, this.n);
- };
- // RSAKey.prototype.doPrivate = RSADoPrivate;
- // Perform raw private operation on "x": return x^d (mod n)
- RSAKey.prototype.doPrivate = function (x) {
- if (this.p == null || this.q == null) {
- return x.modPow(this.d, this.n);
- }
- // TODO: re-calculate any missing CRT params
- var xp = x.mod(this.p).modPow(this.dmp1, this.p);
- var xq = x.mod(this.q).modPow(this.dmq1, this.q);
- while (xp.compareTo(xq) < 0) {
- xp = xp.add(this.p);
- }
- return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
- };
- //#endregion PROTECTED
- //#region PUBLIC
- // RSAKey.prototype.setPublic = RSASetPublic;
- // Set the public key fields N and e from hex strings
- RSAKey.prototype.setPublic = function (N, E) {
- if (N != null && E != null && N.length > 0 && E.length > 0) {
- this.n = parseBigInt(N, 16);
- this.e = parseInt(E, 16);
- }
- else {
- console.error("Invalid RSA public key");
- }
- };
- // RSAKey.prototype.encrypt = RSAEncrypt;
- // Return the PKCS#1 RSA encryption of "text" as an even-length hex string
- RSAKey.prototype.encrypt = function (text) {
- var maxLength = (this.n.bitLength() + 7) >> 3;
- var m = pkcs1pad2(text, maxLength);
- if (m == null) {
- return null;
- }
- var c = this.doPublic(m);
- if (c == null) {
- return null;
- }
- var h = c.toString(16);
- var length = h.length;
- // fix zero before result
- for (var i = 0; i < maxLength * 2 - length; i++) {
- h = "0" + h;
- }
- return h;
- };
- // RSAKey.prototype.setPrivate = RSASetPrivate;
- // Set the private key fields N, e, and d from hex strings
- RSAKey.prototype.setPrivate = function (N, E, D) {
- if (N != null && E != null && N.length > 0 && E.length > 0) {
- this.n = parseBigInt(N, 16);
- this.e = parseInt(E, 16);
- this.d = parseBigInt(D, 16);
- }
- else {
- console.error("Invalid RSA private key");
- }
- };
- // RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
- // Set the private key fields N, e, d and CRT params from hex strings
- RSAKey.prototype.setPrivateEx = function (N, E, D, P, Q, DP, DQ, C) {
- if (N != null && E != null && N.length > 0 && E.length > 0) {
- this.n = parseBigInt(N, 16);
- this.e = parseInt(E, 16);
- this.d = parseBigInt(D, 16);
- this.p = parseBigInt(P, 16);
- this.q = parseBigInt(Q, 16);
- this.dmp1 = parseBigInt(DP, 16);
- this.dmq1 = parseBigInt(DQ, 16);
- this.coeff = parseBigInt(C, 16);
- }
- else {
- console.error("Invalid RSA private key");
- }
- };
- // RSAKey.prototype.generate = RSAGenerate;
- // Generate a new random private key B bits long, using public expt E
- RSAKey.prototype.generate = function (B, E) {
- var rng = new SecureRandom();
- var qs = B >> 1;
- this.e = parseInt(E, 16);
- var ee = new BigInteger(E, 16);
- for (;;) {
- for (;;) {
- this.p = new BigInteger(B - qs, 1, rng);
- if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) {
- break;
- }
- }
- for (;;) {
- this.q = new BigInteger(qs, 1, rng);
- if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) {
- break;
- }
- }
- if (this.p.compareTo(this.q) <= 0) {
- var t = this.p;
- this.p = this.q;
- this.q = t;
- }
- var p1 = this.p.subtract(BigInteger.ONE);
- var q1 = this.q.subtract(BigInteger.ONE);
- var phi = p1.multiply(q1);
- if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
- this.n = this.p.multiply(this.q);
- this.d = ee.modInverse(phi);
- this.dmp1 = this.d.mod(p1);
- this.dmq1 = this.d.mod(q1);
- this.coeff = this.q.modInverse(this.p);
- break;
- }
- }
- };
- // RSAKey.prototype.decrypt = RSADecrypt;
- // Return the PKCS#1 RSA decryption of "ctext".
- // "ctext" is an even-length hex string and the output is a plain string.
- RSAKey.prototype.decrypt = function (ctext) {
- var c = parseBigInt(ctext, 16);
- var m = this.doPrivate(c);
- if (m == null) {
- return null;
- }
- return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3);
- };
- // Generate a new random private key B bits long, using public expt E
- RSAKey.prototype.generateAsync = function (B, E, callback) {
- var rng = new SecureRandom();
- var qs = B >> 1;
- this.e = parseInt(E, 16);
- var ee = new BigInteger(E, 16);
- var rsa = this;
- // These functions have non-descript names because they were originally for(;;) loops.
- // I don't know about cryptography to give them better names than loop1-4.
- var loop1 = function () {
- var loop4 = function () {
- if (rsa.p.compareTo(rsa.q) <= 0) {
- var t = rsa.p;
- rsa.p = rsa.q;
- rsa.q = t;
- }
- var p1 = rsa.p.subtract(BigInteger.ONE);
- var q1 = rsa.q.subtract(BigInteger.ONE);
- var phi = p1.multiply(q1);
- if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
- rsa.n = rsa.p.multiply(rsa.q);
- rsa.d = ee.modInverse(phi);
- rsa.dmp1 = rsa.d.mod(p1);
- rsa.dmq1 = rsa.d.mod(q1);
- rsa.coeff = rsa.q.modInverse(rsa.p);
- setTimeout(function () { callback(); }, 0); // escape
- }
- else {
- setTimeout(loop1, 0);
- }
- };
- var loop3 = function () {
- rsa.q = nbi();
- rsa.q.fromNumberAsync(qs, 1, rng, function () {
- rsa.q.subtract(BigInteger.ONE).gcda(ee, function (r) {
- if (r.compareTo(BigInteger.ONE) == 0 && rsa.q.isProbablePrime(10)) {
- setTimeout(loop4, 0);
- }
- else {
- setTimeout(loop3, 0);
- }
- });
- });
- };
- var loop2 = function () {
- rsa.p = nbi();
- rsa.p.fromNumberAsync(B - qs, 1, rng, function () {
- rsa.p.subtract(BigInteger.ONE).gcda(ee, function (r) {
- if (r.compareTo(BigInteger.ONE) == 0 && rsa.p.isProbablePrime(10)) {
- setTimeout(loop3, 0);
- }
- else {
- setTimeout(loop2, 0);
- }
- });
- });
- };
- setTimeout(loop2, 0);
- };
- setTimeout(loop1, 0);
- };
- RSAKey.prototype.sign = function (text, digestMethod, digestName) {
- var header = getDigestHeader(digestName);
- var digest = header + digestMethod(text).toString();
- var m = pkcs1pad1(digest, this.n.bitLength() / 4);
- if (m == null) {
- return null;
- }
- var c = this.doPrivate(m);
- if (c == null) {
- return null;
- }
- var h = c.toString(16);
- if ((h.length & 1) == 0) {
- return h;
- }
- else {
- return "0" + h;
- }
- };
- RSAKey.prototype.verify = function (text, signature, digestMethod) {
- var c = parseBigInt(signature, 16);
- var m = this.doPublic(c);
- if (m == null) {
- return null;
- }
- var unpadded = m.toString(16).replace(/^1f+00/, "");
- var digest = removeDigestHeader(unpadded);
- return digest == digestMethod(text).toString();
- };
- return RSAKey;
- }());
- export { RSAKey };
- // Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
- function pkcs1unpad2(d, n) {
- var b = d.toByteArray();
- var i = 0;
- while (i < b.length && b[i] == 0) {
- ++i;
- }
- if (b.length - i != n - 1 || b[i] != 2) {
- return null;
- }
- ++i;
- while (b[i] != 0) {
- if (++i >= b.length) {
- return null;
- }
- }
- var ret = "";
- while (++i < b.length) {
- var c = b[i] & 255;
- if (c < 128) { // utf-8 decode
- ret += String.fromCharCode(c);
- }
- else if ((c > 191) && (c < 224)) {
- ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));
- ++i;
- }
- else {
- ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));
- i += 2;
- }
- }
- return ret;
- }
- // https://tools.ietf.org/html/rfc3447#page-43
- var DIGEST_HEADERS = {
- md2: "3020300c06082a864886f70d020205000410",
- md5: "3020300c06082a864886f70d020505000410",
- sha1: "3021300906052b0e03021a05000414",
- sha224: "302d300d06096086480165030402040500041c",
- sha256: "3031300d060960864801650304020105000420",
- sha384: "3041300d060960864801650304020205000430",
- sha512: "3051300d060960864801650304020305000440",
- ripemd160: "3021300906052b2403020105000414"
- };
- function getDigestHeader(name) {
- return DIGEST_HEADERS[name] || "";
- }
- function removeDigestHeader(str) {
- for (var name_1 in DIGEST_HEADERS) {
- if (DIGEST_HEADERS.hasOwnProperty(name_1)) {
- var header = DIGEST_HEADERS[name_1];
- var len = header.length;
- if (str.substr(0, len) == header) {
- return str.substr(len);
- }
- }
- }
- return str;
- }
- // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
- // function RSAEncryptB64(text) {
- // var h = this.encrypt(text);
- // if(h) return hex2b64(h); else return null;
- // }
- // public
- // RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
|