├── src ├── keygen.php ├── login.html ├── sha1.js ├── login.txt ├── login.php ├── rsa.js └── jsbn.js ├── README.md └── LICENSE /src/keygen.php: -------------------------------------------------------------------------------- 1 | 512 5 | ); 6 | 7 | // Create the keypair 8 | $res=openssl_pkey_new($config); 9 | openssl_pkey_export($res, $privkey, $pass); 10 | echo $privkey."\n"; 11 | // Get public key 12 | $pubkey=openssl_pkey_get_details($res); 13 | //var_dump($res); 14 | $pubkey=$pubkey["key"]; 15 | echo $pubkey; 16 | ?> 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | javascript-rsa 2 | ============== 3 | 4 | RSA encryption with javascript 5 | 6 | * relies on jsbn library from Tom Wu 7 | http://www-cs-students.stanford.edu/~tjw/jsbn/ 8 | 9 | Security 10 | -------- 11 | 12 | This is a pet project done a long time ago just for fun. In general, don't do cryptography in javascript, see: 13 | 14 | -------------------------------------------------------------------------------- /src/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Javascript RSA - Login Test 5 | 6 | 7 | 8 | 22 | 23 | 24 | 25 | 26 |

Javascript RSA - Login Test

27 | This test is an example to perform user login using javascript RSA.
28 |
    29 |
  1. The user type in E-mail as username and a password.
  2. 30 |
  3. The client-side javascript hashes the password using SHA-1.
  4. 31 |
  5. The client-side javascript attach a timestamp to the end of the hash.
  6. 32 |
  7. The client-side javascript encrypt the whole thing with the RSA public key.
  8. 33 | 34 |
  9. The browser submits the encrypted data.
  10. 35 |
36 | For testing purpose, the credential to login is any E-mail with the password "test".

37 | 38 |
39 | Email:
40 |
41 | Password:
42 |
43 |
44 |
45 |
46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Ziyan Zhou 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | Licensing 24 | --------- 25 | 26 | This software is covered under the following copyright: 27 | 28 | /* 29 | * Copyright (c) 2003-2005 Tom Wu 30 | * All Rights Reserved. 31 | * 32 | * Permission is hereby granted, free of charge, to any person obtaining 33 | * a copy of this software and associated documentation files (the 34 | * "Software"), to deal in the Software without restriction, including 35 | * without limitation the rights to use, copy, modify, merge, publish, 36 | * distribute, sublicense, and/or sell copies of the Software, and to 37 | * permit persons to whom the Software is furnished to do so, subject to 38 | * the following conditions: 39 | * 40 | * The above copyright notice and this permission notice shall be 41 | * included in all copies or substantial portions of the Software. 42 | * 43 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 44 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 45 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 46 | * 47 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 48 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 49 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 50 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 51 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 52 | * 53 | * In addition, the following condition applies: 54 | * 55 | * All redistributions must retain an intact copy of this copyright notice 56 | * and disclaimer. 57 | */ 58 | 59 | Address all questions regarding this license to: 60 | 61 | Tom Wu 62 | tjw@cs.Stanford.EDU 63 | -------------------------------------------------------------------------------- /src/sha1.js: -------------------------------------------------------------------------------- 1 | function sha1(msg) 2 | { 3 | // constants 4 | var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; 5 | 6 | // PREPROCESSING 7 | msg += String.fromCharCode(0x80); // add trailing '1' bit to string 8 | 9 | // convert string msg into 512-bit/16-integer blocks arrays of ints 10 | var l = Math.ceil(msg.length/4) + 2; // long enough to contain msg plus 2-word length 11 | var N = Math.ceil(l/16); // in N 16-int blocks 12 | var M = new Array(N); 13 | 14 | for (var i=0; i>> 32, but since JS converts 23 | // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators 24 | M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14]) 25 | M[N-1][15] = ((msg.length-1)*8) & 0xffffffff; 26 | 27 | // set initial hash value 28 | var H0 = 0x67452301; 29 | var H1 = 0xefcdab89; 30 | var H2 = 0x98badcfe; 31 | var H3 = 0x10325476; 32 | var H4 = 0xc3d2e1f0; 33 | 34 | // HASH COMPUTATION 35 | var W = new Array(80); 36 | var a, b, c, d, e; 37 | 38 | for (var i=0; i>>31); 44 | } 45 | 46 | // 2 - initialise five working variables a, b, c, d, e with previous hash value 47 | a = H0; b = H1; c = H2; d = H3; e = H4; 48 | 49 | // 3 - main loop 50 | for (var t=0; t<80; t++) { 51 | var s = Math.floor(t/20); // seq for blocks of 'f' functions and 'K' constants 52 | var T = ((a<<5) | (a>>>27)) + e + K[s] + W[t]; 53 | switch(s) { 54 | case 0: T += (b & c) ^ (~b & d); break; // Ch() 55 | case 1: T += b ^ c ^ d; break; // Parity() 56 | case 2: T += (b & c) ^ (b & d) ^ (c & d); break; // Maj() 57 | case 3: T += b ^ c ^ d; break; // Parity() 58 | } 59 | e = d; 60 | d = c; 61 | c = (b << 30) | (b>>>2); 62 | b = a; 63 | a = T; 64 | } 65 | 66 | // 4 - compute the new intermediate hash value 67 | H0 = (H0+a) & 0xffffffff; // note 'addition modulo 2^32' 68 | H1 = (H1+b) & 0xffffffff; 69 | H2 = (H2+c) & 0xffffffff; 70 | H3 = (H3+d) & 0xffffffff; 71 | H4 = (H4+e) & 0xffffffff; 72 | } 73 | 74 | var hex = ""; 75 | for (var i=7; i>=0; i--) { var v = (H0>>>(i*4)) & 0xf; hex += v.toString(16); } 76 | for (var i=7; i>=0; i--) { var v = (H1>>>(i*4)) & 0xf; hex += v.toString(16); } 77 | for (var i=7; i>=0; i--) { var v = (H2>>>(i*4)) & 0xf; hex += v.toString(16); } 78 | for (var i=7; i>=0; i--) { var v = (H3>>>(i*4)) & 0xf; hex += v.toString(16); } 79 | for (var i=7; i>=0; i--) { var v = (H4>>>(i*4)) & 0xf; hex += v.toString(16); } 80 | return hex; 81 | } 82 | -------------------------------------------------------------------------------- /src/login.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Javascript RSA - Login Test 5 | 6 | 7 | 8 | 9 | 30) { 51 | echo "Timestamp expired. Client and server times may be out of sync.\n"; 52 | return false; 53 | } 54 | // construct stamp 55 | //$stamp = "user.login.".sha1($email).".".$stamp; 56 | // take a note of the stamp, each unique stamp can only be used once 57 | //if($memcache->get($stamp) != NULL) return false; 58 | //$memcache->set($stamp,1,USER_LOGIN_TIMESTAMP_TTL); 59 | // connect to db and check password 60 | // check password 61 | if (pack("H*",$password)!=pack("H*",sha1(TEST_PASSWORD))) { 62 | echo "Password incorrect.\n"; 63 | return false; 64 | } 65 | return true; 66 | } 67 | 68 | 69 | ?> 70 | 71 |

Javascript RSA - Login Test

72 | This test is an example to perform user login using javascript RSA.
73 |
    74 |
  1. Once the encrypted data is received, the server side decrypt using private key.
  2. 75 |
  3. The message is separated into two parts, the hash and the timestamp.
  4. 76 |
  5. The timestamp is checked to make sure the request is made in recent time. Set to allow up to 30 second difference.
  6. 77 |
  7. The timestamp is recorded to make sure no single timestamp is repeated for a user.
  8. 78 |
  9. The password hash is compared to the hash in the database.
  10. 79 |
80 | For testing purpose, the credential to login is any E-mail with the password "test".
81 | No database connection is made in this test. A hardcoded check is used.
82 | Also the duplicate timestamp check is by-passed since it requires the presence of a memcached server.
83 | The result is displayed:
84 | 85 |
 86 | 
 94 | 
95 | The source code for this php file is available here. 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/login.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Javascript RSA - Login Test 5 | 6 | 7 | 8 | 9 | 300) { 51 | echo "Timestamp expired. Client and server times may be out of sync.\n"; 52 | return false; 53 | } 54 | // construct stamp 55 | //$stamp = "user.login.".sha1($email).".".$stamp; 56 | // take a note of the stamp, each unique stamp can only be used once 57 | //if($memcache->get($stamp) != NULL) return false; 58 | //$memcache->set($stamp,1,USER_LOGIN_TIMESTAMP_TTL); 59 | // connect to db and check password 60 | // check password 61 | if (pack("H*",$password)!=pack("H*",sha1(TEST_PASSWORD))) { 62 | echo "Password incorrect.\n"; 63 | return false; 64 | } 65 | return true; 66 | } 67 | 68 | 69 | ?> 70 | 71 |

Javascript RSA - Login Test

72 | This test is an example to perform user login using javascript RSA.
73 |
    74 |
  1. Once the encrypted data is received, the server side decrypt using private key.
  2. 75 |
  3. The message is separated into two parts, the hash and the timestamp.
  4. 76 |
  5. The timestamp is checked to make sure the request is made in recent time. Set to allow up to 30 second difference.
  6. 77 |
  7. The timestamp is recorded to make sure no single timestamp is repeated for a user.
  8. 78 |
  9. The password hash is compared to the hash in the database.
  10. 79 |
80 | For testing purpose, the credential to login is any E-mail with the password "test".
81 | No database connection is made in this test. A hardcoded check is used.
82 | Also the duplicate timestamp check is by-passed since it requires the presence of a memcached server.
83 | The result is displayed:
84 | 85 |
 86 | 
 94 | 
95 | The source code for this php file is available here. 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/rsa.js: -------------------------------------------------------------------------------- 1 | var RSAPublicKey = function($modulus, $encryptionExponent) { 2 | this.modulus = new BigInteger(Hex.encode($modulus), 16); 3 | this.encryptionExponent = new BigInteger(Hex.encode($encryptionExponent), 16); 4 | } 5 | 6 | var UTF8 = { 7 | encode: function($input) { 8 | $input = $input.replace(/\r\n/g,"\n"); 9 | var $output = ""; 10 | for (var $n = 0; $n < $input.length; $n++) { 11 | var $c = $input.charCodeAt($n); 12 | if ($c < 128) { 13 | $output += String.fromCharCode($c); 14 | } else if (($c > 127) && ($c < 2048)) { 15 | $output += String.fromCharCode(($c >> 6) | 192); 16 | $output += String.fromCharCode(($c & 63) | 128); 17 | } else { 18 | $output += String.fromCharCode(($c >> 12) | 224); 19 | $output += String.fromCharCode((($c >> 6) & 63) | 128); 20 | $output += String.fromCharCode(($c & 63) | 128); 21 | } 22 | } 23 | return $output; 24 | }, 25 | decode: function($input) { 26 | var $output = ""; 27 | var $i = 0; 28 | var $c = $c1 = $c2 = 0; 29 | while ( $i < $input.length ) { 30 | $c = $input.charCodeAt($i); 31 | if ($c < 128) { 32 | $output += String.fromCharCode($c); 33 | $i++; 34 | } else if(($c > 191) && ($c < 224)) { 35 | $c2 = $input.charCodeAt($i+1); 36 | $output += String.fromCharCode((($c & 31) << 6) | ($c2 & 63)); 37 | $i += 2; 38 | } else { 39 | $c2 = $input.charCodeAt($i+1); 40 | $c3 = $input.charCodeAt($i+2); 41 | $output += String.fromCharCode((($c & 15) << 12) | (($c2 & 63) << 6) | ($c3 & 63)); 42 | $i += 3; 43 | } 44 | } 45 | return $output; 46 | } 47 | }; 48 | 49 | var Base64 = { 50 | base64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 51 | encode: function($input) { 52 | if (!$input) { 53 | return false; 54 | } 55 | //$input = UTF8.encode($input); 56 | var $output = ""; 57 | var $chr1, $chr2, $chr3; 58 | var $enc1, $enc2, $enc3, $enc4; 59 | var $i = 0; 60 | do { 61 | $chr1 = $input.charCodeAt($i++); 62 | $chr2 = $input.charCodeAt($i++); 63 | $chr3 = $input.charCodeAt($i++); 64 | $enc1 = $chr1 >> 2; 65 | $enc2 = (($chr1 & 3) << 4) | ($chr2 >> 4); 66 | $enc3 = (($chr2 & 15) << 2) | ($chr3 >> 6); 67 | $enc4 = $chr3 & 63; 68 | if (isNaN($chr2)) $enc3 = $enc4 = 64; 69 | else if (isNaN($chr3)) $enc4 = 64; 70 | $output += this.base64.charAt($enc1) + this.base64.charAt($enc2) + this.base64.charAt($enc3) + this.base64.charAt($enc4); 71 | } while ($i < $input.length); 72 | return $output; 73 | }, 74 | decode: function($input) { 75 | if(!$input) return false; 76 | $input = $input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 77 | var $output = ""; 78 | var $enc1, $enc2, $enc3, $enc4; 79 | var $i = 0; 80 | do { 81 | $enc1 = this.base64.indexOf($input.charAt($i++)); 82 | $enc2 = this.base64.indexOf($input.charAt($i++)); 83 | $enc3 = this.base64.indexOf($input.charAt($i++)); 84 | $enc4 = this.base64.indexOf($input.charAt($i++)); 85 | $output += String.fromCharCode(($enc1 << 2) | ($enc2 >> 4)); 86 | if ($enc3 != 64) $output += String.fromCharCode((($enc2 & 15) << 4) | ($enc3 >> 2)); 87 | if ($enc4 != 64) $output += String.fromCharCode((($enc3 & 3) << 6) | $enc4); 88 | } while ($i < $input.length); 89 | return $output; //UTF8.decode($output); 90 | } 91 | }; 92 | 93 | var Hex = { 94 | hex: "0123456789abcdef", 95 | encode: function($input) { 96 | if(!$input) return false; 97 | var $output = ""; 98 | var $k; 99 | var $i = 0; 100 | do { 101 | $k = $input.charCodeAt($i++); 102 | $output += this.hex.charAt(($k >> 4) &0xf) + this.hex.charAt($k & 0xf); 103 | } while ($i < $input.length); 104 | return $output; 105 | }, 106 | decode: function($input) { 107 | if(!$input) return false; 108 | $input = $input.replace(/[^0-9abcdef]/g, ""); 109 | var $output = ""; 110 | var $i = 0; 111 | do { 112 | $output += String.fromCharCode(((this.hex.indexOf($input.charAt($i++)) << 4) & 0xf0) | (this.hex.indexOf($input.charAt($i++)) & 0xf)); 113 | } while ($i < $input.length); 114 | return $output; 115 | } 116 | }; 117 | 118 | var ASN1Data = function($data) { 119 | this.error = false; 120 | this.parse = function($data) { 121 | if (!$data) { 122 | this.error = true; 123 | return null; 124 | } 125 | var $result = []; 126 | while($data.length > 0) { 127 | // get the tag 128 | var $tag = $data.charCodeAt(0); 129 | $data = $data.substr(1); 130 | // get length 131 | var $length = 0; 132 | // ignore any null tag 133 | if (($tag & 31) == 0x5) $data = $data.substr(1); 134 | else { 135 | if ($data.charCodeAt(0) & 128) { 136 | var $lengthSize = $data.charCodeAt(0) & 127; 137 | $data = $data.substr(1); 138 | if($lengthSize > 0) $length = $data.charCodeAt(0); 139 | if($lengthSize > 1) $length = (($length << 8) | $data.charCodeAt(1)); 140 | if($lengthSize > 2) { 141 | this.error = true; 142 | return null; 143 | } 144 | $data = $data.substr($lengthSize); 145 | } else { 146 | $length = $data.charCodeAt(0); 147 | $data = $data.substr(1); 148 | } 149 | } 150 | // get value 151 | var $value = ""; 152 | if($length) { 153 | if ($length > $data.length){ 154 | this.error = true; 155 | return null; 156 | } 157 | $value = $data.substr(0, $length); 158 | $data = $data.substr($length); 159 | } 160 | if ($tag & 32) 161 | $result.push(this.parse($value)); // sequence 162 | else 163 | $result.push(this.value(($tag & 128) ? 4 : ($tag & 31), $value)); 164 | } 165 | return $result; 166 | }; 167 | this.value = function($tag, $data) { 168 | if ($tag == 1) 169 | return $data ? true : false; 170 | else if ($tag == 2) //integer 171 | return $data; 172 | else if ($tag == 3) //bit string 173 | return this.parse($data.substr(1)); 174 | else if ($tag == 5) //null 175 | return null; 176 | else if ($tag == 6){ //ID 177 | var $res = []; 178 | var $d0 = $data.charCodeAt(0); 179 | $res.push(Math.floor($d0 / 40)); 180 | $res.push($d0 - $res[0]*40); 181 | var $stack = []; 182 | var $powNum = 0; 183 | var $i; 184 | for($i=1;$i<$data.length;$i++){ 185 | var $token = $data.charCodeAt($i); 186 | $stack.push($token & 127); 187 | if ( $token & 128 ) 188 | $powNum++; 189 | else { 190 | var $j; 191 | var $sum = 0; 192 | for($j=0;$j<$stack.length;$j++) 193 | $sum += $stack[$j] * Math.pow(128, $powNum--); 194 | $res.push($sum); 195 | $powNum = 0; 196 | $stack = []; 197 | } 198 | } 199 | return $res.join("."); 200 | } 201 | return null; 202 | } 203 | this.data = this.parse($data); 204 | }; 205 | 206 | var RSA = { 207 | getPublicKey: function($pem) { 208 | if($pem.length<50) return false; 209 | if($pem.substr(0,26)!="-----BEGIN PUBLIC KEY-----") return false; 210 | $pem = $pem.substr(26); 211 | if($pem.substr($pem.length-24)!="-----END PUBLIC KEY-----") return false; 212 | $pem = $pem.substr(0,$pem.length-24); 213 | $pem = new ASN1Data(Base64.decode($pem)); 214 | if($pem.error) return false; 215 | $pem = $pem.data; 216 | if($pem[0][0][0]=="1.2.840.113549.1.1.1") 217 | return new RSAPublicKey($pem[0][1][0][0], $pem[0][1][0][1]); 218 | return false; 219 | }, 220 | encrypt: function($data, $pubkey) { 221 | if (!$pubkey) return false; 222 | var bytes = ($pubkey.modulus.bitLength()+7)>>3; 223 | $data = this.pkcs1pad2($data,bytes); 224 | if(!$data) return false; 225 | $data = $data.modPowInt($pubkey.encryptionExponent, $pubkey.modulus); 226 | if(!$data) return false; 227 | $data = $data.toString(16); 228 | while ($data.length < bytes*2) 229 | $data = '0' + $data; 230 | return Base64.encode(Hex.decode($data)); 231 | }, 232 | pkcs1pad2: function($data, $keysize) { 233 | if($keysize < $data.length + 11) 234 | return null; 235 | var $buffer = []; 236 | var $i = $data.length - 1; 237 | while($i >= 0 && $keysize > 0) 238 | $buffer[--$keysize] = $data.charCodeAt($i--); 239 | $buffer[--$keysize] = 0; 240 | while($keysize > 2) 241 | $buffer[--$keysize] = Math.floor(Math.random()*254) + 1; 242 | $buffer[--$keysize] = 2; 243 | $buffer[--$keysize] = 0; 244 | return new BigInteger($buffer); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/jsbn.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2005 Tom Wu 2 | // All Rights Reserved. 3 | // See "LICENSE" for details. 4 | 5 | // Basic JavaScript BN library - subset useful for RSA encryption. 6 | 7 | // Bits per digit 8 | var dbits; 9 | 10 | // JavaScript engine analysis 11 | var canary = 0xdeadbeefcafe; 12 | var j_lm = ((canary&0xffffff)==0xefcafe); 13 | 14 | // (public) Constructor 15 | function BigInteger(a,b,c) { 16 | if(a != null) 17 | if("number" == typeof a) this.fromNumber(a,b,c); 18 | else if(b == null && "string" != typeof a) this.fromString(a,256); 19 | else this.fromString(a,b); 20 | } 21 | 22 | // return new, unset BigInteger 23 | function nbi() { return new BigInteger(null); } 24 | 25 | // am: Compute w_j += (x*this_i), propagate carries, 26 | // c is initial carry, returns final carry. 27 | // c < 3*dvalue, x < 2*dvalue, this_i < dvalue 28 | // We need to select the fastest one that works in this environment. 29 | 30 | // am1: use a single mult and divide to get the high bits, 31 | // max digit bits should be 26 because 32 | // max internal value = 2*dvalue^2-2*dvalue (< 2^53) 33 | function am1(i,x,w,j,c,n) { 34 | while(--n >= 0) { 35 | var v = x*this[i++]+w[j]+c; 36 | c = Math.floor(v/0x4000000); 37 | w[j++] = v&0x3ffffff; 38 | } 39 | return c; 40 | } 41 | // am2 avoids a big mult-and-extract completely. 42 | // Max digit bits should be <= 30 because we do bitwise ops 43 | // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) 44 | function am2(i,x,w,j,c,n) { 45 | var xl = x&0x7fff, xh = x>>15; 46 | while(--n >= 0) { 47 | var l = this[i]&0x7fff; 48 | var h = this[i++]>>15; 49 | var m = xh*l+h*xl; 50 | l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); 51 | c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); 52 | w[j++] = l&0x3fffffff; 53 | } 54 | return c; 55 | } 56 | // Alternately, set max digit bits to 28 since some 57 | // browsers slow down when dealing with 32-bit numbers. 58 | function am3(i,x,w,j,c,n) { 59 | var xl = x&0x3fff, xh = x>>14; 60 | while(--n >= 0) { 61 | var l = this[i]&0x3fff; 62 | var h = this[i++]>>14; 63 | var m = xh*l+h*xl; 64 | l = xl*l+((m&0x3fff)<<14)+w[j]+c; 65 | c = (l>>28)+(m>>14)+xh*h; 66 | w[j++] = l&0xfffffff; 67 | } 68 | return c; 69 | } 70 | if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { 71 | BigInteger.prototype.am = am2; 72 | dbits = 30; 73 | } 74 | else if(j_lm && (navigator.appName != "Netscape")) { 75 | BigInteger.prototype.am = am1; 76 | dbits = 26; 77 | } 78 | else { // Mozilla/Netscape seems to prefer am3 79 | BigInteger.prototype.am = am3; 80 | dbits = 28; 81 | } 82 | 83 | BigInteger.prototype.DB = dbits; 84 | BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; 112 | r.t = this.t; 113 | r.s = this.s; 114 | } 115 | 116 | // (protected) set from integer value x, -DV <= x < DV 117 | function bnpFromInt(x) { 118 | this.t = 1; 119 | this.s = (x<0)?-1:0; 120 | if(x > 0) this[0] = x; 121 | else if(x < -1) this[0] = x+DV; 122 | else this.t = 0; 123 | } 124 | 125 | // return bigint initialized to value 126 | function nbv(i) { var r = nbi(); r.fromInt(i); return r; } 127 | 128 | // (protected) set from string and radix 129 | function bnpFromString(s,b) { 130 | var k; 131 | if(b == 16) k = 4; 132 | else if(b == 8) k = 3; 133 | else if(b == 256) k = 8; // byte array 134 | else if(b == 2) k = 1; 135 | else if(b == 32) k = 5; 136 | else if(b == 4) k = 2; 137 | else { this.fromRadix(s,b); return; } 138 | this.t = 0; 139 | this.s = 0; 140 | var i = s.length, mi = false, sh = 0; 141 | while(--i >= 0) { 142 | var x = (k==8)?s[i]&0xff:intAt(s,i); 143 | if(x < 0) { 144 | if(s.charAt(i) == "-") mi = true; 145 | continue; 146 | } 147 | mi = false; 148 | if(sh == 0) 149 | this[this.t++] = x; 150 | else if(sh+k > this.DB) { 151 | this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); 153 | } 154 | else 155 | this[this.t-1] |= x<= this.DB) sh -= this.DB; 158 | } 159 | if(k == 8 && (s[0]&0x80) != 0) { 160 | this.s = -1; 161 | if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; 171 | } 172 | 173 | // (public) return string representation in given radix 174 | function bnToString(b) { 175 | if(this.s < 0) return "-"+this.negate().toString(b); 176 | var k; 177 | if(b == 16) k = 4; 178 | else if(b == 8) k = 3; 179 | else if(b == 2) k = 1; 180 | else if(b == 32) k = 5; 181 | else if(b == 4) k = 2; 182 | else return this.toRadix(b); 183 | var km = (1< 0) { 186 | if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } 187 | while(i >= 0) { 188 | if(p < k) { 189 | d = (this[i]&((1<>(p+=this.DB-k); 191 | } 192 | else { 193 | d = (this[i]>>(p-=k))&km; 194 | if(p <= 0) { p += this.DB; --i; } 195 | } 196 | if(d > 0) m = true; 197 | if(m) r += int2char(d); 198 | } 199 | } 200 | return m?r:"0"; 201 | } 202 | 203 | // (public) -this 204 | function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } 205 | 206 | // (public) |this| 207 | function bnAbs() { return (this.s<0)?this.negate():this; } 208 | 209 | // (public) return + if this > a, - if this < a, 0 if equal 210 | function bnCompareTo(a) { 211 | var r = this.s-a.s; 212 | if(r != 0) return r; 213 | var i = this.t; 214 | r = i-a.t; 215 | if(r != 0) return (this.s<0)?-r:r; 216 | while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; 217 | return 0; 218 | } 219 | 220 | // returns bit length of the integer x 221 | function nbits(x) { 222 | var r = 1, t; 223 | if((t=x>>>16) != 0) { x = t; r += 16; } 224 | if((t=x>>8) != 0) { x = t; r += 8; } 225 | if((t=x>>4) != 0) { x = t; r += 4; } 226 | if((t=x>>2) != 0) { x = t; r += 2; } 227 | if((t=x>>1) != 0) { x = t; r += 1; } 228 | return r; 229 | } 230 | 231 | // (public) return the number of bits in "this" 232 | function bnBitLength() { 233 | if(this.t <= 0) return 0; 234 | return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); 235 | } 236 | 237 | // (protected) r = this << n*DB 238 | function bnpDLShiftTo(n,r) { 239 | var i; 240 | for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; 241 | for(i = n-1; i >= 0; --i) r[i] = 0; 242 | r.t = this.t+n; 243 | r.s = this.s; 244 | } 245 | 246 | // (protected) r = this >> n*DB 247 | function bnpDRShiftTo(n,r) { 248 | for(var i = n; i < this.t; ++i) r[i-n] = this[i]; 249 | r.t = Math.max(this.t-n,0); 250 | r.s = this.s; 251 | } 252 | 253 | // (protected) r = this << n 254 | function bnpLShiftTo(n,r) { 255 | var bs = n%this.DB; 256 | var cbs = this.DB-bs; 257 | var bm = (1<= 0; --i) { 260 | r[i+ds+1] = (this[i]>>cbs)|c; 261 | c = (this[i]&bm)<= 0; --i) r[i] = 0; 264 | r[ds] = c; 265 | r.t = this.t+ds+1; 266 | r.s = this.s; 267 | r.clamp(); 268 | } 269 | 270 | // (protected) r = this >> n 271 | function bnpRShiftTo(n,r) { 272 | r.s = this.s; 273 | var ds = Math.floor(n/this.DB); 274 | if(ds >= this.t) { r.t = 0; return; } 275 | var bs = n%this.DB; 276 | var cbs = this.DB-bs; 277 | var bm = (1<>bs; 279 | for(var i = ds+1; i < this.t; ++i) { 280 | r[i-ds-1] |= (this[i]&bm)<>bs; 282 | } 283 | if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; 295 | } 296 | if(a.t < this.t) { 297 | c -= a.s; 298 | while(i < this.t) { 299 | c += this[i]; 300 | r[i++] = c&this.DM; 301 | c >>= this.DB; 302 | } 303 | c += this.s; 304 | } 305 | else { 306 | c += this.s; 307 | while(i < a.t) { 308 | c -= a[i]; 309 | r[i++] = c&this.DM; 310 | c >>= this.DB; 311 | } 312 | c -= a.s; 313 | } 314 | r.s = (c<0)?-1:0; 315 | if(c < -1) r[i++] = this.DV+c; 316 | else if(c > 0) r[i++] = c; 317 | r.t = i; 318 | r.clamp(); 319 | } 320 | 321 | // (protected) r = this * a, r != this,a (HAC 14.12) 322 | // "this" should be the larger one if appropriate. 323 | function bnpMultiplyTo(a,r) { 324 | var x = this.abs(), y = a.abs(); 325 | var i = x.t; 326 | r.t = i+y.t; 327 | while(--i >= 0) r[i] = 0; 328 | for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); 329 | r.s = 0; 330 | r.clamp(); 331 | if(this.s != a.s) BigInteger.ZERO.subTo(r,r); 332 | } 333 | 334 | // (protected) r = this^2, r != this (HAC 14.16) 335 | function bnpSquareTo(r) { 336 | var x = this.abs(); 337 | var i = r.t = 2*x.t; 338 | while(--i >= 0) r[i] = 0; 339 | for(i = 0; i < x.t-1; ++i) { 340 | var c = x.am(i,x[i],r,2*i,0,1); 341 | if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { 342 | r[i+x.t] -= x.DV; 343 | r[i+x.t+1] = 1; 344 | } 345 | } 346 | if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); 347 | r.s = 0; 348 | r.clamp(); 349 | } 350 | 351 | // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) 352 | // r != q, this != m. q or r may be null. 353 | function bnpDivRemTo(m,q,r) { 354 | var pm = m.abs(); 355 | if(pm.t <= 0) return; 356 | var pt = this.abs(); 357 | if(pt.t < pm.t) { 358 | if(q != null) q.fromInt(0); 359 | if(r != null) this.copyTo(r); 360 | return; 361 | } 362 | if(r == null) r = nbi(); 363 | var y = nbi(), ts = this.s, ms = m.s; 364 | var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus 365 | if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } 366 | else { pm.copyTo(y); pt.copyTo(r); } 367 | var ys = y.t; 368 | var y0 = y[ys-1]; 369 | if(y0 == 0) return; 370 | var yt = y0*(1<1)?y[ys-2]>>this.F2:0); 371 | var d1 = this.FV/yt, d2 = (1<= 0) { 375 | r[r.t++] = 1; 376 | r.subTo(t,r); 377 | } 378 | BigInteger.ONE.dlShiftTo(ys,t); 379 | t.subTo(y,y); // "negative" y so we can replace sub with am later 380 | while(y.t < ys) y[y.t++] = 0; 381 | while(--j >= 0) { 382 | // Estimate quotient digit 383 | var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); 384 | if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out 385 | y.dlShiftTo(j,t); 386 | r.subTo(t,r); 387 | while(r[i] < --qd) r.subTo(t,r); 388 | } 389 | } 390 | if(q != null) { 391 | r.drShiftTo(ys,q); 392 | if(ts != ms) BigInteger.ZERO.subTo(q,q); 393 | } 394 | r.t = ys; 395 | r.clamp(); 396 | if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder 397 | if(ts < 0) BigInteger.ZERO.subTo(r,r); 398 | } 399 | 400 | // (public) this mod a 401 | function bnMod(a) { 402 | var r = nbi(); 403 | this.abs().divRemTo(a,null,r); 404 | if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); 405 | return r; 406 | } 407 | 408 | // Modular reduction using "classic" algorithm 409 | function Classic(m) { this.m = m; } 410 | function cConvert(x) { 411 | if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); 412 | else return x; 413 | } 414 | function cRevert(x) { return x; } 415 | function cReduce(x) { x.divRemTo(this.m,null,x); } 416 | function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 417 | function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 418 | 419 | Classic.prototype.convert = cConvert; 420 | Classic.prototype.revert = cRevert; 421 | Classic.prototype.reduce = cReduce; 422 | Classic.prototype.mulTo = cMulTo; 423 | Classic.prototype.sqrTo = cSqrTo; 424 | 425 | // (protected) return "-1/this % 2^DB"; useful for Mont. reduction 426 | // justification: 427 | // xy == 1 (mod m) 428 | // xy = 1+km 429 | // xy(2-xy) = (1+km)(1-km) 430 | // x[y(2-xy)] = 1-k^2m^2 431 | // x[y(2-xy)] == 1 (mod m^2) 432 | // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 433 | // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. 434 | // JS multiply "overflows" differently from C/C++, so care is needed here. 435 | function bnpInvDigit() { 436 | if(this.t < 1) return 0; 437 | var x = this[0]; 438 | if((x&1) == 0) return 0; 439 | var y = x&3; // y == 1/x mod 2^2 440 | y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 441 | y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 442 | y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 443 | // last step - calculate inverse mod DV directly; 444 | // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints 445 | y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits 446 | // we really want the negative inverse, and -DV < y < DV 447 | return (y>0)?this.DV-y:-y; 448 | } 449 | 450 | // Montgomery reduction 451 | function Montgomery(m) { 452 | this.m = m; 453 | this.mp = m.invDigit(); 454 | this.mpl = this.mp&0x7fff; 455 | this.mph = this.mp>>15; 456 | this.um = (1<<(m.DB-15))-1; 457 | this.mt2 = 2*m.t; 458 | } 459 | 460 | // xR mod m 461 | function montConvert(x) { 462 | var r = nbi(); 463 | x.abs().dlShiftTo(this.m.t,r); 464 | r.divRemTo(this.m,null,r); 465 | if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); 466 | return r; 467 | } 468 | 469 | // x/R mod m 470 | function montRevert(x) { 471 | var r = nbi(); 472 | x.copyTo(r); 473 | this.reduce(r); 474 | return r; 475 | } 476 | 477 | // x = x/R mod m (HAC 14.32) 478 | function montReduce(x) { 479 | while(x.t <= this.mt2) // pad x so am has enough room later 480 | x[x.t++] = 0; 481 | for(var i = 0; i < this.m.t; ++i) { 482 | // faster way of calculating u0 = x[i]*mp mod DV 483 | var j = x[i]&0x7fff; 484 | var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; 485 | // use am to combine the multiply-shift-add into one call 486 | j = i+this.m.t; 487 | x[j] += this.m.am(0,u0,x,i,0,this.m.t); 488 | // propagate carry 489 | while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } 490 | } 491 | x.clamp(); 492 | x.drShiftTo(this.m.t,x); 493 | if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); 494 | } 495 | 496 | // r = "x^2/R mod m"; x != r 497 | function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 498 | 499 | // r = "xy/R mod m"; x,y != r 500 | function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 501 | 502 | Montgomery.prototype.convert = montConvert; 503 | Montgomery.prototype.revert = montRevert; 504 | Montgomery.prototype.reduce = montReduce; 505 | Montgomery.prototype.mulTo = montMulTo; 506 | Montgomery.prototype.sqrTo = montSqrTo; 507 | 508 | // (protected) true iff this is even 509 | function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } 510 | 511 | // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) 512 | function bnpExp(e,z) { 513 | if(e > 0xffffffff || e < 1) return BigInteger.ONE; 514 | var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; 515 | g.copyTo(r); 516 | while(--i >= 0) { 517 | z.sqrTo(r,r2); 518 | if((e&(1< 0) z.mulTo(r2,g,r); 519 | else { var t = r; r = r2; r2 = t; } 520 | } 521 | return z.revert(r); 522 | } 523 | 524 | // (public) this^e % m, 0 <= e < 2^32 525 | function bnModPowInt(e,m) { 526 | var z; 527 | if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); 528 | return this.exp(e,z); 529 | } 530 | 531 | // protected 532 | BigInteger.prototype.copyTo = bnpCopyTo; 533 | BigInteger.prototype.fromInt = bnpFromInt; 534 | BigInteger.prototype.fromString = bnpFromString; 535 | BigInteger.prototype.clamp = bnpClamp; 536 | BigInteger.prototype.dlShiftTo = bnpDLShiftTo; 537 | BigInteger.prototype.drShiftTo = bnpDRShiftTo; 538 | BigInteger.prototype.lShiftTo = bnpLShiftTo; 539 | BigInteger.prototype.rShiftTo = bnpRShiftTo; 540 | BigInteger.prototype.subTo = bnpSubTo; 541 | BigInteger.prototype.multiplyTo = bnpMultiplyTo; 542 | BigInteger.prototype.squareTo = bnpSquareTo; 543 | BigInteger.prototype.divRemTo = bnpDivRemTo; 544 | BigInteger.prototype.invDigit = bnpInvDigit; 545 | BigInteger.prototype.isEven = bnpIsEven; 546 | BigInteger.prototype.exp = bnpExp; 547 | 548 | // public 549 | BigInteger.prototype.toString = bnToString; 550 | BigInteger.prototype.negate = bnNegate; 551 | BigInteger.prototype.abs = bnAbs; 552 | BigInteger.prototype.compareTo = bnCompareTo; 553 | BigInteger.prototype.bitLength = bnBitLength; 554 | BigInteger.prototype.mod = bnMod; 555 | BigInteger.prototype.modPowInt = bnModPowInt; 556 | 557 | // "constants" 558 | BigInteger.ZERO = nbv(0); 559 | BigInteger.ONE = nbv(1); 560 | --------------------------------------------------------------------------------