From 68cf5ab063f27e295ec4c63cf015d505d32841b3 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 31 Oct 2013 10:50:19 -0700 Subject: [PATCH 01/14] Removed an unused import. --- paramiko/kex_gex.py | 1 - 1 file changed, 1 deletion(-) diff --git a/paramiko/kex_gex.py b/paramiko/kex_gex.py index c0455a1..71218ac 100644 --- a/paramiko/kex_gex.py +++ b/paramiko/kex_gex.py @@ -23,7 +23,6 @@ client side, and a B{lot} more on the server side. """ from Crypto.Hash import SHA -from Crypto.Util import number from paramiko.common import * from paramiko import util From 4d3e0b711a98c440810004cb599a00d0f72978d7 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 29 Mar 2014 16:55:01 -0700 Subject: [PATCH 02/14] Switched hash functions from PyCrypto to hashlib. There's a few advantages to this: 1) It's probably fast, OpenSSL, which typically backs hashlib, receives far more attention for optimizaitons than PyCrypto. 2) It's the first step to supporting PyPy, where PyCrypto doesn't run. --- paramiko/dsskey.py | 7 ++++--- paramiko/ecdsakey.py | 13 +++++++------ paramiko/hostkeys.py | 10 ++++++---- paramiko/kex_gex.py | 6 +++--- paramiko/kex_group1.py | 6 +++--- paramiko/packet.py | 9 ++------- paramiko/pkey.py | 8 ++++---- paramiko/rsakey.py | 7 ++++--- paramiko/sftp_server.py | 18 +++++++++--------- paramiko/transport.py | 22 +++++++++++----------- paramiko/util.py | 9 ++++----- tests/test_packetizer.py | 9 ++++++--- tests/test_pkey.py | 8 +++++--- tests/test_util.py | 5 +++-- 14 files changed, 71 insertions(+), 66 deletions(-) diff --git a/paramiko/dsskey.py b/paramiko/dsskey.py index c26966e..04281ea 100644 --- a/paramiko/dsskey.py +++ b/paramiko/dsskey.py @@ -20,8 +20,9 @@ DSS keys. """ +from hashlib import sha1 + from Crypto.PublicKey import DSA -from Crypto.Hash import SHA from paramiko import util from paramiko.common import zero_byte, rng @@ -96,7 +97,7 @@ class DSSKey (PKey): return self.x is not None def sign_ssh_data(self, rng, data): - digest = SHA.new(data).digest() + digest = sha1(data).digest() dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x))) # generate a suitable k qsize = len(util.deflate_long(self.q, 0)) @@ -130,7 +131,7 @@ class DSSKey (PKey): # pull out (r, s) which are NOT encoded as mpints sigR = util.inflate_long(sig[:20], 1) sigS = util.inflate_long(sig[20:], 1) - sigM = util.inflate_long(SHA.new(data).digest(), 1) + sigM = util.inflate_long(sha1(data).digest(), 1) dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q))) return dss.verify(sigM, (sigR, sigS)) diff --git a/paramiko/ecdsakey.py b/paramiko/ecdsakey.py index 6ae2d27..7e2ee7f 100644 --- a/paramiko/ecdsakey.py +++ b/paramiko/ecdsakey.py @@ -21,11 +21,12 @@ L{ECDSAKey} """ import binascii -from ecdsa import SigningKey, VerifyingKey, der, curves -from Crypto.Hash import SHA256 -from ecdsa.test_pyecdsa import ECDSA -from paramiko.common import four_byte, one_byte +from hashlib import sha256 +from ecdsa import SigningKey, VerifyingKey, der, curves +from ecdsa.test_pyecdsa import ECDSA + +from paramiko.common import four_byte, one_byte from paramiko.message import Message from paramiko.pkey import PKey from paramiko.py3compat import byte_chr, u @@ -98,7 +99,7 @@ class ECDSAKey (PKey): return self.signing_key is not None def sign_ssh_data(self, rpool, data): - digest = SHA256.new(data).digest() + digest = sha256(data).digest() sig = self.signing_key.sign_digest(digest, entropy=rpool.read, sigencode=self._sigencode) m = Message() @@ -113,7 +114,7 @@ class ECDSAKey (PKey): # verify the signature by SHA'ing the data and encrypting it # using the public key. - hash_obj = SHA256.new(data).digest() + hash_obj = sha256(data).digest() return self.verifying_key.verify_digest(sig, hash_obj, sigdecode=self._sigdecode) diff --git a/paramiko/hostkeys.py b/paramiko/hostkeys.py index f32fbeb..e1e7a18 100644 --- a/paramiko/hostkeys.py +++ b/paramiko/hostkeys.py @@ -18,7 +18,9 @@ import binascii -from Crypto.Hash import SHA, HMAC +from hashlib import sha1 +from hmac import HMAC + from paramiko.common import rng from paramiko.py3compat import b, u, encodebytes, decodebytes @@ -262,13 +264,13 @@ class HostKeys (MutableMapping): :return: the hashed hostname as a `str` """ if salt is None: - salt = rng.read(SHA.digest_size) + salt = rng.read(sha1().digest_size) else: if salt.startswith('|1|'): salt = salt.split('|')[2] salt = decodebytes(b(salt)) - assert len(salt) == SHA.digest_size - hmac = HMAC.HMAC(salt, b(hostname), SHA).digest() + assert len(salt) == sha1().digest_size + hmac = HMAC(salt, b(hostname), sha1).digest() hostkey = '|1|%s|%s' % (u(encodebytes(salt)), u(encodebytes(hmac))) return hostkey.replace('\n', '') hash_host = staticmethod(hash_host) diff --git a/paramiko/kex_gex.py b/paramiko/kex_gex.py index 02e507b..26b2243 100644 --- a/paramiko/kex_gex.py +++ b/paramiko/kex_gex.py @@ -22,7 +22,7 @@ generator "g" are provided by the server. A bit more work is required on the client side, and a B{lot} more on the server side. """ -from Crypto.Hash import SHA +from hashlib import sha1 from paramiko import util from paramiko.common import DEBUG @@ -203,7 +203,7 @@ class KexGex (object): hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) - H = SHA.new(hm.asbytes()).digest() + H = sha1(hm.asbytes()).digest() self.transport._set_K_H(K, H) # sign it sig = self.transport.get_server_key().sign_ssh_data(self.transport.rng, H) @@ -238,6 +238,6 @@ class KexGex (object): hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) - self.transport._set_K_H(K, SHA.new(hm.asbytes()).digest()) + self.transport._set_K_H(K, sha1(hm.asbytes()).digest()) self.transport._verify_key(host_key, sig) self.transport._activate_outbound() diff --git a/paramiko/kex_group1.py b/paramiko/kex_group1.py index 3dfb7f1..f792eff 100644 --- a/paramiko/kex_group1.py +++ b/paramiko/kex_group1.py @@ -21,7 +21,7 @@ Standard SSH key exchange ("kex" if you wanna sound cool). Diffie-Hellman of 1024 bit key halves, using a known "p" prime and "g" generator. """ -from Crypto.Hash import SHA +from hashlib import sha1 from paramiko import util from paramiko.common import max_byte, zero_byte @@ -105,7 +105,7 @@ class KexGroup1(object): hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) - self.transport._set_K_H(K, SHA.new(hm.asbytes()).digest()) + self.transport._set_K_H(K, sha1(hm.asbytes()).digest()) self.transport._verify_key(host_key, sig) self.transport._activate_outbound() @@ -124,7 +124,7 @@ class KexGroup1(object): hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) - H = SHA.new(hm.asbytes()).digest() + H = sha1(hm.asbytes()).digest() self.transport._set_K_H(K, H) # sign it sig = self.transport.get_server_key().sign_ssh_data(self.transport.rng, H) diff --git a/paramiko/packet.py b/paramiko/packet.py index 0f51df5..5cffe95 100644 --- a/paramiko/packet.py +++ b/paramiko/packet.py @@ -25,6 +25,7 @@ import socket import struct import threading import time +from hmac import HMAC from paramiko import util from paramiko.common import linefeed_byte, cr_byte_value, asbytes, MSG_NAMES, \ @@ -34,12 +35,6 @@ from paramiko.ssh_exception import SSHException, ProxyCommandFailure from paramiko.message import Message -try: - from r_hmac import HMAC -except ImportError: - from Crypto.Hash.HMAC import HMAC - - def compute_hmac(key, message, digest_class): return HMAC(key, message, digest_class).digest() @@ -359,7 +354,7 @@ class Packetizer (object): raise SSHException('Mismatched MAC') padding = byte_ord(packet[0]) payload = packet[1:packet_size - padding] - + if self.__dump_packets: self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding)) diff --git a/paramiko/pkey.py b/paramiko/pkey.py index c8f84e0..7d5da40 100644 --- a/paramiko/pkey.py +++ b/paramiko/pkey.py @@ -23,8 +23,8 @@ Common API for all public keys. import base64 from binascii import hexlify, unhexlify import os +from hashlib import md5 -from Crypto.Hash import MD5 from Crypto.Cipher import DES3, AES from paramiko import util @@ -126,7 +126,7 @@ class PKey (object): a 16-byte `string ` (binary) of the MD5 fingerprint, in SSH format. """ - return MD5.new(self.asbytes()).digest() + return md5(self.asbytes()).digest() def get_base64(self): """ @@ -300,7 +300,7 @@ class PKey (object): keysize = self._CIPHER_TABLE[encryption_type]['keysize'] mode = self._CIPHER_TABLE[encryption_type]['mode'] salt = unhexlify(b(saltstr)) - key = util.generate_key_bytes(MD5, salt, password, keysize) + key = util.generate_key_bytes(md5, salt, password, keysize) return cipher.new(key, mode, salt).decrypt(data) def _write_private_key_file(self, tag, filename, data, password=None): @@ -332,7 +332,7 @@ class PKey (object): blocksize = self._CIPHER_TABLE[cipher_name]['blocksize'] mode = self._CIPHER_TABLE[cipher_name]['mode'] salt = rng.read(16) - key = util.generate_key_bytes(MD5, salt, password, keysize) + key = util.generate_key_bytes(md5, salt, password, keysize) if len(data) % blocksize != 0: n = blocksize - len(data) % blocksize #data += rng.read(n) diff --git a/paramiko/rsakey.py b/paramiko/rsakey.py index c93f321..fc09b5c 100644 --- a/paramiko/rsakey.py +++ b/paramiko/rsakey.py @@ -20,8 +20,9 @@ RSA keys. """ +from hashlib import sha1 + from Crypto.PublicKey import RSA -from Crypto.Hash import SHA from paramiko import util from paramiko.common import rng, max_byte, zero_byte, one_byte @@ -91,7 +92,7 @@ class RSAKey (PKey): return self.d is not None def sign_ssh_data(self, rpool, data): - digest = SHA.new(data).digest() + digest = sha1(data).digest() rsa = RSA.construct((long(self.n), long(self.e), long(self.d))) sig = util.deflate_long(rsa.sign(self._pkcs1imify(digest), bytes())[0], 0) m = Message() @@ -106,7 +107,7 @@ class RSAKey (PKey): # verify the signature by SHA'ing the data and encrypting it using the # public key. some wackiness ensues where we "pkcs1imify" the 20-byte # hash into a string as long as the RSA key. - hash_obj = util.inflate_long(self._pkcs1imify(SHA.new(data).digest()), True) + hash_obj = util.inflate_long(self._pkcs1imify(sha1(data).digest()), True) rsa = RSA.construct((long(self.n), long(self.e))) return rsa.verify(hash_obj, (sig,)) diff --git a/paramiko/sftp_server.py b/paramiko/sftp_server.py index dadfd02..2d8d190 100644 --- a/paramiko/sftp_server.py +++ b/paramiko/sftp_server.py @@ -22,9 +22,9 @@ Server-mode SFTP support. import os import errno - -from Crypto.Hash import MD5, SHA import sys +from hashlib import md5, sha1 + from paramiko import util from paramiko.sftp import BaseSFTP, Message, SFTP_FAILURE, \ SFTP_PERMISSION_DENIED, SFTP_NO_SUCH_FILE @@ -45,8 +45,8 @@ from paramiko.sftp import CMD_HANDLE, SFTP_DESC, CMD_STATUS, SFTP_EOF, CMD_NAME, CMD_READLINK, CMD_SYMLINK, CMD_REALPATH, CMD_EXTENDED, SFTP_OP_UNSUPPORTED _hash_class = { - 'sha1': SHA, - 'md5': MD5, + 'sha1': sha1, + 'md5': md5, } @@ -82,14 +82,14 @@ class SFTPServer (BaseSFTP, SubsystemHandler): self.file_table = {} self.folder_table = {} self.server = sftp_si(server, *largs, **kwargs) - + def _log(self, level, msg): if issubclass(type(msg), list): for m in msg: super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + m) else: super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + msg) - + def start_subsystem(self, name, transport, channel): self.sock = channel self._log(DEBUG, 'Started sftp server on channel %s' % repr(channel)) @@ -157,7 +157,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler): This is meant to be a handy helper function for translating SFTP file requests into local file operations. - + :param str filename: name of the file to alter (should usually be an absolute path). :param .SFTPAttributes attr: attributes to change. @@ -281,7 +281,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler): # don't try to read more than about 64KB at a time chunklen = min(blocklen, 65536) count = 0 - hash_obj = alg.new() + hash_obj = alg() while count < blocklen: data = f.read(offset, chunklen) if not isinstance(data, bytes_types): @@ -298,7 +298,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler): msg.add_string(algname) msg.add_bytes(sum_out) self._send_packet(CMD_EXTENDED_REPLY, msg) - + def _convert_pflags(self, pflags): """convert SFTP-style open() flags to Python's os.open() flags""" if (pflags & SFTP_FLAG_READ) and (pflags & SFTP_FLAG_WRITE): diff --git a/paramiko/transport.py b/paramiko/transport.py index 1471b54..437cd27 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -25,6 +25,7 @@ import sys import threading import time import weakref +from hashlib import md5, sha1 import paramiko from paramiko import util @@ -59,7 +60,6 @@ from paramiko.util import retry_on_signal from Crypto import Random from Crypto.Cipher import Blowfish, AES, DES3, ARC4 -from Crypto.Hash import SHA, MD5 try: from Crypto.Util import Counter except ImportError: @@ -107,10 +107,10 @@ class Transport (threading.Thread): } _mac_info = { - 'hmac-sha1': {'class': SHA, 'size': 20}, - 'hmac-sha1-96': {'class': SHA, 'size': 12}, - 'hmac-md5': {'class': MD5, 'size': 16}, - 'hmac-md5-96': {'class': MD5, 'size': 12}, + 'hmac-sha1': {'class': sha1, 'size': 20}, + 'hmac-sha1-96': {'class': sha1, 'size': 12}, + 'hmac-md5': {'class': md5, 'size': 16}, + 'hmac-md5-96': {'class': md5, 'size': 12}, } _key_info = { @@ -1338,13 +1338,13 @@ class Transport (threading.Thread): m.add_bytes(self.H) m.add_byte(b(id)) m.add_bytes(self.session_id) - out = sofar = SHA.new(m.asbytes()).digest() + out = sofar = sha1(m.asbytes()).digest() while len(out) < nbytes: m = Message() m.add_mpint(self.K) m.add_bytes(self.H) m.add_bytes(sofar) - digest = SHA.new(m.asbytes()).digest() + digest = sha1(m.asbytes()).digest() out += digest sofar += digest return out[:nbytes] @@ -1719,9 +1719,9 @@ class Transport (threading.Thread): # initial mac keys are done in the hash's natural size (not the potentially truncated # transmission size) if self.server_mode: - mac_key = self._compute_key('E', mac_engine.digest_size) + mac_key = self._compute_key('E', mac_engine().digest_size) else: - mac_key = self._compute_key('F', mac_engine.digest_size) + mac_key = self._compute_key('F', mac_engine().digest_size) self.packetizer.set_inbound_cipher(engine, block_size, mac_engine, mac_size, mac_key) compress_in = self._compression_info[self.remote_compression][1] if (compress_in is not None) and ((self.remote_compression != 'zlib@openssh.com') or self.authenticated): @@ -1746,9 +1746,9 @@ class Transport (threading.Thread): # initial mac keys are done in the hash's natural size (not the potentially truncated # transmission size) if self.server_mode: - mac_key = self._compute_key('F', mac_engine.digest_size) + mac_key = self._compute_key('F', mac_engine().digest_size) else: - mac_key = self._compute_key('E', mac_engine.digest_size) + mac_key = self._compute_key('E', mac_engine().digest_size) sdctr = self.local_cipher.endswith('-ctr') self.packetizer.set_outbound_cipher(engine, block_size, mac_engine, mac_size, mac_key, sdctr) compress_out = self._compression_info[self.local_compression][0] diff --git a/paramiko/util.py b/paramiko/util.py index dbcbbae..f4ee3ad 100644 --- a/paramiko/util.py +++ b/paramiko/util.py @@ -143,15 +143,14 @@ def tb_strings(): return ''.join(traceback.format_exception(*sys.exc_info())).split('\n') -def generate_key_bytes(hashclass, salt, key, nbytes): +def generate_key_bytes(hash_alg, salt, key, nbytes): """ Given a password, passphrase, or other human-source key, scramble it through a secure hash into some keyworthy bytes. This specific algorithm is used for encrypting/decrypting private key files. - :param class hashclass: - class from `Crypto.Hash` that can be used as a secure hashing function - (like ``MD5`` or ``SHA``). + :param function hash_alg: A function which creates a new hash object, such + as ``hashlib.sha256``. :param salt: data to salt the hash with. :type salt: byte string :param str key: human-entered password or passphrase. @@ -163,7 +162,7 @@ def generate_key_bytes(hashclass, salt, key, nbytes): if len(salt) > 8: salt = salt[:8] while nbytes > 0: - hash_obj = hashclass.new() + hash_obj = hash_alg() if len(digest) > 0: hash_obj.update(digest) hash_obj.update(b(key)) diff --git a/tests/test_packetizer.py b/tests/test_packetizer.py index d4d5544..a8c0f97 100644 --- a/tests/test_packetizer.py +++ b/tests/test_packetizer.py @@ -21,9 +21,12 @@ Some unit tests for the ssh2 protocol in Transport. """ import unittest +from hashlib import sha1 + from tests.loop import LoopSocket + from Crypto.Cipher import AES -from Crypto.Hash import SHA + from paramiko import Message, Packetizer, util from paramiko.common import byte_chr, zero_byte @@ -41,7 +44,7 @@ class PacketizerTest (unittest.TestCase): p.set_log(util.get_logger('paramiko.transport')) p.set_hexdump(True) cipher = AES.new(zero_byte * 16, AES.MODE_CBC, x55 * 16) - p.set_outbound_cipher(cipher, 16, SHA, 12, x1f * 20) + p.set_outbound_cipher(cipher, 16, sha1, 12, x1f * 20) # message has to be at least 16 bytes long, so we'll have at least one # block of data encrypted that contains zero random padding bytes @@ -64,7 +67,7 @@ class PacketizerTest (unittest.TestCase): p.set_log(util.get_logger('paramiko.transport')) p.set_hexdump(True) cipher = AES.new(zero_byte * 16, AES.MODE_CBC, x55 * 16) - p.set_inbound_cipher(cipher, 16, SHA, 12, x1f * 20) + p.set_inbound_cipher(cipher, 16, sha1, 12, x1f * 20) wsock.send(b'\x43\x91\x97\xbd\x5b\x50\xac\x25\x87\xc2\xc4\x6b\xc7\xe9\x38\xc0\x90\xd2\x16\x56\x0d\x71\x73\x61\x38\x7c\x4c\x3d\xfb\x97\x7d\xe2\x6e\x03\xb1\xa0\xc2\x1c\xd6\x41\x41\x4c\xb4\x59') cmd, m = p.read_message() self.assertEqual(100, cmd) diff --git a/tests/test_pkey.py b/tests/test_pkey.py index 6ff68fc..c457f0e 100644 --- a/tests/test_pkey.py +++ b/tests/test_pkey.py @@ -20,11 +20,14 @@ Some unit tests for public/private key objects. """ -from binascii import hexlify import unittest +from binascii import hexlify +from hashlib import md5 + from paramiko import RSAKey, DSSKey, ECDSAKey, Message, util from paramiko.py3compat import StringIO, byte_chr, b, bytes from paramiko.common import rng + from tests.util import test_path # from openssh's ssh-keygen @@ -90,8 +93,7 @@ class KeyTest (unittest.TestCase): pass def test_1_generate_key_bytes(self): - from Crypto.Hash import MD5 - key = util.generate_key_bytes(MD5, x1234, 'happy birthday', 30) + key = util.generate_key_bytes(md5, x1234, 'happy birthday', 30) exp = b'\x61\xE1\xF2\x72\xF4\xC1\xC4\x56\x15\x86\xBD\x32\x24\x98\xC0\xE9\x24\x67\x27\x80\xF4\x7B\xB3\x7D\xDA\x7D\x54\x01\x9E\x64' self.assertEqual(exp, key) diff --git a/tests/test_util.py b/tests/test_util.py index 6bde404..af6eceb 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -23,7 +23,8 @@ Some unit tests for utility functions. from binascii import hexlify import errno import os -from Crypto.Hash import SHA +from hashlib import sha1 + import paramiko.util from paramiko.util import lookup_ssh_host_config as host_config from paramiko.py3compat import StringIO, byte_ord @@ -136,7 +137,7 @@ class UtilTest(ParamikoTest): ) def test_4_generate_key_bytes(self): - x = paramiko.util.generate_key_bytes(SHA, b'ABCDEFGH', 'This is my secret passphrase.', 64) + x = paramiko.util.generate_key_bytes(sha1, b'ABCDEFGH', 'This is my secret passphrase.', 64) hex = ''.join(['%02x' % byte_ord(c) for c in x]) self.assertEqual(hex, '9110e2f6793b69363e58173e9436b13a5a4b339005741d5c680e505f57d871347b4239f14fb5c46e857d5e100424873ba849ac699cea98d729e57b3e84378e8b') From 23528069ec2d14464c8cb5754cca8e039692f255 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 29 Mar 2014 17:17:20 -0700 Subject: [PATCH 03/14] Remove unused function --- paramiko/primes.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/paramiko/primes.py b/paramiko/primes.py index 58d158c..5279b1a 100644 --- a/paramiko/primes.py +++ b/paramiko/primes.py @@ -20,31 +20,11 @@ Utility functions for dealing with primes. """ -from Crypto.Util import number - from paramiko import util from paramiko.py3compat import byte_mask, long from paramiko.ssh_exception import SSHException -def _generate_prime(bits, rng): - """primtive attempt at prime generation""" - hbyte_mask = pow(2, bits % 8) - 1 - while True: - # loop catches the case where we increment n into a higher bit-range - x = rng.read((bits + 7) // 8) - if hbyte_mask > 0: - x = byte_mask(x[0], hbyte_mask) + x[1:] - n = util.inflate_long(x, 1) - n |= 1 - n |= (1 << (bits - 1)) - while not number.isPrime(n): - n += 2 - if util.bit_length(n) == bits: - break - return n - - def _roll_random(rng, n): """returns a random # from 0 to N-1""" bits = util.bit_length(n - 1) From e1d92087fae4ba0404c40ef1ee383294777476b8 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Tue, 1 Apr 2014 11:13:26 -0700 Subject: [PATCH 04/14] Minor site cleanup --- sites/_shared_static/logo.png | Bin 6401 -> 0 bytes sites/shared_conf.py | 2 -- 2 files changed, 2 deletions(-) delete mode 100644 sites/_shared_static/logo.png diff --git a/sites/_shared_static/logo.png b/sites/_shared_static/logo.png deleted file mode 100644 index bc76697e6ac461a259dcbf0bb04edea99b161341..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6401 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 Date: Tue, 1 Apr 2014 12:36:21 -0700 Subject: [PATCH 05/14] Fix broken tag-tree links in changelog --- sites/www/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sites/www/conf.py b/sites/www/conf.py index 1c6c925..0c8af16 100644 --- a/sites/www/conf.py +++ b/sites/www/conf.py @@ -14,7 +14,8 @@ rss_description = 'Paramiko project news' # Releases changelog extension extensions.append('releases') -releases_release_uri = "https://github.com/paramiko/paramiko/tree/%s" +# Paramiko 1.x tags start with 'v'. Meh. +releases_release_uri = "https://github.com/paramiko/paramiko/tree/v%s" releases_issue_uri = "https://github.com/paramiko/paramiko/issues/%s" # Intersphinx for referencing API/usage docs From 658d202cc780f7cf536668cbca62601659369285 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Tue, 1 Apr 2014 13:28:54 -0700 Subject: [PATCH 06/14] This setting no longer needed & causes warnings if left in --- sites/shared_conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sites/shared_conf.py b/sites/shared_conf.py index eb89983..18c3bb4 100644 --- a/sites/shared_conf.py +++ b/sites/shared_conf.py @@ -7,7 +7,6 @@ import alabaster html_theme_path = [alabaster.get_path()] extensions = ['alabaster'] # Paths relative to invoking conf.py - not this shared file -html_static_path = ['../_shared_static'] html_theme = 'alabaster' html_theme_options = { 'description': "A Python implementation of SSHv2.", From b81025e3d2ddb71fd28632b33b3bf9b5cc56c826 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Sun, 6 Apr 2014 12:36:50 -0700 Subject: [PATCH 07/14] Formatting --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 2910a7f..4a85832 100644 --- a/setup.py +++ b/setup.py @@ -40,9 +40,10 @@ import sys try: from setuptools import setup kw = { - 'install_requires': ['pycrypto >= 2.1, != 2.4', - 'ecdsa', - ], + 'install_requires': [ + 'pycrypto >= 2.1, != 2.4', + 'ecdsa', + ], } except ImportError: from distutils.core import setup From b85a09673a31719b76b3998270137f0189c226e5 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Sun, 6 Apr 2014 16:19:03 -0700 Subject: [PATCH 08/14] Use newer alabaster w/ showhidden in sidebar TOC Lets us not have 2x TOCs on landing page --- dev-requirements.txt | 2 +- sites/www/index.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 5744f33..91ae854 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,5 +5,5 @@ tox>=1.4,<1.5 invoke>=0.7.0 invocations>=0.5.0 sphinx>=1.1.3 -alabaster>=0.3.1 +alabaster>=0.4.0 releases>=0.5.2 diff --git a/sites/www/index.rst b/sites/www/index.rst index cb3961c..03189cf 100644 --- a/sites/www/index.rst +++ b/sites/www/index.rst @@ -12,6 +12,8 @@ usage and API documentation can be found at our code documentation site, `docs.paramiko.org `_. .. toctree:: + :hidden: + changelog FAQs installing From 1103416d8386e7965bae0d51d596efc4f5a75670 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Sun, 6 Apr 2014 16:24:16 -0700 Subject: [PATCH 09/14] Put blog into a branch --- sites/www/blog.py | 140 --------------------------------- sites/www/blog.rst | 16 ---- sites/www/blog/first-post.rst | 7 -- sites/www/blog/second-post.rst | 7 -- sites/www/conf.py | 6 -- sites/www/index.rst | 9 +-- 6 files changed, 2 insertions(+), 183 deletions(-) delete mode 100644 sites/www/blog.py delete mode 100644 sites/www/blog.rst delete mode 100644 sites/www/blog/first-post.rst delete mode 100644 sites/www/blog/second-post.rst diff --git a/sites/www/blog.py b/sites/www/blog.py deleted file mode 100644 index 3b129eb..0000000 --- a/sites/www/blog.py +++ /dev/null @@ -1,140 +0,0 @@ -from collections import namedtuple -from datetime import datetime -import time -import email.utils - -from sphinx.util.compat import Directive -from docutils import nodes - - -class BlogDateDirective(Directive): - """ - Used to parse/attach date info to blog post documents. - - No nodes generated, since none are needed. - """ - has_content = True - - def run(self): - # Tag parent document with parsed date value. - self.state.document.blog_date = datetime.strptime( - self.content[0], "%Y-%m-%d" - ) - # Don't actually insert any nodes, we're already done. - return [] - -class blog_post_list(nodes.General, nodes.Element): - pass - -class BlogPostListDirective(Directive): - """ - Simply spits out a 'blog_post_list' temporary node for replacement. - - Gets replaced at doctree-resolved time - only then will all blog post - documents be written out (& their date directives executed). - """ - def run(self): - return [blog_post_list('')] - - -Post = namedtuple('Post', 'name doc title date opener') - -def get_posts(app): - # Obtain blog posts - post_names = filter(lambda x: x.startswith('blog/'), app.env.found_docs) - posts = map(lambda x: (x, app.env.get_doctree(x)), post_names) - # Obtain common data used for list page & RSS - data = [] - for post, doc in sorted(posts, key=lambda x: x[1].blog_date, reverse=True): - # Welp. No "nice" way to get post title. Thanks Sphinx. - title = doc[0][0][0] - # Date. This may or may not end up reflecting the required - # *input* format, but doing it here gives us flexibility. - date = doc.blog_date - # 1st paragraph as opener. TODO: allow a role or something marking - # where to actually pull from? - opener = doc.traverse(nodes.paragraph)[0] - data.append(Post(post, doc, title, date, opener)) - return data - -def replace_blog_post_lists(app, doctree, fromdocname): - """ - Replace blog_post_list nodes with ordered list-o-links to posts. - """ - # Obtain blog posts - post_names = filter(lambda x: x.startswith('blog/'), app.env.found_docs) - posts = map(lambda x: (x, app.env.get_doctree(x)), post_names) - # Build "list" of links/etc - post_links = [] - for post, doc, title, date, opener in get_posts(app): - # Link itself - uri = app.builder.get_relative_uri(fromdocname, post) - link = nodes.reference('', '', refdocname=post, refuri=uri) - # Title, bolded. TODO: use 'topic' or something maybe? - link.append(nodes.strong('', title)) - date = date.strftime("%Y-%m-%d") - # Meh @ not having great docutils nodes which map to this. - html = '
%s
' % date - timestamp = nodes.raw(text=html, format='html') - # NOTE: may group these within another element later if styling - # necessitates it - group = [timestamp, nodes.paragraph('', '', link), opener] - post_links.extend(group) - - # Replace temp node(s) w/ expanded list-o-links - for node in doctree.traverse(blog_post_list): - node.replace_self(post_links) - -def rss_timestamp(timestamp): - # Use horribly inappropriate module for its magical daylight-savings-aware - # timezone madness. Props to Tinkerer for the idea. - return email.utils.formatdate( - time.mktime(timestamp.timetuple()), - localtime=True - ) - -def generate_rss(app): - # Meh at having to run this subroutine like 3x per build. Not worth trying - # to be clever for now tho. - posts_ = get_posts(app) - # LOL URLs - root = app.config.rss_link - if not root.endswith('/'): - root += '/' - # Oh boy - posts = [ - ( - root + app.builder.get_target_uri(x.name), - x.title, - str(x.opener[0]), # Grab inner text element from paragraph - rss_timestamp(x.date), - ) - for x in posts_ - ] - location = 'blog/rss.xml' - context = { - 'title': app.config.project, - 'link': root, - 'atom': root + location, - 'description': app.config.rss_description, - # 'posts' is sorted by date already - 'date': rss_timestamp(posts_[0].date), - 'posts': posts, - } - yield (location, context, 'rss.xml') - -def setup(app): - # Link in RSS feed back to main website, e.g. 'http://paramiko.org' - app.add_config_value('rss_link', None, '') - # Ditto for RSS description field - app.add_config_value('rss_description', None, '') - # Interprets date metadata in blog post documents - app.add_directive('date', BlogDateDirective) - # Inserts blog post list node (in e.g. a listing page) for replacement - # below - app.add_node(blog_post_list) - app.add_directive('blog-posts', BlogPostListDirective) - # Performs abovementioned replacement - app.connect('doctree-resolved', replace_blog_post_lists) - # Generates RSS page from whole cloth at page generation step - app.connect('html-collect-pages', generate_rss) diff --git a/sites/www/blog.rst b/sites/www/blog.rst deleted file mode 100644 index af9651e..0000000 --- a/sites/www/blog.rst +++ /dev/null @@ -1,16 +0,0 @@ -==== -Blog -==== - -.. blog-posts directive gets replaced with an ordered list of blog posts. - -.. blog-posts:: - - -.. The following toctree ensures blog posts get processed. - -.. toctree:: - :hidden: - :glob: - - blog/* diff --git a/sites/www/blog/first-post.rst b/sites/www/blog/first-post.rst deleted file mode 100644 index 7b07507..0000000 --- a/sites/www/blog/first-post.rst +++ /dev/null @@ -1,7 +0,0 @@ -=========== -First post! -=========== - -A blog post. - -.. date:: 2013-12-04 diff --git a/sites/www/blog/second-post.rst b/sites/www/blog/second-post.rst deleted file mode 100644 index c4463f3..0000000 --- a/sites/www/blog/second-post.rst +++ /dev/null @@ -1,7 +0,0 @@ -=========== -Another one -=========== - -.. date:: 2013-12-05 - -Indeed! diff --git a/sites/www/conf.py b/sites/www/conf.py index 0c8af16..5047fa6 100644 --- a/sites/www/conf.py +++ b/sites/www/conf.py @@ -6,12 +6,6 @@ from os.path import abspath, join, dirname sys.path.append(abspath(join(dirname(__file__), '..'))) from shared_conf import * -# Local blog extension -sys.path.append(abspath('.')) -extensions.append('blog') -rss_link = 'http://paramiko.org' -rss_description = 'Paramiko project news' - # Releases changelog extension extensions.append('releases') # Paramiko 1.x tags start with 'v'. Meh. diff --git a/sites/www/index.rst b/sites/www/index.rst index 03189cf..77e5fcb 100644 --- a/sites/www/index.rst +++ b/sites/www/index.rst @@ -11,6 +11,8 @@ contribution guidelines, development roadmap, news/blog, and so forth. Detailed usage and API documentation can be found at our code documentation site, `docs.paramiko.org `_. +Please see the sidebar to the left to bebin. + .. toctree:: :hidden: @@ -20,13 +22,6 @@ usage and API documentation can be found at our code documentation site, contributing contact -.. Hide blog in hidden toctree for now (to avoid warnings.) - -.. toctree:: - :hidden: - - blog - .. rubric:: Footnotes From 8b9e60f4ce66c913cd7a8371349b07e476e0e6e6 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Sun, 6 Apr 2014 16:25:02 -0700 Subject: [PATCH 10/14] Wow. Just wow. --- sites/www/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sites/www/index.rst b/sites/www/index.rst index 77e5fcb..1b60970 100644 --- a/sites/www/index.rst +++ b/sites/www/index.rst @@ -11,7 +11,7 @@ contribution guidelines, development roadmap, news/blog, and so forth. Detailed usage and API documentation can be found at our code documentation site, `docs.paramiko.org `_. -Please see the sidebar to the left to bebin. +Please see the sidebar to the left to begin. .. toctree:: :hidden: From 57e647341f416c879ae3841c9a7be50c52a21327 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Sun, 6 Apr 2014 18:52:58 -0700 Subject: [PATCH 11/14] Nuke Fab-oriented link color override --- sites/shared_conf.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sites/shared_conf.py b/sites/shared_conf.py index c61ca63..c265fc4 100644 --- a/sites/shared_conf.py +++ b/sites/shared_conf.py @@ -14,9 +14,6 @@ html_theme_options = { 'github_repo': 'paramiko', 'gittip_user': 'bitprophet', 'analytics_id': 'UA-18486793-2', - - 'link': '#3782BE', - 'link_hover': '#3782BE', } html_sidebars = { '**': [ From be7c679942b9b3a1838cce692f87e1c3d45092cf Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Mon, 14 Apr 2014 10:48:33 -0400 Subject: [PATCH 12/14] Errything uses intersphinx to Python --- sites/docs/conf.py | 7 +------ sites/shared_conf.py | 7 ++++++- sites/www/conf.py | 4 +--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/sites/docs/conf.py b/sites/docs/conf.py index f935571..5674fed 100644 --- a/sites/docs/conf.py +++ b/sites/docs/conf.py @@ -5,16 +5,11 @@ sys.path.append(os.path.abspath('../..')) from shared_conf import * # Enable autodoc, intersphinx -extensions.extend(['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']) +extensions.extend(['sphinx.ext.autodoc']) # Autodoc settings autodoc_default_flags = ['members', 'special-members'] -# Intersphinx connection to stdlib -intersphinx_mapping = { - 'python': ('http://docs.python.org/2.6', None), -} - # Sister-site links to WWW html_theme_options['extra_nav_links'] = { "Main website": 'http://www.paramiko.org', diff --git a/sites/shared_conf.py b/sites/shared_conf.py index c265fc4..e0afe92 100644 --- a/sites/shared_conf.py +++ b/sites/shared_conf.py @@ -5,7 +5,7 @@ import alabaster # Alabaster theme + mini-extension html_theme_path = [alabaster.get_path()] -extensions = ['alabaster'] +extensions = ['alabaster', 'sphinx.ext.intersphinx'] # Paths relative to invoking conf.py - not this shared file html_theme = 'alabaster' html_theme_options = { @@ -24,6 +24,11 @@ html_sidebars = { ] } +# Everything intersphinx's to Python +intersphinx_mapping = { + 'python': ('http://docs.python.org/2.6', None), +} + # Regular settings project = 'Paramiko' year = datetime.now().year diff --git a/sites/www/conf.py b/sites/www/conf.py index 5047fa6..bdb5929 100644 --- a/sites/www/conf.py +++ b/sites/www/conf.py @@ -20,9 +20,7 @@ target = join(dirname(__file__), '..', 'docs', '_build') if os.environ.get('READTHEDOCS') == 'True': # TODO: switch to docs.paramiko.org post go-live of sphinx API docs target = 'http://docs.paramiko.org/en/latest/' -intersphinx_mapping = { - 'docs': (target, None), -} +intersphinx_mapping['docs'] = (target, None) # Sister-site links to API docs html_theme_options['extra_nav_links'] = { From 160e2c08e0b7652a92d879c0e481ce72cddafef7 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Mon, 14 Apr 2014 10:48:55 -0400 Subject: [PATCH 13/14] Changelog, closes #295 --- sites/www/changelog.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index 4563877..eff8c2e 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -2,6 +2,8 @@ Changelog ========= +* :support:`295` Swap out a bunch of PyCrypto hash functions with use of + `hashlib` * :support:`290` (also :issue:`292`) Add support for building universal (Python 2+3 compatible) wheel files during the release process. Courtesy of Alex Gaynor. From 1e0e296b05a3e63b33291cfe3d688a435f592c3c Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Mon, 14 Apr 2014 10:50:12 -0400 Subject: [PATCH 14/14] Derp --- sites/www/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index eff8c2e..5f019bb 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -3,7 +3,7 @@ Changelog ========= * :support:`295` Swap out a bunch of PyCrypto hash functions with use of - `hashlib` + `hashlib`. Thanks to Alex Gaynor. * :support:`290` (also :issue:`292`) Add support for building universal (Python 2+3 compatible) wheel files during the release process. Courtesy of Alex Gaynor.