├── .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 |
--------------------------------------------------------------------------------