├── .gitignore ├── compress.js ├── kryptos ├── Random │ ├── Fortuna │ │ ├── SHAd256.js │ │ ├── FortunaGenerator.js │ │ └── FortunaAccumulator.js │ ├── OSRNG │ │ └── browser.js │ ├── LICENSE.txt │ └── _UserFriendlyRNG.js ├── PublicKey │ ├── RSA.js │ └── DSA.js ├── Hash │ ├── baseHash.js │ ├── SHA.js │ ├── HMAC.js │ ├── SHA256.js │ └── MD5.js ├── Cipher │ ├── nss3.js │ ├── ARC4.js │ ├── AES.js │ └── DES3.js └── kryptos.js ├── unknown_key.js ├── sign_ssh_data_worker.js ├── readme.md ├── kex_group14.js ├── license.txt ├── common.js ├── ber.js ├── win_pageant.js ├── python_shim.js ├── kex_group1.js ├── sftp_attr.js ├── agent.js ├── kex_gex.js ├── ssh_exception.js ├── sftp.js ├── rsakey.js ├── dsskey.js ├── message.js ├── util.js ├── hostkeys.js ├── packet.js ├── auth_handler.js └── file.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_STORE 2 | *.pyc 3 | *.svn 4 | *.swp 5 | CVS 6 | -------------------------------------------------------------------------------- /compress.js: -------------------------------------------------------------------------------- 1 | paramikojs.ZlibCompressor = function () { 2 | 3 | } 4 | 5 | paramikojs.ZlibCompressor.prototype = { 6 | 7 | }; 8 | 9 | paramikojs.ZlibDecompressor = function () { 10 | 11 | } 12 | 13 | paramikojs.ZlibDecompressor.prototype = { 14 | 15 | }; 16 | -------------------------------------------------------------------------------- /kryptos/Random/Fortuna/SHAd256.js: -------------------------------------------------------------------------------- 1 | kryptos.random.Fortuna.SHAd256 = function(str) { 2 | inherit(this, new kryptos.hash.baseHash(str)); 3 | } 4 | 5 | kryptos.random.Fortuna.SHAd256.digest_size = 32; 6 | 7 | kryptos.random.Fortuna.SHAd256.prototype = { 8 | type : 'sha256', 9 | 10 | digest : function() { 11 | return new kryptos.hash.SHA256(new kryptos.hash.SHA256(this.data).digest()).digest(); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /kryptos/Random/OSRNG/browser.js: -------------------------------------------------------------------------------- 1 | kryptos.random.OSRNG.BrowserRNG = function() { 2 | 3 | } 4 | 5 | kryptos.random.OSRNG.BrowserRNG.prototype = { 6 | flush : function() { 7 | // pass 8 | }, 9 | 10 | read : function(N) { 11 | var array = new Uint8Array(N); 12 | crypto.getRandomValues(array); 13 | 14 | var str = ""; // todo fixme - use native array types, and move to chrome worker 15 | for (var x = 0; x < N; ++x) { 16 | str += String.fromCharCode(array[x]); 17 | } 18 | 19 | return str; 20 | }, 21 | 22 | close: function() { 23 | // pass 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /unknown_key.js: -------------------------------------------------------------------------------- 1 | /* 2 | Representation of a key that we don't know about. 3 | */ 4 | paramikojs.UnknownKey = function(keytype, key) { 5 | inherit(this, new paramikojs.PKey()); 6 | 7 | this.keytype = keytype; 8 | this.key = key; 9 | } 10 | 11 | paramikojs.UnknownKey.prototype = { 12 | toString : function() { 13 | return this.key; 14 | }, 15 | 16 | compare : function(other) { 17 | if (this.get_name() != other.get_name()) { 18 | return false; 19 | } 20 | if (this.key != other.key) { 21 | return false; 22 | } 23 | return true; 24 | }, 25 | 26 | get_name : function() { 27 | return this.keytype; 28 | } 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /sign_ssh_data_worker.js: -------------------------------------------------------------------------------- 1 | importScripts('kryptos/kryptos.js', 2 | 'kryptos/PublicKey/RSA.js', 3 | 'common.js', 4 | 'python_shim.js', 5 | 'BigInteger.js', 6 | 'util.js'); 7 | 8 | onmessage = function(event) { 9 | var rsa = new kryptos.publicKey.RSA().construct(new BigInteger(event.data.n, 10), 10 | new BigInteger(event.data.e, 10), 11 | new BigInteger(event.data.d, 10)); 12 | var inflated = paramikojs.util.inflate_long(event.data.pkcs1imified, true); 13 | postMessage(rsa.sign(inflated, '')[0].toString()); 14 | }; 15 | -------------------------------------------------------------------------------- /kryptos/PublicKey/RSA.js: -------------------------------------------------------------------------------- 1 | kryptos.publicKey.RSA = function() { 2 | 3 | } 4 | 5 | kryptos.publicKey.RSA.prototype = { 6 | construct : function(n, e, d) { 7 | this.n = n; 8 | this.e = e; 9 | this.d = d; 10 | 11 | return this; 12 | }, 13 | 14 | sign : function(m, K) { 15 | return [m.modPow(this.d, this.n), '']; 16 | }, 17 | 18 | verify : function(m, sig) { 19 | var s = sig[0]; // HACK - We should use the previous line instead, but 20 | // this is more compatible and we're going to replace 21 | // the Crypto.PublicKey API soon anyway. 22 | return s.modPow(this.e, this.n).equals(m); 23 | }, 24 | 25 | generate : function() { 26 | alert('NOT_IMPLEMENTED'); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /kryptos/Hash/baseHash.js: -------------------------------------------------------------------------------- 1 | kryptos.hash.baseHash = function(data) { 2 | if (data instanceof Array) { 3 | data = kryptos.fromByteArray(data); 4 | } 5 | this.data = data || ""; 6 | } 7 | 8 | kryptos.hash.baseHash.prototype = { 9 | type : '', 10 | 11 | update : function(data) { 12 | if (data instanceof Array) { 13 | data = kryptos.fromByteArray(data); 14 | } 15 | this.data = this.data + data; 16 | }, 17 | 18 | digest : function() { 19 | var hashData = []; 20 | for (var x = 0; x < this.data.length; ++x) { 21 | hashData.push(this.data.charCodeAt(x)); 22 | } 23 | 24 | if(!(Components && Components.classes)) { 25 | throw new Error("Unable to use " + this.type + " hash without Mozilla's Components.classes"); //FIXME 26 | } 27 | var hashComp = Components.classes["@mozilla.org/security/hash;1"].createInstance(Components.interfaces.nsICryptoHash); 28 | hashComp.initWithString(this.type); 29 | hashComp.update(hashData, hashData.length); 30 | var result = hashComp.finish(false); 31 | 32 | return result; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # paramikojs 2 | 3 | ## About 4 | 5 | `paramikojs` is a javascript port of [paramiko](http://www.lag.net/paramiko/). It powers the [fireftp](https://github.com/mimecuvalo/fireftp) and [firessh](https://github.com/mimecuvalo/firessh) projects. However, some caution should be taken if reusing the code as not all code paths have been tested (in python, yes, but not in the translated javascript). So, coder beware! 6 | 7 | ## Getting Started 8 | 9 | `paramikojs` doesn't work out of the box. You should check out the [fireftp](https://github.com/mimecuvalo/fireftp) or [firessh](https://github.com/mimecuvalo/firessh) projects for working examples on how to get something working. In particular, look at [ssh2.js](https://github.com/mimecuvalo/fireftp/blob/master/src/content/js/connection/ssh2.js) as a basic start to see how it glues together. 10 | 11 | ## Note! 12 | 13 | Before you ask, **no** this doesn't work on regular web pages. Being able to make an SSH connection only works currently in the context of a Firefox add-on which gives provides extra libraries/permissions (i.e. ahem, sockets) 14 | -------------------------------------------------------------------------------- /kryptos/Random/LICENSE.txt: -------------------------------------------------------------------------------- 1 | # Written in 2008 by Dwayne C. Litzenberger 2 | # 3 | # =================================================================== 4 | # The contents of this directory are dedicated to the public domain. To 5 | # the extent that dedication to the public domain is not available, 6 | # everyone is granted a worldwide, perpetual, royalty-free, 7 | # non-exclusive license to exercise all rights associated with the 8 | # contents of this file for any purpose whatsoever. 9 | # No rights are reserved. 10 | # 11 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 12 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 13 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 15 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 16 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 17 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | # SOFTWARE. 19 | # =================================================================== 20 | -------------------------------------------------------------------------------- /kex_group14.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Torsten Landschoff 3 | 4 | Standard SSH key exchange ("kex" if you wanna sound cool). Diffie-Hellman of 5 | 2048 bit key halves, using a known "p" prime and "g" generator. 6 | */ 7 | 8 | paramikojs.KexGroup14 = function(transport) { 9 | inherit(this, new paramikojs.KexGroup1(transport)); 10 | 11 | this.P = paramikojs.KexGroup14.P; 12 | this.G = paramikojs.KexGroup14.G; 13 | this.hash_algo = kryptos.hash.SHA; 14 | } 15 | 16 | // http://tools.ietf.org/html/rfc3526#section-3 17 | paramikojs.KexGroup14.P = new BigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16); 18 | paramikojs.KexGroup14.G = new BigInteger("2", 10); 19 | 20 | paramikojs.KexGroup14.prototype = { 21 | name : 'diffie-hellman-group14-sha1' 22 | }; 23 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | paramikojs is a port of the paramiko library: http://www.lag.net/paramiko/ 2 | 3 | The Initial Developer of the Original Code is Robey Pointer 4 | Portions created by the Initial Developer are Copyright (C) 2011 5 | the Initial Developer. All Rights Reserved. 6 | 7 | Contributor(s): 8 | Robey Pointer (original author) 9 | 10 | Mime Cuvalo (translator, python -> javascript) 11 | 12 | This library is free software; you can redistribute it and/or 13 | modify it under the terms of the GNU Lesser General Public 14 | License as published by the Free Software Foundation; either 15 | version 2.1 of the License, or (at your option) any later version. 16 | 17 | This library is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | Lesser General Public License for more details. 21 | 22 | You should have received a copy of the GNU Lesser General Public 23 | License along with this library; if not, write to the Free Software 24 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 25 | You may also obtain a copy of the License at http://www.gnu.org/licenses/lgpl-2.1.html 26 | -------------------------------------------------------------------------------- /kryptos/Cipher/nss3.js: -------------------------------------------------------------------------------- 1 | Components.utils.import("resource://services-sync/util.js"); 2 | 3 | kryptos.cipher.nss3 = function(key, algorithm, mode, iv, counter) { 4 | this.key = base64.encodestring(key); 5 | this.algorithm = algorithm; 6 | this.mode = mode; 7 | this.iv = base64.encodestring(iv); 8 | this.counter = counter; 9 | 10 | this.cryptoSvc = Svc.Crypto; 11 | this.cryptoSvc.algorithm = this.algorithm; 12 | } 13 | 14 | kryptos.cipher.nss3.MODE_CBC = 2; 15 | kryptos.cipher.nss3.MODE_CTR = 6; 16 | 17 | kryptos.cipher.nss3.RC4_128 = 154; 18 | kryptos.cipher.nss3.DES_EDE3_CBC = 156; 19 | kryptos.cipher.nss3.AES_128_CBC = 184; 20 | kryptos.cipher.nss3.AES_256_CBC = 188; 21 | 22 | // todo counter 23 | kryptos.cipher.nss3.prototype = { 24 | encrypt : function(plaintext) { 25 | var ciphertext = base64.decodestring(this.cryptoSvc.encrypt(plaintext, this.key, this.iv)); 26 | if (this.mode == kryptos.cipher.nss3.MODE_CBC) { 27 | this.iv = base64.encodestring(ciphertext.slice(-16)); 28 | } 29 | return ciphertext; 30 | }, 31 | 32 | decrypt : function(ciphertext) { 33 | ciphertext = kryptos.toByteArray(ciphertext); 34 | if (this.mode == kryptos.cipher.nss3.MODE_CBC) { 35 | var plaintext = this.cipher.decrypt(ciphertext, this.iv); 36 | this.iv = ciphertext.slice(-16); 37 | } else { 38 | var plaintext = this.cipher.encrypt(ciphertext, this.iv, this.counter); 39 | } 40 | return kryptos.fromByteArray(plaintext); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /kryptos/kryptos.js: -------------------------------------------------------------------------------- 1 | kryptos = function() {}; 2 | kryptos.prototype = {}; 3 | 4 | kryptos.cipher = {}; 5 | kryptos.hash = {}; 6 | kryptos.protocol = {}; 7 | kryptos.publicKey = {}; 8 | kryptos.random = {}; 9 | kryptos.random.Fortuna = {}; 10 | kryptos.random.OSRNG = {}; 11 | kryptos.util = {}; 12 | 13 | kryptos.toByteArray = function(str) { 14 | function charToUint(chr) { return chr.charCodeAt(0) } 15 | return str.split('').map(charToUint); 16 | }; 17 | 18 | kryptos.fromByteArray = function(data) { 19 | function uintToChar(uint) { return String.fromCharCode(uint) } 20 | return data.map(uintToChar).join(''); 21 | }; 22 | 23 | kryptos.bytesToWords = function(bytes) { 24 | for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) { 25 | words[b >>> 5] |= (bytes[i] & 0xFF) << (24 - b % 32); 26 | } 27 | return words; 28 | }; 29 | 30 | kryptos.wordsToBytes = function(words) { 31 | for (var bytes = [], b = 0; b < words.length * 32; b += 8) { 32 | bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); 33 | } 34 | return bytes; 35 | }; 36 | 37 | kryptos.hexToBytes = function(hexStr) { 38 | for (var bytes = [], ch = 0; ch < hexStr.length; ch += 2) 39 | bytes.push(parseInt(hexStr.substr(ch, 2), 16)); 40 | return bytes; 41 | }; 42 | 43 | kryptos.bytesToHex = function(bytes) { 44 | for (var hexStr = [], ch = 0; ch < bytes.length; ch++) { 45 | hexStr.push((bytes[ch] >>> 4).toString(16)); 46 | hexStr.push((bytes[ch] & 15).toString(16)); 47 | } 48 | return hexStr.join(''); 49 | }; 50 | -------------------------------------------------------------------------------- /kryptos/PublicKey/DSA.js: -------------------------------------------------------------------------------- 1 | kryptos.publicKey.DSA = function() { 2 | 3 | } 4 | 5 | kryptos.publicKey.DSA.prototype = { 6 | construct : function(y, g, p, q, x) { 7 | this.y = y; 8 | this.g = g; 9 | this.p = p; 10 | this.q = q; 11 | this.x = x; 12 | 13 | return this; 14 | }, 15 | 16 | sign : function(m, k) { 17 | // SECURITY TODO - We _should_ be computing SHA1(m), but we don't because that's the API. 18 | var one = BigInteger.ONE; 19 | if (!(k.compareTo(one) > 0 && this.q.compareTo(k) > 0)) { 20 | throw "k is not between 2 and q-1"; 21 | } 22 | var inv_k = k.modInverse(this.q); // Compute k**-1 mod q 23 | var r = this.g.modPow(k, this.p).mod(this.q); // r = (g**k mod p) mod q 24 | var s = inv_k.multiply(m.add(this.x.multiply(r))).mod(this.q); 25 | return [r, s]; 26 | }, 27 | 28 | verify : function(m, sig) { 29 | var r = sig[0]; 30 | var s = sig[1]; 31 | var zero = BigInteger.ZERO; 32 | // SECURITY TODO - We _should_ be computing SHA1(m), but we don't because that's the API. 33 | if (!(r.compareTo(zero) > 0 && this.q.compareTo(r) > 0) || !(s.compareTo(zero) > 0 && this.q.compareTo(s) > 0)) { 34 | return false; 35 | } 36 | var w = s.modInverse(this.q); 37 | var u1 = m.multiply(w).mod(this.q); 38 | var u2 = r.multiply(w).mod(this.q); 39 | var v = this.g.modPow(u1, this.p).multiply(this.y.modPow(u2, this.p)).mod(this.p).mod(this.q); 40 | return v.equals(r); 41 | }, 42 | 43 | generate : function() { 44 | alert('NOT_IMPLEMENTED'); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /kryptos/Hash/SHA.js: -------------------------------------------------------------------------------- 1 | kryptos.hash.SHA = function(str) { 2 | inherit(this, new kryptos.hash.baseHash(str)); 3 | } 4 | 5 | kryptos.hash.SHA.digest_size = 20; 6 | 7 | kryptos.hash.SHA.prototype = { 8 | type : 'sha1' 9 | }; 10 | 11 | // http://code.google.com/p/crypto-js/source/browse/branches/2.x/src/SHA1.js 12 | // BSD license: http://www.opensource.org/licenses/bsd-license.php 13 | if (!(Components && Components.classes)) { // Chrome 14 | kryptos.hash.SHA.prototype = { 15 | digest: function() { 16 | var hashData = kryptos.toByteArray(this.data); 17 | 18 | var m = kryptos.bytesToWords(hashData), 19 | l = hashData.length * 8, 20 | w = [], 21 | H0 = 1732584193, 22 | H1 = -271733879, 23 | H2 = -1732584194, 24 | H3 = 271733878, 25 | H4 = -1009589776; 26 | 27 | // Padding 28 | m[l >> 5] |= 0x80 << (24 - l % 32); 29 | m[((l + 64 >>> 9) << 4) + 15] = l; 30 | 31 | for (var i = 0; i < m.length; i += 16) { 32 | var a = H0, 33 | b = H1, 34 | c = H2, 35 | d = H3, 36 | e = H4; 37 | 38 | for (var j = 0; j < 80; j++) { 39 | if (j < 16) { 40 | w[j] = m[i + j]; 41 | } else { 42 | var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16]; 43 | w[j] = (n << 1) | (n >>> 31); 44 | } 45 | 46 | var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + ( 47 | j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 : 48 | j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 : 49 | j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 : 50 | (H1 ^ H2 ^ H3) - 899497514); 51 | 52 | H4 = H3; 53 | H3 = H2; 54 | H2 = (H1 << 30) | (H1 >>> 2); 55 | H1 = H0; 56 | H0 = t; 57 | } 58 | 59 | H0 += a; 60 | H1 += b; 61 | H2 += c; 62 | H3 += d; 63 | H4 += e; 64 | } 65 | 66 | return kryptos.fromByteArray(kryptos.wordsToBytes([H0, H1, H2, H3, H4])); 67 | } 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /kryptos/Hash/HMAC.js: -------------------------------------------------------------------------------- 1 | if (Components && Components.classes) { // Mozilla extension 2 | kryptos.hash.HMAC = function(key, msg, digestmod) { 3 | var hasher = Components.classes["@mozilla.org/security/hmac;1"].createInstance(Components.interfaces.nsICryptoHMAC); 4 | var keyObject = Components.classes["@mozilla.org/security/keyobjectfactory;1"] 5 | .getService(Components.interfaces.nsIKeyObjectFactory) 6 | .keyFromString(Components.interfaces.nsIKeyObject.HMAC, key); 7 | 8 | hasher.init(digestmod, keyObject); 9 | var data = kryptos.toByteArray(msg); 10 | hasher.update(data, data.length); 11 | return hasher.finish(false); 12 | }; 13 | 14 | kryptos.hash.HMAC_SHA = Components.classes["@mozilla.org/security/hmac;1"].createInstance(Components.interfaces.nsICryptoHMAC).SHA1; 15 | kryptos.hash.HMAC_SHA256 = Components.classes["@mozilla.org/security/hmac;1"].createInstance(Components.interfaces.nsICryptoHMAC).SHA256; 16 | kryptos.hash.HMAC_SHA512 = Components.classes["@mozilla.org/security/hmac;1"].createInstance(Components.interfaces.nsICryptoHMAC).SHA512; 17 | kryptos.hash.HMAC_MD5 = Components.classes["@mozilla.org/security/hmac;1"].createInstance(Components.interfaces.nsICryptoHMAC).MD5; 18 | } else { // Chrome or plain Mozilla 19 | kryptos.hash.HMAC = function(key, msg, digestmod) { 20 | var blocksize = 64; 21 | var ipad = 0x36; 22 | var opad = 0x5C; 23 | 24 | var hasher; 25 | switch (digestmod) { 26 | case 2: 27 | hasher = kryptos.hash.MD5; 28 | break; 29 | case 3: 30 | hasher = kryptos.hash.SHA; 31 | break; 32 | case 4: 33 | hasher = kryptos.hash.SHA256; 34 | break; 35 | case 6: 36 | hasher = kryptos.hash.SHA512; 37 | blocksize = 128; 38 | break; 39 | } 40 | 41 | var outer = new hasher(); 42 | var inner = new hasher(); 43 | 44 | if (key.length > blocksize) { 45 | key = new hasher(key).digest(); 46 | } 47 | 48 | key = key + new Array(blocksize - key.length + 1).join('\x00'); 49 | 50 | var okey = kryptos.toByteArray(key).slice(0); 51 | var ikey = kryptos.toByteArray(key).slice(0); 52 | 53 | for (var x = 0; x < blocksize; ++x) { 54 | okey[x] ^= opad; 55 | ikey[x] ^= ipad; 56 | } 57 | 58 | outer.update(okey); 59 | inner.update(ikey); 60 | inner.update(msg); 61 | outer.update(inner.digest()); 62 | return outer.digest(); 63 | }; 64 | kryptos.hash.HMAC_SHA = 3; 65 | kryptos.hash.HMAC_SHA256 = 4; 66 | kryptos.hash.HMAC_SHA512 = 6; 67 | kryptos.hash.HMAC_MD5 = 2; 68 | } 69 | -------------------------------------------------------------------------------- /kryptos/Random/_UserFriendlyRNG.js: -------------------------------------------------------------------------------- 1 | kryptos.random.Random = function () { 2 | this._fa = new kryptos.random.Fortuna.FortunaAccumulator(); 3 | this._ec = new kryptos.random._EntropyCollector(this._fa); 4 | this.reinit(); 5 | } 6 | 7 | kryptos.random.Random.prototype = { 8 | reinit : function() { 9 | /* 10 | Initialize the random number generator and seed it with entropy from 11 | the operating system. 12 | */ 13 | this._ec.reinit(); 14 | }, 15 | 16 | flush : function(s) { 17 | // pass 18 | }, 19 | 20 | // Return N bytes from the RNG. 21 | read : function(N, dontFlush) { 22 | // Collect some entropy and feed it to Fortuna 23 | this._ec.collect(dontFlush); 24 | 25 | // Ask Fortuna to generate some bytes 26 | var retval = this._fa.random_data(N); 27 | 28 | // Return the random data. 29 | return retval; 30 | } 31 | }; 32 | 33 | 34 | kryptos.random._EntropySource = function(accumulator, src_num) { 35 | this._fortuna = accumulator; 36 | this._src_num = src_num; 37 | this._pool_num = 0; 38 | } 39 | 40 | kryptos.random._EntropySource.prototype = { 41 | feed : function(data) { 42 | this._fortuna.add_random_event(this._src_num, this._pool_num, data); 43 | this._pool_num = (this._pool_num + 1) & 31; 44 | } 45 | }; 46 | 47 | kryptos.random._EntropyCollector = function(accumulator) { 48 | this._osrng = new kryptos.random.OSRNG.BrowserRNG(); 49 | this._osrng_es = new kryptos.random._EntropySource(accumulator, 255); 50 | this._time_es = new kryptos.random._EntropySource(accumulator, 254); 51 | this._time2_es = new kryptos.random._EntropySource(accumulator, 253); 52 | 53 | this.previousMilliseconds = new Date().getMilliseconds(); 54 | } 55 | 56 | kryptos.random._EntropyCollector.prototype = { 57 | reinit : function() { 58 | // Add 256 bits to each of the 32 pools, twice. (For a total of 16384 59 | // bits collected from the operating system.) 60 | for (var i = 0; i < 2; ++i) { 61 | var block = this._osrng.read(32*32); 62 | for (var p = 0; p < 32; ++p) { 63 | this._osrng_es.feed(block.substring(p*32,(p+1)*32)); 64 | } 65 | block = null; 66 | } 67 | this._osrng.flush(); 68 | }, 69 | 70 | collect : function(dontFlush) { 71 | // Collect 64 bits of entropy from the operating system and feed it to Fortuna. 72 | this._osrng_es.feed(this._osrng.read(8, dontFlush)); 73 | 74 | // Add the fractional part of date 75 | var t = new Date().getMilliseconds() * Math.random() / 1000; 76 | this._time_es.feed(struct.pack("@I", parseInt(Math.pow(2, 30) * (t - Math.floor(t))))); 77 | 78 | // Add another fractional part of date 79 | var newMilliseconds = new Date().getMilliseconds(); 80 | t = ((this.previousMilliseconds + newMilliseconds) % 1000) * Math.random() / 1000; 81 | this.previousMilliseconds = newMilliseconds; 82 | this._time2_es.feed(struct.pack("@I", parseInt(Math.pow(2, 30) * (t - Math.floor(t))))); 83 | } 84 | }; 85 | -------------------------------------------------------------------------------- /common.js: -------------------------------------------------------------------------------- 1 | function paramikojs() { } 2 | paramikojs = { 3 | MSG_DISCONNECT : 1, 4 | MSG_IGNORE : 2, 5 | MSG_UNIMPLEMENTED : 3, 6 | MSG_DEBUG : 4, 7 | MSG_SERVICE_REQUEST : 5, 8 | MSG_SERVICE_ACCEPT : 6, 9 | 10 | MSG_KEXINIT : 20, 11 | MSG_NEWKEYS : 21, 12 | 13 | MSG_USERAUTH_REQUEST : 50, 14 | MSG_USERAUTH_FAILURE : 51, 15 | MSG_USERAUTH_SUCCESS : 52, 16 | MSG_USERAUTH_BANNER : 53, 17 | 18 | MSG_USERAUTH_PK_OK : 60, 19 | 20 | MSG_USERAUTH_INFO_REQUEST : 60, 21 | MSG_USERAUTH_INFO_RESPONSE : 61, 22 | 23 | MSG_GLOBAL_REQUEST : 80, 24 | MSG_REQUEST_SUCCESS : 81, 25 | MSG_REQUEST_FAILURE : 82, 26 | 27 | MSG_CHANNEL_OPEN : 90, 28 | MSG_CHANNEL_OPEN_SUCCESS : 91, 29 | MSG_CHANNEL_OPEN_FAILURE : 92, 30 | MSG_CHANNEL_WINDOW_ADJUST : 93, 31 | MSG_CHANNEL_DATA : 94, 32 | MSG_CHANNEL_EXTENDED_DATA : 95, 33 | MSG_CHANNEL_EOF : 96, 34 | MSG_CHANNEL_CLOSE : 97, 35 | MSG_CHANNEL_REQUEST : 98, 36 | MSG_CHANNEL_SUCCESS : 99, 37 | MSG_CHANNEL_FAILURE : 100, 38 | 39 | // for debugging: 40 | MSG_NAMES : { 41 | 1: 'disconnect', 42 | 2: 'ignore', 43 | 3: 'unimplemented', 44 | 4: 'debug', 45 | 5: 'service-request', 46 | 6: 'service-accept', 47 | 20: 'kexinit', 48 | 21: 'newkeys', 49 | 30: 'kex30', 50 | 31: 'kex31', 51 | 32: 'kex32', 52 | 33: 'kex33', 53 | 34: 'kex34', 54 | 50: 'userauth-request', 55 | 51: 'userauth-failure', 56 | 52: 'userauth-success', 57 | 53: 'userauth--banner', 58 | 60: 'userauth-60(pk-ok/info-request)', 59 | 61: 'userauth-info-response', 60 | 80: 'global-request', 61 | 81: 'request-success', 62 | 82: 'request-failure', 63 | 90: 'channel-open', 64 | 91: 'channel-open-success', 65 | 92: 'channel-open-failure', 66 | 93: 'channel-window-adjust', 67 | 94: 'channel-data', 68 | 95: 'channel-extended-data', 69 | 96: 'channel-eof', 70 | 97: 'channel-close', 71 | 98: 'channel-request', 72 | 99: 'channel-success', 73 | 100: 'channel-failure' 74 | }, 75 | 76 | // authentication request return codes: 77 | AUTH_SUCCESSFUL : 0, 78 | AUTH_PARTIALLY_SUCCESSFUL : 1, 79 | AUTH_FAILED : 2, 80 | 81 | // channel request failed reasons: 82 | OPEN_SUCCEEDED : 0, 83 | OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED : 1, 84 | OPEN_FAILED_CONNECT_FAILED : 2, 85 | OPEN_FAILED_UNKNOWN_CHANNEL_TYPE : 3, 86 | OPEN_FAILED_RESOURCE_SHORTAGE : 4, 87 | 88 | CONNECTION_FAILED_CODE : { 89 | 1: 'Administratively prohibited', 90 | 2: 'Connect failed', 91 | 3: 'Unknown channel type', 92 | 4: 'Resource shortage' 93 | }, 94 | 95 | DISCONNECT_SERVICE_NOT_AVAILABLE : 7, 96 | DISCONNECT_AUTH_CANCELLED_BY_USER : 13, 97 | DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE : 14 98 | }; 99 | 100 | function inherit(derived, base) { 101 | for (property in base) { 102 | if (!derived[property]) { 103 | derived[property] = base[property]; 104 | } 105 | } 106 | } 107 | 108 | var logging = { 109 | DEBUG : 10, 110 | INFO : 20, 111 | WARNING : 30, 112 | ERROR : 40, 113 | CRITICAL : 50, 114 | 115 | log : function(level, msg) { 116 | /* Override this as desired. */ 117 | } 118 | }; 119 | DEBUG = logging.DEBUG; 120 | INFO = logging.INFO; 121 | WARNING = logging.WARNING; 122 | ERROR = logging.ERROR; 123 | CRITICAL = logging.CRITICAL; 124 | -------------------------------------------------------------------------------- /kryptos/Random/Fortuna/FortunaGenerator.js: -------------------------------------------------------------------------------- 1 | kryptos.random.Fortuna.FortunaGenerator = {}; 2 | 3 | kryptos.random.Fortuna.FortunaGenerator.AESGenerator = function() { 4 | /* 5 | The Fortuna "generator" 6 | 7 | This is used internally by the Fortuna PRNG to generate arbitrary amounts 8 | of pseudorandom data from a smaller amount of seed data. 9 | 10 | The output is generated by running AES-256 in counter mode and re-keying 11 | after every mebibyte (2**16 blocks) of output. 12 | */ 13 | 14 | this.counter = new paramikojs.util.Counter(this.block_size * 8, 0); 15 | this.key = null; 16 | 17 | // Set some helper constants 18 | this.block_size_shift = 4; // exact_log2(this.block_size); 19 | 20 | this.blocks_per_key = 2; // exact_div(this.key_size, this.block_size); 21 | 22 | this.max_bytes_per_request = this.max_blocks_per_request * this.block_size; 23 | } 24 | 25 | kryptos.random.Fortuna.FortunaGenerator.AESGenerator.prototype = { 26 | block_size : 16, // output block size in octets (128 bits) 27 | key_size : 32, // key size in octets (256 bits) 28 | 29 | // Because of the birthday paradox, we expect to find approximately one 30 | // collision for every 2**64 blocks of output from a real random source. 31 | // However, this code generates pseudorandom data by running AES in 32 | // counter mode, so there will be no collisions until the counter 33 | // (theoretically) wraps around at 2**128 blocks. Thus, in order to prevent 34 | // Fortuna's pseudorandom output from deviating perceptibly from a true 35 | // random source, Ferguson and Schneier specify a limit of 2**16 blocks 36 | // without rekeying. 37 | max_blocks_per_request : Math.pow(2, 16), // Allow no more than this number of blocks per _pseudo_random_data request 38 | 39 | _four_kiblocks_of_zeros : new Array(16 * 4096 + 1).join("\0"), // 16 == block_size 40 | 41 | reseed : function(seed) { 42 | if (!this.key) { 43 | this.key = new Array(this.key_size + 1).join("\0"); 44 | } 45 | this._set_key(new kryptos.random.Fortuna.SHAd256(this.key + seed).digest()); 46 | this.counter.call(); // increment counter 47 | }, 48 | 49 | pseudo_random_data : function(bytes) { 50 | var num_full_blocks = bytes >> 20; 51 | var remainder = bytes & ((1<<20)-1); 52 | 53 | var retval = []; 54 | for (var i = 0; i < num_full_blocks; ++i) { 55 | retval.push(this._pseudo_random_data(1<<20)); 56 | } 57 | retval.push(this._pseudo_random_data(remainder)); 58 | 59 | return retval.join(""); 60 | }, 61 | 62 | _set_key : function(key) { 63 | this.key = key; 64 | this._cipher = new kryptos.cipher.AES(key, kryptos.cipher.AES.MODE_CTR, 0, this.counter); 65 | }, 66 | 67 | _pseudo_random_data : function(bytes) { 68 | var num_blocks = Math.ceil(1.0 * bytes / this.block_size_shift); // num_blocks = ceil(bytes / self.block_size) 69 | 70 | // Compute the output 71 | var retval = this._generate_blocks(num_blocks).substring(0, bytes); 72 | 73 | // Switch to a new key to avoid later compromises of this output (i.e. 74 | // state compromise extension attacks) 75 | this._set_key(this._generate_blocks(this.blocks_per_key)); 76 | 77 | return retval; 78 | }, 79 | 80 | _generate_blocks : function(num_blocks) { 81 | var retval = []; 82 | for (var i = 0; i < num_blocks >> 12; ++i) { // xrange(num_blocks / 4096) 83 | retval.push(this._cipher.encrypt(this._four_kiblocks_of_zeros)); 84 | } 85 | var remaining_bytes = (num_blocks & 4095) << this.block_size_shift; // (num_blocks % 4095) * self.block_size 86 | retval.push(this._cipher.encrypt(this._four_kiblocks_of_zeros.substring(0, remaining_bytes))); 87 | return retval.join(""); 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /ber.js: -------------------------------------------------------------------------------- 1 | paramikojs.BER = function (content) { 2 | this.content = content; 3 | this.idx = 0; 4 | } 5 | 6 | paramikojs.BER.prototype = { 7 | toString : function() { 8 | return this.content; 9 | }, 10 | 11 | decode : function() { 12 | return this.decode_next(); 13 | }, 14 | 15 | decode_next : function() { 16 | if (this.idx >= this.content.length) { 17 | return null; 18 | } 19 | var ident = this.content[this.idx].charCodeAt(0); 20 | var t; 21 | this.idx += 1; 22 | if ((ident & 31) == 31) { 23 | // identifier > 30 24 | ident = 0; 25 | while (this.idx < this.content.length) { 26 | t = this.content[this.idx].charCodeAt(0); 27 | this.idx += 1; 28 | ident = (ident << 7) | (t & 0x7f); 29 | if (!(t & 0x80)) { 30 | break; 31 | } 32 | } 33 | } 34 | if (this.idx >= this.content.length) { 35 | return null; 36 | } 37 | // now fetch length 38 | var size = this.content[this.idx].charCodeAt(0); 39 | this.idx += 1; 40 | if (size & 0x80) { 41 | // more complimicated... 42 | // FIXME: theoretically should handle indefinite-length (0x80) 43 | t = size & 0x7f; 44 | if (this.idx + t > this.content.length) { 45 | return null; 46 | } 47 | size = paramikojs.util.inflate_long(this.content.substring(this.idx, this.idx + t), true).intValue(); 48 | this.idx += t; 49 | } 50 | if (this.idx + size > this.content.length) { 51 | // can't fit 52 | return null; 53 | } 54 | var data = this.content.substring(this.idx, this.idx + size); 55 | this.idx += size; 56 | // now switch on id 57 | if (ident == 0x30) { 58 | // sequence 59 | return this.decode_sequence(data); 60 | } else if (ident == 2) { 61 | // int 62 | return paramikojs.util.inflate_long(data); 63 | } else { 64 | // 1: boolean (00 false, otherwise true) 65 | throw new paramikojs.ssh_exception.BERException('Unknown ber encoding type ' + ident + ' (robey is lazy)'); 66 | } 67 | }, 68 | 69 | decode_sequence : function(data) { 70 | var out = []; 71 | var b = new paramikojs.BER(data); 72 | while (true) { 73 | var x = b.decode_next(); 74 | if (!x) { 75 | break; 76 | } 77 | out.push(x); 78 | } 79 | return out; 80 | }, 81 | 82 | encode_tlv : function(ident, val) { 83 | // no need to support ident > 31 here 84 | this.content += String.fromCharCode(ident); 85 | if (val.length > 0x7f) { 86 | var lenstr = paramikojs.util.deflate_long(val.length); 87 | this.content += String.fromCharCode(0x80 + lenstr.length) + lenstr; 88 | } else { 89 | this.content += String.fromCharCode(val.length); 90 | } 91 | this.content += val; 92 | }, 93 | 94 | encode : function(x) { 95 | if (typeof x == "boolean") { 96 | if (x) { 97 | this.encode_tlv(1, '\xff'); 98 | } else { 99 | this.encode_tlv(1, '\x00'); 100 | } 101 | } else if (typeof x == "number") { 102 | this.encode_tlv(2, paramikojs.util.deflate_long(x)); 103 | } else if (typeof x == "string") { 104 | this.encode_tlv(4, x); 105 | } else if (x instanceof Array) { 106 | this.encode_tlv(0x30, this.encode_sequence(x)); 107 | } else { 108 | throw new paramikojs.ssh_exception.BERException('Unknown type for encoding: ' + typeof x); 109 | } 110 | }, 111 | 112 | encode_sequence : function(data) { 113 | var b = new paramikojs.BER(); 114 | for (var x = 0; x < data.length; ++x) { 115 | b.encode(data[x]); 116 | } 117 | return str(b); 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /win_pageant.js: -------------------------------------------------------------------------------- 1 | paramikojs.win_pageant = function() { 2 | 3 | } 4 | 5 | paramikojs.win_pageant.prototype = { 6 | _AGENT_COPYDATA_ID : 0x804e50ba, 7 | _AGENT_MAX_MSGLEN : 8192, 8 | // Note: The WM_COPYDATA value is pulled from win32con, as a workaround 9 | // so we do not have to import this huge library just for this one variable. 10 | win32con_WM_COPYDATA : 74, 11 | 12 | _get_pageant_window_object : function() { 13 | return ctypes.windll.user32.FindWindowA('Pageant', 'Pageant'); // todo fixme 14 | }, 15 | 16 | /* 17 | Check to see if there is a "Pageant" agent we can talk to. 18 | 19 | This checks both if we have the required libraries (win32all or ctypes) 20 | and if there is a Pageant currently running. 21 | */ 22 | can_talk_to_agent : function() { 23 | if (this._get_pageant_window_object()) { 24 | return true; 25 | } 26 | return false; 27 | }, 28 | 29 | _query_pageant : function(msg) { 30 | var hwnd = _get_pageant_window_object(); // todo fixme this whole thing! 31 | if (!hwnd) { 32 | // Raise a failure to connect exception, pageant isn't running anymore! 33 | return null; 34 | } 35 | 36 | // Write our pageant request string into the file (pageant will read this to determine what to do) 37 | var filename = tempfile.mktemp('.pag'); // todo fixme 38 | var map_filename = os.path.basename(filename); // todo fixme 39 | 40 | var f = open(filename, 'w+b'); // todo fixme 41 | f.write(msg); 42 | // Ensure the rest of the file is empty, otherwise pageant will read this 43 | f.write('\0' * (this._AGENT_MAX_MSGLEN - msg.length)); 44 | // Create the shared file map that pageant will use to read from 45 | var pymap = mmap.mmap(f.fileno(), this._AGENT_MAX_MSGLEN, tagname=map_filename, access=mmap.ACCESS_WRITE); 46 | try { 47 | // Create an array buffer containing the mapped filename 48 | var char_buffer = array.array("c", map_filename + '\0'); 49 | char_buffer_address, char_buffer_size = char_buffer.buffer_info(); 50 | // Create a string to use for the SendMessage function call 51 | cds = struct.pack("LLP", this._AGENT_COPYDATA_ID, char_buffer_size, char_buffer_address); 52 | 53 | _buf = array.array('B', cds); 54 | _addr, _size = _buf.buffer_info(); 55 | response = ctypes.windll.user32.SendMessageA(hwnd, win32con_WM_COPYDATA, _size, _addr); 56 | 57 | if (response > 0) { 58 | datalen = pymap.read(4); 59 | retlen = struct.unpack('>I', datalen)[0]; 60 | return datalen + pymap.read(retlen); 61 | } 62 | return null; 63 | } catch(ex) { 64 | } finally { 65 | pymap.close(); 66 | f.close(); 67 | // Remove the file, it was temporary only 68 | os.unlink(filename); 69 | } 70 | }, 71 | 72 | /* 73 | Mock "connection" to an agent which roughly approximates the behavior of 74 | a unix local-domain socket (as used by Agent). Requests are sent to the 75 | pageant daemon via special Windows magick, and responses are buffered back 76 | for subsequent reads. 77 | */ 78 | PageantConnection : { 79 | response : null, 80 | 81 | send : function(data) { 82 | this._response = paramikojs.win_pageant._query_pageant(data); 83 | }, 84 | 85 | recv : function(n) { 86 | if (!this._response) { 87 | return ''; 88 | } 89 | ret = this._response.substring(0, n); 90 | this._response = this._response.substring(n); 91 | if (this._response == '') { 92 | this._response = null; 93 | } 94 | return ret; 95 | }, 96 | 97 | close : function() {} 98 | } 99 | }; 100 | 101 | 102 | -------------------------------------------------------------------------------- /python_shim.js: -------------------------------------------------------------------------------- 1 | var binascii = { 2 | hexlify : function(str, padding) { 3 | var result = ""; 4 | padding = padding || ''; 5 | for (var x = 0; x < str.length; ++x) { 6 | var c = str.charCodeAt(x).toString(16); 7 | result += (c.length == 1 ? '0' : '') + c + padding; 8 | } 9 | return result; 10 | }, 11 | 12 | unhexlify : function(str) { 13 | var result = ""; 14 | for (var x = 0; x < str.length; x += 2) { 15 | result += String.fromCharCode(parseInt(str.charAt(x) + str.charAt(x + 1), 16)); 16 | } 17 | return result; 18 | } 19 | }; 20 | 21 | var base64 = { 22 | encodestring : function(input) { 23 | return window.btoa(input); 24 | }, 25 | 26 | decodestring : function(input) { 27 | return window.atob(input); 28 | } 29 | }; 30 | 31 | /* 32 | This is a dumbed down version of the python function only doing a couple formats and big-endian only currently. 33 | todo: allow for unlimited arguments 34 | */ 35 | var struct = { 36 | pack : function(fmt, v) { 37 | var type = fmt[1]; 38 | var result = ""; 39 | switch (type) { 40 | case 'Q': 41 | var ff = new BigInteger('ff', 16); 42 | result += String.fromCharCode(v.shiftRight(56).and(ff)); 43 | result += String.fromCharCode(v.shiftRight(48).and(ff)); 44 | result += String.fromCharCode(v.shiftRight(40).and(ff)); 45 | result += String.fromCharCode(v.shiftRight(32).and(ff)); 46 | result += String.fromCharCode(v.shiftRight(24).and(ff)); 47 | result += String.fromCharCode(v.shiftRight(16).and(ff)); 48 | result += String.fromCharCode(v.shiftRight(8).and(ff)); 49 | result += String.fromCharCode(v.and(ff)); 50 | break; 51 | case 'I': 52 | result += String.fromCharCode(v >>> 24 & 0xff); 53 | result += String.fromCharCode(v >>> 16 & 0xff); 54 | result += String.fromCharCode(v >>> 8 & 0xff); 55 | // fall through 56 | case 'B': 57 | result += String.fromCharCode(v & 0xff); 58 | break; 59 | } 60 | 61 | return result; 62 | }, 63 | 64 | unpack : function(fmt, str) { 65 | var type = fmt[1]; 66 | var result = []; 67 | var index = 0; 68 | var v = 0; 69 | switch (type) { 70 | case 'Q': 71 | v = new BigInteger("0", 10); 72 | 73 | v = v.add(new BigInteger(str.charCodeAt(0).toString(), 10).shiftLeft(56)); 74 | v = v.add(new BigInteger(str.charCodeAt(1).toString(), 10).shiftLeft(48)); 75 | v = v.add(new BigInteger(str.charCodeAt(2).toString(), 10).shiftLeft(40)); 76 | v = v.add(new BigInteger(str.charCodeAt(3).toString(), 10).shiftLeft(32)); 77 | v = v.add(new BigInteger(str.charCodeAt(4).toString(), 10).shiftLeft(24)); 78 | v = v.add(new BigInteger(str.charCodeAt(5).toString(), 10).shiftLeft(16)); 79 | v = v.add(new BigInteger(str.charCodeAt(6).toString(), 10).shiftLeft(8)); 80 | v = v.add(new BigInteger(str.charCodeAt(7).toString(), 10).shiftLeft(0)); 81 | result.push(v); 82 | break; 83 | case 'I': 84 | v += str.charCodeAt(0) << 24 >>> 0; 85 | v += str.charCodeAt(1) << 16 >>> 0; 86 | v += str.charCodeAt(2) << 8 >>> 0; 87 | index += 3; 88 | // fall through 89 | case 'B': 90 | v += str.charCodeAt(0 + index) << 0 >>> 0; 91 | result.push(v); 92 | break; 93 | } 94 | 95 | return result; 96 | } 97 | }; 98 | 99 | var sys = { 100 | 'browser' : typeof window !== 'undefined' ? (navigator.userAgent.toLowerCase().indexOf('chrome') != -1 ? 'chrome' : 'mozilla') : 'server', 101 | 'platform' : typeof window !== 'undefined' ? (navigator.platform.toLowerCase().indexOf('linux') != -1 ? 'linux' : 102 | (navigator.platform.toLowerCase().indexOf('mac') != -1 ? 'darwin' : 'win32')) : 'server' 103 | }; 104 | -------------------------------------------------------------------------------- /kryptos/Hash/SHA256.js: -------------------------------------------------------------------------------- 1 | kryptos.hash.SHA256 = function(str) { 2 | inherit(this, new kryptos.hash.baseHash(str)); 3 | } 4 | 5 | kryptos.hash.SHA256.digest_size = 32; 6 | 7 | kryptos.hash.SHA256.prototype = { 8 | type : 'sha256' 9 | }; 10 | 11 | // http://code.google.com/p/crypto-js/source/browse/branches/2.x/src/SHA256.js 12 | // BSD license: http://www.opensource.org/licenses/bsd-license.php 13 | if (!(Components && Components.classes)) { // Chrome 14 | kryptos.hash.SHA256.prototype = { 15 | digest: function() { 16 | var hashData = kryptos.toByteArray(this.data); 17 | 18 | // Constants 19 | var K = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 20 | 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 21 | 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 22 | 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 23 | 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 24 | 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 25 | 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 26 | 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 27 | 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 28 | 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 29 | 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 30 | 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 31 | 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 32 | 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 33 | 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 34 | 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 ]; 35 | 36 | var m = kryptos.bytesToWords(hashData), 37 | l = hashData.length * 8, 38 | H = [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 39 | 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ], 40 | w = [], 41 | a, b, c, d, e, f, g, h, i, j, 42 | t1, t2; 43 | 44 | // Padding 45 | m[l >> 5] |= 0x80 << (24 - l % 32); 46 | m[((l + 64 >> 9) << 4) + 15] = l; 47 | 48 | for (var i = 0; i < m.length; i += 16) { 49 | a = H[0]; 50 | b = H[1]; 51 | c = H[2]; 52 | d = H[3]; 53 | e = H[4]; 54 | f = H[5]; 55 | g = H[6]; 56 | h = H[7]; 57 | 58 | for (var j = 0; j < 64; j++) { 59 | if (j < 16) { 60 | w[j] = m[j + i]; 61 | } else { 62 | var gamma0x = w[j - 15], 63 | gamma1x = w[j - 2], 64 | gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ 65 | ((gamma0x << 14) | (gamma0x >>> 18)) ^ 66 | (gamma0x >>> 3), 67 | gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ 68 | ((gamma1x << 13) | (gamma1x >>> 19)) ^ 69 | (gamma1x >>> 10); 70 | 71 | w[j] = gamma0 + (w[j - 7] >>> 0) + 72 | gamma1 + (w[j - 16] >>> 0); 73 | } 74 | 75 | var ch = e & f ^ ~e & g, 76 | maj = a & b ^ a & c ^ b & c, 77 | sigma0 = ((a << 30) | (a >>> 2)) ^ 78 | ((a << 19) | (a >>> 13)) ^ 79 | ((a << 10) | (a >>> 22)), 80 | sigma1 = ((e << 26) | (e >>> 6)) ^ 81 | ((e << 21) | (e >>> 11)) ^ 82 | ((e << 7) | (e >>> 25)); 83 | 84 | t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0); 85 | t2 = sigma0 + maj; 86 | 87 | h = g; 88 | g = f; 89 | f = e; 90 | e = (d + t1) >>> 0; 91 | d = c; 92 | c = b; 93 | b = a; 94 | a = (t1 + t2) >>> 0; 95 | } 96 | 97 | H[0] += a; 98 | H[1] += b; 99 | H[2] += c; 100 | H[3] += d; 101 | H[4] += e; 102 | H[5] += f; 103 | H[6] += g; 104 | H[7] += h; 105 | } 106 | 107 | return kryptos.fromByteArray(kryptos.wordsToBytes(H)); 108 | } 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /kryptos/Random/Fortuna/FortunaAccumulator.js: -------------------------------------------------------------------------------- 1 | kryptos.random.Fortuna.FortunaPool = function() { 2 | /* 3 | Fortuna pool type 4 | 5 | This object acts like a hash object, with the following differences: 6 | 7 | - It keeps a count (the .length attribute) of the number of bytes that 8 | have been added to the pool 9 | - It supports a .reset() method for in-place reinitialization 10 | - The method to add bytes to the pool is .append(), not .update(). 11 | */ 12 | this.reset(); 13 | } 14 | 15 | kryptos.random.Fortuna.FortunaPool.prototype = { 16 | digest_size : kryptos.random.Fortuna.SHAd256.digest_size, 17 | 18 | append : function(data) { 19 | this._h.update(data); 20 | this.length += data.length; 21 | }, 22 | 23 | digest : function() { 24 | return this._h.digest(); 25 | }, 26 | 27 | reset : function() { 28 | this._h = new kryptos.random.Fortuna.SHAd256(); 29 | this.length = 0; 30 | } 31 | }; 32 | 33 | 34 | kryptos.random.Fortuna.which_pools = function(r) { 35 | /* 36 | Return a list of pools indexes (in range(32)) that are to be included during reseed number r. 37 | 38 | According to _Practical Cryptography_, chapter 10.5.2 "Pools": 39 | 40 | "Pool P_i is included if 2**i is a divisor of r. Thus P_0 is used 41 | every reseed, P_1 every other reseed, P_2 every fourth reseed, etc." 42 | */ 43 | // This is a separate function so that it can be unit-tested. 44 | 45 | var retval = []; 46 | var mask = 0; 47 | for (var i = 0; i < 32; ++i) { 48 | // "Pool P_i is included if 2**i is a divisor of [reseed_count]" 49 | if ((r & mask) == 0) { 50 | retval.push(i); 51 | } else { 52 | break; // optimization. once this fails, it always fails 53 | } 54 | mask = (mask << 1) | 1; 55 | } 56 | return retval; 57 | } 58 | 59 | 60 | kryptos.random.Fortuna.FortunaAccumulator = function() { 61 | this.reseed_count = 0; 62 | this.generator = new kryptos.random.Fortuna.FortunaGenerator.AESGenerator(); 63 | this.last_reseed = null; 64 | 65 | // Initialize 32 FortunaPool instances. 66 | // NB: This is _not_ equivalent to [FortunaPool()]*32, which would give 67 | // us 32 references to the _same_ FortunaPool instance (and cause the 68 | // assertion below to fail). 69 | this.pools = []; 70 | for (var i = 0; i < 32; ++i) { // 32 pools 71 | this.pools.push(new kryptos.random.Fortuna.FortunaPool()); 72 | } 73 | } 74 | 75 | kryptos.random.Fortuna.FortunaAccumulator.prototype = { 76 | min_pool_size : 64, // TODO: explain why 77 | reseed_interval : 0.100, // 100 ms TODO: explain why 78 | 79 | random_data : function(bytes) { 80 | var current_time = new Date(); 81 | if (this.last_reseed > current_time) { 82 | // warnings.warn("Clock rewind detected. Resetting last_reseed.", ClockRewindWarning) 83 | this.last_reseed = null; 84 | } 85 | if (this.pools[0].length >= this.min_pool_size && 86 | (!this.last_reseed || 87 | current_time > this.last_reseed + this.reseed_interval)) { 88 | this._reseed(current_time); 89 | } 90 | // The following should fail if we haven't seeded the pool yet. 91 | return this.generator.pseudo_random_data(bytes); 92 | }, 93 | 94 | _reseed : function(current_time) { 95 | if (!current_time) { 96 | current_time = new Date(); 97 | } 98 | var seed = []; 99 | this.reseed_count += 1; 100 | this.last_reseed = current_time; 101 | var which_pools = kryptos.random.Fortuna.which_pools(this.reseed_count); 102 | for (var i = 0; i < which_pools.length; ++i) { 103 | seed.push(this.pools[i].digest()); 104 | this.pools[i].reset(); 105 | } 106 | 107 | seed = seed.join(""); 108 | this.generator.reseed(seed); 109 | }, 110 | 111 | add_random_event : function(source_number, pool_number, data) { 112 | this.pools[pool_number].append(String.fromCharCode(source_number)); 113 | this.pools[pool_number].append(String.fromCharCode(data.length)); 114 | this.pools[pool_number].append(data); 115 | } 116 | }; 117 | -------------------------------------------------------------------------------- /kex_group1.js: -------------------------------------------------------------------------------- 1 | /* 2 | Standard SSH key exchange ("kex" if you wanna sound cool). Diffie-Hellman of 3 | 1024 bit key halves, using a known "p" prime and "g" generator. 4 | */ 5 | 6 | paramikojs.KexGroup1 = function(transport) { 7 | this.transport = transport; 8 | this.x = new BigInteger("0", 10); 9 | this.e = new BigInteger("0", 10); 10 | this.f = new BigInteger("0", 10); 11 | 12 | this.P = paramikojs.KexGroup1.P; 13 | this.G = paramikojs.KexGroup1.G; 14 | this.hash_algo = kryptos.hash.SHA; 15 | } 16 | 17 | paramikojs.KexGroup1._MSG_KEXDH_INIT = 30; 18 | paramikojs.KexGroup1._MSG_KEXDH_REPLY = 31; 19 | 20 | // draft-ietf-secsh-transport-09.txt, page 17 21 | paramikojs.KexGroup1.P = new BigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16); 22 | paramikojs.KexGroup1.G = new BigInteger("2", 10); 23 | 24 | paramikojs.KexGroup1.prototype = { 25 | name : 'diffie-hellman-group1-sha1', 26 | 27 | start_kex : function() { 28 | this._generate_x(); 29 | if (this.transport.server_mode) { 30 | // compute f = g^x mod p, but don't send it yet 31 | this.f = this.G.modPow(this.x, this.P); 32 | this.transport._expect_packet(paramikojs.KexGroup1._MSG_KEXDH_INIT); 33 | return; 34 | } 35 | // compute e = g^x mod p (where g=2), and send it 36 | this.e = this.G.modPow(this.x, this.P); 37 | var m = new paramikojs.Message(); 38 | m.add_byte(String.fromCharCode(paramikojs.KexGroup1._MSG_KEXDH_INIT)); 39 | m.add_mpint(this.e); 40 | this.transport._send_message(m); 41 | this.transport._expect_packet(paramikojs.KexGroup1._MSG_KEXDH_REPLY); 42 | }, 43 | 44 | parse_next : function(ptype, m) { 45 | if (this.transport.server_mode && ptype == paramikojs.KexGroup1._MSG_KEXDH_INIT) { 46 | return this._parse_kexdh_init(m); 47 | } else if (!this.transport.server_mode && ptype == paramikojs.KexGroup1._MSG_KEXDH_REPLY) { 48 | return this._parse_kexdh_reply(m); 49 | } 50 | throw new paramikojs.ssh_exception.SSHException('KexGroup1 asked to handle packet type ' + ptype); 51 | }, 52 | 53 | 54 | // internals... 55 | 56 | _generate_x : function() { 57 | // generate an "x" (1 < x < q), where q is (p-1)/2. 58 | // p is a 128-byte (1024-bit) number, where the first 64 bits are 1. 59 | // therefore q can be approximated as a 2^1023. we drop the subset of 60 | // potential x where the first 63 bits are 1, because some of those will be 61 | // larger than q (but this is a tiny tiny subset of potential x). 62 | var x_bytes; 63 | while (true) { 64 | x_bytes = this.transport.rng.read(128); 65 | x_bytes = String.fromCharCode(x_bytes[0].charCodeAt(0) & 0x7f) + x_bytes.substring(1); 66 | if (x_bytes.substring(0, 8) != '\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF' && x_bytes.substring(0, 8) != '\x00\x00\x00\x00\x00\x00\x00\x00') { 67 | break; 68 | } 69 | } 70 | this.x = paramikojs.util.inflate_long(x_bytes, false); 71 | }, 72 | 73 | _parse_kexdh_reply : function(m) { 74 | // client mode 75 | var host_key = m.get_string(); 76 | this.f = m.get_mpint(); 77 | var one = BigInteger.ONE; 78 | if (one.compareTo(this.f) > 0 || this.f.compareTo(this.P.subtract(one)) > 0) { 79 | throw new paramikojs.ssh_exception.SSHException('Server kex "f" is out of range'); 80 | } 81 | var sig = m.get_string(); 82 | var K = this.f.modPow(this.x, this.P); 83 | // okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K) 84 | var hm = new paramikojs.Message(); 85 | hm.add(this.transport.local_version, this.transport.remote_version, 86 | this.transport.local_kex_init, this.transport.remote_kex_init); 87 | hm.add_string(host_key); 88 | hm.add_mpint(this.e); 89 | hm.add_mpint(this.f); 90 | hm.add_mpint(K); 91 | this.transport._set_K_H(K, new kryptos.hash.SHA(hm.toString()).digest()); 92 | this.transport._verify_key(host_key, sig); 93 | this.transport._activate_outbound(); 94 | } 95 | 96 | }; 97 | -------------------------------------------------------------------------------- /kryptos/Cipher/ARC4.js: -------------------------------------------------------------------------------- 1 | // Copyright 2005 The Closure Library Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /** 16 | * @fileoverview ARC4 streamcipher implementation. A description of the 17 | * algorithm can be found at: 18 | * http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt. 19 | * 20 | * Usage: 21 | * 22 | * var arc4 = new goog.crypt.Arc4(); 23 | * arc4.setKey(key); 24 | * arc4.discard(1536); 25 | * arc4.crypt(bytes); 26 | * 27 | * 28 | * Note: For converting between strings and byte arrays, goog.crypt.base64 may 29 | * be useful. 30 | * 31 | */ 32 | 33 | goog = {}; 34 | goog.crypt = {}; 35 | 36 | 37 | /** 38 | * ARC4 streamcipher implementation. 39 | * @constructor 40 | */ 41 | goog.crypt.Arc4 = function() { 42 | /** 43 | * A permutation of all 256 possible bytes. 44 | * @type {Array.} 45 | * @private 46 | */ 47 | this.state_ = []; 48 | 49 | /** 50 | * 8 bit index pointer into this.state_. 51 | * @type {number} 52 | * @private 53 | */ 54 | this.index1_ = 0; 55 | 56 | /** 57 | * 8 bit index pointer into this.state_. 58 | * @type {number} 59 | * @private 60 | */ 61 | this.index2_ = 0; 62 | }; 63 | 64 | 65 | /** 66 | * Initialize the cipher for use with new key. 67 | * @param {Array.} key A byte array containing the key. 68 | * @param {number=} opt_length Indicates # of bytes to take from the key. 69 | */ 70 | goog.crypt.Arc4.prototype.setKey = function(key, opt_length) { 71 | if (!opt_length) { 72 | opt_length = key.length; 73 | } 74 | 75 | var state = this.state_; 76 | 77 | for (var i = 0; i < 256; ++i) { 78 | state[i] = i; 79 | } 80 | 81 | var j = 0; 82 | for (var i = 0; i < 256; ++i) { 83 | j = (j + state[i] + key[i % opt_length]) & 255; 84 | 85 | var tmp = state[i]; 86 | state[i] = state[j]; 87 | state[j] = tmp; 88 | } 89 | 90 | this.index1_ = 0; 91 | this.index2_ = 0; 92 | }; 93 | 94 | 95 | /** 96 | * Discards n bytes of the keystream. 97 | * These days 1536 is considered a decent amount to drop to get the key state 98 | * warmed-up enough for secure usage. This is not done in the constructor to 99 | * preserve efficiency for use cases that do not need this. 100 | * @param {number} n Number of bytes to disregard from the stream. 101 | */ 102 | goog.crypt.Arc4.prototype.discard = function(n) { 103 | var devnul = new Array(n); 104 | this.crypt(devnul); 105 | }; 106 | 107 | 108 | /** 109 | * En- or decrypt (same operation for streamciphers like ARC4) 110 | * @param {Array.} data The data to be xor-ed in place. 111 | * @param {number=} opt_length The number of bytes to crypt. 112 | */ 113 | goog.crypt.Arc4.prototype.crypt = function(data, opt_length) { 114 | if (!opt_length) { 115 | opt_length = data.length; 116 | } 117 | 118 | var i = this.index1_; 119 | var j = this.index2_; 120 | var state = this.state_; 121 | 122 | for (var n = 0; n < opt_length; ++n) { 123 | i = (i + 1) & 255; 124 | j = (j + state[i]) & 255; 125 | 126 | var tmp = state[i]; 127 | state[i] = state[j]; 128 | state[j] = tmp; 129 | 130 | data[n] ^= state[(state[i] + state[j]) & 255]; 131 | } 132 | 133 | this.index1_ = i; 134 | this.index2_ = j; 135 | }; 136 | 137 | 138 | kryptos.cipher.ARC4 = function(key) { 139 | this.cipher = new goog.crypt.Arc4(); 140 | this.cipher.setKey(kryptos.toByteArray(key)); 141 | } 142 | 143 | kryptos.cipher.ARC4.prototype = { 144 | encrypt : function(data) { 145 | var data = kryptos.toByteArray(data); 146 | this.cipher.crypt(data); 147 | return kryptos.fromByteArray(data); 148 | }, 149 | 150 | decrypt : function(data) { 151 | return this.encrypt(data); 152 | } 153 | }; 154 | -------------------------------------------------------------------------------- /sftp_attr.js: -------------------------------------------------------------------------------- 1 | paramikojs.SFTPAttributes = function () { 2 | /* 3 | Create a new (empty) SFTPAttributes object. All fields will be empty. 4 | */ 5 | this._flags = 0; 6 | this.st_size = null; 7 | this.st_uid = null; 8 | this.st_gid = null; 9 | this.st_mode = null; 10 | this.st_atime = null; 11 | this.st_mtime = null; 12 | this.attr = {}; 13 | } 14 | 15 | paramikojs.SFTPAttributes.prototype = { 16 | /* 17 | Representation of the attributes of a file (or proxied file) for SFTP in 18 | client or server mode. It attemps to mirror the object returned by 19 | C{os.stat} as closely as possible, so it may have the following fields, 20 | with the same meanings as those returned by an C{os.stat} object: 21 | - st_size 22 | - st_uid 23 | - st_gid 24 | - st_mode 25 | - st_atime 26 | - st_mtime 27 | 28 | Because SFTP allows flags to have other arbitrary named attributes, these 29 | are stored in a dict named C{attr}. Occasionally, the filename is also 30 | stored, in C{filename}. 31 | */ 32 | 33 | FLAG_SIZE : 1, 34 | FLAG_UIDGID : 2, 35 | FLAG_PERMISSIONS : 4, 36 | FLAG_AMTIME : 8, 37 | FLAG_EXTENDED : 0x80000000, 38 | 39 | /* 40 | Create an SFTPAttributes object from an existing C{stat} object (an 41 | object returned by C{os.stat}). 42 | 43 | @param obj: an object returned by C{os.stat} (or equivalent). 44 | @type obj: object 45 | @param filename: the filename associated with this file. 46 | @type filename: str 47 | @return: new L{SFTPAttributes} object with the same attribute fields. 48 | @rtype: L{SFTPAttributes} 49 | */ 50 | from_stat : function(obj, filename) { 51 | var attr = this; 52 | attr.st_size = obj.st_size; 53 | attr.st_uid = obj.st_uid; 54 | attr.st_gid = obj.st_gid; 55 | attr.st_mode = obj.st_mode; 56 | attr.st_atime = obj.st_atime; 57 | attr.st_mtime = obj.st_mtime; 58 | if (filename) { 59 | attr.filename = filename; 60 | } 61 | return attr; 62 | }, 63 | 64 | 65 | // internals... 66 | 67 | 68 | _from_msg : function(msg, filename, longname) { 69 | var attr = this; 70 | attr._unpack(msg); 71 | if (filename) { 72 | attr.filename = filename; 73 | } 74 | if (longname) { 75 | attr.longname = longname; 76 | } 77 | return attr; 78 | }, 79 | 80 | _unpack : function(msg) { 81 | this._flags = msg.get_int(); 82 | if (this._flags & this.FLAG_SIZE) { 83 | this.st_size = msg.get_int64(); 84 | } 85 | if (this._flags & this.FLAG_UIDGID) { 86 | this.st_uid = msg.get_int(); 87 | this.st_gid = msg.get_int(); 88 | } 89 | if (this._flags & this.FLAG_PERMISSIONS) { 90 | this.st_mode = msg.get_int(); 91 | } 92 | if (this._flags & this.FLAG_AMTIME) { 93 | this.st_atime = msg.get_int(); 94 | this.st_mtime = msg.get_int(); 95 | } 96 | if (this._flags & this.FLAG_EXTENDED) { 97 | var count = msg.get_int(); 98 | for (var x = 0; x < count.length; ++x) { 99 | this.attr[msg.get_string()] = msg.get_string(); 100 | } 101 | } 102 | }, 103 | 104 | _pack : function(msg) { 105 | this._flags = 0; 106 | if (this.st_size) { 107 | this._flags |= this.FLAG_SIZE; 108 | } 109 | if (this.st_uid && this.st_gid) { 110 | this._flags |= this.FLAG_UIDGID; 111 | } 112 | if (this.st_mode) { 113 | this._flags |= this.FLAG_PERMISSIONS; 114 | } 115 | if (this.st_atime && this.st_mtime) { 116 | this._flags |= this.FLAG_AMTIME; 117 | } 118 | var i; 119 | for (i in this.attr) { // lamesauce :-/ 120 | break; 121 | } 122 | if (i) { 123 | this._flags |= this.FLAG_EXTENDED; 124 | } 125 | msg.add_int(this._flags); 126 | if (this._flags & this.FLAG_SIZE) { 127 | msg.add_int64(this.st_size); 128 | } 129 | if (this._flags & this.FLAG_UIDGID) { 130 | msg.add_int(this.st_uid); 131 | msg.add_int(this.st_gid); 132 | } 133 | if (this._flags & this.FLAG_PERMISSIONS) { 134 | msg.add_int(this.st_mode); 135 | } 136 | if (this._flags & this.FLAG_AMTIME) { 137 | // throw away any fractional seconds 138 | msg.add_int(this.st_atime); 139 | msg.add_int(this.st_mtime); 140 | } 141 | if (this._flags & this.FLAG_EXTENDED) { 142 | msg.add_int(this.attr.length); 143 | for (var key in this.attr) { 144 | msg.add_string(key); 145 | msg.add_string(this.attr[key]); 146 | } 147 | } 148 | }, 149 | 150 | _rwx : function(n, suid, sticky) { 151 | if (suid) { 152 | suid = 2; 153 | } 154 | var out = '-r'.charCodeAt(n >> 2) + '-w'.charCodeAt((n >> 1) & 1); 155 | if (sticky) { 156 | out += '-xTt'.charCodeAt(suid + (n & 1)); 157 | } else { 158 | out += '-xSs'.charCodeAt(suid + (n & 1)); 159 | } 160 | return out; 161 | }, 162 | 163 | toString : function() { 164 | // todo, implement if necessary 165 | } 166 | }; 167 | -------------------------------------------------------------------------------- /agent.js: -------------------------------------------------------------------------------- 1 | /* 2 | Client interface for using private keys from an SSH agent running on the 3 | local machine. If an SSH agent is running, this class can be used to 4 | connect to it and retreive L{PKey} objects which can be used when 5 | attempting to authenticate to remote SSH servers. 6 | 7 | Because the SSH agent protocol uses environment variables and unix-domain 8 | sockets, this probably doesn't work on Windows. It does work on most 9 | posix platforms though (Linux and MacOS X, for example). 10 | */ 11 | paramikojs.Agent = function () { 12 | /* 13 | Open a session with the local machine's SSH agent, if one is running. 14 | If no agent is running, initialization will succeed, but L{get_keys} 15 | will return an empty tuple. 16 | 17 | @raise SSHException: if an SSH agent is found, but speaks an 18 | incompatible protocol 19 | */ 20 | 21 | this.conn = null; 22 | this.keys = []; 23 | 24 | if(!(Components && Components.classes)) { 25 | throw new Error("Unable to use OS environment without Mozilla's Components.classes"); //FIXME 26 | } 27 | var userEnvironment = Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment); 28 | if (userEnvironment.exists('SSH_AUTH_SOCK') && sys.platform != 'win32') { 29 | var conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM); // todo, fixme, doesn't work right now :-/ 30 | var auth_sock = userEnvironment.get('SSH_AUTH_SOCK'); 31 | try { 32 | conn.connect(auth_sock); 33 | } catch(ex) { 34 | // probably a dangling env var: the ssh agent is gone 35 | return; 36 | } 37 | this.conn = conn; 38 | } else if (sys.platform == 'win32') { 39 | var win_pageant = new paramikojs.win_pageant(); 40 | if (win_pageant.can_talk_to_agent()) { 41 | this.conn = win_pageant.PageantConnection(); 42 | } else { 43 | return; 44 | } 45 | } else { 46 | // no agent support 47 | return; 48 | } 49 | 50 | var msg = this._send_message(String.fromCharCode(paramikojs.Agent.SSH2_AGENTC_REQUEST_IDENTITIES)); 51 | if (msg.ptype != paramikojs.Agent.SSH2_AGENT_IDENTITIES_ANSWER) { 52 | throw new paramikojs.ssh_exception.SSHException('could not get keys from ssh-agent'); 53 | } 54 | 55 | var max = msg.result.get_int(); 56 | for (var x = 0; x < max; ++x) { 57 | this.keys.push(new paramikojs.AgentKey(this, msg.result.get_string())); 58 | msg.result.get_string(); 59 | } 60 | } 61 | 62 | paramikojs.Agent.SSH2_AGENTC_REQUEST_IDENTITIES = 11; 63 | paramikojs.Agent.SSH2_AGENT_IDENTITIES_ANSWER = 12; 64 | paramikojs.Agent.SSH2_AGENTC_SIGN_REQUEST = 13; 65 | paramikojs.Agent.SSH2_AGENT_SIGN_RESPONSE = 14; 66 | 67 | paramikojs.Agent.prototype = { 68 | 69 | /* 70 | Close the SSH agent connection. 71 | */ 72 | close : function() { 73 | if (this.conn) { 74 | this.conn.close(); 75 | } 76 | this.conn = null; 77 | this.keys = []; 78 | }, 79 | 80 | /* 81 | Return the list of keys available through the SSH agent, if any. If 82 | no SSH agent was running (or it couldn't be contacted), an empty list 83 | will be returned. 84 | 85 | @return: a list of keys available on the SSH agent 86 | @rtype: tuple of L{AgentKey} 87 | */ 88 | get_keys : function() { 89 | return this.keys; 90 | }, 91 | 92 | _send_message : function(msg) { 93 | var msg = msg.toString(); 94 | this.conn.send(struct.pack('>I', msg.length) + msg); // TODO, fixme 95 | var l = this._read_all(4); 96 | msg = new paramikojs.Message(this._read_all(struct.unpack('>I', l)[0])); 97 | return { 'ptype': msg.get_byte().charCodeAt(0), 'result': msg }; 98 | }, 99 | 100 | _read_all : function(wanted) { 101 | var result = this.conn.recv(wanted); // TODO, fixme 102 | while (result.length < wanted) { 103 | if (result.length == 0) { 104 | throw new paramikojs.ssh_exception.SSHException('lost ssh-agent'); 105 | } 106 | var extra = this.conn.recv(wanted - result.length); 107 | if (extra.length == 0) { 108 | throw new paramikojs.ssh_exception.SSHException('lost ssh-agent'); 109 | } 110 | result += extra; 111 | } 112 | return result; 113 | } 114 | }; 115 | 116 | 117 | 118 | /* 119 | Private key held in a local SSH agent. This type of key can be used for 120 | authenticating to a remote server (signing). Most other key operations 121 | work as expected. 122 | */ 123 | paramikojs.AgentKey = function(agent, blob) { 124 | inherit(this, new paramikojs.PKey()); 125 | 126 | this.agent = agent; 127 | this.blob = blob; 128 | this.name = new paramikojs.Message(blob).get_string(); 129 | } 130 | 131 | paramikojs.AgentKey.prototype = { 132 | toString : function() { 133 | return this.blob; 134 | }, 135 | 136 | get_name : function() { 137 | return this.name; 138 | }, 139 | 140 | sign_ssh_data : function(rng, data, callback) { 141 | var msg = new paramikojs.Message(); 142 | msg.add_byte(String.fromCharCode(paramikojs.Agent.SSH2_AGENTC_SIGN_REQUEST)); 143 | msg.add_string(this.blob); 144 | msg.add_string(data); 145 | msg.add_int(0); 146 | var msg = this.agent._send_message(msg); 147 | if (msg.ptype != paramikojs.Agent.SSH2_AGENT_SIGN_RESPONSE) { 148 | throw new paramikojs.ssh_exception.SSHException('key cannot be used for signing'); 149 | } 150 | callback(msg.result.get_string()); 151 | } 152 | }; 153 | -------------------------------------------------------------------------------- /kex_gex.js: -------------------------------------------------------------------------------- 1 | /* 2 | Variant on L{KexGroup1 } where the prime "p" and 3 | generator "g" are provided by the server. A bit more work is required on the 4 | client side, and a B{lot} more on the server side. 5 | */ 6 | 7 | paramikojs.KexGex = function(transport) { 8 | this.transport = transport; 9 | this.p = null; 10 | this.q = null; 11 | this.g = null; 12 | this.x = null; 13 | this.e = null; 14 | this.f = null; 15 | this.old_style = false; 16 | this.hash_algo = kryptos.hash.SHA; 17 | } 18 | 19 | 20 | paramikojs.KexGex._MSG_KEXDH_GEX_REQUEST_OLD = 30; 21 | paramikojs.KexGex._MSG_KEXDH_GEX_GROUP = 31; 22 | paramikojs.KexGex._MSG_KEXDH_GEX_INIT = 32; 23 | paramikojs.KexGex._MSG_KEXDH_GEX_REPLY = 33; 24 | paramikojs.KexGex._MSG_KEXDH_GEX_REQUEST = 34; 25 | 26 | paramikojs.KexGex.prototype = { 27 | name : 'diffie-hellman-group-exchange-sha1', 28 | min_bits : 1024, 29 | max_bits : 8192, 30 | preferred_bits : 2048, 31 | 32 | start_kex : function(_test_old_style) { 33 | if (this.transport.server_mode) { 34 | this.transport._expect_packet(paramikojs.KexGex._MSG_KEXDH_GEX_REQUEST, paramikojs.KexGex._MSG_KEXDH_GEX_REQUEST_OLD); 35 | return; 36 | } 37 | // request a bit range: we accept (min_bits) to (max_bits), but prefer 38 | // (preferred_bits). according to the spec, we shouldn't pull the 39 | // minimum up above 1024. 40 | var m = new paramikojs.Message(); 41 | if (_test_old_style) { 42 | // only used for unit tests: we shouldn't ever send this 43 | m.add_byte(String.fromCharCode(paramikojs.KexGex._MSG_KEXDH_GEX_REQUEST_OLD)); 44 | m.add_int(this.preferred_bits); 45 | this.old_style = true; 46 | } else { 47 | m.add_byte(String.fromCharCode(paramikojs.KexGex._MSG_KEXDH_GEX_REQUEST)); 48 | m.add_int(this.min_bits); 49 | m.add_int(this.preferred_bits); 50 | m.add_int(this.max_bits); 51 | } 52 | this.transport._send_message(m); 53 | this.transport._expect_packet(paramikojs.KexGex._MSG_KEXDH_GEX_GROUP); 54 | }, 55 | 56 | parse_next : function(ptype, m) { 57 | if (ptype == paramikojs.KexGex._MSG_KEXDH_GEX_GROUP) { 58 | return this._parse_kexdh_gex_group(m); 59 | } else if (ptype == paramikojs.KexGex._MSG_KEXDH_GEX_REPLY) { 60 | return this._parse_kexdh_gex_reply(m); 61 | } 62 | throw new paramikojs.ssh_exception.SSHException('KexGex asked to handle packet type ' + ptype); 63 | }, 64 | 65 | 66 | // internals... 67 | 68 | _generate_x : function() { 69 | // generate an "x" (1 < x < (p-1)/2). 70 | var one = BigInteger.ONE; 71 | var q = this.p.subtract(one); 72 | q = q.divide(new BigInteger("2", 10)); 73 | var qnorm = paramikojs.util.deflate_long(q, 0); 74 | var qhbyte = qnorm[0].charCodeAt(0); 75 | var bytes = qnorm.length; 76 | var qmask = 0xff; 77 | while (!(qhbyte & 0x80)) { 78 | qhbyte <<= 1; 79 | qmask >>= 1; 80 | } 81 | var x; 82 | while (true) { 83 | var x_bytes = this.transport.rng.read(bytes); 84 | x_bytes = String.fromCharCode(x_bytes[0].charCodeAt(0) & qmask) + x_bytes.substring(1); 85 | x = paramikojs.util.inflate_long(x_bytes, 1); 86 | if (x.compareTo(one) > 0 && q.compareTo(x) > 0) { 87 | break; 88 | } 89 | } 90 | this.x = x; 91 | }, 92 | 93 | _parse_kexdh_gex_group : function(m) { 94 | this.p = m.get_mpint(); 95 | this.g = m.get_mpint(); 96 | // reject if p's bit length < 1024 or > 8192 97 | var bitlen = paramikojs.util.bit_length(this.p); 98 | if (bitlen < 1024 || bitlen > 8192) { 99 | throw new paramikojs.ssh_exception.SSHException('Server-generated gex p (don\'t ask) is out of range (' + bitlen + ' bits)'); 100 | } 101 | this.transport._log(DEBUG, 'Got server p (' + bitlen + ' bits)'); 102 | this._generate_x(); 103 | // now compute e = g^x mod p 104 | this.e = this.g.modPow(this.x, this.p); 105 | m = new paramikojs.Message(); 106 | m.add_byte(String.fromCharCode(paramikojs.KexGex._MSG_KEXDH_GEX_INIT)); 107 | m.add_mpint(this.e); 108 | this.transport._send_message(m); 109 | this.transport._expect_packet(paramikojs.KexGex._MSG_KEXDH_GEX_REPLY); 110 | }, 111 | 112 | _parse_kexdh_gex_reply : function(m) { 113 | var host_key = m.get_string(); 114 | this.f = m.get_mpint(); 115 | var sig = m.get_string(); 116 | var one = BigInteger.ONE; 117 | if (one.compareTo(this.f) > 0 || this.f.compareTo(this.p.subtract(one)) > 0) { 118 | throw new paramikojs.ssh_exception.SSHException('Server kex "f" is out of range'); 119 | } 120 | var K = this.f.modPow(this.x, this.p); 121 | // okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) 122 | var hm = new paramikojs.Message(); 123 | hm.add(this.transport.local_version, this.transport.remote_version, 124 | this.transport.local_kex_init, this.transport.remote_kex_init, 125 | host_key); 126 | if (!this.old_style) { 127 | hm.add_int(this.min_bits); 128 | } 129 | hm.add_int(this.preferred_bits); 130 | if (!this.old_style) { 131 | hm.add_int(this.max_bits); 132 | } 133 | hm.add_mpint(this.p); 134 | hm.add_mpint(this.g); 135 | hm.add_mpint(this.e); 136 | hm.add_mpint(this.f); 137 | hm.add_mpint(K); 138 | this.transport._set_K_H(K, new this.hash_algo(hm.toString()).digest()); 139 | this.transport._verify_key(host_key, sig); 140 | this.transport._activate_outbound(); 141 | } 142 | }; 143 | 144 | paramikojs.KexGexSHA256 = function(transport) { 145 | inherit(this, new paramikojs.KexGex(transport)); 146 | this.name = 'diffie-hellman-group-exchange-sha256'; 147 | this.hash_algo = kryptos.hash.SHA256; 148 | }; 149 | -------------------------------------------------------------------------------- /ssh_exception.js: -------------------------------------------------------------------------------- 1 | paramikojs.ssh_exception = {} 2 | 3 | /* 4 | Exception raised by failures in SSH2 protocol negotiation or logic errors. 5 | */ 6 | paramikojs.ssh_exception.SSHException = function(message) { 7 | this.message = message; 8 | this.custom = true; 9 | this.name = "SSHException"; 10 | }; 11 | 12 | paramikojs.ssh_exception.SSHException.prototype.toString = function () { 13 | return this.name + ': "' + this.message + '"'; 14 | } 15 | 16 | paramikojs.ssh_exception.WaitException = function(message) { 17 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 18 | inherit(this, baseEx); 19 | this.toString = baseEx.toString; 20 | this.name = "WaitException"; 21 | } 22 | 23 | paramikojs.ssh_exception.CipherException = function(message) { 24 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 25 | inherit(this, baseEx); 26 | this.toString = baseEx.toString; 27 | this.name = "CipherException"; 28 | } 29 | 30 | paramikojs.ssh_exception.EOFError = function(message) { 31 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 32 | inherit(this, baseEx); 33 | this.toString = baseEx.toString; 34 | this.name = "EOFError"; 35 | } 36 | 37 | paramikojs.ssh_exception.IOError = function(message) { 38 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 39 | inherit(this, baseEx); 40 | this.toString = baseEx.toString; 41 | this.name = "IOError"; 42 | } 43 | 44 | paramikojs.ssh_exception.SFTPError = function(message) { 45 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 46 | inherit(this, baseEx); 47 | this.toString = baseEx.toString; 48 | this.name = "SFTPError"; 49 | } 50 | 51 | paramikojs.ssh_exception.UserRequestedDisconnect = function(message) { 52 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 53 | inherit(this, baseEx); 54 | this.toString = baseEx.toString; 55 | this.name = "UserRequestedDisconnect"; 56 | } 57 | 58 | paramikojs.ssh_exception.IsPuttyKey = function(message, lines) { 59 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 60 | inherit(this, baseEx); 61 | this.toString = baseEx.toString; 62 | this.name = "IsPuttyKey"; 63 | this.lines = lines; 64 | } 65 | 66 | paramikojs.ssh_exception.BERException = function(message) { 67 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 68 | inherit(this, baseEx); 69 | this.toString = baseEx.toString; 70 | this.name = "BERException"; 71 | } 72 | 73 | paramikojs.ssh_exception.NeedRekeyException = function(message) { 74 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 75 | inherit(this, baseEx); 76 | this.toString = baseEx.toString; 77 | this.name = "NeedRekeyException"; 78 | } 79 | 80 | /* 81 | Exception raised when authentication failed for some reason. It may be 82 | possible to retry with different credentials. (Other classes specify more 83 | specific reasons.) 84 | 85 | @since: 1.6 86 | */ 87 | paramikojs.ssh_exception.AuthenticationException = function(message) { 88 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 89 | inherit(this, baseEx); 90 | this.toString = baseEx.toString; 91 | this.name = "AuthenticationException"; 92 | } 93 | 94 | /* 95 | Exception raised when a password is needed to unlock a private key file. 96 | */ 97 | paramikojs.ssh_exception.PasswordRequiredException = function(message) { 98 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 99 | inherit(this, baseEx); 100 | this.toString = baseEx.toString; 101 | this.name = "PasswordRequiredException"; 102 | } 103 | 104 | /* 105 | Exception raised when an authentication type (like password) is used, but 106 | the server isn't allowing that type. (It may only allow public-key, for 107 | example.) 108 | 109 | @ivar allowed_types: list of allowed authentication types provided by the 110 | server (possible values are: C{"none"}, C{"password"}, and 111 | C{"publickey"}). 112 | @type allowed_types: list 113 | 114 | @since: 1.1 115 | */ 116 | paramikojs.ssh_exception.BadAuthenticationType = function(message, types) { 117 | var baseEx = new paramikojs.ssh_exception.SSHException(message); 118 | inherit(this, baseEx); 119 | this.toString = baseEx.toString; 120 | this.name = "BadAuthenticationType"; 121 | this.allowed_types = types; 122 | } 123 | 124 | paramikojs.ssh_exception.BadAuthenticationType.prototype.toString = function () { 125 | return this.name + ': "' + this.message + '"' + '(allowed_types=' + JSON.stringify(this.allowed_types) + ')'; 126 | } 127 | 128 | /* 129 | An internal exception thrown in the case of partial authentication. 130 | */ 131 | paramikojs.ssh_exception.PartialAuthentication = function(types) { 132 | var baseEx = new paramikojs.ssh_exception.SSHException('partial authentication'); 133 | inherit(this, baseEx); 134 | this.toString = baseEx.toString; 135 | this.name = "PartialAuthentication"; 136 | this.allowed_types = types; 137 | } 138 | 139 | /* 140 | Exception raised when an attempt to open a new L{Channel} fails. 141 | 142 | @ivar code: the error code returned by the server 143 | @type code: int 144 | 145 | @since: 1.6 146 | */ 147 | paramikojs.ssh_exception.ChannelException = function(code, text) { 148 | var baseEx = new paramikojs.ssh_exception.SSHException(text); 149 | inherit(this, baseEx); 150 | this.toString = baseEx.toString; 151 | this.name = "ChannelException"; 152 | this.code = code; 153 | } 154 | 155 | /* 156 | The host key given by the SSH server did not match what we were expecting. 157 | 158 | @ivar hostname: the hostname of the SSH server 159 | @type hostname: str 160 | @ivar key: the host key presented by the server 161 | @type key: L{PKey} 162 | @ivar expected_key: the host key expected 163 | @type expected_key: L{PKey} 164 | 165 | @since: 1.6 166 | */ 167 | paramikojs.ssh_exception.BadHostKeyException = function(hostname, got_key, expected_key) { 168 | var baseEx = new paramikojs.ssh_exception.SSHException('Host key for server ' + hostname + ' does not match!'); 169 | inherit(this, baseEx); 170 | this.toString = baseEx.toString; 171 | this.name = "BadHostKeyException"; 172 | this.hostname = hostname; 173 | this.key = got_key; 174 | this.expected_key = expected_key; 175 | } 176 | -------------------------------------------------------------------------------- /kryptos/Hash/MD5.js: -------------------------------------------------------------------------------- 1 | kryptos.hash.MD5 = function(str) { 2 | inherit(this, new kryptos.hash.baseHash(str)); 3 | } 4 | 5 | kryptos.hash.MD5.digest_size = 16; 6 | 7 | kryptos.hash.MD5.prototype = { 8 | type : 'md5' 9 | }; 10 | 11 | // http://code.google.com/p/crypto-js/source/browse/branches/2.x/src/MD5.js 12 | // BSD license: http://www.opensource.org/licenses/bsd-license.php 13 | if (!(Components && Components.classes)) { // Chrome 14 | kryptos.hash.MD5.prototype = { 15 | digest: function() { 16 | var hashData = kryptos.toByteArray(this.data); 17 | 18 | var m = kryptos.bytesToWords(hashData), 19 | l = hashData.length * 8, 20 | a = 1732584193, 21 | b = -271733879, 22 | c = -1732584194, 23 | d = 271733878; 24 | 25 | // Swap endian 26 | for (var i = 0; i < m.length; i++) { 27 | m[i] = ((m[i] << 8) | (m[i] >>> 24)) & 0x00FF00FF | 28 | ((m[i] << 24) | (m[i] >>> 8)) & 0xFF00FF00; 29 | } 30 | 31 | // Padding 32 | m[l >>> 5] |= 0x80 << (l % 32); 33 | m[(((l + 64) >>> 9) << 4) + 14] = l; 34 | 35 | // Method shortcuts 36 | var FF = function (a, b, c, d, x, s, t) { 37 | var n = a + (b & c | ~b & d) + (x >>> 0) + t; 38 | return ((n << s) | (n >>> (32 - s))) + b; 39 | }; 40 | var GG = function (a, b, c, d, x, s, t) { 41 | var n = a + (b & d | c & ~d) + (x >>> 0) + t; 42 | return ((n << s) | (n >>> (32 - s))) + b; 43 | }; 44 | var HH = function (a, b, c, d, x, s, t) { 45 | var n = a + (b ^ c ^ d) + (x >>> 0) + t; 46 | return ((n << s) | (n >>> (32 - s))) + b; 47 | }; 48 | var II = function (a, b, c, d, x, s, t) { 49 | var n = a + (c ^ (b | ~d)) + (x >>> 0) + t; 50 | return ((n << s) | (n >>> (32 - s))) + b; 51 | }; 52 | 53 | for (var i = 0; i < m.length; i += 16) { 54 | var aa = a, 55 | bb = b, 56 | cc = c, 57 | dd = d; 58 | 59 | a = FF(a, b, c, d, m[i+ 0], 7, -680876936); 60 | d = FF(d, a, b, c, m[i+ 1], 12, -389564586); 61 | c = FF(c, d, a, b, m[i+ 2], 17, 606105819); 62 | b = FF(b, c, d, a, m[i+ 3], 22, -1044525330); 63 | a = FF(a, b, c, d, m[i+ 4], 7, -176418897); 64 | d = FF(d, a, b, c, m[i+ 5], 12, 1200080426); 65 | c = FF(c, d, a, b, m[i+ 6], 17, -1473231341); 66 | b = FF(b, c, d, a, m[i+ 7], 22, -45705983); 67 | a = FF(a, b, c, d, m[i+ 8], 7, 1770035416); 68 | d = FF(d, a, b, c, m[i+ 9], 12, -1958414417); 69 | c = FF(c, d, a, b, m[i+10], 17, -42063); 70 | b = FF(b, c, d, a, m[i+11], 22, -1990404162); 71 | a = FF(a, b, c, d, m[i+12], 7, 1804603682); 72 | d = FF(d, a, b, c, m[i+13], 12, -40341101); 73 | c = FF(c, d, a, b, m[i+14], 17, -1502002290); 74 | b = FF(b, c, d, a, m[i+15], 22, 1236535329); 75 | 76 | a = GG(a, b, c, d, m[i+ 1], 5, -165796510); 77 | d = GG(d, a, b, c, m[i+ 6], 9, -1069501632); 78 | c = GG(c, d, a, b, m[i+11], 14, 643717713); 79 | b = GG(b, c, d, a, m[i+ 0], 20, -373897302); 80 | a = GG(a, b, c, d, m[i+ 5], 5, -701558691); 81 | d = GG(d, a, b, c, m[i+10], 9, 38016083); 82 | c = GG(c, d, a, b, m[i+15], 14, -660478335); 83 | b = GG(b, c, d, a, m[i+ 4], 20, -405537848); 84 | a = GG(a, b, c, d, m[i+ 9], 5, 568446438); 85 | d = GG(d, a, b, c, m[i+14], 9, -1019803690); 86 | c = GG(c, d, a, b, m[i+ 3], 14, -187363961); 87 | b = GG(b, c, d, a, m[i+ 8], 20, 1163531501); 88 | a = GG(a, b, c, d, m[i+13], 5, -1444681467); 89 | d = GG(d, a, b, c, m[i+ 2], 9, -51403784); 90 | c = GG(c, d, a, b, m[i+ 7], 14, 1735328473); 91 | b = GG(b, c, d, a, m[i+12], 20, -1926607734); 92 | 93 | a = HH(a, b, c, d, m[i+ 5], 4, -378558); 94 | d = HH(d, a, b, c, m[i+ 8], 11, -2022574463); 95 | c = HH(c, d, a, b, m[i+11], 16, 1839030562); 96 | b = HH(b, c, d, a, m[i+14], 23, -35309556); 97 | a = HH(a, b, c, d, m[i+ 1], 4, -1530992060); 98 | d = HH(d, a, b, c, m[i+ 4], 11, 1272893353); 99 | c = HH(c, d, a, b, m[i+ 7], 16, -155497632); 100 | b = HH(b, c, d, a, m[i+10], 23, -1094730640); 101 | a = HH(a, b, c, d, m[i+13], 4, 681279174); 102 | d = HH(d, a, b, c, m[i+ 0], 11, -358537222); 103 | c = HH(c, d, a, b, m[i+ 3], 16, -722521979); 104 | b = HH(b, c, d, a, m[i+ 6], 23, 76029189); 105 | a = HH(a, b, c, d, m[i+ 9], 4, -640364487); 106 | d = HH(d, a, b, c, m[i+12], 11, -421815835); 107 | c = HH(c, d, a, b, m[i+15], 16, 530742520); 108 | b = HH(b, c, d, a, m[i+ 2], 23, -995338651); 109 | 110 | a = II(a, b, c, d, m[i+ 0], 6, -198630844); 111 | d = II(d, a, b, c, m[i+ 7], 10, 1126891415); 112 | c = II(c, d, a, b, m[i+14], 15, -1416354905); 113 | b = II(b, c, d, a, m[i+ 5], 21, -57434055); 114 | a = II(a, b, c, d, m[i+12], 6, 1700485571); 115 | d = II(d, a, b, c, m[i+ 3], 10, -1894986606); 116 | c = II(c, d, a, b, m[i+10], 15, -1051523); 117 | b = II(b, c, d, a, m[i+ 1], 21, -2054922799); 118 | a = II(a, b, c, d, m[i+ 8], 6, 1873313359); 119 | d = II(d, a, b, c, m[i+15], 10, -30611744); 120 | c = II(c, d, a, b, m[i+ 6], 15, -1560198380); 121 | b = II(b, c, d, a, m[i+13], 21, 1309151649); 122 | a = II(a, b, c, d, m[i+ 4], 6, -145523070); 123 | d = II(d, a, b, c, m[i+11], 10, -1120210379); 124 | c = II(c, d, a, b, m[i+ 2], 15, 718787259); 125 | b = II(b, c, d, a, m[i+ 9], 21, -343485551); 126 | 127 | a = (a + aa) >>> 0; 128 | b = (b + bb) >>> 0; 129 | c = (c + cc) >>> 0; 130 | d = (d + dd) >>> 0; 131 | } 132 | 133 | var rotl = function (n, b) { 134 | return (n << b) | (n >>> (32 - b)); 135 | }; 136 | 137 | var endian = function (n) { 138 | // If number given, swap endian 139 | if (n.constructor == Number) { 140 | return rotl(n, 8) & 0x00FF00FF | 141 | rotl(n, 24) & 0xFF00FF00; 142 | } 143 | 144 | // Else, assume array and swap all items 145 | for (var i = 0; i < n.length; i++) { 146 | n[i] = endian(n[i]); 147 | } 148 | return n; 149 | }; 150 | 151 | return kryptos.fromByteArray(kryptos.wordsToBytes(endian([a, b, c, d]))); 152 | } 153 | }; 154 | } 155 | -------------------------------------------------------------------------------- /sftp.js: -------------------------------------------------------------------------------- 1 | paramikojs.BaseSFTP = function () { 2 | this.logger = paramikojs.util.get_logger(); 3 | this.ultra_debug = false; 4 | } 5 | 6 | paramikojs.BaseSFTP.prototype = { 7 | CMD_INIT : 1, 8 | CMD_VERSION : 2, 9 | CMD_OPEN : 3, 10 | CMD_CLOSE : 4, 11 | CMD_READ : 5, 12 | CMD_WRITE : 6, 13 | CMD_LSTAT : 7, 14 | CMD_FSTAT : 8, 15 | CMD_SETSTAT : 9, 16 | CMD_FSETSTAT : 10, 17 | CMD_OPENDIR : 11, 18 | CMD_READDIR : 12, 19 | CMD_REMOVE : 13, 20 | CMD_MKDIR : 14, 21 | CMD_RMDIR : 15, 22 | CMD_REALPATH : 16, 23 | CMD_STAT : 17, 24 | CMD_RENAME : 18, 25 | CMD_READLINK : 19, 26 | CMD_SYMLINK : 20, 27 | 28 | CMD_STATUS : 101, 29 | CMD_HANDLE : 102, 30 | CMD_DATA : 103, 31 | CMD_NAME : 104, 32 | CMD_ATTRS : 105, 33 | 34 | CMD_EXTENDED : 200, 35 | CMD_EXTENDED_REPLY : 201, 36 | 37 | SFTP_OK : 0, 38 | SFTP_EOF : 1, 39 | SFTP_NO_SUCH_FILE : 2, 40 | SFTP_PERMISSION_DENIED : 3, 41 | SFTP_FAILURE : 4, 42 | SFTP_BAD_MESSAGE : 5, 43 | SFTP_NO_CONNECTION : 6, 44 | SFTP_CONNECTION_LOST : 7, 45 | SFTP_OP_UNSUPPORTED : 8, 46 | 47 | SFTP_DESC : [ 'Success', 48 | 'End of file', 49 | 'No such file', 50 | 'Permission denied', 51 | 'Failure', 52 | 'Bad message', 53 | 'No connection', 54 | 'Connection lost', 55 | 'Operation unsupported' ], 56 | 57 | SFTP_FLAG_READ : 0x1, 58 | SFTP_FLAG_WRITE : 0x2, 59 | SFTP_FLAG_APPEND : 0x4, 60 | SFTP_FLAG_CREATE : 0x8, 61 | SFTP_FLAG_TRUNC : 0x10, 62 | SFTP_FLAG_EXCL : 0x20, 63 | 64 | _VERSION : 3, 65 | 66 | 67 | // for debugging 68 | CMD_NAMES : { 69 | 1: 'init', 70 | 2: 'version', 71 | 3: 'open', 72 | 4: 'close', 73 | 5: 'read', 74 | 6: 'write', 75 | 7: 'lstat', 76 | 8: 'fstat', 77 | 9: 'setstat', 78 | 10: 'fsetstat', 79 | 11: 'opendir', 80 | 12: 'readdir', 81 | 13: 'remove', 82 | 14: 'mkdir', 83 | 15: 'rmdir', 84 | 16: 'realpath', 85 | 17: 'stat', 86 | 18: 'rename', 87 | 19: 'readlink', 88 | 20: 'symlink', 89 | 101: 'status', 90 | 102: 'handle', 91 | 103: 'data', 92 | 104: 'name', 93 | 105: 'attrs', 94 | 200: 'extended', 95 | 201: 'extended_reply' 96 | }, 97 | 98 | 99 | // internals... 100 | 101 | _send_version : function(callback) { 102 | var self = this; 103 | var send_packet_callback = function() { 104 | self._send_version_callback(callback); 105 | }; 106 | this._send_packet(this.CMD_INIT, struct.pack('>I', this._VERSION), send_packet_callback); 107 | }, 108 | 109 | _send_version_callback : function(callback) { 110 | try { 111 | var packet = this._read_packet(); 112 | } catch(ex) { 113 | if (ex instanceof paramikojs.ssh_exception.WaitException) { 114 | // waiting on socket 115 | var self = this; 116 | var wait_callback = function() { self._send_version_callback(callback) }; 117 | setTimeout(wait_callback, 10); 118 | return; 119 | } else { 120 | throw ex; 121 | } 122 | } 123 | 124 | if (packet[0] != this.CMD_VERSION) { 125 | throw 'Incompatible sftp protocol'; 126 | } 127 | var version = struct.unpack('>I', packet[1].substring(0, 4))[0]; 128 | // if version != _VERSION: 129 | // raise SFTPError('Incompatible sftp protocol') 130 | callback(version); 131 | }, 132 | 133 | _send_server_version : function() { 134 | // winscp will freak out if the server sends version info before the 135 | // client finishes sending INIT. 136 | var packet = this._read_packet(); 137 | if (t != this.CMD_INIT) { 138 | throw 'Incompatible sftp protocol'; 139 | } 140 | var version = struct.unpack('>I', packet[0].substring(0, 4))[0]; 141 | // advertise that we support "check-file" 142 | var extension_pairs = [ 'check-file', 'md5,sha1' ]; 143 | var msg = new paramikojs.Message(); 144 | msg.add_int(this._VERSION); 145 | msg.add(extension_pairs); 146 | this._send_packet(this.CMD_VERSION, msg.toString()); 147 | return version; 148 | }, 149 | 150 | _log : function(level, msg) { 151 | this.logger.log(level, msg); 152 | }, 153 | 154 | _write_all : function(out, send_packet_callback) { 155 | while (out.length > 0) { 156 | try { 157 | var n = this.sock.send(out); 158 | } catch(ex) { 159 | if (ex instanceof paramikojs.ssh_exception.WaitException) { 160 | // waiting on window adjust 161 | var self = this; 162 | var wait_callback = function() { self._write_all(out, send_packet_callback) }; 163 | setTimeout(wait_callback, 10); 164 | return; 165 | } else { 166 | throw ex; 167 | } 168 | } 169 | if (n <= 0) { 170 | throw new paramikojs.ssh_exception.EOFError(); 171 | } 172 | if (n == out.length) { 173 | if (send_packet_callback) { 174 | send_packet_callback(); 175 | } 176 | return; 177 | } 178 | out = out.substring(n); 179 | } 180 | }, 181 | 182 | _read_all : function(n) { 183 | var out = this.sock.recv(n); 184 | if (out.length < n) { 185 | // waiting on socket 186 | this.sock.in_buffer = out + this.sock.in_buffer; // add data back into in_buffer 187 | throw new paramikojs.ssh_exception.WaitException("wait"); 188 | } 189 | return out; 190 | }, 191 | 192 | _send_packet : function(t, packet, send_packet_callback) { 193 | //self._log(DEBUG2, 'write: %s (len=%d)' % (CMD_NAMES.get(t, '0x%02x' % t), len(packet))) 194 | this.logger.log(DEBUG, 'write: ' + this.CMD_NAMES[t] + '(len=' + packet.length + ')'); 195 | var out = struct.pack('>I', packet.length + 1) + String.fromCharCode(t) + packet; 196 | if (this.ultra_debug) { 197 | this.logger.log(DEBUG, paramikojs.util.format_binary(out, 'OUT: ')); 198 | } 199 | this._write_all(out, send_packet_callback); 200 | }, 201 | 202 | _read_packet : function() { 203 | var x = this._read_all(4); 204 | // most sftp servers won't accept packets larger than about 32k, so 205 | // anything with the high byte set (> 16MB) is just garbage. 206 | if (x[0] != '\x00') { 207 | throw 'Garbage packet received'; 208 | } 209 | var size = struct.unpack('>I', x)[0]; 210 | try { 211 | var data = this._read_all(size); 212 | } catch(ex) { 213 | if (ex instanceof paramikojs.ssh_exception.WaitException) { 214 | // waiting on socket 215 | this.sock.in_buffer = x + this.sock.in_buffer; // add header back into in_buffer 216 | throw new paramikojs.ssh_exception.WaitException("wait"); // rethrow exception 217 | } else { 218 | throw ex; 219 | } 220 | } 221 | if (this.ultra_debug) { 222 | this.logger.log(DEBUG, paramikojs.util.format_binary(data, 'IN: ')); 223 | } 224 | if (size > 0) { 225 | var t = data[0].charCodeAt(0); 226 | this.logger.log(DEBUG, 'read: ' + this.CMD_NAMES[t] + '(len=' + (data.length - 1) + ')'); 227 | return [t, data.substring(1)]; 228 | } 229 | return [0, '']; 230 | } 231 | }; 232 | -------------------------------------------------------------------------------- /rsakey.js: -------------------------------------------------------------------------------- 1 | /* 2 | Representation of an RSA key which can be used to sign and verify SSH2 3 | data. 4 | */ 5 | paramikojs.RSAKey = function(msg, data, filename, password, vals, file_obj) { 6 | inherit(this, new paramikojs.PKey()); 7 | 8 | this.n = null; 9 | this.e = null; 10 | this.d = null; 11 | this.p = null; 12 | this.q = null; 13 | if (file_obj) { 14 | this._from_private_key(file_obj, password); 15 | return; 16 | } 17 | if (filename) { 18 | this._from_private_key_file(filename, password); 19 | return; 20 | } 21 | if (!msg && data) { 22 | msg = new paramikojs.Message(data); 23 | } 24 | if (vals) { 25 | this.e = vals[0]; 26 | this.n = vals[1]; 27 | } else { 28 | if (!msg) { 29 | throw new paramikojs.ssh_exception.SSHException('Key object may not be empty'); 30 | } 31 | if (msg.get_string() != 'ssh-rsa') { 32 | throw new paramikojs.ssh_exception.SSHException('Invalid key'); 33 | } 34 | this.e = msg.get_mpint(); 35 | this.n = msg.get_mpint(); 36 | } 37 | this.size = paramikojs.util.bit_length(this.n); 38 | } 39 | 40 | paramikojs.RSAKey.prototype = { 41 | toString : function () { 42 | var m = new paramikojs.Message(); 43 | m.add_string('ssh-rsa'); 44 | m.add_mpint(this.e); 45 | m.add_mpint(this.n); 46 | return m.toString(); 47 | }, 48 | 49 | compare : function(other) { 50 | if (this.get_name() != other.get_name()) { 51 | return false; 52 | } 53 | if (!this.e.equals(other.e)) { 54 | return false; 55 | } 56 | if (!this.n.equals(other.n)) { 57 | return false; 58 | } 59 | return true; 60 | }, 61 | 62 | get_name : function() { 63 | return 'ssh-rsa'; 64 | }, 65 | 66 | get_bits : function() { 67 | return this.size; 68 | }, 69 | 70 | can_sign : function() { 71 | return this.d != null; 72 | }, 73 | 74 | sign_ssh_data : function(rpool, data, callback) { 75 | var digest = new kryptos.hash.SHA(data).digest(); 76 | var pkcs1imified = this._pkcs1imify(digest); 77 | 78 | // XXX well, ain't this some shit. We have to use gRsaKeyWorkerJs b/c 79 | // the relative url won't work if we have ssh:// or sftp:// as the url instead of chrome:// 80 | // AAARRRGH 81 | // var worker = new Worker('./js/connection/paramikojs/sign_ssh_data_worker.js'); 82 | var worker = new Worker(gRsaKeyWorkerJs); 83 | worker.onmessage = function(event) { 84 | var m = new paramikojs.Message(); 85 | m.add_string('ssh-rsa'); 86 | m.add_string(paramikojs.util.deflate_long(new BigInteger(event.data, 10), 0)); 87 | callback(m); 88 | }; 89 | worker.postMessage({ n: this.n.toString(), e: this.e.toString(), d: this.d.toString(), pkcs1imified: pkcs1imified }); 90 | }, 91 | 92 | verify_ssh_sig : function(data, msg) { 93 | if (msg.get_string() != 'ssh-rsa') { 94 | return false; 95 | } 96 | var sig = paramikojs.util.inflate_long(msg.get_string(), true); 97 | // verify the signature by SHA'ing the data and encrypting it using the 98 | // public key. some wackiness ensues where we "pkcs1imify" the 20-byte 99 | // hash into a string as long as the RSA key. 100 | var hash_obj = paramikojs.util.inflate_long(this._pkcs1imify(new kryptos.hash.SHA(data).digest()), true); 101 | var rsa = new kryptos.publicKey.RSA().construct(this.n, this.e); 102 | return rsa.verify(hash_obj, [sig]); 103 | }, 104 | 105 | _encode_key : function() { 106 | if (!this.p || !this.q) { 107 | throw new paramikojs.ssh_exception.SSHException('Not enough key info to write private key file'); 108 | } 109 | keylist = [ 0, this.n, this.e, this.d, this.p, this.q, 110 | this.d % (this.p - 1), this.d % (this.q - 1), 111 | paramikojs.util.mod_inverse(this.q, this.p) ]; 112 | var b; 113 | try { 114 | b = new paramikojs.BER(); 115 | b.encode(keylist); 116 | } catch(ex) { 117 | throw new paramikojs.ssh_exception.SSHException('Unable to create ber encoding of key'); 118 | } 119 | return b.toString(); 120 | }, 121 | 122 | write_private_key_file : function(filename, password) { 123 | this._write_private_key_file('RSA', filename, this._encode_key(), password); 124 | }, 125 | 126 | write_private_key : function(file_obj, password) { 127 | this._write_private_key('RSA', file_obj, this._encode_key(), password); 128 | }, 129 | 130 | /* 131 | Generate a new private RSA key. This factory function can be used to 132 | generate a new host key or authentication key. 133 | 134 | @param bits: number of bits the generated key should be. 135 | @type bits: int 136 | @param progress_func: an optional function to call at key points in 137 | key generation (used by C{pyCrypto.PublicKey}). 138 | @type progress_func: function 139 | @return: new private key 140 | @rtype: L{RSAKey} 141 | */ 142 | generate : function(bits, progress_func) { 143 | var rsa = new kryptos.publicKey.RSA().generate(bits, paramikojs.rng.read, progress_func); 144 | var key = new paramikojs.RSAKey(null, null, null, null, [rsa.e, rsa.n], null); 145 | key.d = rsa.d; 146 | key.p = rsa.p; 147 | key.q = rsa.q; 148 | return key; 149 | }, 150 | 151 | 152 | // internals... 153 | 154 | 155 | /* 156 | turn a 20-byte SHA1 hash into a blob of data as large as the key's N, 157 | using PKCS1's \"emsa-pkcs1-v1_5\" encoding. totally bizarre. 158 | */ 159 | _pkcs1imify : function(data) { 160 | var SHA1_DIGESTINFO = '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'; 161 | var size = paramikojs.util.deflate_long(this.n, 0).length; 162 | var filler = new Array(size - SHA1_DIGESTINFO.length - data.length - 3 + 1).join('\xff'); 163 | return '\x00\x01' + filler + '\x00' + SHA1_DIGESTINFO + data; 164 | }, 165 | 166 | _from_private_key_file : function(filename, password) { 167 | var data; 168 | var keylist = null; 169 | try { 170 | data = this._read_private_key_file('RSA', filename, password); 171 | } catch (ex) { 172 | if (ex instanceof paramikojs.ssh_exception.IsPuttyKey) { 173 | data = null; 174 | keylist = this._read_putty_private_key('RSA', ex.lines, password); 175 | } else { 176 | throw ex; 177 | } 178 | } 179 | this._decode_key(data, keylist); 180 | }, 181 | 182 | _from_private_key : function(file_obj, password) { 183 | var data = this._read_private_key('RSA', file_obj, password); 184 | this._decode_key(data); 185 | }, 186 | 187 | _decode_key : function(data, keylist) { 188 | // private key file contains: 189 | // RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p } 190 | try { 191 | keylist = keylist || new paramikojs.BER(data).decode(); 192 | } catch(ex) { 193 | throw new paramikojs.ssh_exception.SSHException('Unable to parse key file'); 194 | } 195 | if (!(keylist instanceof Array) || keylist.length < 4 || keylist[0] != 0) { 196 | throw new paramikojs.ssh_exception.SSHException('Not a valid RSA private key file (bad ber encoding)'); 197 | } 198 | this.n = keylist[1]; 199 | this.e = keylist[2]; 200 | this.d = keylist[3]; 201 | // not really needed 202 | this.p = keylist[4]; 203 | this.q = keylist[5]; 204 | this.size = paramikojs.util.bit_length(this.n); 205 | } 206 | }; 207 | -------------------------------------------------------------------------------- /dsskey.js: -------------------------------------------------------------------------------- 1 | /* 2 | Representation of a DSS key which can be used to sign and verify SSH2 3 | data. 4 | */ 5 | paramikojs.DSSKey = function(msg, data, filename, password, vals, file_obj) { 6 | inherit(this, new paramikojs.PKey()); 7 | 8 | this.p = null; 9 | this.q = null; 10 | this.g = null; 11 | this.y = null; 12 | this.x = null; 13 | if (file_obj) { 14 | this._from_private_key(file_obj, password); 15 | return; 16 | } 17 | if (filename) { 18 | this._from_private_key_file(filename, password); 19 | return; 20 | } 21 | if (!msg && data) { 22 | msg = new paramikojs.Message(data); 23 | } 24 | if (vals) { 25 | this.p = vals[0]; 26 | this.q = vals[1]; 27 | this.g = vals[2]; 28 | this.y = vals[3]; 29 | } else { 30 | if (!msg) { 31 | throw new paramikojs.ssh_exception.SSHException('Key object may not be empty'); 32 | } 33 | if (msg.get_string() != 'ssh-dss') { 34 | throw new paramikojs.ssh_exception.SSHException('Invalid key'); 35 | } 36 | this.p = msg.get_mpint(); 37 | this.q = msg.get_mpint(); 38 | this.g = msg.get_mpint(); 39 | this.y = msg.get_mpint(); 40 | } 41 | this.size = paramikojs.util.bit_length(this.p); 42 | } 43 | 44 | paramikojs.DSSKey.prototype = { 45 | toString : function() { 46 | var m = new paramikojs.Message(); 47 | m.add_string('ssh-dss'); 48 | m.add_mpint(this.p); 49 | m.add_mpint(this.q); 50 | m.add_mpint(this.g); 51 | m.add_mpint(this.y); 52 | return m.toString(); 53 | }, 54 | 55 | compare : function(other) { 56 | if (this.get_name() != other.get_name()) { 57 | return false; 58 | } 59 | if (!this.p.equals(other.p)) { 60 | return false; 61 | } 62 | if (!this.q.equals(other.q)) { 63 | return false; 64 | } 65 | if (!this.g.equals(other.g)) { 66 | return false; 67 | } 68 | if (!this.y.equals(other.y)) { 69 | return false; 70 | } 71 | return true; 72 | }, 73 | 74 | get_name : function() { 75 | return 'ssh-dss'; 76 | }, 77 | 78 | get_bits : function() { 79 | return this.size; 80 | }, 81 | 82 | can_sign : function() { 83 | return this.x != null; 84 | }, 85 | 86 | sign_ssh_data : function(rng, data, callback) { 87 | var digest = new kryptos.hash.SHA(data).digest(); 88 | var dss = new kryptos.publicKey.DSA().construct(this.y, this.g, this.p, this.q, this.x); 89 | // generate a suitable k 90 | var qsize = paramikojs.util.deflate_long(this.q, 0).length; 91 | var k; 92 | var two = new BigInteger("2", 10); 93 | while (true) { 94 | k = paramikojs.util.inflate_long(rng.read(qsize), 1); 95 | if (k.compareTo(two) > 0 && this.q.compareTo(k) > 0) { 96 | break; 97 | } 98 | } 99 | var result = dss.sign(paramikojs.util.inflate_long(digest, 1), k); 100 | var m = new paramikojs.Message(); 101 | m.add_string('ssh-dss'); 102 | // apparently, in rare cases, r or s may be shorter than 20 bytes! 103 | var rstr = paramikojs.util.deflate_long(result[0], 0); 104 | var sstr = paramikojs.util.deflate_long(result[1], 0); 105 | if (rstr.length < 20) { 106 | rstr = new Array(20 - rstr.length + 1).join('\x00') + rstr; 107 | } 108 | if (sstr.length < 20) { 109 | sstr = new Array(20 - rstr.length + 1).join('\x00') + sstr; 110 | } 111 | m.add_string(rstr + sstr); 112 | callback(m); 113 | }, 114 | 115 | verify_ssh_sig : function(data, msg) { 116 | var sig; 117 | var kind; 118 | if (msg.toString().length == 40) { 119 | // spies.com bug: signature has no header 120 | sig = msg.toString(); 121 | } else { 122 | kind = msg.get_string(); 123 | if (kind != 'ssh-dss') { 124 | return 0; 125 | } 126 | sig = msg.get_string(); 127 | } 128 | 129 | // pull out (r, s) which are NOT encoded as mpints 130 | var sigR = paramikojs.util.inflate_long(sig.substring(0, 20), 1); 131 | var sigS = paramikojs.util.inflate_long(sig.substring(20), 1); 132 | var sigM = paramikojs.util.inflate_long(new kryptos.hash.SHA(data).digest(), 1); 133 | 134 | var dss = new kryptos.publicKey.DSA().construct(this.y, this.g, this.p, this.q); 135 | return dss.verify(sigM, [sigR, sigS]); 136 | }, 137 | 138 | _encode_key : function() { 139 | if (!this.x) { 140 | throw new paramikojs.ssh_exception.SSHException('Not enough key information'); 141 | } 142 | var keylist = [ 0, this.p, this.q, this.g, this.y, this.x ]; 143 | var b; 144 | try { 145 | b = new paramikojs.BER(); 146 | b.encode(keylist); 147 | } catch(ex) { 148 | throw new paramikojs.ssh_exception.SSHException('Unable to create ber encoding of key'); 149 | } 150 | return b.toString(); 151 | }, 152 | 153 | write_private_key_file : function(filename, password) { 154 | this._write_private_key_file('DSA', filename, this._encode_key(), password); 155 | }, 156 | 157 | write_private_key : function(file_obj, password) { 158 | this._write_private_key('DSA', file_obj, this._encode_key(), password); 159 | }, 160 | 161 | /* 162 | Generate a new private DSS key. This factory function can be used to 163 | generate a new host key or authentication key. 164 | 165 | @param bits: number of bits the generated key should be. 166 | @type bits: int 167 | @param progress_func: an optional function to call at key points in 168 | key generation (used by C{pyCrypto.PublicKey}). 169 | @type progress_func: function 170 | @return: new private key 171 | @rtype: L{DSSKey} 172 | */ 173 | generate : function(bits, progress_func) { 174 | bits = bits || 1024; 175 | var dsa = new kryptos.publicKey.DSA().generate(bits, paramikojs.rng.read, progress_func); 176 | var key = new paramikojs.DSSKey(null, null, null, null, [dsa.p, dsa.q, dsa.g, dsa.y], null); 177 | key.x = dsa.x; 178 | return key; 179 | }, 180 | 181 | 182 | // internals... 183 | 184 | 185 | _from_private_key_file : function(filename, password) { 186 | var data; 187 | var keylist = null; 188 | try { 189 | data = this._read_private_key_file('DSA', filename, password); 190 | } catch (ex) { 191 | if (ex instanceof paramikojs.ssh_exception.IsPuttyKey) { 192 | data = null; 193 | keylist = this._read_putty_private_key('DSA', ex.lines, password); 194 | } else { 195 | throw ex; 196 | } 197 | } 198 | this._decode_key(data, keylist); 199 | }, 200 | 201 | _from_private_key : function(file_obj, password) { 202 | var data = this._read_private_key('DSA', file_obj, password); 203 | this._decode_key(data); 204 | }, 205 | 206 | _decode_key : function(data, keylist) { 207 | // private key file contains: 208 | // DSAPrivateKey = { version = 0, p, q, g, y, x } 209 | try { 210 | keylist = keylist || new paramikojs.BER(data).decode(); 211 | } catch(ex) { 212 | throw new paramikojs.ssh_exception.SSHException('Unable to parse key file'); 213 | } 214 | if (!(keylist instanceof Array) || keylist.length < 6 || keylist[0] != 0) { 215 | throw new paramikojs.ssh_exception.SSHException('not a valid DSA private key file (bad ber encoding)'); 216 | } 217 | this.p = keylist[1]; 218 | this.q = keylist[2]; 219 | this.g = keylist[3]; 220 | this.y = keylist[4]; 221 | this.x = keylist[5]; 222 | this.size = paramikojs.util.bit_length(this.p); 223 | } 224 | }; 225 | -------------------------------------------------------------------------------- /message.js: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation of an SSH2 "message". 3 | 4 | An SSH2 I{Message} is a stream of bytes that encodes some combination of 5 | strings, integers, bools, and infinite-precision integers (known in python 6 | as I{long}s). This class builds or breaks down such a byte stream. 7 | 8 | Normally you don't need to deal with anything this low-level, but it's 9 | exposed for people implementing custom extensions, or features that 10 | paramiko doesn't support yet. 11 | */ 12 | 13 | paramikojs.Message = function(content) { 14 | /* 15 | Create a new SSH2 Message. 16 | 17 | @param content: the byte stream to use as the Message content (passed 18 | in only when decomposing a Message). 19 | @type content: string 20 | */ 21 | if (content) { 22 | this.packet = new String(content); 23 | } else { 24 | this.packet = new String(); 25 | } 26 | this.position = 0; 27 | } 28 | 29 | paramikojs.Message.prototype = { 30 | toString : function() { 31 | return this.packet; 32 | }, 33 | 34 | /* 35 | Rewind the message to the beginning as if no items had been parsed 36 | out of it yet. 37 | */ 38 | rewind : function() { 39 | this.position = 0; 40 | }, 41 | 42 | /* 43 | Return the bytes of this Message that haven't already been parsed and 44 | returned. 45 | 46 | @return: a string of the bytes not parsed yet. 47 | @rtype: string 48 | */ 49 | get_remainder : function() { 50 | return this.packet.substring(this.position); 51 | }, 52 | 53 | /* 54 | Returns the bytes of this Message that have been parsed and returned. 55 | The string passed into a Message's constructor can be regenerated by 56 | concatenating C{get_so_far} and L{get_remainder}. 57 | 58 | @return: a string of the bytes parsed so far. 59 | @rtype: string 60 | */ 61 | get_so_far : function() { 62 | return this.packet.substring(0, this.position); 63 | }, 64 | 65 | /* 66 | Return the next C{n} bytes of the Message, without decomposing into 67 | an int, string, etc. Just the raw bytes are returned. 68 | 69 | @return: a string of the next C{n} bytes of the Message, or a string 70 | of C{n} zero bytes, if there aren't C{n} bytes remaining. 71 | @rtype: string 72 | */ 73 | get_bytes : function(n) { 74 | var b = this.packet.substring(this.position, this.position + n); 75 | this.position += n; 76 | var max_pad_size = 1 << 20; // Limit padding to 1 MB 77 | if (b.length < n && n < max_pad_size) { 78 | return b + new Array(n - b.length + 1).join('\x00'); 79 | } 80 | return b; 81 | }, 82 | 83 | /* 84 | Return the next byte of the Message, without decomposing it. This 85 | is equivalent to L{get_bytes(1)}. 86 | 87 | @return: the next byte of the Message, or C{'\000'} if there aren't 88 | any bytes remaining. 89 | @rtype: string 90 | */ 91 | get_byte : function() { 92 | return this.get_bytes(1); 93 | }, 94 | 95 | /* 96 | Fetch a boolean from the stream. 97 | 98 | @return: C{True} or C{False} (from the Message). 99 | @rtype: bool 100 | */ 101 | get_boolean : function() { 102 | var b = this.get_bytes(1); 103 | return b != '\x00'; 104 | }, 105 | 106 | /* 107 | Fetch an int from the stream. 108 | 109 | @return: a 32-bit unsigned integer. 110 | @rtype: int 111 | */ 112 | get_int : function() { 113 | return struct.unpack('>I', this.get_bytes(4))[0]; 114 | }, 115 | 116 | /* 117 | Fetch a 64-bit int from the stream. 118 | 119 | @return: a 64-bit unsigned integer. 120 | @rtype: long 121 | */ 122 | get_int64 : function() { 123 | return struct.unpack('>Q', this.get_bytes(8))[0]; 124 | }, 125 | 126 | /* 127 | Fetch a long int (mpint) from the stream. 128 | 129 | @return: an arbitrary-length integer. 130 | @rtype: long 131 | */ 132 | get_mpint : function() { 133 | return paramikojs.util.inflate_long(this.get_string()); 134 | }, 135 | 136 | /* 137 | Fetch a string from the stream. This could be a byte string and may 138 | contain unprintable characters. (It's not unheard of for a string to 139 | contain another byte-stream Message.) 140 | 141 | @return: a string. 142 | @rtype: string 143 | */ 144 | get_string : function() { 145 | return this.get_bytes(this.get_int()); 146 | }, 147 | 148 | /* 149 | Fetch a list of strings from the stream. These are trivially encoded 150 | as comma-separated values in a string. 151 | 152 | @return: a list of strings. 153 | @rtype: list of strings 154 | */ 155 | get_list : function() { 156 | return this.get_string().split(','); 157 | }, 158 | 159 | /* 160 | Write bytes to the stream, without any formatting. 161 | 162 | @param b: bytes to add 163 | @type b: str 164 | */ 165 | add_bytes : function(b) { 166 | this.packet += b; 167 | return this; 168 | }, 169 | 170 | /* 171 | Write a single byte to the stream, without any formatting. 172 | 173 | @param b: byte to add 174 | @type b: str 175 | */ 176 | add_byte : function(b) { 177 | this.packet += b; 178 | return this; 179 | }, 180 | 181 | /* 182 | Add a boolean value to the stream. 183 | 184 | @param b: boolean value to add 185 | @type b: bool 186 | */ 187 | add_boolean : function(b) { 188 | if (b) { 189 | this.add_byte('\x01'); 190 | } else { 191 | this.add_byte('\x00'); 192 | } 193 | return this; 194 | }, 195 | 196 | /* 197 | Add an integer to the stream. 198 | 199 | @param n: integer to add 200 | @type n: int 201 | */ 202 | add_int : function(n) { 203 | this.packet += struct.pack('>I', n); 204 | return this; 205 | }, 206 | 207 | /* 208 | Add a 64-bit int to the stream. 209 | 210 | @param n: long int to add 211 | @type n: long 212 | */ 213 | add_int64 : function(n) { 214 | this.packet += struct.pack('>Q', n); 215 | return this; 216 | }, 217 | 218 | /* 219 | Add a long int to the stream, encoded as an infinite-precision 220 | integer. This method only works on positive numbers. 221 | 222 | @param z: long int to add 223 | @type z: long 224 | */ 225 | add_mpint : function(z) { 226 | this.add_string(paramikojs.util.deflate_long(z)); 227 | return this; 228 | }, 229 | 230 | /* 231 | Add a string to the stream. 232 | 233 | @param s: string to add 234 | @type s: str 235 | */ 236 | add_string : function(s) { 237 | this.add_int(s.length); 238 | this.packet += s; 239 | return this; 240 | }, 241 | 242 | /* 243 | Add a list of strings to the stream. They are encoded identically to 244 | a single string of values separated by commas. (Yes, really, that's 245 | how SSH2 does it.) 246 | 247 | @param l: list of strings to add 248 | @type l: list(str) 249 | */ 250 | add_list : function(l) { 251 | this.add_string(l.join(',')); 252 | return this; 253 | }, 254 | 255 | _add : function(i) { 256 | if (typeof i == "string") { 257 | return this.add_string(i); 258 | } else if (typeof i == "number") { 259 | return this.add_int(i); 260 | } else if (i instanceof BigInteger) { 261 | return this.add_mpint(i); 262 | } else if (typeof i == "boolean") { 263 | return this.add_boolean(i); 264 | } else if (i instanceof Array) { 265 | return this.add_list(i); 266 | } else { 267 | throw 'Unknown type'; 268 | } 269 | }, 270 | 271 | /* 272 | Add a sequence of items to the stream. The values are encoded based 273 | on their type: str, int, bool, list, or long. 274 | 275 | @param seq: the sequence of items 276 | @type seq: sequence 277 | 278 | @bug: longs are encoded non-deterministically. Don't use this method. 279 | */ 280 | add : function() { 281 | for (var x = 0; x < arguments.length; ++x) { 282 | this._add(arguments[x]); 283 | } 284 | } 285 | }; 286 | -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | paramikojs.util = {}; 2 | 3 | paramikojs.util.inflate_long = function(s, always_positive) { 4 | var out = new BigInteger("0", 10); 5 | var negative = 0; 6 | if (!always_positive && s.length > 0 && s.charCodeAt(0) >= 0x80) { 7 | negative = 1; 8 | } 9 | if (s.length % 4) { 10 | var filler = '\x00'; 11 | if (negative) { 12 | filler = '\xff'; 13 | } 14 | s = new Array(4 - s.length % 4 + 1).join(filler) + s; 15 | } 16 | for (var i = 0; i < s.length; i += 4) { 17 | out = out.shiftLeft(32); 18 | out = out.add(new BigInteger(struct.unpack('>I', s.substring(i, i+4))[0].toString(), 10)); 19 | } 20 | if (negative) { 21 | var one = new BigInteger("1", 10); 22 | out = one.shiftLeft(8 * s.length); 23 | out = out.subtract(one); 24 | } 25 | return out; 26 | }; 27 | 28 | paramikojs.util.deflate_long = function(n, add_sign_padding) { 29 | n = typeof n == "number" ? new BigInteger(n.toString(), 10) : n.clone(); 30 | add_sign_padding = add_sign_padding == undefined ? true : add_sign_padding; 31 | var s = ''; 32 | var negOne = new BigInteger("-1", 10); 33 | var bottom32BitMask = new BigInteger("ffffffff", 16); 34 | while (!n.equals(BigInteger.ZERO) && !n.equals(negOne)) { 35 | s = struct.pack('>I', n.and(bottom32BitMask)) + s; 36 | n = n.shiftRight(32); 37 | } 38 | // strip off leading zeros, FFs 39 | var found = false; 40 | var i = 0; 41 | for (; i < s.length; ++i) { 42 | if (n.equals(BigInteger.ZERO) && s.charAt(i) != '\000') { 43 | found = true; 44 | break; 45 | } 46 | if (n.equals(negOne) && s.charAt(i) != '\xff') { 47 | found = true; 48 | break; 49 | } 50 | } 51 | if (!found) { 52 | // degenerate case, n was either 0 or -1 53 | i = 0; 54 | if (n.equals(BigInteger.ZERO)) { 55 | s = '\000'; 56 | } else { 57 | s = '\xff'; 58 | } 59 | } 60 | s = s.substring(i); 61 | if (add_sign_padding) { 62 | if (n.equals(BigInteger.ZERO) && s.charCodeAt(0) >= 0x80) { 63 | s = '\x00' + s; 64 | } 65 | if (n.equals(negOne) && s.charCodeAt(0) < 0x80) { 66 | s = '\xff' + s; 67 | } 68 | } 69 | return s; 70 | }; 71 | 72 | paramikojs.util.format_binary_weird = function(data) { 73 | var out = ''; 74 | for (var x = 0; x < data.length; ++x) { 75 | var i = data[x]; 76 | out += '%02X' % i[1].charCodeAt(0); 77 | if (i[0] % 2) { 78 | out += ' '; 79 | } 80 | if (i[0] % 16 == 15) { 81 | out += '\n'; 82 | } 83 | } 84 | return out; 85 | }; 86 | 87 | paramikojs.util.format_binary = function(data, prefix) { 88 | prefix = prefix || ''; 89 | var x = 0; 90 | var out = []; 91 | while (data.length > x + 16) { 92 | out.push(paramikojs.util.format_binary_line(data.substring(x, x+16))); 93 | x += 16; 94 | } 95 | if (x < data.length) { 96 | out.push(paramikojs.util.format_binary_line(data.substring(x))); 97 | } 98 | var ret = []; 99 | for (var i = 0; i < out.length; ++i) { 100 | ret.push(prefix + out[i]); 101 | } 102 | return '\n' + ret.join('\n'); 103 | }; 104 | 105 | paramikojs.util.format_binary_line = function(data) { 106 | var left = paramikojs.util.hexify(data, ' '); 107 | left = left.length < 50 ? left + new Array(50 - left.length + 1).join(' ') : left; 108 | var right = ""; 109 | for (var x = 0; x < data.length; ++x) { 110 | var c = data[x]; 111 | right += parseInt((c.charCodeAt(0) + 63) / 95) == 1 ? c : '.'; 112 | } 113 | return left + ' ' + right; 114 | }; 115 | 116 | paramikojs.util.hexify = function(s, padding) { 117 | return binascii.hexlify(s, padding).toUpperCase(); 118 | }; 119 | 120 | paramikojs.util.unhexify = function(s) { 121 | return binascii.unhexlify(s); 122 | }; 123 | 124 | paramikojs.util.safe_string = function (s) { 125 | out = ''; 126 | for (var x = 0; x < s.length; ++x) { 127 | var c = s[x]; 128 | if (c.charCodeAt(0) >= 32 && c.charCodeAt(0) <= 127) { 129 | out += c; 130 | } else { 131 | out += '%' + c.charCodeAt(0) + ' '; 132 | } 133 | } 134 | return out; 135 | }; 136 | 137 | // ''.join([['%%%02X' % ord(c), c][(ord(c) >= 32) and (ord(c) <= 127)] for c in s]) 138 | 139 | paramikojs.util.bit_length = function(n) { 140 | var norm = paramikojs.util.deflate_long(n, 0); 141 | var hbyte = norm[0].charCodeAt(0); 142 | if (hbyte == 0) { 143 | return 1; 144 | } 145 | var bitlen = norm.length * 8; 146 | while (!(hbyte & 0x80)) { 147 | hbyte <<= 1; 148 | bitlen -= 1; 149 | } 150 | return bitlen; 151 | }; 152 | 153 | paramikojs.util.tb_strings = function() { 154 | return ''; // todo print stack 155 | }; 156 | 157 | /* 158 | Given a password, passphrase, or other human-source key, scramble it 159 | through a secure hash into some keyworthy bytes. This specific algorithm 160 | is used for encrypting/decrypting private key files. 161 | 162 | @param hashclass: class from L{Crypto.Hash} that can be used as a secure 163 | hashing function (like C{MD5} or C{SHA}). 164 | @type hashclass: L{Crypto.Hash} 165 | @param salt: data to salt the hash with. 166 | @type salt: string 167 | @param key: human-entered password or passphrase. 168 | @type key: string 169 | @param nbytes: number of bytes to generate. 170 | @type nbytes: int 171 | @return: key data 172 | @rtype: string 173 | */ 174 | paramikojs.util.generate_key_bytes = function(hashclass, salt, key, nbytes) { 175 | var keydata = ''; 176 | var digest = ''; 177 | if (salt.length > 8) { 178 | salt = salt.substring(0, 8); 179 | } 180 | while (nbytes > 0) { 181 | var hash_obj = new hashclass(); 182 | if (digest.length > 0) { 183 | hash_obj.update(digest); 184 | } 185 | hash_obj.update(key); 186 | hash_obj.update(salt); 187 | digest = hash_obj.digest(); 188 | var size = Math.min(nbytes, digest.length); 189 | keydata += digest.substring(0, size); 190 | nbytes -= size; 191 | } 192 | return keydata; 193 | }; 194 | 195 | /* 196 | Read a file of known SSH host keys, in the format used by openssh, and 197 | return a compound dict of C{hostname -> keytype ->} L{PKey }. 198 | The hostname may be an IP address or DNS name. The keytype will be either 199 | C{"ssh-rsa"} or C{"ssh-dss"}. 200 | 201 | This type of file unfortunately doesn't exist on Windows, but on posix, 202 | it will usually be stored in C{os.path.expanduser("~/.ssh/known_hosts")}. 203 | 204 | Since 1.5.3, this is just a wrapper around L{HostKeys}. 205 | 206 | @param filename: name of the file to read host keys from 207 | @type filename: str 208 | @return: dict of host keys, indexed by hostname and then keytype 209 | @rtype: dict(hostname, dict(keytype, L{PKey })) 210 | */ 211 | paramikojs.util.load_host_keys = function(filename) { 212 | return new paramikojs.HostKeys(filename); 213 | }; 214 | 215 | /* 216 | Provided only as a backward-compatible wrapper around L{SSHConfig}. 217 | */ 218 | paramikojs.util.parse_ssh_config = function(file_obj) { 219 | var config = new paramikojs.SSHConfig(); 220 | config.parse(file_obj); 221 | return config; 222 | }; 223 | 224 | /* 225 | Provided only as a backward-compatible wrapper around L{SSHConfig}. 226 | */ 227 | paramikojs.util.lookup_ssh_host_config = function(hostname, config) { 228 | return config.lookup(hostname); 229 | }; 230 | 231 | paramikojs.util.mod_inverse = function(x, m) { 232 | var u1 = 1; var u2 = 0; var u3 = m; 233 | var v1 = 0; var v2 = 1; var v3 = x; 234 | 235 | while (v3 > 0) { 236 | var q = parseInt(u3 / v3); 237 | var t = v1; 238 | v1 = u1 - v1 * q; 239 | u1 = t; 240 | t = v2; 241 | v2 = u2 - v2 * q; 242 | u2 = t; 243 | t = v3; 244 | v3 = u3 - v3 * q; 245 | u3 = t; 246 | } 247 | if (u2 < 0) { 248 | u2 += m; 249 | } 250 | return u2; 251 | }; 252 | 253 | // Stateful counter for CTR mode crypto 254 | paramikojs.util.Counter = function(nbits, initial_value, overflow) { 255 | initial_value = initial_value == undefined ? 1 : initial_value; 256 | overflow = overflow || 0; 257 | this.blocksize = nbits / 8; 258 | this.overflow = overflow; 259 | // start with value - 1 so we don't have to store intermediate values when counting 260 | // could the iv be 0? 261 | if (initial_value == 0) { 262 | this.value = new Array(this.blocksize + 1).join('\xFF'); 263 | } else { 264 | var one = BigInteger.ONE; 265 | var x = paramikojs.util.deflate_long(initial_value.subtract(one), false); 266 | this.value = new Array(this.blocksize - x.length + 1).join('\x00') + x; 267 | } 268 | }; 269 | 270 | paramikojs.util.Counter.prototype = { 271 | // Increment the counter and return the new value 272 | call : function() { 273 | var i = this.blocksize - 1; 274 | while (i > -1) { 275 | var c = String.fromCharCode((this.value.charCodeAt(i) + 1) % 256); 276 | this.value = paramikojs.util.setCharAt(this.value, i, c); 277 | if (c != '\x00') { 278 | return this.value; 279 | } 280 | i -= 1; 281 | } 282 | // counter reset 283 | var x = paramikojs.util.deflate_long(this.overflow, false); 284 | this.value = new Array(this.blocksize - x.length + 1).join('\x00') + x; 285 | return this.value; 286 | } 287 | }; 288 | 289 | paramikojs.util.setCharAt = function(str, index, ch) { // how annoying 290 | return str.substr(0, index) + ch + str.substr(index + 1); 291 | }; 292 | 293 | 294 | paramikojs.util.get_logger = function(name) { 295 | return logging; 296 | } 297 | -------------------------------------------------------------------------------- /hostkeys.js: -------------------------------------------------------------------------------- 1 | /* 2 | Representation of a line in an OpenSSH-style "known hosts" file. 3 | */ 4 | paramikojs.HostKeyEntry = function(hostnames, key) { 5 | this.valid = hostnames && key; 6 | this.hostnames = hostnames; 7 | this.key = key; 8 | } 9 | 10 | paramikojs.HostKeyEntry.prototype = { 11 | /* 12 | Parses the given line of text to find the names for the host, 13 | the type of key, and the key data. The line is expected to be in the 14 | format used by the openssh known_hosts file. 15 | 16 | Lines are expected to not have leading or trailing whitespace. 17 | We don't bother to check for comments or empty lines. All of 18 | that should be taken care of before sending the line to us. 19 | 20 | @param line: a line from an OpenSSH known_hosts file 21 | @type line: str 22 | */ 23 | from_line : function(line) { 24 | var fields = line.split(' '); 25 | if (fields.length < 3) { 26 | // Bad number of fields 27 | return null; 28 | } 29 | fields = fields.slice(0, 3); 30 | 31 | var names = fields[0]; 32 | var keytype = fields[1]; 33 | var key = fields[2]; 34 | names = names.split(','); 35 | 36 | // Decide what kind of key we're looking at and create an object 37 | // to hold it accordingly. 38 | if (keytype == 'ssh-rsa') { 39 | key = new paramikojs.RSAKey(null, base64.decodestring(key)); 40 | } else if (keytype == 'ssh-dss') { 41 | key = new paramikojs.DSSKey(null, base64.decodestring(key)); 42 | } else { 43 | key = new paramikojs.UnknownKey(keytype, base64.decodestring(key)); 44 | } 45 | 46 | return new paramikojs.HostKeyEntry(names, key); 47 | }, 48 | 49 | /* 50 | Returns a string in OpenSSH known_hosts file format, or None if 51 | the object is not in a valid state. A trailing newline is 52 | included. 53 | */ 54 | to_line : function() { 55 | if (this.valid) { 56 | return this.hostnames.join(',') + ' ' + this.key.get_name() + ' ' + this.key.get_base64() + '\n'; 57 | } 58 | return null; 59 | } 60 | }; 61 | 62 | 63 | /* 64 | Representation of an openssh-style "known hosts" file. Host keys can be 65 | read from one or more files, and then individual hosts can be looked up to 66 | verify server keys during SSH negotiation. 67 | 68 | A HostKeys object can be treated like a dict; any dict lookup is equivalent 69 | to calling L{lookup}. 70 | 71 | @since: 1.5.3 72 | */ 73 | paramikojs.HostKeys = function(filename) { 74 | /* 75 | Create a new HostKeys object, optionally loading keys from an openssh 76 | style host-key file. 77 | 78 | @param filename: filename to load host keys from, or C{None} 79 | @type filename: str 80 | */ 81 | // emulate a dict of { hostname: { keytype: PKey } } 82 | this._entries = []; 83 | if (filename) { 84 | this.load(filename); 85 | } 86 | } 87 | 88 | paramikojs.HostKeys.prototype = { 89 | /* 90 | Add a host key entry to the table. Any existing entry for a 91 | C{(hostname, keytype)} pair will be replaced. 92 | 93 | @param hostname: the hostname (or IP) to add 94 | @type hostname: str 95 | @param keytype: key type (C{"ssh-rsa"} or C{"ssh-dss"}) 96 | @type keytype: str 97 | @param key: the key to add 98 | @type key: L{PKey} 99 | */ 100 | add : function(hostname, keytype, key) { 101 | for (var x = 0; x < this._entries.length; ++x) { 102 | if (this._entries[x].hostnames.indexOf(hostname) != -1 && this._entries[x].key.get_name() == keytype) { 103 | this._entries[x].key = key; 104 | return; 105 | } 106 | } 107 | this._entries.push(new paramikojs.HostKeyEntry([hostname], key)); 108 | }, 109 | 110 | /* 111 | Read a file of known SSH host keys, in the format used by openssh. 112 | This type of file unfortunately doesn't exist on Windows, but on 113 | posix, it will usually be stored in 114 | C{os.path.expanduser("~/.ssh/known_hosts")}. 115 | 116 | If this method is called multiple times, the host keys are merged, 117 | not cleared. So multiple calls to C{load} will just call L{add}, 118 | replacing any existing entries and adding new ones. 119 | 120 | @param filename: name of the file to read host keys from 121 | @type filename: str 122 | 123 | @raise IOError: if there was an error reading the file 124 | */ 125 | load : function(filename) { 126 | if ((Components && Components.classes)) { // Mozilla 127 | var file = localFile.init(filename); 128 | if (!file.exists()) { 129 | this._entries = []; 130 | return; 131 | } 132 | 133 | var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream); 134 | fstream.init(file, -1, 0, 0); 135 | 136 | var charset = "UTF-8"; 137 | var is = Components.classes["@mozilla.org/intl/converter-input-stream;1"].createInstance(Components.interfaces.nsIConverterInputStream); 138 | is.init(fstream, charset, 1024, 0xFFFD); 139 | is.QueryInterface(Components.interfaces.nsIUnicharLineInputStream); 140 | this.loadHelper(is); 141 | } else { // Chrome 142 | var self = this; 143 | chrome.storage.local.get("host_keys", function(value) { 144 | is = value.host_keys || ''; 145 | self.loadHelper(is); 146 | }); 147 | } 148 | }, 149 | 150 | loadHelper : function(is) { 151 | var line = {}; 152 | var cont; 153 | do { 154 | line = {}; 155 | if ((Components && Components.classes)) { // Mozilla 156 | cont = is.readLine(line); 157 | line = line.value.trim(); 158 | } else { // Chrome 159 | line = is.substring(0, is.indexOf('\n')); 160 | is = is.substring(line.length + 1); 161 | line = line.trim(); 162 | cont = line.length; 163 | } 164 | if (!line.length || line[0] == '#') { 165 | continue; 166 | } 167 | var e = new paramikojs.HostKeyEntry().from_line(line); 168 | if (e) { 169 | this._entries.push(e); 170 | } 171 | // Now you can do something with line.value 172 | } while (cont); 173 | 174 | if ((Components && Components.classes)) { 175 | is.close(); 176 | } 177 | }, 178 | 179 | /* 180 | Save host keys into a file, in the format used by openssh. The order of 181 | keys in the file will be preserved when possible (if these keys were 182 | loaded from a file originally). The single exception is that combined 183 | lines will be split into individual key lines, which is arguably a bug. 184 | 185 | @param filename: name of the file to write 186 | @type filename: str 187 | 188 | @raise IOError: if there was an error writing the file 189 | 190 | @since: 1.6.1 191 | */ 192 | save : function(filename) { 193 | if ((Components && Components.classes)) { // Mozilla 194 | var file = localFile.init(filename); 195 | var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); 196 | foStream.init(file, 0x02 | 0x08 | 0x20, 0644, 0); 197 | var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream); 198 | converter.init(foStream, "UTF-8", 0, 0); 199 | } 200 | 201 | var data = ""; 202 | for (var x = 0; x < this._entries.length; ++x) { 203 | var line = this._entries[x].to_line(); 204 | if (line) { 205 | data += line; 206 | } 207 | } 208 | 209 | if ((Components && Components.classes)) { // Mozilla 210 | converter.writeString(data); 211 | converter.close(); 212 | } else { 213 | chrome.storage.local.set({'host_keys': data}); 214 | } 215 | }, 216 | 217 | /* 218 | Find a hostkey entry for a given hostname or IP. If no entry is found, 219 | C{None} is returned. Otherwise a dictionary of keytype to key is 220 | returned. The keytype will be either C{"ssh-rsa"} or C{"ssh-dss"}. 221 | 222 | @param hostname: the hostname (or IP) to lookup 223 | @type hostname: str 224 | @return: keys associated with this host (or C{None}) 225 | @rtype: dict(str, L{PKey}) 226 | */ 227 | lookup : function(hostname) { 228 | var entries = {}; 229 | for (var x = 0; x < this._entries.length; ++x) { 230 | for (var y = 0; y < this._entries[x].hostnames.length; ++y) { 231 | var h = this._entries[x].hostnames[y]; 232 | if ((h.indexOf('|1|') == 0 && this.hash_host(hostname, h) == h) || h == hostname) { 233 | entries[this._entries[x].key.get_name()] = this._entries[x].key; 234 | } 235 | } 236 | } 237 | return entries; 238 | }, 239 | 240 | get : function(hostname) { 241 | return this.lookup(hostname); 242 | }, 243 | 244 | /* 245 | Return True if the given key is associated with the given hostname 246 | in this dictionary. 247 | 248 | @param hostname: hostname (or IP) of the SSH server 249 | @type hostname: str 250 | @param key: the key to check 251 | @type key: L{PKey} 252 | @return: C{True} if the key is associated with the hostname; C{False} 253 | if not 254 | @rtype: bool 255 | */ 256 | check : function(hostname, key) { 257 | var k = this.lookup(hostname); 258 | if (!k) { 259 | return false; 260 | } 261 | var host_key = k.get(key.get_name(), null); 262 | if (!host_key) { 263 | return false; 264 | } 265 | return host_key.toString() == key.toString(); 266 | }, 267 | 268 | /* 269 | Remove all host keys from the dictionary. 270 | */ 271 | clear : function() { 272 | this._entries = []; 273 | }, 274 | 275 | keys : function() { 276 | var ret = []; 277 | for (var x = 0; x < this._entries.length; ++x) { 278 | for (var y = 0; y < this._entries[x].hostnames.length; ++y) { 279 | var h = this._entries[x].hostnames[y]; 280 | if (ret.indexOf(h) == -1) { 281 | ret.push(h); 282 | } 283 | } 284 | } 285 | return ret; 286 | }, 287 | 288 | values : function() { 289 | var ret = []; 290 | var keys = this.keys(); 291 | for (var x; x < keys.length; ++x) { 292 | ret.push(this.lookup(keys[x])); 293 | } 294 | return ret; 295 | }, 296 | 297 | /* 298 | Return a "hashed" form of the hostname, as used by openssh when storing 299 | hashed hostnames in the known_hosts file. 300 | 301 | @param hostname: the hostname to hash 302 | @type hostname: str 303 | @param salt: optional salt to use when hashing (must be 20 bytes long) 304 | @type salt: str 305 | @return: the hashed hostname 306 | @rtype: str 307 | */ 308 | hash_host : function(hostname, salt) { 309 | if (!salt) { 310 | salt = paramikojs.rng.read(kryptos.hash.SHA.digest_size); 311 | } else { 312 | if (salt.indexOf('|1|') == 0) { 313 | salt = salt.split('|')[2]; 314 | } 315 | salt = base64.decodestring(salt); 316 | } 317 | var hmac = kryptos.hash.HMAC(salt, hostname, kryptos.hash.HMAC_SHA); 318 | var hostkey = '|1|' + base64.encodestring(salt) + '|' + base64.encodestring(hmac); 319 | return hostkey.replace('\n', ''); 320 | } 321 | }; 322 | -------------------------------------------------------------------------------- /kryptos/Cipher/AES.js: -------------------------------------------------------------------------------- 1 | /** @fileOverview Low-level AES implementation. 2 | * 3 | * This file contains a low-level implementation of AES, optimized for 4 | * size and for efficiency on several browsers. It is based on 5 | * OpenSSL's aes_core.c, a public-domain implementation by Vincent 6 | * Rijmen, Antoon Bosselaers and Paulo Barreto. 7 | * 8 | * An older version of this implementation is available in the public 9 | * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh, 10 | * Stanford University 2008-2010 and BSD-licensed for liability 11 | * reasons. 12 | * 13 | * @author Emily Stark 14 | * @author Mike Hamburg 15 | * @author Dan Boneh 16 | */ 17 | 18 | /** 19 | * Schedule out an AES key for both encryption and decryption. This 20 | * is a low-level class. Use a cipher mode to do bulk encryption. 21 | * 22 | * @constructor 23 | * @param {Array} key The key as an array of 4, 6 or 8 words. 24 | * 25 | * @class Advanced Encryption Standard (low-level interface) 26 | */ 27 | sjcl = {}; 28 | sjcl.cipher = {}; 29 | sjcl.cipher.aes = function (key, mode) { 30 | if (!this._tables[0][0][0]) { 31 | this._precompute(); 32 | } 33 | 34 | this.mode = mode; 35 | 36 | var i, j, tmp, 37 | encKey, decKey, 38 | sbox = this._tables[0][4], decTable = this._tables[1], 39 | keyLen = key.length, rcon = 1; 40 | 41 | if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) { 42 | throw "invalid aes key size"; 43 | } 44 | 45 | this._key = [encKey = key.slice(0), decKey = []]; 46 | 47 | // schedule encryption keys 48 | for (i = keyLen; i < 4 * keyLen + 28; i++) { 49 | tmp = encKey[i-1]; 50 | 51 | // apply sbox 52 | if (i%keyLen === 0 || (keyLen === 8 && i%keyLen === 4)) { 53 | tmp = sbox[tmp>>>24]<<24 ^ sbox[tmp>>16&255]<<16 ^ sbox[tmp>>8&255]<<8 ^ sbox[tmp&255]; 54 | 55 | // shift rows and add rcon 56 | if (i%keyLen === 0) { 57 | tmp = tmp<<8 ^ tmp>>>24 ^ rcon<<24; 58 | rcon = rcon<<1 ^ (rcon>>7)*283; 59 | } 60 | } 61 | 62 | encKey[i] = encKey[i-keyLen] ^ tmp; 63 | } 64 | 65 | // schedule decryption keys 66 | for (j = 0; i; j++, i--) { 67 | tmp = encKey[j&3 ? i : i - 4]; 68 | if (i<=4 || j<4) { 69 | decKey[j] = tmp; 70 | } else { 71 | decKey[j] = decTable[0][sbox[tmp>>>24 ]] ^ 72 | decTable[1][sbox[tmp>>16 & 255]] ^ 73 | decTable[2][sbox[tmp>>8 & 255]] ^ 74 | decTable[3][sbox[tmp & 255]]; 75 | } 76 | } 77 | }; 78 | 79 | sjcl.cipher.aes.MODE_CBC = 2; 80 | sjcl.cipher.aes.MODE_CTR = 6; 81 | 82 | sjcl.cipher.aes.prototype = { 83 | // public 84 | /* Something like this might appear here eventually 85 | name: "AES", 86 | blockSize: 4, 87 | keySizes: [4,6,8], 88 | */ 89 | 90 | /** 91 | * Encrypt an array of 4 big-endian words. 92 | * @param {Array} data The plaintext. 93 | * @return {Array} The ciphertext. 94 | */ 95 | encrypt:function (data, iv, counter) { 96 | var ct = this.mode == sjcl.cipher.aes.MODE_CBC ? iv : []; 97 | 98 | var ret = []; 99 | 100 | for (var block = 0; block < data.length / 16; block++) { 101 | var aBlock = data.slice(block * 16, (block + 1) * 16); 102 | if (this.mode == sjcl.cipher.aes.MODE_CBC) { 103 | for (var i = 0; i < 16; i++) { 104 | aBlock[i] ^= ct[i]; 105 | } 106 | ct = sjcl.codec.bytes.fromBits(this._crypt(sjcl.codec.bytes.toBits(aBlock),0)); 107 | } else { 108 | ct = sjcl.codec.bytes.fromBits(this._crypt(sjcl.codec.bytes.toBits(kryptos.toByteArray(counter.call())),0)); 109 | for (var i = 0; i < 16; i++) { 110 | ct[i] ^= aBlock[i]; 111 | } 112 | } 113 | ret = ret.concat(ct); 114 | } 115 | 116 | return ret; 117 | }, 118 | 119 | /** 120 | * Decrypt an array of 4 big-endian words. 121 | * @param {Array} data The ciphertext. 122 | * @return {Array} The plaintext. 123 | */ 124 | decrypt:function (data, iv) { 125 | var ret = []; 126 | 127 | for (var block = 0; block < data.length / 16; block++) { 128 | var aBlock = data.slice(block * 16, (block + 1) * 16); 129 | ct = sjcl.codec.bytes.fromBits(this._crypt(sjcl.codec.bytes.toBits(aBlock),1)); 130 | if (this.mode == sjcl.cipher.aes.MODE_CBC) { 131 | for (var i = 0; i < 16; i++) { 132 | ct[i] ^= iv[i]; 133 | } 134 | iv = aBlock; 135 | } 136 | ret = ret.concat(ct); 137 | } 138 | return ret; 139 | }, 140 | 141 | /** 142 | * The expanded S-box and inverse S-box tables. These will be computed 143 | * on the client so that we don't have to send them down the wire. 144 | * 145 | * There are two tables, _tables[0] is for encryption and 146 | * _tables[1] is for decryption. 147 | * 148 | * The first 4 sub-tables are the expanded S-box with MixColumns. The 149 | * last (_tables[01][4]) is the S-box itself. 150 | * 151 | * @private 152 | */ 153 | _tables: [[[],[],[],[],[]],[[],[],[],[],[]]], 154 | 155 | /** 156 | * Expand the S-box tables. 157 | * 158 | * @private 159 | */ 160 | _precompute: function () { 161 | var encTable = this._tables[0], decTable = this._tables[1], 162 | sbox = encTable[4], sboxInv = decTable[4], 163 | i, x, xInv, d=[], th=[], x2, x4, x8, s, tEnc, tDec; 164 | 165 | // Compute double and third tables 166 | for (i = 0; i < 256; i++) { 167 | th[( d[i] = i<<1 ^ (i>>7)*283 )^i]=i; 168 | } 169 | 170 | for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) { 171 | // Compute sbox 172 | s = xInv ^ xInv<<1 ^ xInv<<2 ^ xInv<<3 ^ xInv<<4; 173 | s = s>>8 ^ s&255 ^ 99; 174 | sbox[x] = s; 175 | sboxInv[s] = x; 176 | 177 | // Compute MixColumns 178 | x8 = d[x4 = d[x2 = d[x]]]; 179 | tDec = x8*0x1010101 ^ x4*0x10001 ^ x2*0x101 ^ x*0x1010100; 180 | tEnc = d[s]*0x101 ^ s*0x1010100; 181 | 182 | for (i = 0; i < 4; i++) { 183 | encTable[i][x] = tEnc = tEnc<<24 ^ tEnc>>>8; 184 | decTable[i][s] = tDec = tDec<<24 ^ tDec>>>8; 185 | } 186 | } 187 | 188 | // Compactify. Considerable speedup on Firefox. 189 | for (i = 0; i < 5; i++) { 190 | encTable[i] = encTable[i].slice(0); 191 | decTable[i] = decTable[i].slice(0); 192 | } 193 | }, 194 | 195 | /** 196 | * Encryption and decryption core. 197 | * @param {Array} input Four words to be encrypted or decrypted. 198 | * @param dir The direction, 0 for encrypt and 1 for decrypt. 199 | * @return {Array} The four encrypted or decrypted words. 200 | * @private 201 | */ 202 | _crypt:function (input, dir) { 203 | if (input.length !== 4) { 204 | throw "invalid aes block size"; 205 | } 206 | 207 | var key = this._key[dir], 208 | // state variables a,b,c,d are loaded with pre-whitened data 209 | a = input[0] ^ key[0], 210 | b = input[dir ? 3 : 1] ^ key[1], 211 | c = input[2] ^ key[2], 212 | d = input[dir ? 1 : 3] ^ key[3], 213 | a2, b2, c2, 214 | 215 | nInnerRounds = key.length/4 - 2, 216 | i, 217 | kIndex = 4, 218 | out = [0,0,0,0], 219 | table = this._tables[dir], 220 | 221 | // load up the tables 222 | t0 = table[0], 223 | t1 = table[1], 224 | t2 = table[2], 225 | t3 = table[3], 226 | sbox = table[4]; 227 | 228 | // Inner rounds. Cribbed from OpenSSL. 229 | for (i = 0; i < nInnerRounds; i++) { 230 | a2 = t0[a>>>24] ^ t1[b>>16 & 255] ^ t2[c>>8 & 255] ^ t3[d & 255] ^ key[kIndex]; 231 | b2 = t0[b>>>24] ^ t1[c>>16 & 255] ^ t2[d>>8 & 255] ^ t3[a & 255] ^ key[kIndex + 1]; 232 | c2 = t0[c>>>24] ^ t1[d>>16 & 255] ^ t2[a>>8 & 255] ^ t3[b & 255] ^ key[kIndex + 2]; 233 | d = t0[d>>>24] ^ t1[a>>16 & 255] ^ t2[b>>8 & 255] ^ t3[c & 255] ^ key[kIndex + 3]; 234 | kIndex += 4; 235 | a=a2; b=b2; c=c2; 236 | } 237 | 238 | // Last round. 239 | for (i = 0; i < 4; i++) { 240 | out[dir ? 3&-i : i] = 241 | sbox[a>>>24 ]<<24 ^ 242 | sbox[b>>16 & 255]<<16 ^ 243 | sbox[c>>8 & 255]<<8 ^ 244 | sbox[d & 255] ^ 245 | key[kIndex++]; 246 | a2=a; a=b; b=c; c=d; d=a2; 247 | } 248 | 249 | return out; 250 | } 251 | }; 252 | 253 | /** @fileOverview Bit array codec implementations. 254 | * 255 | * @author Emily Stark 256 | * @author Mike Hamburg 257 | * @author Dan Boneh 258 | */ 259 | 260 | /** @namespace Arrays of bytes */ 261 | sjcl.codec = {}; 262 | sjcl.codec.bytes = { 263 | /** Convert from a bitArray to an array of bytes. */ 264 | fromBits: function (arr) { 265 | var out = [], bl = sjcl.bitArray.bitLength(arr), i, tmp; 266 | for (i=0; i>> 24); 271 | tmp <<= 8; 272 | } 273 | return out; 274 | }, 275 | /** Convert from an array of bytes to a bitArray. */ 276 | toBits: function (bytes) { 277 | var out = [], i, tmp=0; 278 | for (i=0; i 304 | * These objects are the currency accepted by SJCL's crypto functions. 305 | *

306 | * 307 | *

308 | * Most of our crypto primitives operate on arrays of 4-byte words internally, 309 | * but many of them can take arguments that are not a multiple of 4 bytes. 310 | * This library encodes arrays of bits (whose size need not be a multiple of 8 311 | * bits) as arrays of 32-bit words. The bits are packed, big-endian, into an 312 | * array of words, 32 bits at a time. Since the words are double-precision 313 | * floating point numbers, they fit some extra data. We use this (in a private, 314 | * possibly-changing manner) to encode the number of bits actually present 315 | * in the last word of the array. 316 | *

317 | * 318 | *

319 | * Because bitwise ops clear this out-of-band data, these arrays can be passed 320 | * to ciphers like AES which want arrays of words. 321 | *

322 | */ 323 | sjcl.bitArray = { 324 | /** 325 | * Find the length of an array of bits. 326 | * @param {bitArray} a The array. 327 | * @return {Number} The length of a, in bits. 328 | */ 329 | bitLength: function (a) { 330 | var l = a.length, x; 331 | if (l === 0) { return 0; } 332 | x = a[l - 1]; 333 | return (l-1) * 32 + sjcl.bitArray.getPartial(x); 334 | }, 335 | 336 | /** 337 | * Make a partial word for a bit array. 338 | * @param {Number} len The number of bits in the word. 339 | * @param {Number} x The bits. 340 | * @param {Number} [0] _end Pass 1 if x has already been shifted to the high side. 341 | * @return {Number} The partial word. 342 | */ 343 | partial: function (len, x, _end) { 344 | if (len === 32) { return x; } 345 | return (_end ? x|0 : x << (32-len)) + len * 0x10000000000; 346 | }, 347 | 348 | /** 349 | * Get the number of bits used by a partial word. 350 | * @param {Number} x The partial word. 351 | * @return {Number} The number of bits used by the partial word. 352 | */ 353 | getPartial: function (x) { 354 | return Math.round(x/0x10000000000) || 32; 355 | } 356 | }; 357 | 358 | 359 | 360 | kryptos.cipher.AES = function(key, mode, iv, counter) { 361 | this.cipher = new sjcl.cipher.aes(sjcl.codec.bytes.toBits(kryptos.toByteArray(key)), mode); 362 | this.mode = mode; 363 | if (this.mode == kryptos.cipher.AES.MODE_CBC) { 364 | this.iv = kryptos.toByteArray(iv); 365 | } 366 | this.counter = counter; 367 | } 368 | 369 | kryptos.cipher.AES.MODE_CTR = 6; 370 | kryptos.cipher.AES.MODE_CBC = 2; 371 | 372 | kryptos.cipher.AES.prototype = { 373 | encrypt : function(plaintext) { 374 | var ciphertext = this.cipher.encrypt(kryptos.toByteArray(plaintext), this.iv, this.counter); 375 | if (this.mode == kryptos.cipher.AES.MODE_CBC) { 376 | this.iv = ciphertext.slice(-16); 377 | } 378 | return kryptos.fromByteArray(ciphertext); 379 | }, 380 | 381 | decrypt : function(ciphertext) { 382 | ciphertext = kryptos.toByteArray(ciphertext); 383 | if (this.mode == kryptos.cipher.AES.MODE_CBC) { 384 | var plaintext = this.cipher.decrypt(ciphertext, this.iv); 385 | this.iv = ciphertext.slice(-16); 386 | } else { 387 | var plaintext = this.cipher.encrypt(ciphertext, this.iv, this.counter); 388 | } 389 | return kryptos.fromByteArray(plaintext); 390 | } 391 | }; 392 | -------------------------------------------------------------------------------- /packet.js: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation of the base SSH packet protocol. 3 | */ 4 | 5 | paramikojs.Packetizer = function(socket) { 6 | this.__socket = socket; 7 | this.__logger = null; 8 | this.__closed = false; 9 | this.__dump_packets = false; 10 | this.__need_rekey = false; 11 | this.__init_count = 0; 12 | this.__remainder = ''; 13 | this.__decrypted_header = ''; 14 | 15 | // used for noticing when to re-key: 16 | this.__sent_bytes = 0; 17 | this.__sent_packets = 0; 18 | this.__received_bytes = 0; 19 | this.__received_packets = 0; 20 | this.__received_packets_overflow = 0; 21 | 22 | // current inbound/outbound ciphering: 23 | this.__block_size_out = 8; 24 | this.__block_size_in = 8; 25 | this.__mac_size_out = 0; 26 | this.__mac_size_in = 0; 27 | this.__block_engine_out = null; 28 | this.__block_engine_in = null; 29 | this.__mac_engine_out = null; 30 | this.__mac_engine_in = null; 31 | this.__mac_key_out = ''; 32 | this.__mac_key_in = ''; 33 | this.__compress_engine_out = null; 34 | this.__compress_engine_in = null; 35 | this.__sequence_number_out = 0; 36 | this.__sequence_number_in = 0; 37 | 38 | // keepalives: 39 | this.__keepalive_interval = 0; 40 | this.__keepalive_last = new Date(); 41 | this.__keepalive_callback = null; 42 | } 43 | 44 | paramikojs.Packetizer.prototype = { 45 | // READ the secsh RFC's before raising these values. if anything, 46 | // they should probably be lower. 47 | REKEY_PACKETS : Math.pow(2, 29), 48 | REKEY_BYTES : Math.pow(2, 29), 49 | REKEY_PACKETS_OVERFLOW_MAX : Math.pow(2, 29), // Allow receiving this many packets after a re-key request before terminating 50 | REKEY_BYTES_OVERFLOW_MAX : Math.pow(2, 29), // Allow receiving this many bytes after a re-key request before terminating 51 | 52 | set_log : function(log) { 53 | this.__logger = log; 54 | }, 55 | 56 | /* 57 | Switch outbound data cipher. 58 | */ 59 | set_outbound_cipher : function(block_engine, block_size, mac_engine, mac_size, mac_key) { 60 | this.__block_engine_out = block_engine; 61 | this.__block_size_out = block_size; 62 | this.__mac_engine_out = mac_engine; 63 | this.__mac_size_out = mac_size; 64 | this.__mac_key_out = mac_key; 65 | this.__sent_bytes = 0; 66 | this.__sent_packets = 0; 67 | // wait until the reset happens in both directions before clearing rekey flag 68 | this.__init_count |= 1; 69 | if (this.__init_count == 3) { 70 | this.__init_count = 0; 71 | this.__need_rekey = false; 72 | } 73 | }, 74 | 75 | /* 76 | Switch inbound data cipher. 77 | */ 78 | set_inbound_cipher : function(block_engine, block_size, mac_engine, mac_size, mac_key) { 79 | this.__block_engine_in = block_engine; 80 | this.__block_size_in = block_size; 81 | this.__mac_engine_in = mac_engine; 82 | this.__mac_size_in = mac_size; 83 | this.__mac_key_in = mac_key; 84 | this.__received_bytes = 0; 85 | this.__received_packets = 0; 86 | this.__received_bytes_overflow = 0; 87 | this.__received_packets_overflow = 0; 88 | // wait until the reset happens in both directions before clearing rekey flag 89 | this.__init_count |= 2; 90 | if (this.__init_count == 3) { 91 | this.__init_count = 0; 92 | this.__need_rekey = false; 93 | } 94 | }, 95 | 96 | set_outbound_compressor : function(compressor) { 97 | this.__compress_engine_out = compressor; 98 | }, 99 | 100 | set_inbound_compressor : function(compressor) { 101 | this.__compress_engine_in = compressor; 102 | }, 103 | 104 | close : function() { 105 | this.__closed = true; 106 | }, 107 | 108 | set_hexdump : function(hexdump) { 109 | this.__dump_packets = hexdump; 110 | }, 111 | 112 | get_hexdump : function() { 113 | return this.__dump_packets; 114 | }, 115 | 116 | get_mac_size_in : function() { 117 | return this.__mac_size_in; 118 | }, 119 | 120 | get_mac_size_out : function() { 121 | return this.__mac_size_out; 122 | }, 123 | 124 | /* 125 | Returns C{True} if a new set of keys needs to be negotiated. This 126 | will be triggered during a packet read or write, so it should be 127 | checked after every read or write, or at least after every few. 128 | 129 | @return: C{True} if a new set of keys needs to be negotiated 130 | */ 131 | need_rekey : function() { 132 | return this.__need_rekey; 133 | }, 134 | 135 | /* 136 | Turn on/off the callback keepalive. If C{interval} seconds pass with 137 | no data read from or written to the socket, the callback will be 138 | executed and the timer will be reset. 139 | */ 140 | set_keepalive : function(interval, callback) { 141 | this.__keepalive_interval = interval; 142 | this.__keepalive_callback = callback; 143 | this.__keepalive_last = new Date(); 144 | }, 145 | 146 | /* 147 | Read as close to N bytes as possible, blocking as long as necessary. 148 | 149 | @param n: number of bytes to read 150 | @type n: int 151 | @return: the data read 152 | @rtype: str 153 | @raise EOFError: if the socket was closed before all the bytes could 154 | be read 155 | */ 156 | read_all : function(n, check_rekey) { 157 | //if (this.__remainder.length + this.__socket.fullBuffer.length < n) { 158 | if (this.__socket.fullBuffer.length < n) { 159 | throw new paramikojs.ssh_exception.WaitException("wait"); 160 | } 161 | 162 | var out = ''; 163 | // handle over-reading from reading the banner line 164 | /*if (this.__remainder.length > 0) { 165 | out = this.__remainder.substring(0, n); 166 | this.__remainder = this.__remainder.substring(n); 167 | n -= out.length; 168 | }*/ 169 | out += this.__socket.fullBuffer.substring(0, n); 170 | this.__socket.fullBuffer = this.__socket.fullBuffer.substring(n); 171 | return out; 172 | }, 173 | 174 | write_all : function(out) { 175 | this.__keepalive_last = new Date(); 176 | //this.__socket.writeControl(out); 177 | this.__socket.writeCallback(out); 178 | }, 179 | 180 | /* 181 | Read a line from the socket. We assume no data is pending after the 182 | line, so it's okay to attempt large reads. 183 | */ 184 | readline : function(timeout) { 185 | //var buf = this.__remainder; 186 | var buf = ''; 187 | while (buf.indexOf('\n') == -1) { 188 | buf += this._read_timeout(timeout); 189 | } 190 | var n = buf.indexOf('\n'); 191 | this.__socket.fullBuffer = buf.substring(n + 1) + this.__socket.fullBuffer; 192 | buf = buf.substring(0, n); 193 | if (buf.length > 0 && buf.charAt(buf.length - 1) == '\r') { 194 | buf = buf.substring(0, buf.length - 1); 195 | } 196 | return buf; 197 | }, 198 | 199 | /* 200 | Write a block of data using the current cipher, as an SSH block. 201 | */ 202 | send_message : function(data) { 203 | // encrypt this sucka 204 | data = data.toString(); 205 | var cmd = data[0].charCodeAt(0); 206 | var cmd_name; 207 | if (cmd in paramikojs.MSG_NAMES) { 208 | cmd_name = paramikojs.MSG_NAMES[cmd]; 209 | } else { 210 | cmd_name = '$' + cmd; 211 | } 212 | var orig_len = data.length; 213 | if (this.__compress_engine_out) { 214 | data = this.__compress_engine_out.compress(data); 215 | } 216 | var packet = this._build_packet(data); 217 | if (this.__dump_packets) { 218 | this._log(DEBUG, 'Write packet <' + cmd_name + '>, length ' + orig_len); 219 | this._log(DEBUG, paramikojs.util.format_binary(packet, 'OUT: ')); 220 | } 221 | var out; 222 | if (this.__block_engine_out) { 223 | out = this.__block_engine_out.encrypt(packet); 224 | } else { 225 | out = packet; 226 | } 227 | 228 | // + mac 229 | var payload; 230 | if (this.__block_engine_out) { 231 | payload = struct.pack('>I', this.__sequence_number_out) + packet; 232 | out += kryptos.hash.HMAC(this.__mac_key_out, payload, this.__mac_engine_out).substring(0, this.__mac_size_out); 233 | } 234 | this.__sequence_number_out = (this.__sequence_number_out + 1) & 0xffffffff; 235 | this.write_all(out); 236 | 237 | this.__sent_bytes += out.length; 238 | this.__sent_packets += 1; 239 | if ((this.__sent_packets >= this.REKEY_PACKETS || this.__sent_bytes >= this.REKEY_BYTES) 240 | && !this.__need_rekey) { 241 | // only ask once for rekeying 242 | this._log(DEBUG, 'Rekeying (hit ' + this.__sent_packets + ' packets, ' + this.__sent_bytes + ' bytes sent)'); 243 | this.__received_bytes_overflow = 0; 244 | this.__received_packets_overflow = 0; 245 | this._trigger_rekey(); 246 | } 247 | }, 248 | 249 | /* 250 | Only one thread should ever be in this function (no other locking is 251 | done). 252 | 253 | @raise SSHException: if the packet is mangled 254 | @raise NeedRekeyException: if the transport should rekey 255 | */ 256 | read_message : function() { 257 | var header; 258 | if (!this.__decrypted_header) { 259 | header = this.read_all(this.__block_size_in, true); 260 | if (this.__block_engine_in) { 261 | header = this.__block_engine_in.decrypt(header); 262 | } 263 | if (this.__dump_packets) { 264 | this._log(DEBUG, paramikojs.util.format_binary(header, 'IN: ')); 265 | } 266 | } else { 267 | header = this.__decrypted_header; 268 | this.__decrypted_header = ''; 269 | } 270 | 271 | var packet_size = struct.unpack('>I', header.substring(0, 4))[0]; 272 | // leftover contains decrypted bytes from the first block (after the length field) 273 | var leftover = header.substring(4); 274 | if ((packet_size - leftover.length) % this.__block_size_in != 0) { 275 | throw new paramikojs.ssh_exception.SSHException('Invalid packet blocking'); 276 | } 277 | 278 | var buf; 279 | try { 280 | buf = this.read_all(packet_size + this.__mac_size_in - leftover.length); 281 | } catch(ex) { 282 | if (ex instanceof paramikojs.ssh_exception.WaitException) { 283 | // not enough data yet to complete the packet 284 | this.__decrypted_header = header; 285 | throw new paramikojs.ssh_exception.WaitException("wait"); // rethrow exception 286 | } else { 287 | throw ex; 288 | } 289 | } 290 | 291 | var packet = buf.substring(0, packet_size - leftover.length); 292 | var post_packet = buf.substring(packet_size - leftover.length); 293 | if (this.__block_engine_in && packet) { 294 | packet = this.__block_engine_in.decrypt(packet); 295 | } 296 | if (this.__dump_packets) { 297 | this._log(DEBUG, paramikojs.util.format_binary(packet, 'IN: ')); 298 | } 299 | packet = leftover + packet; 300 | 301 | if (this.__mac_size_in > 0) { 302 | var mac = post_packet.substring(0, this.__mac_size_in); 303 | var mac_payload = struct.pack('>I', this.__sequence_number_in) + struct.pack('>I', packet_size) + packet; 304 | var my_mac = kryptos.hash.HMAC(this.__mac_key_in, mac_payload, this.__mac_engine_in).substring(0, this.__mac_size_in); 305 | if (my_mac != mac) { 306 | throw new paramikojs.ssh_exception.SSHException('Mismatched MAC'); 307 | } 308 | } 309 | var padding = packet[0].charCodeAt(0); 310 | var payload = packet.substring(1, packet_size - padding); 311 | if (this.__dump_packets) { 312 | this._log(DEBUG, 'Got payload (' + packet_size + ' bytes, ' + padding + ' padding)'); 313 | } 314 | 315 | if (this.__compress_engine_in) { 316 | payload = this.__compress_engine_in.decompress(payload); 317 | } 318 | 319 | var msg = new paramikojs.Message(payload.substring(1)); 320 | msg.seqno = this.__sequence_number_in; 321 | this.__sequence_number_in = (this.__sequence_number_in + 1) & 0xffffffff; 322 | 323 | // check for rekey 324 | var raw_packet_size = packet_size + this.__mac_size_in + 4; 325 | this.__received_bytes += raw_packet_size; 326 | this.__received_packets += 1; 327 | if (this.__need_rekey) { 328 | // we've asked to rekey -- give them some packets to comply before 329 | // dropping the connection 330 | this.__received_bytes_overflow += raw_packet_size; 331 | this.__received_packets_overflow += 1; 332 | if (this.__received_packets_overflow >= this.REKEY_PACKETS_OVERFLOW_MAX || 333 | this.__received_bytes_overflow >= this.REKEY_BYTES_OVERFLOW_MAX) { 334 | throw new paramikojs.ssh_exception.SSHException('Remote transport is ignoring rekey requests'); 335 | } 336 | } else if (this.__received_packets >= this.REKEY_PACKETS || 337 | this.__received_bytes >= this.REKEY_BYTES) { 338 | // only ask once for rekeying 339 | this._log(DEBUG, 'Rekeying (hit ' + this.__received_packets + ' packets, ' + this.__received_bytes + ' bytes received)'); 340 | this.__received_bytes_overflow = 0; 341 | this.__received_packets_overflow = 0; 342 | this._trigger_rekey(); 343 | } 344 | 345 | var cmd = payload[0].charCodeAt(0); 346 | var cmd_name; 347 | if (cmd in paramikojs.MSG_NAMES) { 348 | cmd_name = paramikojs.MSG_NAMES[cmd]; 349 | } else { 350 | cmd_name = '$' + cmd; 351 | } 352 | if (this.__dump_packets) { 353 | this._log(DEBUG, 'Read packet <' + cmd_name + '>, length ' + payload.length); 354 | } 355 | if (false) { 356 | this.__socket.run({ 'ptype': cmd, 'm': msg }); 357 | } 358 | return { 'ptype': cmd, 'm': msg }; 359 | }, 360 | 361 | 362 | // protected 363 | 364 | _log : function(level, msg) { 365 | this.__logger.log(level, msg); 366 | }, 367 | 368 | _check_keepalive : function() { 369 | if (!this.__keepalive_interval || !this.__block_engine_out || this.__need_rekey) { 370 | // wait till we're encrypting, and not in the middle of rekeying 371 | return; 372 | } 373 | var now = new Date(); 374 | if (now > this.__keepalive_last + this.__keepalive_interval) { 375 | this.__keepalive_callback(); 376 | this.__keepalive_last = now; 377 | } 378 | }, 379 | 380 | _read_timeout : function(timeout) { 381 | var buf = this.__socket.fullBuffer.substring(0, 128); 382 | this.__socket.fullBuffer = this.__socket.fullBuffer.substring(128); 383 | return buf; 384 | }, 385 | 386 | _build_packet : function(payload) { 387 | // pad up at least 4 bytes, to nearest block-size (usually 8) 388 | var bsize = this.__block_size_out; 389 | var padding = 3 + bsize - ((payload.length + 8) % bsize); 390 | var packet = struct.pack('>I', payload.length + padding + 1) + struct.pack('>B', padding); 391 | packet += payload; 392 | if (this.__block_engine_out) { 393 | packet += this.__socket.rng.read(padding, true); 394 | } else { 395 | // cute trick i caught openssh doing: if we're not encrypting, 396 | // don't waste random bytes for the padding 397 | packet += new Array(padding + 1).join('\x00'); 398 | } 399 | return packet; 400 | }, 401 | 402 | _trigger_rekey : function() { 403 | // outside code should check for this flag 404 | this.__need_rekey = true; 405 | } 406 | 407 | }; 408 | -------------------------------------------------------------------------------- /auth_handler.js: -------------------------------------------------------------------------------- 1 | /* 2 | Internal class to handle the mechanics of authentication. 3 | */ 4 | paramikojs.AuthHandler = function(transport) { 5 | this.transport = transport; 6 | this.username = null; 7 | this.authenticated = false; 8 | this.auth_method = ''; 9 | this.password = null; 10 | this.private_key = null; 11 | this.interactive_handler = null; 12 | this.submethods = null; 13 | // for server mode: 14 | this.auth_username = null; 15 | this.auth_fail_count = 0; 16 | this.callback = null; 17 | 18 | this.triedKeyboard = false; 19 | this.triedPublicKey = false; 20 | } 21 | 22 | paramikojs.AuthHandler.prototype = { 23 | is_authenticated : function() { 24 | return this.authenticated; 25 | }, 26 | 27 | get_username : function() { 28 | if (this.transport.server_mode) { 29 | return this.auth_username; 30 | } else { 31 | return this.username; 32 | } 33 | }, 34 | 35 | auth_none : function(username) { 36 | this.auth_method = 'none'; 37 | this.username = username; 38 | this._request_auth(); 39 | }, 40 | 41 | auth_publickey : function(username, key) { 42 | this.auth_method = 'publickey'; 43 | this.username = username; 44 | this.private_key = key; 45 | this.triedPublicKey = true; 46 | this._request_auth(); 47 | }, 48 | 49 | auth_password : function(username, password) { 50 | this.auth_method = 'password'; 51 | this.username = username; 52 | this.password = password; 53 | this._request_auth(); 54 | }, 55 | 56 | /* 57 | response_list = handler(title, instructions, prompt_list) 58 | */ 59 | auth_interactive : function(username, handler, submethods) { 60 | this.auth_method = 'keyboard-interactive'; 61 | this.username = username; 62 | this.interactive_handler = handler; 63 | this.submethods = submethods || ''; 64 | this.triedKeyboard = true; 65 | this._request_auth(); 66 | }, 67 | 68 | abort : function() { 69 | 70 | }, 71 | 72 | 73 | // internals... 74 | 75 | 76 | _request_auth : function(self) { 77 | var m = new paramikojs.Message(); 78 | m.add_byte(String.fromCharCode(paramikojs.MSG_SERVICE_REQUEST)); 79 | m.add_string('ssh-userauth'); 80 | this.transport._send_message(m); 81 | }, 82 | 83 | _disconnect_service_not_available : function() { 84 | var m = new paramikojs.Message(); 85 | m.add_byte(String.fromCharCode(paramikojs.MSG_DISCONNECT)); 86 | m.add_int(paramikojs.DISCONNECT_SERVICE_NOT_AVAILABLE); 87 | m.add_string('Service not available'); 88 | m.add_string('en'); 89 | this.transport._send_message(m); 90 | this.transport.close(); 91 | }, 92 | 93 | _disconnect_no_more_auth : function() { 94 | var m = new paramikojs.Message(); 95 | m.add_byte(String.fromCharCode(paramikojs.MSG_DISCONNECT)); 96 | m.add_int(paramikojs.DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE); 97 | m.add_string('No more auth methods available'); 98 | m.add_string('en'); 99 | this.transport._send_message(m); 100 | this.transport.close(); 101 | }, 102 | 103 | _get_session_blob : function(key, service, username) { 104 | var m = new paramikojs.Message(); 105 | m.add_string(this.transport.session_id); 106 | m.add_byte(String.fromCharCode(paramikojs.MSG_USERAUTH_REQUEST)); 107 | m.add_string(username); 108 | m.add_string(service); 109 | m.add_string('publickey'); 110 | m.add_boolean(1); 111 | m.add_string(key.get_name()); 112 | m.add_string(key.toString()); 113 | return m.toString(); 114 | }, 115 | 116 | wait_for_response : function() { 117 | return; // do nothing 118 | }, 119 | 120 | _parse_service_request : function(m) { 121 | var service = m.get_string(); 122 | if (this.transport.server_mode && service == 'ssh-userauth') { 123 | // accepted 124 | var m = new paramikojs.Message(); 125 | m.add_byte(String.fromCharCode(paramikojs.MSG_SERVICE_ACCEPT)); 126 | m.add_string(service); 127 | this.transport._send_message(m); 128 | return; 129 | } 130 | // dunno this one 131 | this._disconnect_service_not_available(); 132 | }, 133 | 134 | _parse_service_accept : function(m) { 135 | var service = m.get_string(); 136 | if (service == 'ssh-userauth') { 137 | this.transport._log(DEBUG, 'userauth is OK'); 138 | var m = new paramikojs.Message(); 139 | m.add_byte(String.fromCharCode(paramikojs.MSG_USERAUTH_REQUEST)); 140 | m.add_string(this.username); 141 | m.add_string('ssh-connection'); 142 | m.add_string(this.auth_method); 143 | if (this.auth_method == 'password') { 144 | m.add_boolean(false); 145 | var password = this.password; 146 | try { 147 | password = this.transport.toUTF8.convertStringToUTF8(password, "UTF-8", 1); 148 | } catch(ex) { 149 | this.transport._log(DEBUG, ex); 150 | } 151 | m.add_string(password); 152 | } else if (this.auth_method == 'publickey') { 153 | m.add_boolean(true); 154 | m.add_string(this.private_key.get_name()); 155 | m.add_string(this.private_key.toString()); 156 | var blob = this._get_session_blob(this.private_key, 'ssh-connection', this.username); 157 | 158 | var self = this; 159 | var callback = function(sig) { 160 | m.add_string(sig.toString()); 161 | self.transport._send_message(m); 162 | }; 163 | this.private_key.sign_ssh_data(this.transport.rng, blob, callback); // mime: changed to support workers 164 | return; 165 | } else if (this.auth_method == 'keyboard-interactive') { 166 | m.add_string(''); 167 | m.add_string(this.submethods); 168 | } else if (this.auth_method == 'none') { 169 | // do nothing 170 | } else { 171 | throw new paramikojs.ssh_exception.SSHException('Unknown auth method "' + this.auth_method + '"'); 172 | } 173 | this.transport._send_message(m); 174 | } else { 175 | this.transport._log(DEBUG, 'Service request "' + service + '" accepted (?)'); 176 | } 177 | }, 178 | 179 | _send_auth_result : function(username, method, result) { 180 | // okay, send result 181 | var m = new paramikojs.Message(); 182 | if (result == paramikojs.AUTH_SUCCESSFUL) { 183 | this.transport._log(INFO, 'Auth granted (' + method + ').'); 184 | m.add_byte(String.fromCharCode(paramikojs.MSG_USERAUTH_SUCCESS)); 185 | this.authenticated = true; 186 | } else { 187 | this.transport._log(INFO, 'Auth rejected (' + method + ').'); 188 | m.add_byte(String.fromCharCode(paramikojs.MSG_USERAUTH_FAILURE)); 189 | m.add_string(this.transport.server_object.get_allowed_auths(username)); 190 | if (result == paramikojs.AUTH_PARTIALLY_SUCCESSFUL) { 191 | m.add_boolean(1); 192 | } else { 193 | m.add_boolean(0); 194 | this.auth_fail_count += 1; 195 | } 196 | } 197 | this.transport._send_message(m); 198 | if (this.auth_fail_count >= 10) { 199 | this._disconnect_no_more_auth(); 200 | } 201 | if (result == paramikojs.AUTH_SUCCESSFUL) { 202 | this.transport._auth_trigger(); 203 | } 204 | }, 205 | 206 | _interactive_query : function(q) { 207 | // make interactive query instead of response 208 | var m = new paramikojs.Message(); 209 | m.add_byte(String.fromCharCode(paramikojs.MSG_USERAUTH_INFO_REQUEST)); 210 | m.add_string(q.name); 211 | m.add_string(q.instructions); 212 | m.add_string(''); 213 | m.add_int(q.prompts.length); 214 | for (var x = 0; x < q.prompts.length; ++x) { 215 | m.add_string(q.prompts[x][0]); 216 | m.add_boolean(q.prompts[x][1]); 217 | } 218 | this.transport._send_message(m); 219 | }, 220 | 221 | _parse_userauth_request : function(m) { 222 | if (!this.transport.server_mode) { 223 | // er, uh... what? 224 | m = new paramikojs.Message(); 225 | m.add_byte(String.fromCharCode(paramikojs.MSG_USERAUTH_FAILURE)); 226 | m.add_string('none'); 227 | m.add_boolean(0); 228 | this.transport._send_message(m); 229 | return; 230 | } 231 | if (this.authenticated) { 232 | // ignore 233 | return; 234 | } 235 | var username = m.get_string(); 236 | var service = m.get_string(); 237 | var method = m.get_string(); 238 | this.transport._log(DEBUG, 'Auth request (type=' + method + ') service=' + service + ', username=' + username); 239 | if (service != 'ssh-connection') { 240 | this._disconnect_service_not_available(); 241 | return; 242 | } 243 | if (this.auth_username && this.auth_username != username) { 244 | this.transport._log(INFO, 'Auth rejected because the client attempted to change username in mid-flight'); 245 | this._disconnect_no_more_auth(); 246 | return; 247 | } 248 | this.auth_username = username; 249 | 250 | var result; 251 | if (method == 'none') { 252 | result = this.transport.server_object.check_auth_none(username); 253 | } else if (method == 'password') { 254 | var changereq = m.get_boolean(); 255 | var password = m.get_string(); 256 | password = this.transport.fromUTF8.ConvertFromUnicode(password) + this.transport.fromUTF8.Finish(); 257 | 258 | if (changereq) { 259 | // always treated as failure, since we don't support changing passwords, but collect 260 | // the list of valid auth types from the callback anyway 261 | this.transport._log(DEBUG, 'Auth request to change passwords (rejected)'); 262 | var newpassword = m.get_string(); 263 | newpassword = this.transport.fromUTF8.ConvertFromUnicode(newpassword) + this.transport.fromUTF8.Finish(); 264 | result = paramikojs.AUTH_FAILED; 265 | } else { 266 | result = this.transport.server_object.check_auth_password(username, password); 267 | } 268 | } else if (method == 'publickey') { 269 | var sig_attached = m.get_boolean(); 270 | var keytype = m.get_string(); 271 | var keyblob = m.get_string(); 272 | try { 273 | key = this.transport._key_info[keytype](new paramikojs.Message(keyblob)); 274 | } catch(ex) { 275 | this.transport._log(INFO, 'Auth rejected: public key: ' + ex.toString()); 276 | key = null; 277 | } 278 | if (!key) { 279 | this._disconnect_no_more_auth(); 280 | return; 281 | } 282 | // first check if this key is okay... if not, we can skip the verify 283 | result = this.transport.server_object.check_auth_publickey(username, key); 284 | if (result != paramikojs.AUTH_FAILED) { 285 | // key is okay, verify it 286 | if (!sig_attached) { 287 | // client wants to know if this key is acceptable, before it 288 | // signs anything... send special "ok" message 289 | m = new paramikojs.Message(); 290 | m.add_byte(String.fromCharCode(paramikojs.MSG_USERAUTH_PK_OK)); 291 | m.add_string(keytype); 292 | m.add_string(keyblob); 293 | this.transport._send_message(m); 294 | return; 295 | } 296 | var sig = new paramikojs.Message(m.get_string()); 297 | var blob = this._get_session_blob(key, service, username); 298 | if (!key.verify_ssh_sig(blob, sig)) { 299 | this.transport._log(INFO, 'Auth rejected: invalid signature'); 300 | result = paramikojs.AUTH_FAILED; 301 | } 302 | } 303 | } else if (method == 'keyboard-interactive') { 304 | var lang = m.get_string(); 305 | var submethods = m.get_string(); 306 | result = this.transport.server_object.check_auth_interactive(username, submethods); 307 | if (result instanceof paramikojs.InteractiveQuery) { 308 | // make interactive query instead of response 309 | this._interactive_query(result); 310 | return; 311 | } 312 | } else { 313 | result = this.transport.server_object.check_auth_none(username); 314 | } 315 | // okay, send result 316 | this._send_auth_result(username, method, result); 317 | }, 318 | 319 | _parse_userauth_success : function(m) { 320 | this.transport._log(INFO, 'Authentication (' + this.auth_method + ') successful!'); 321 | this.authenticated = true; 322 | this.transport._auth_trigger(); 323 | this.transport.auth_callback(true); 324 | }, 325 | 326 | _parse_userauth_failure : function(m) { 327 | var authlist = m.get_list(); 328 | var partial = m.get_boolean(); 329 | var nextOptions = null; 330 | if (partial) { 331 | this.transport._log(INFO, 'Authentication continues...'); 332 | this.transport._log(DEBUG, 'Methods: ' + authlist.toString()); 333 | //this.transport.saved_exception = new paramikojs.ssh_exception.PartialAuthentication(authlist); 334 | nextOptions = authlist; 335 | } else if (authlist.indexOf(this.auth_method) == -1) { 336 | this.transport._log(DEBUG, 'Authentication type (' + this.auth_method + ') not permitted.'); 337 | this.transport._log(DEBUG, 'Allowed methods: ' + authlist.toString()); 338 | //this.transport.saved_exception = new paramikojs.ssh_exception.BadAuthenticationType('Bad authentication type', authlist); 339 | nextOptions = authlist; 340 | } else { 341 | this.transport._log(INFO, 'Authentication (' + this.auth_method + ') failed.'); 342 | } 343 | this.authenticated = false; 344 | this.username = null; 345 | this.transport.auth_callback(false, authlist, this.triedKeyboard, this.triedPublicKey); 346 | }, 347 | 348 | _parse_userauth_banner : function(m) { 349 | var banner = m.get_string(); 350 | var lang = m.get_string(); 351 | this.transport._log(INFO, 'Auth banner: ' + banner); 352 | // who cares. 353 | }, 354 | 355 | _parse_userauth_info_request : function(m) { 356 | if (this.auth_method != 'keyboard-interactive') { 357 | throw new paramikojs.ssh_exception.SSHException('Illegal info request from server'); 358 | } 359 | var title = m.get_string(); 360 | var instructions = m.get_string(); 361 | m.get_string(); // lang 362 | var prompts = m.get_int(); 363 | var prompt_list = []; 364 | for (var x = 0; x < prompts; ++x) { 365 | prompt_list.push([m.get_string(), m.get_boolean()]); 366 | } 367 | var response_list = this.interactive_handler(title, instructions, prompt_list); 368 | 369 | m = new paramikojs.Message(); 370 | m.add_byte(String.fromCharCode(paramikojs.MSG_USERAUTH_INFO_RESPONSE)); 371 | m.add_int(response_list.length); 372 | for (var x = 0; x < response_list.length; ++x) { 373 | m.add_string(response_list[x]); 374 | } 375 | this.transport._send_message(m); 376 | }, 377 | 378 | _parse_userauth_info_response : function(m) { 379 | if (!this.transport.server_mode) { 380 | throw new paramikojs.ssh_exception.SSHException('Illegal info response from server'); 381 | } 382 | var n = m.get_int(); 383 | var responses = []; 384 | for (var x = 0; x < n; ++x) { 385 | responses.append(m.get_string()); 386 | } 387 | var result = this.transport.server_object.check_auth_interactive_response(responses); 388 | if (result instanceof paramikojs.InteractiveQuery) { 389 | // make interactive query instead of response 390 | this._interactive_query(result); 391 | return; 392 | } 393 | this._send_auth_result(this.auth_username, 'keyboard-interactive', result); 394 | }, 395 | 396 | _handler_table : { 397 | 5: function(self, m) { self._parse_service_request(m) }, 398 | 6: function(self, m) { self._parse_service_accept(m) }, 399 | 50: function(self, m) { self._parse_userauth_request(m) }, 400 | 51: function(self, m) { self._parse_userauth_failure(m) }, 401 | 52: function(self, m) { self._parse_userauth_success(m) }, 402 | 53: function(self, m) { self._parse_userauth_banner(m) }, 403 | 60: function(self, m) { self._parse_userauth_info_request(m) }, 404 | 61: function(self, m) { self._parse_userauth_info_response(m) } 405 | } 406 | }; 407 | -------------------------------------------------------------------------------- /file.js: -------------------------------------------------------------------------------- 1 | /* 2 | Reusable base class to implement python-style file buffering around a 3 | simpler stream. 4 | */ 5 | paramikojs.BufferedFile = function() { 6 | this.newlines = null; 7 | this._flags = 0; 8 | this._bufsize = this._DEFAULT_BUFSIZE; 9 | this._wbuffer = ""; 10 | this._rbuffer = ""; 11 | this._at_trailing_cr = false; 12 | this._closed = false; 13 | // pos - position within the file, according to the user 14 | // realpos - position according the OS 15 | // (these may be different because we buffer for line reading) 16 | this._pos = this._realpos = 0; 17 | // size only matters for seekable files 18 | this._size = 0; 19 | } 20 | 21 | paramikojs.BufferedFile.prototype = { 22 | _DEFAULT_BUFSIZE : 8192, 23 | 24 | SEEK_SET : 0, 25 | SEEK_CUR : 1, 26 | SEEK_END : 2, 27 | 28 | FLAG_READ : 0x1, 29 | FLAG_WRITE : 0x2, 30 | FLAG_APPEND : 0x4, 31 | FLAG_BINARY : 0x10, 32 | FLAG_BUFFERED : 0x20, 33 | FLAG_LINE_BUFFERED : 0x40, 34 | FLAG_UNIVERSAL_NEWLINE : 0x80, 35 | 36 | /* 37 | Close the file. Future read and write operations will fail. 38 | */ 39 | __close : function() { 40 | this.flush(); 41 | this._closed = true; 42 | }, 43 | 44 | close : function() { 45 | this.__close(); 46 | }, 47 | 48 | /* 49 | Write out any data in the write buffer. This may do nothing if write 50 | buffering is not turned on. 51 | */ 52 | flush : function(callback) { 53 | this._write_all(this._wbuffer, callback); 54 | this._wbuffer = ""; 55 | return; 56 | }, 57 | 58 | /* 59 | Returns the next line from the input, or raises L{StopIteration} when 60 | EOF is hit. Unlike python file objects, it's okay to mix calls to 61 | C{next} and L{readline}. 62 | 63 | @raise StopIteration: when the end of the file is reached. 64 | 65 | @return: a line read from the file. 66 | @rtype: str 67 | */ 68 | next : function() { 69 | var line = this.readline(); 70 | if (!line) { 71 | throw StopIteration; 72 | } 73 | return line; 74 | }, 75 | 76 | /* 77 | Read at most C{size} bytes from the file (less if we hit the end of the 78 | file first). If the C{size} argument is negative or omitted, read all 79 | the remaining data in the file. 80 | 81 | @param size: maximum number of bytes to read 82 | @type size: int 83 | @return: data read from the file, or an empty string if EOF was 84 | encountered immediately 85 | @rtype: str 86 | */ 87 | read : function(size, callback) { 88 | if (this._closed) { 89 | throw new paramikojs.ssh_exception.IOError('File is closed'); 90 | } 91 | if (!(this._flags & this.FLAG_READ)) { 92 | throw new paramikojs.ssh_exception.IOError('File is not open for reading'); 93 | } 94 | var result; 95 | if (!size || size < 0) { 96 | // go for broke 97 | result = this._rbuffer; 98 | this._rbuffer = ''; 99 | this._pos += result.length; 100 | this.read_all(callback, result); 101 | return; 102 | } 103 | if (size <= this._rbuffer.length) { 104 | result = this._rbuffer.substring(0, size); 105 | this._rbuffer = this._rbuffer.substring(size); 106 | this._pos += result.length; 107 | callback(result); 108 | return; 109 | } 110 | this.read_some(callback, size); 111 | }, 112 | 113 | read_all : function(callback, result) { 114 | var self = this; 115 | var read_callback = function(new_data, eofError, ioError) { 116 | if (eofError) { 117 | new_data = null; 118 | } 119 | 120 | if (!new_data || new_data.length == 0) { 121 | callback(result); 122 | return; 123 | } 124 | result += new_data; 125 | self._realpos += new_data.length; 126 | self._pos += new_data.length; 127 | self.read_all(callback, result); 128 | }; 129 | this._read(this._DEFAULT_BUFSIZE, read_callback); 130 | }, 131 | 132 | read_some : function(callback, size) { 133 | var self = this; 134 | var read_callback = function(new_data, eofError, ioError) { 135 | if (eofError) { 136 | new_data = null; 137 | } 138 | 139 | if (!new_data || new_data.length == 0) { 140 | self.read_finish(callback, size); 141 | return; 142 | } 143 | 144 | self._rbuffer += new_data; 145 | self._realpos += new_data.length; 146 | 147 | self.read_some(callback, size); 148 | }; 149 | 150 | if (this._rbuffer.length < size) { 151 | var read_size = size - this._rbuffer.length; 152 | if (this._flags & this.FLAG_BUFFERED) { 153 | read_size = Math.max(this._bufsize, read_size); 154 | } 155 | this._read(read_size, read_callback); 156 | return; 157 | } 158 | this.read_finish(callback, size); 159 | }, 160 | 161 | read_finish : function(callback, size) { 162 | var result = this._rbuffer.substring(0, size); 163 | this._rbuffer = this._rbuffer.substring(size); 164 | this._pos += result.length; 165 | callback(result); 166 | }, 167 | 168 | /* 169 | Read one entire line from the file. A trailing newline character is 170 | kept in the string (but may be absent when a file ends with an 171 | incomplete line). If the size argument is present and non-negative, it 172 | is a maximum byte count (including the trailing newline) and an 173 | incomplete line may be returned. An empty string is returned only when 174 | EOF is encountered immediately. 175 | 176 | @note: Unlike stdio's C{fgets()}, the returned string contains null 177 | characters (C{'\\0'}) if they occurred in the input. 178 | 179 | @param size: maximum length of returned string. 180 | @type size: int 181 | @return: next line of the file, or an empty string if the end of the 182 | file has been reached. 183 | @rtype: str 184 | */ 185 | readline : function(size) { 186 | // todo transcode if necessary 187 | /* 188 | # it's almost silly how complex this function is. 189 | if self._closed: 190 | raise IOError('File is closed') 191 | if not (self._flags & self.FLAG_READ): 192 | raise IOError('File not open for reading') 193 | line = self._rbuffer 194 | while True: 195 | if self._at_trailing_cr and (self._flags & self.FLAG_UNIVERSAL_NEWLINE) and (len(line) > 0): 196 | # edge case: the newline may be '\r\n' and we may have read 197 | # only the first '\r' last time. 198 | if line[0] == '\n': 199 | line = line[1:] 200 | self._record_newline('\r\n') 201 | else: 202 | self._record_newline('\r') 203 | self._at_trailing_cr = False 204 | # check size before looking for a linefeed, in case we already have 205 | # enough. 206 | if (size is not None) and (size >= 0): 207 | if len(line) >= size: 208 | # truncate line and return 209 | self._rbuffer = line[size:] 210 | line = line[:size] 211 | self._pos += len(line) 212 | return line 213 | n = size - len(line) 214 | else: 215 | n = self._bufsize 216 | if ('\n' in line) or ((self._flags & self.FLAG_UNIVERSAL_NEWLINE) and ('\r' in line)): 217 | break 218 | try: 219 | new_data = self._read(n) 220 | except EOFError: 221 | new_data = None 222 | if (new_data is None) or (len(new_data) == 0): 223 | self._rbuffer = '' 224 | self._pos += len(line) 225 | return line 226 | line += new_data 227 | self._realpos += len(new_data) 228 | # find the newline 229 | pos = line.find('\n') 230 | if self._flags & self.FLAG_UNIVERSAL_NEWLINE: 231 | rpos = line.find('\r') 232 | if (rpos >= 0) and ((rpos < pos) or (pos < 0)): 233 | pos = rpos 234 | xpos = pos + 1 235 | if (line[pos] == '\r') and (xpos < len(line)) and (line[xpos] == '\n'): 236 | xpos += 1 237 | self._rbuffer = line[xpos:] 238 | lf = line[pos:xpos] 239 | line = line[:pos] + '\n' 240 | if (len(self._rbuffer) == 0) and (lf == '\r'): 241 | # we could read the line up to a '\r' and there could still be a 242 | # '\n' following that we read next time. note that and eat it. 243 | self._at_trailing_cr = True 244 | else: 245 | self._record_newline(lf) 246 | self._pos += len(line) 247 | return line 248 | */ 249 | }, 250 | 251 | /* 252 | Read all remaining lines using L{readline} and return them as a list. 253 | If the optional C{sizehint} argument is present, instead of reading up 254 | to EOF, whole lines totalling approximately sizehint bytes (possibly 255 | after rounding up to an internal buffer size) are read. 256 | 257 | @param sizehint: desired maximum number of bytes to read. 258 | @type sizehint: int 259 | @return: list of lines read from the file. 260 | @rtype: list 261 | */ 262 | readlines : function(sizehint) { 263 | // todo transcode if necessary 264 | /* 265 | lines = [] 266 | bytes = 0 267 | while True: 268 | line = self.readline() 269 | if len(line) == 0: 270 | break 271 | lines.append(line) 272 | bytes += len(line) 273 | if (sizehint is not None) and (bytes >= sizehint): 274 | break 275 | return lines 276 | */ 277 | }, 278 | 279 | /* 280 | Set the file's current position, like stdio's C{fseek}. Not all file 281 | objects support seeking. 282 | 283 | @note: If a file is opened in append mode (C{'a'} or C{'a+'}), any seek 284 | operations will be undone at the next write (as the file position 285 | will move back to the end of the file). 286 | 287 | @param offset: position to move to within the file, relative to 288 | C{whence}. 289 | @type offset: int 290 | @param whence: type of movement: 0 = absolute; 1 = relative to the 291 | current position; 2 = relative to the end of the file. 292 | @type whence: int 293 | 294 | @raise IOError: if the file doesn't support random access. 295 | */ 296 | seek : function(offset, whence) { 297 | throw new paramikojs.ssh_exception.IOError('File does not support seeking.'); 298 | }, 299 | 300 | /* 301 | Return the file's current position. This may not be accurate or 302 | useful if the underlying file doesn't support random access, or was 303 | opened in append mode. 304 | 305 | @return: file position (in bytes). 306 | @rtype: int 307 | */ 308 | tell : function() { 309 | return this._pos; 310 | }, 311 | 312 | /* 313 | Write data to the file. If write buffering is on (C{bufsize} was 314 | specified and non-zero), some or all of the data may not actually be 315 | written yet. (Use L{flush} or L{close} to force buffered data to be 316 | written out.) 317 | 318 | @param data: data to write. 319 | @type data: str 320 | */ 321 | write : function(data, callback) { 322 | if (this._closed) { 323 | throw new paramikojs.ssh_exception.IOError('File is closed'); 324 | } 325 | if (!(this._flags & this.FLAG_WRITE)) { 326 | throw new paramikojs.ssh_exception.IOError('File not open for writing'); 327 | } 328 | if (!(this._flags & this.FLAG_BUFFERED)) { 329 | this._write_all(data, callback); 330 | return; 331 | } 332 | this._wbuffer += data; 333 | if (this._flags & this.FLAG_LINE_BUFFERED) { 334 | // only scan the new data for linefeed, to avoid wasting time. 335 | var last_newline_pos = data.lastIndexOf('\n'); 336 | if (last_newline_pos >= 0) { 337 | var wbuf = this._wbuffer; 338 | last_newline_pos += wbuf.length - data.length; 339 | this._write_all(wbuf.substring(0, last_newline_pos + 1), callback); 340 | this._wbuffer = ""; 341 | this._wbuffer += wbuf.substring(last_newline_pos + 1); 342 | } 343 | return; 344 | } 345 | // even if we're line buffering, if the buffer has grown past the 346 | // buffer size, force a flush. 347 | if (this._wbuffer.length >= this._bufsize) { 348 | this.flush(callback); 349 | } 350 | return; 351 | }, 352 | 353 | /* 354 | Write a sequence of strings to the file. The sequence can be any 355 | iterable object producing strings, typically a list of strings. (The 356 | name is intended to match L{readlines}; C{writelines} does not add line 357 | separators.) 358 | 359 | @param sequence: an iterable sequence of strings. 360 | @type sequence: sequence 361 | */ 362 | writelines : function(sequence) { 363 | for (var x = 0; x < sequence.length; ++x) { 364 | this.write(sequence[x]); 365 | } 366 | return; 367 | }, 368 | 369 | /* 370 | Identical to C{iter(f)}. This is a deprecated file interface that 371 | predates python iterator support. 372 | 373 | @return: an iterator. 374 | @rtype: iterator 375 | */ 376 | xreadlines : function() { 377 | return this; 378 | }, 379 | 380 | 381 | // overrides... 382 | 383 | 384 | /* 385 | I{(subclass override)} 386 | Read data from the stream. Return C{None} or raise C{EOFError} to 387 | indicate EOF. 388 | */ 389 | _read : function(size) { 390 | throw new paramikojs.ssh_exception.EOFError(); 391 | }, 392 | 393 | /* 394 | I{(subclass override)} 395 | Write data into the stream. 396 | */ 397 | _write : function(data) { 398 | throw new paramikojs.ssh_exception.IOError('write not implemented'); 399 | }, 400 | 401 | /* 402 | I{(subclass override)} 403 | Return the size of the file. This is called from within L{_set_mode} 404 | if the file is opened in append mode, so the file position can be 405 | tracked and L{seek} and L{tell} will work correctly. If the file is 406 | a stream that can't be randomly accessed, you don't need to override 407 | this method, 408 | */ 409 | _get_size : function() { 410 | return 0; 411 | }, 412 | 413 | 414 | // internals... 415 | 416 | 417 | /* 418 | Subclasses call this method to initialize the BufferedFile. 419 | */ 420 | _set_mode : function(mode, bufsize) { 421 | mode = mode || 'r'; 422 | bufsize = bufsize || -1; 423 | 424 | // set bufsize in any event, because it's used for readline(). 425 | this._bufsize = this._DEFAULT_BUFSIZE; 426 | if (bufsize < 0) { 427 | // do no buffering by default, because otherwise writes will get 428 | // buffered in a way that will probably confuse people. 429 | bufsize = 0; 430 | } 431 | if (bufsize == 1) { 432 | // apparently, line buffering only affects writes. reads are only 433 | // buffered if you call readline (directly or indirectly: iterating 434 | // over a file will indirectly call readline). 435 | this._flags |= this.FLAG_BUFFERED | this.FLAG_LINE_BUFFERED; 436 | } else if (bufsize > 1) { 437 | this._bufsize = bufsize; 438 | this._flags |= this.FLAG_BUFFERED 439 | this._flags &= ~this.FLAG_LINE_BUFFERED; 440 | } else if (bufsize == 0) { 441 | // unbuffered 442 | this._flags &= ~(this.FLAG_BUFFERED | this.FLAG_LINE_BUFFERED); 443 | } 444 | 445 | if (mode.indexOf('r') != -1 || mode.indexOf('+') != -1) { 446 | this._flags |= this.FLAG_READ; 447 | } 448 | if (mode.indexOf('w') != -1 || mode.indexOf('+') != -1) { 449 | this._flags |= this.FLAG_WRITE; 450 | } 451 | if (mode.indexOf('a') != -1) { 452 | this._flags |= this.FLAG_WRITE | this.FLAG_APPEND; 453 | this._size = this._get_size(); 454 | this._pos = this._realpos = this._size; 455 | } 456 | if (mode.indexOf('b') != -1) { 457 | this._flags |= this.FLAG_BINARY; 458 | } 459 | if (mode.indexOf('U') != -1) { 460 | this._flags |= this.FLAG_UNIVERSAL_NEWLINE; 461 | // built-in file objects have this attribute to store which kinds of 462 | // line terminations they've seen: 463 | // 464 | this.newlines = null; 465 | } 466 | }, 467 | 468 | _write_all : function(data, callback) { 469 | // the underlying stream may be something that does partial writes (like 470 | // a socket). 471 | while (data.length > 0) { 472 | var count = this._write(data, callback, data.length); 473 | data = data.substring(count); 474 | if (this._flags & this.FLAG_APPEND) { 475 | this._size += count; 476 | this._pos = this._realpos = this._size; 477 | } else { 478 | this._pos += count; 479 | this._realpos += count; 480 | } 481 | } 482 | /*if (callback) { 483 | callback(); 484 | }*/ 485 | return null; 486 | }, 487 | 488 | _record_newline : function(newline) { 489 | // todo transcode if necessary 490 | /* 491 | # silliness about tracking what kinds of newlines we've seen. 492 | # i don't understand why it can be None, a string, or a tuple, instead 493 | # of just always being a tuple, but we'll emulate that behavior anyway. 494 | if not (self._flags & self.FLAG_UNIVERSAL_NEWLINE): 495 | return 496 | if self.newlines is None: 497 | self.newlines = newline 498 | elif (type(self.newlines) is str) and (self.newlines != newline): 499 | self.newlines = (self.newlines, newline) 500 | elif newline not in self.newlines: 501 | self.newlines += (newline,) 502 | */ 503 | } 504 | }; 505 | -------------------------------------------------------------------------------- /kryptos/Cipher/DES3.js: -------------------------------------------------------------------------------- 1 | var DES3 = (function() { 2 | //Paul Tero, July 2001 3 | //http://www.tero.co.uk/des/ 4 | // 5 | //Optimised for performance with large blocks by Michael Hayworth, November 2001 6 | //http://www.netdealing.com 7 | // 8 | //THIS SOFTWARE IS PROVIDED "AS IS" AND 9 | //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 10 | //IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 11 | //ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 12 | //FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 13 | //DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 14 | //OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 15 | //HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 16 | //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 17 | //OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 18 | //SUCH DAMAGE. 19 | 20 | //des 21 | //this takes the key, the message, and whether to encrypt or decrypt 22 | function des (key, message, encrypt, mode, iv, padding, keys) { 23 | //declaring this locally speeds things up a bit 24 | var spfunction1 = new Array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004); 25 | var spfunction2 = new Array (-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000); 26 | var spfunction3 = new Array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200); 27 | var spfunction4 = new Array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080); 28 | var spfunction5 = new Array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100); 29 | var spfunction6 = new Array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010); 30 | var spfunction7 = new Array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002); 31 | var spfunction8 = new Array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000); 32 | 33 | //create the 16 or 48 subkeys we will need 34 | var keys = keys || des_createKeys (key); 35 | var m=0, i, j, temp, temp2, right1, right2, left, right, looping; 36 | var cbcleft, cbcleft2, cbcright, cbcright2 37 | var endloop, loopinc; 38 | var len = message.length; 39 | var chunk = 0; 40 | //set up the loops for single and triple des 41 | var iterations = keys.length == 32 ? 3 : 9; //single or triple des 42 | if (iterations == 3) {looping = encrypt ? new Array (0, 32, 2) : new Array (30, -2, -2);} 43 | else {looping = encrypt ? new Array (0, 32, 2, 62, 30, -2, 64, 96, 2) : new Array (94, 62, -2, 32, 64, 2, 30, -2, -2);} 44 | 45 | //pad the message depending on the padding parameter 46 | if (padding == 2) message += " "; //pad the message with spaces 47 | else if (padding == 1) {temp = 8-(len%8); message += String.fromCharCode (temp,temp,temp,temp,temp,temp,temp,temp); if (temp==8) len+=8;} //PKCS7 padding 48 | else if (!padding) message += "\0\0\0\0\0\0\0\0"; //pad the message out with null bytes 49 | 50 | //store the result here 51 | var result = ""; 52 | var tempresult = ""; 53 | 54 | if (mode == 1) { //CBC mode 55 | cbcleft = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++); 56 | cbcright = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++); 57 | m=0; 58 | } 59 | 60 | //loop through each 64 bit chunk of the message 61 | while (m < len) { 62 | left = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++); 63 | right = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++); 64 | 65 | //for Cipher Block Chaining mode, xor the message with the previous result 66 | if (mode == 1) {if (encrypt) {left ^= cbcleft; right ^= cbcright;} else {cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;}} 67 | 68 | //first each 64 but chunk of the message must be permuted according to IP 69 | temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 70 | temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); 71 | temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); 72 | temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 73 | temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 74 | 75 | left = ((left << 1) | (left >>> 31)); 76 | right = ((right << 1) | (right >>> 31)); 77 | 78 | //do this either 1 or 3 times for each chunk of the message 79 | for (j=0; j>> 4) | (right << 28)) ^ keys[i+1]; 86 | //the result is attained by passing these bytes through the S selection functions 87 | temp = left; 88 | left = right; 89 | right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f] 90 | | spfunction6[(right1 >>> 8) & 0x3f] | spfunction8[right1 & 0x3f] 91 | | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f] 92 | | spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]); 93 | } 94 | temp = left; left = right; right = temp; //unreverse left and right 95 | } //for either 1 or 3 iterations 96 | 97 | //move then each one bit to the right 98 | left = ((left >>> 1) | (left << 31)); 99 | right = ((right >>> 1) | (right << 31)); 100 | 101 | //now perform IP-1, which is IP in the opposite direction 102 | temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 103 | temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 104 | temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); 105 | temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); 106 | temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 107 | 108 | //for Cipher Block Chaining mode, xor the message with the previous result 109 | if (mode == 1) {if (encrypt) {cbcleft = left; cbcright = right;} else {left ^= cbcleft2; right ^= cbcright2;}} 110 | tempresult += String.fromCharCode ((left>>>24), ((left>>>16) & 0xff), ((left>>>8) & 0xff), (left & 0xff), (right>>>24), ((right>>>16) & 0xff), ((right>>>8) & 0xff), (right & 0xff)); 111 | 112 | chunk += 8; 113 | if (chunk == 512) {result += tempresult; tempresult = ""; chunk = 0;} 114 | } //for every 8 characters, or 64 bits in the message 115 | 116 | //return the result as an array 117 | return result + tempresult; 118 | } //end of des 119 | 120 | 121 | 122 | //des_createKeys 123 | //this takes as input a 64 bit key (even though only 56 bits are used) 124 | //as an array of 2 integers, and returns 16 48 bit keys 125 | function des_createKeys (key) { 126 | //declaring this locally speeds things up a bit 127 | var pc2bytes0 = new Array (0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204); 128 | var pc2bytes1 = new Array (0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101); 129 | var pc2bytes2 = new Array (0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808); 130 | var pc2bytes3 = new Array (0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000); 131 | var pc2bytes4 = new Array (0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010); 132 | var pc2bytes5 = new Array (0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420); 133 | var pc2bytes6 = new Array (0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002); 134 | var pc2bytes7 = new Array (0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800); 135 | var pc2bytes8 = new Array (0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002); 136 | var pc2bytes9 = new Array (0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408); 137 | var pc2bytes10 = new Array (0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020); 138 | var pc2bytes11 = new Array (0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200); 139 | var pc2bytes12 = new Array (0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010); 140 | var pc2bytes13 = new Array (0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105); 141 | 142 | //how many iterations (1 for des, 3 for triple des) 143 | var iterations = key.length > 8 ? 3 : 1; //changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys 144 | //stores the return keys 145 | var keys = new Array (32 * iterations); 146 | //now define the left shifts which need to be done 147 | var shifts = new Array (0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0); 148 | //other variables 149 | var lefttemp, righttemp, m=0, n=0, temp, left, right; 150 | 151 | for (var j=0; j>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 156 | temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16); 157 | temp = ((left >>> 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2); 158 | temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16); 159 | temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 160 | temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 161 | temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 162 | 163 | //the right side needs to be shifted and to get the last four bits of the left side 164 | temp = (left << 8) | ((right >>> 20) & 0x000000f0); 165 | //left needs to be put upside down 166 | left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0); 167 | right = temp; 168 | 169 | //now go through and perform these shifts on the left and right keys 170 | for (var i=0; i < shifts.length; i++) { 171 | //shift the keys either one or two bits to the left 172 | if (shifts[i]) {left = (left << 2) | (left >>> 26); right = (right << 2) | (right >>> 26);} 173 | else {left = (left << 1) | (left >>> 27); right = (right << 1) | (right >>> 27);} 174 | left &= -0xf; right &= -0xf; 175 | 176 | //now apply PC-2, in such a way that E is easier when encrypting or decrypting 177 | //this conversion will look like PC-2 except only the last 6 bits of each byte are used 178 | //rather than 48 consecutive bits and the order of lines will be according to 179 | //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7 180 | lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf] 181 | | pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf] 182 | | pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf] 183 | | pc2bytes6[(left >>> 4) & 0xf]; 184 | righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf] 185 | | pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf] 186 | | pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf] 187 | | pc2bytes13[(right >>> 4) & 0xf]; 188 | temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff; 189 | keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16); 190 | } 191 | } //for each iterations 192 | //return the keys we've created 193 | return keys; 194 | } //end of des_createKeys 195 | 196 | return { 197 | des : des, 198 | des_createKeys : des_createKeys 199 | } 200 | 201 | })(); 202 | 203 | kryptos.cipher.DES3 = function(key, mode, iv) { 204 | this.keys = DES3.des_createKeys(key); 205 | this.mode = mode == kryptos.cipher.DES3.MODE_CBC ? 1 : 0; 206 | this.iv = iv; 207 | } 208 | 209 | kryptos.cipher.DES3.MODE_CBC = 2; 210 | 211 | kryptos.cipher.DES3.prototype = { 212 | encrypt : function(plaintext) { 213 | var ciphertext = DES3.des(null, plaintext, true, this.mode, this.iv, null, this.keys); 214 | this.iv = ciphertext.substring(ciphertext.length - 8); 215 | return ciphertext; 216 | }, 217 | 218 | decrypt : function(ciphertext) { 219 | var plaintext = DES3.des(null, ciphertext, false, this.mode, this.iv, null, this.keys); 220 | this.iv = ciphertext.substring(ciphertext.length - 8); 221 | return plaintext; 222 | } 223 | }; 224 | --------------------------------------------------------------------------------