summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dev-requirements.txt2
-rw-r--r--paramiko/__init__.py2
-rw-r--r--paramiko/agent.py2
-rw-r--r--paramiko/auth_handler.py2
-rw-r--r--paramiko/channel.py5
-rw-r--r--paramiko/common.py5
-rw-r--r--paramiko/dsskey.py22
-rw-r--r--paramiko/ecdsakey.py14
-rw-r--r--paramiko/hostkeys.py13
-rw-r--r--paramiko/kex_gex.py13
-rw-r--r--paramiko/kex_group1.py11
-rw-r--r--paramiko/packet.py14
-rw-r--r--paramiko/pkey.py17
-rw-r--r--paramiko/primes.py29
-rw-r--r--paramiko/rsakey.py20
-rw-r--r--paramiko/sftp_server.py18
-rw-r--r--paramiko/transport.py40
-rw-r--r--paramiko/util.py9
-rw-r--r--setup.py2
-rw-r--r--sites/www/blog.py140
-rw-r--r--sites/www/blog.rst16
-rw-r--r--sites/www/blog/first-post.rst7
-rw-r--r--sites/www/blog/second-post.rst7
-rw-r--r--sites/www/changelog.rst11
-rw-r--r--sites/www/conf.py9
-rw-r--r--sites/www/contributing.rst3
-rw-r--r--sites/www/faq.rst9
-rw-r--r--sites/www/index.rst12
-rw-r--r--sites/www/installing.rst58
-rwxr-xr-xtest.py6
-rw-r--r--tests/test_kex.py17
-rw-r--r--tests/test_packetizer.py9
-rw-r--r--tests/test_pkey.py19
-rw-r--r--tests/test_util.py11
-rw-r--r--tox.ini2
35 files changed, 194 insertions, 382 deletions
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 19cf7503..432c45fd 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -5,6 +5,6 @@ tox>=1.4,<1.5
invoke>=0.7.0,<0.8
invocations>=0.5.0
sphinx>=1.1.3
-alabaster>=0.4.0
+alabaster>=0.6.0
releases>=0.5.2
wheel==0.23.0
diff --git a/paramiko/__init__.py b/paramiko/__init__.py
index d86b868b..2ebc8a65 100644
--- a/paramiko/__init__.py
+++ b/paramiko/__init__.py
@@ -23,7 +23,7 @@ if sys.version_info < (2, 6):
__author__ = "Jeff Forcier <jeff@bitprophet.org>"
-__version__ = "1.13.2"
+__version__ = "1.14.1"
__version_info__ = tuple([ int(d) for d in __version__.split(".") ])
__license__ = "GNU Lesser General Public License (LGPL)"
diff --git a/paramiko/agent.py b/paramiko/agent.py
index 2b11337f..5a08d452 100644
--- a/paramiko/agent.py
+++ b/paramiko/agent.py
@@ -364,7 +364,7 @@ class AgentKey(PKey):
def get_name(self):
return self.name
- def sign_ssh_data(self, rng, data):
+ def sign_ssh_data(self, data):
msg = Message()
msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST)
msg.add_string(self.blob)
diff --git a/paramiko/auth_handler.py b/paramiko/auth_handler.py
index c00ad41c..57babef0 100644
--- a/paramiko/auth_handler.py
+++ b/paramiko/auth_handler.py
@@ -206,7 +206,7 @@ class AuthHandler (object):
m.add_string(self.private_key.get_name())
m.add_string(self.private_key)
blob = self._get_session_blob(self.private_key, 'ssh-connection', self.username)
- sig = self.private_key.sign_ssh_data(self.transport.rng, blob)
+ sig = self.private_key.sign_ssh_data(blob)
m.add_string(sig)
elif self.auth_method == 'keyboard-interactive':
m.add_string('')
diff --git a/paramiko/channel.py b/paramiko/channel.py
index 23323650..49d8dd6e 100644
--- a/paramiko/channel.py
+++ b/paramiko/channel.py
@@ -21,9 +21,10 @@ Abstraction for an SSH2 channel.
"""
import binascii
+import os
+import socket
import time
import threading
-import socket
from paramiko import util
from paramiko.common import cMSG_CHANNEL_REQUEST, cMSG_CHANNEL_WINDOW_ADJUST, \
@@ -358,7 +359,7 @@ class Channel (object):
if auth_protocol is None:
auth_protocol = 'MIT-MAGIC-COOKIE-1'
if auth_cookie is None:
- auth_cookie = binascii.hexlify(self.transport.rng.read(16))
+ auth_cookie = binascii.hexlify(os.urandom(16))
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
diff --git a/paramiko/common.py b/paramiko/common.py
index 9a5e2ee1..18298922 100644
--- a/paramiko/common.py
+++ b/paramiko/common.py
@@ -126,11 +126,6 @@ CONNECTION_FAILED_CODE = {
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
-from Crypto import Random
-
-# keep a crypto-strong PRNG nearby
-rng = Random.new()
-
zero_byte = byte_chr(0)
one_byte = byte_chr(1)
four_byte = byte_chr(4)
diff --git a/paramiko/dsskey.py b/paramiko/dsskey.py
index 6a46d326..1901596d 100644
--- a/paramiko/dsskey.py
+++ b/paramiko/dsskey.py
@@ -20,11 +20,13 @@
DSS keys.
"""
+import os
+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
+from paramiko.common import zero_byte
from paramiko.py3compat import long
from paramiko.ssh_exception import SSHException
from paramiko.message import Message
@@ -91,17 +93,17 @@ class DSSKey (PKey):
def get_bits(self):
return self.size
-
+
def can_sign(self):
return self.x is not None
- def sign_ssh_data(self, rng, data):
- digest = SHA.new(data).digest()
+ def sign_ssh_data(self, data):
+ 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))
while True:
- k = util.inflate_long(rng.read(qsize), 1)
+ k = util.inflate_long(os.urandom(qsize), 1)
if (k > 2) and (k < self.q):
break
r, s = dss.sign(util.inflate_long(digest, 1), k)
@@ -130,7 +132,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))
@@ -163,7 +165,7 @@ class DSSKey (PKey):
by ``pyCrypto.PublicKey``).
:return: new `.DSSKey` private key
"""
- dsa = DSA.generate(bits, rng.read, progress_func)
+ dsa = DSA.generate(bits, os.urandom, progress_func)
key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y))
key.x = dsa.x
return key
@@ -174,11 +176,11 @@ class DSSKey (PKey):
def _from_private_key_file(self, filename, password):
data = self._read_private_key_file('DSA', filename, password)
self._decode_key(data)
-
+
def _from_private_key(self, file_obj, password):
data = self._read_private_key('DSA', file_obj, password)
self._decode_key(data)
-
+
def _decode_key(self, data):
# private key file contains:
# DSAPrivateKey = { version = 0, p, q, g, y, x }
diff --git a/paramiko/ecdsakey.py b/paramiko/ecdsakey.py
index 6ae2d277..6736315f 100644
--- a/paramiko/ecdsakey.py
+++ b/paramiko/ecdsakey.py
@@ -21,11 +21,12 @@ L{ECDSAKey}
"""
import binascii
+from hashlib import sha256
+
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 paramiko.common import four_byte, one_byte
from paramiko.message import Message
from paramiko.pkey import PKey
from paramiko.py3compat import byte_chr, u
@@ -97,10 +98,9 @@ class ECDSAKey (PKey):
def can_sign(self):
return self.signing_key is not None
- def sign_ssh_data(self, rpool, data):
- digest = SHA256.new(data).digest()
- sig = self.signing_key.sign_digest(digest, entropy=rpool.read,
- sigencode=self._sigencode)
+ def sign_ssh_data(self, data):
+ sig = self.signing_key.sign_deterministic(
+ data, sigencode=self._sigencode, hashfunc=sha256)
m = Message()
m.add_string('ecdsa-sha2-nistp256')
m.add_string(sig)
@@ -113,7 +113,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 7c6a5aa5..cd65e77c 100644
--- a/paramiko/hostkeys.py
+++ b/paramiko/hostkeys.py
@@ -18,8 +18,11 @@
import binascii
-from Crypto.Hash import SHA, HMAC
-from paramiko.common import rng
+import os
+
+from hashlib import sha1
+from hmac import HMAC
+
from paramiko.py3compat import b, u, encodebytes, decodebytes
try:
@@ -262,13 +265,13 @@ class HostKeys (MutableMapping):
:return: the hashed hostname as a `str`
"""
if salt is None:
- salt = rng.read(SHA.digest_size)
+ salt = os.urandom(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 02e507b7..5ff8a287 100644
--- a/paramiko/kex_gex.py
+++ b/paramiko/kex_gex.py
@@ -22,7 +22,8 @@ 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
+import os
+from hashlib import sha1
from paramiko import util
from paramiko.common import DEBUG
@@ -101,7 +102,7 @@ class KexGex (object):
qhbyte <<= 1
qmask >>= 1
while True:
- x_bytes = self.transport.rng.read(byte_count)
+ x_bytes = os.urandom(byte_count)
x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:]
x = util.inflate_long(x_bytes, 1)
if (x > 1) and (x < q):
@@ -203,10 +204,10 @@ 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)
+ sig = self.transport.get_server_key().sign_ssh_data(H)
# send reply
m = Message()
m.add_byte(c_MSG_KEXDH_GEX_REPLY)
@@ -215,7 +216,7 @@ class KexGex (object):
m.add_string(sig)
self.transport._send_message(m)
self.transport._activate_outbound()
-
+
def _parse_kexdh_gex_reply(self, m):
host_key = m.get_string()
self.f = m.get_mpint()
@@ -238,6 +239,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 3dfb7f18..7ccceea6 100644
--- a/paramiko/kex_group1.py
+++ b/paramiko/kex_group1.py
@@ -21,7 +21,8 @@ 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
+import os
+from hashlib import sha1
from paramiko import util
from paramiko.common import max_byte, zero_byte
@@ -82,7 +83,7 @@ class KexGroup1(object):
# potential x where the first 63 bits are 1, because some of those will be
# larger than q (but this is a tiny tiny subset of potential x).
while 1:
- x_bytes = self.transport.rng.read(128)
+ x_bytes = os.urandom(128)
x_bytes = byte_mask(x_bytes[0], 0x7f) + x_bytes[1:]
if (x_bytes[:8] != b7fffffffffffffff and
x_bytes[:8] != b0000000000000000):
@@ -105,7 +106,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,10 +125,10 @@ 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)
+ sig = self.transport.get_server_key().sign_ssh_data(H)
# send reply
m = Message()
m.add_byte(c_MSG_KEXDH_REPLY)
diff --git a/paramiko/packet.py b/paramiko/packet.py
index 0f51df5e..e97d92f0 100644
--- a/paramiko/packet.py
+++ b/paramiko/packet.py
@@ -21,25 +21,21 @@ Packet handling
"""
import errno
+import os
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, \
- DEBUG, xffffffff, zero_byte, rng
+ DEBUG, xffffffff, zero_byte
from paramiko.py3compat import u, byte_ord
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 +355,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))
@@ -455,7 +451,7 @@ class Packetizer (object):
# don't waste random bytes for the padding
packet += (zero_byte * padding)
else:
- packet += rng.read(padding)
+ packet += os.urandom(padding)
return packet
def _trigger_rekey(self):
diff --git a/paramiko/pkey.py b/paramiko/pkey.py
index c8f84e0a..373563f6 100644
--- a/paramiko/pkey.py
+++ b/paramiko/pkey.py
@@ -23,12 +23,12 @@ 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
-from paramiko.common import o600, rng, zero_byte
+from paramiko.common import o600, zero_byte
from paramiko.py3compat import u, encodebytes, decodebytes, b
from paramiko.ssh_exception import SSHException, PasswordRequiredException
@@ -126,7 +126,7 @@ class PKey (object):
a 16-byte `string <str>` (binary) of the MD5 fingerprint, in SSH
format.
"""
- return MD5.new(self.asbytes()).digest()
+ return md5(self.asbytes()).digest()
def get_base64(self):
"""
@@ -138,12 +138,11 @@ class PKey (object):
"""
return u(encodebytes(self.asbytes())).replace('\n', '')
- def sign_ssh_data(self, rng, data):
+ def sign_ssh_data(self, data):
"""
Sign a blob of data with this private key, and return a `.Message`
representing an SSH signature message.
- :param .Crypto.Util.rng.RandomPool rng: a secure random number generator.
:param str data: the data to sign.
:return: an SSH signature `message <.Message>`.
"""
@@ -300,7 +299,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):
@@ -331,11 +330,11 @@ class PKey (object):
keysize = self._CIPHER_TABLE[cipher_name]['keysize']
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)
+ salt = os.urandom(16)
+ key = util.generate_key_bytes(md5, salt, password, keysize)
if len(data) % blocksize != 0:
n = blocksize - len(data) % blocksize
- #data += rng.read(n)
+ #data += os.urandom(n)
# that would make more sense ^, but it confuses openssh.
data += zero_byte * n
data = cipher.new(key, mode, salt).encrypt(data)
diff --git a/paramiko/primes.py b/paramiko/primes.py
index 58d158c8..8e02e80c 100644
--- a/paramiko/primes.py
+++ b/paramiko/primes.py
@@ -20,32 +20,14 @@
Utility functions for dealing with primes.
"""
-from Crypto.Util import number
+import os
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):
+def _roll_random(n):
"""returns a random # from 0 to N-1"""
bits = util.bit_length(n - 1)
byte_count = (bits + 7) // 8
@@ -58,7 +40,7 @@ def _roll_random(rng, n):
# fits, so i can't guarantee that this loop will ever finish, but the odds
# of it looping forever should be infinitesimal.
while True:
- x = rng.read(byte_count)
+ x = os.urandom(byte_count)
if hbyte_mask > 0:
x = byte_mask(x[0], hbyte_mask) + x[1:]
num = util.inflate_long(x, 1)
@@ -73,11 +55,10 @@ class ModulusPack (object):
on systems that have such a file.
"""
- def __init__(self, rpool):
+ def __init__(self):
# pack is a hash of: bits -> [ (generator, modulus) ... ]
self.pack = {}
self.discarded = []
- self.rng = rpool
def _parse_modulus(self, line):
timestamp, mod_type, tests, tries, size, generator, modulus = line.split()
@@ -147,5 +128,5 @@ class ModulusPack (object):
if min > good:
good = bitsizes[-1]
# now pick a random modulus of this bitsize
- n = _roll_random(self.rng, len(self.pack[good]))
+ n = _roll_random(len(self.pack[good]))
return self.pack[good][n]
diff --git a/paramiko/rsakey.py b/paramiko/rsakey.py
index c93f3218..d1f3ecfe 100644
--- a/paramiko/rsakey.py
+++ b/paramiko/rsakey.py
@@ -20,11 +20,13 @@
RSA keys.
"""
+import os
+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
+from paramiko.common import max_byte, zero_byte, one_byte
from paramiko.message import Message
from paramiko.ber import BER, BERException
from paramiko.pkey import PKey
@@ -90,8 +92,8 @@ class RSAKey (PKey):
def can_sign(self):
return self.d is not None
- def sign_ssh_data(self, rpool, data):
- digest = SHA.new(data).digest()
+ def sign_ssh_data(self, data):
+ 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 +108,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,))
@@ -125,7 +127,7 @@ class RSAKey (PKey):
def write_private_key_file(self, filename, password=None):
self._write_private_key_file('RSA', filename, self._encode_key(), password)
-
+
def write_private_key(self, file_obj, password=None):
self._write_private_key('RSA', file_obj, self._encode_key(), password)
@@ -140,7 +142,7 @@ class RSAKey (PKey):
by ``pyCrypto.PublicKey``).
:return: new `.RSAKey` private key
"""
- rsa = RSA.generate(bits, rng.read, progress_func)
+ rsa = RSA.generate(bits, os.urandom, progress_func)
key = RSAKey(vals=(rsa.e, rsa.n))
key.d = rsa.d
key.p = rsa.p
@@ -162,11 +164,11 @@ class RSAKey (PKey):
def _from_private_key_file(self, filename, password):
data = self._read_private_key_file('RSA', filename, password)
self._decode_key(data)
-
+
def _from_private_key(self, file_obj, password):
data = self._read_private_key('RSA', file_obj, password)
self._decode_key(data)
-
+
def _decode_key(self, data):
# private key file contains:
# RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p }
diff --git a/paramiko/sftp_server.py b/paramiko/sftp_server.py
index dadfd026..2d8d1909 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 d904da1d..1a33e1ae 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -20,17 +20,19 @@
Core protocol implementation
"""
+import os
import socket
import sys
import threading
import time
import weakref
+from hashlib import md5, sha1
import paramiko
from paramiko import util
from paramiko.auth_handler import AuthHandler
from paramiko.channel import Channel
-from paramiko.common import rng, xffffffff, cMSG_CHANNEL_OPEN, cMSG_IGNORE, \
+from paramiko.common import xffffffff, cMSG_CHANNEL_OPEN, cMSG_IGNORE, \
cMSG_GLOBAL_REQUEST, DEBUG, MSG_KEXINIT, MSG_IGNORE, MSG_DISCONNECT, \
MSG_DEBUG, ERROR, WARNING, cMSG_UNIMPLEMENTED, INFO, cMSG_KEXINIT, \
cMSG_NEWKEYS, MSG_NEWKEYS, cMSG_REQUEST_SUCCESS, cMSG_REQUEST_FAILURE, \
@@ -57,9 +59,7 @@ from paramiko.ssh_exception import (SSHException, BadAuthenticationType,
ChannelException, ProxyCommandFailure)
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 = {
@@ -194,7 +194,6 @@ class Transport (threading.Thread):
# okay, normal socket-ish flow here...
threading.Thread.__init__(self)
self.setDaemon(True)
- self.rng = rng
self.sock = sock
# Python < 2.3 doesn't have the settimeout method - RogerB
try:
@@ -340,7 +339,6 @@ class Transport (threading.Thread):
# synchronous, wait for a result
self.completion_event = event = threading.Event()
self.start()
- Random.atfork()
while True:
event.wait(0.1)
if not self.active:
@@ -476,7 +474,7 @@ class Transport (threading.Thread):
.. note:: This has no effect when used in client mode.
"""
- Transport._modulus_pack = ModulusPack(rng)
+ Transport._modulus_pack = ModulusPack()
# places to look for the openssh "moduli" file
file_list = ['/etc/ssh/moduli', '/usr/local/etc/moduli']
if filename is not None:
@@ -733,8 +731,8 @@ class Transport (threading.Thread):
m = Message()
m.add_byte(cMSG_IGNORE)
if byte_count is None:
- byte_count = (byte_ord(rng.read(1)) % 32) + 10
- m.add_bytes(rng.read(byte_count))
+ byte_count = (byte_ord(os.urandom(1)) % 32) + 10
+ m.add_bytes(os.urandom(byte_count))
self._send_user_message(m)
def renegotiate_keys(self):
@@ -1339,13 +1337,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]
@@ -1403,10 +1401,6 @@ class Transport (threading.Thread):
# interpreter shutdown.
self.sys = sys
- # Required to prevent RNG errors when running inside many subprocess
- # containers.
- Random.atfork()
-
# active=True occurs before the thread is launched, to avoid a race
_active_threads.append(self)
if self.server_mode:
@@ -1591,7 +1585,7 @@ class Transport (threading.Thread):
m = Message()
m.add_byte(cMSG_KEXINIT)
- m.add_bytes(rng.read(16))
+ m.add_bytes(os.urandom(16))
m.add_list(self._preferred_kex)
m.add_list(available_server_keys)
m.add_list(self._preferred_ciphers)
@@ -1720,9 +1714,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):
@@ -1747,9 +1741,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 dbcbbae4..f4ee3adc 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/setup.py b/setup.py
index 082a843e..38e444f5 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,7 @@ if sys.platform == 'darwin':
setup(
name = "paramiko",
- version = "1.13.2",
+ version = "1.14.1",
description = "SSH2 protocol library",
long_description = longdesc,
author = "Jeff Forcier",
diff --git a/sites/www/blog.py b/sites/www/blog.py
deleted file mode 100644
index 3b129ebf..00000000
--- 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 = '<div class="timestamp"><span>%s</span></div>' % 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 af9651e4..00000000
--- 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 7b075073..00000000
--- 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 c4463f33..00000000
--- 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/changelog.rst b/sites/www/changelog.rst
index 0cbc933f..426a9ee3 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -4,6 +4,7 @@ Changelog
* :support:`378 backported` Minor code cleanup in the SSH config module
courtesy of Olle Lundberg.
+* :release:`1.14.1 <2014-08-25>`
* :release:`1.13.2 <2014-08-25>`
* :bug:`376` Be less aggressive about expanding variables in ``ssh_config``
files, which results in a speedup of SSH config parsing. Credit to Olle
@@ -30,6 +31,7 @@ Changelog
Thanks to ``@basictheprogram`` for the initial report, Jelmer Vernooij for
the fix and Andrew Starr-Bochicchio & Jeremy T. Bouse (among others) for
discussion & feedback.
+* :release:`1.14.0 <2014-05-07>`
* :release:`1.13.1 <2014-05-07>`
* :release:`1.12.4 <2014-05-07>`
* :release:`1.11.6 <2014-05-07>`
@@ -54,6 +56,12 @@ Changelog
character. Thanks to Antoine Brenner.
* :bug:`308` Fix regression in dsskey.py that caused sporadic signature
verification failures. Thanks to Chris Rose.
+* :support:`299` Use deterministic signatures for ECDSA keys for improved
+ security. Thanks to Alex Gaynor.
+* :support:`297` Replace PyCrypto's ``Random`` with `os.urandom` for improved
+ speed and security. Thanks again to Alex.
+* :support:`295` Swap out a bunch of PyCrypto hash functions with use of
+ `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.
@@ -67,7 +75,8 @@ Changelog
* :release:`1.11.5 <2014-03-13>`
* :release:`1.10.7 <2014-03-13>`
* :feature:`16` **Python 3 support!** Our test suite passes under Python 3, and
- it (& Fabric's test suite) continues to pass under Python 2.
+ it (& Fabric's test suite) continues to pass under Python 2. **Python 2.5 is
+ no longer supported with this change!**
The merged code was built on many contributors' efforts, both code &
feedback. In no particular order, we thank Daniel Goertzen, Ivan Kolodyazhny,
diff --git a/sites/www/conf.py b/sites/www/conf.py
index c7828203..bdb5929a 100644
--- a/sites/www/conf.py
+++ b/sites/www/conf.py
@@ -6,15 +6,10 @@ 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')
-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
diff --git a/sites/www/contributing.rst b/sites/www/contributing.rst
index 2b752cc5..634c2b26 100644
--- a/sites/www/contributing.rst
+++ b/sites/www/contributing.rst
@@ -17,3 +17,6 @@ How to submit bug reports or new code
Please see `this project-agnostic contribution guide
<http://contribution-guide.org>`_ - we follow it explicitly.
+
+Our current changelog is located in ``sites/www/changelog.rst`` - the top
+level files like ``ChangeLog.*`` and ``NEWS`` are historical only.
diff --git a/sites/www/faq.rst b/sites/www/faq.rst
new file mode 100644
index 00000000..a7e80014
--- /dev/null
+++ b/sites/www/faq.rst
@@ -0,0 +1,9 @@
+===================================
+Frequently Asked/Answered Questions
+===================================
+
+Which version should I use? I see multiple active releases.
+===========================================================
+
+Please see :ref:`the installation docs <release-lines>` which have an explicit
+section about this topic.
diff --git a/sites/www/index.rst b/sites/www/index.rst
index 0f07d7e9..1b609709 100644
--- a/sites/www/index.rst
+++ b/sites/www/index.rst
@@ -1,7 +1,7 @@
Welcome to Paramiko!
====================
-Paramiko is a Python (2.5+) implementation of the SSHv2 protocol [#]_,
+Paramiko is a Python (2.6+, 3.3+) implementation of the SSHv2 protocol [#]_,
providing both client and server functionality. While it leverages a Python C
extension for low level cryptography (`PyCrypto <http://pycrypto.org>`_),
Paramiko itself is a pure Python interface around SSH networking concepts.
@@ -11,21 +11,17 @@ 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 <http://docs.paramiko.org>`_.
+Please see the sidebar to the left to begin.
+
.. toctree::
:hidden:
changelog
+ FAQs <faq>
installing
contributing
contact
-.. Hide blog in hidden toctree for now (to avoid warnings.)
-
-.. toctree::
- :hidden:
-
- blog
-
.. rubric:: Footnotes
diff --git a/sites/www/installing.rst b/sites/www/installing.rst
index 0ca9b156..a28ce6cd 100644
--- a/sites/www/installing.rst
+++ b/sites/www/installing.rst
@@ -2,6 +2,8 @@
Installing
==========
+.. _paramiko-itself:
+
Paramiko itself
===============
@@ -14,50 +16,44 @@ via `pip <http://pip-installer.org>`_::
Users who want the bleeding edge can install the development version via
``pip install paramiko==dev``.
-We currently support **Python 2.5/2.6/2.7**, with support for Python 3 coming
-soon. Users on Python 2.4 or older are urged to upgrade. Paramiko *may* work on
-Python 2.4 still, but there is no longer any support guarantee.
+We currently support **Python 2.6, 2.7 and 3.3** (Python **3.2** should also
+work but has a less-strong compatibility guarantee from us.) Users on Python
+2.5 or older are urged to upgrade.
Paramiko has two dependencies: the pure-Python ECDSA module ``ecdsa``, and the
PyCrypto C extension. ``ecdsa`` is easily installable from wherever you
obtained Paramiko's package; PyCrypto may require more work. Read on for
details.
-PyCrypto
-========
-
-`PyCrypto <https://www.dlitz.net/software/pycrypto/>`_ provides the low-level
-(C-based) encryption algorithms we need to implement the SSH protocol. There
-are a couple gotchas associated with installing PyCrypto: its compatibility
-with Python's package tools, and the fact that it is a C-based extension.
+.. _release-lines:
-.. _pycrypto-and-pip:
+Release lines
+-------------
-Possible gotcha on older Python and/or pip versions
----------------------------------------------------
+Users desiring stability may wish to pin themselves to a specific release line
+once they first start using Paramiko; to assist in this, we guarantee bugfixes
+for at least the last 2-3 releases including the latest stable one. This currently means Paramiko **1.11** through **1.13**.
-We strongly recommend using ``pip`` to as it is newer and generally better than
-``easy_install``. However, a combination of bugs in specific (now rather old)
-versions of Python, ``pip`` and PyCrypto can prevent installation of PyCrypto.
-Specifically:
+If you're unsure which version to install, we have suggestions:
-* Python = 2.5.x
-* PyCrypto >= 2.1 (required for most modern versions of Paramiko)
-* ``pip`` < 0.8.1
+* **Completely new users** should always default to the **latest stable
+ release** (as above, whatever is newest / whatever shows up with ``pip
+ install paramiko``.)
+* **Users upgrading from a much older version** (e.g. the 1.7.x line) should
+ probably get the **oldest actively supported line** (see the paragraph above
+ this list for what that currently is.)
+* **Everybody else** is hopefully already "on" a given version and can
+ carefully upgrade to whichever version they care to, when their release line
+ stops being supported.
-When all three criteria are met, you may encounter ``No such file or
-directory`` IOErrors when trying to ``pip install paramiko`` or ``pip install
-PyCrypto``.
-The fix is to make sure at least one of the above criteria is not met, by doing
-the following (in order of preference):
-
-* Upgrade to ``pip`` 0.8.1 or above, e.g. by running ``pip install -U pip``.
-* Upgrade to Python 2.6 or above.
-* Downgrade to Paramiko 1.7.6 or 1.7.7, which do not require PyCrypto >= 2.1,
- and install PyCrypto 2.0.1 (the oldest version on PyPI which works with
- Paramiko 1.7.6/1.7.7)
+PyCrypto
+========
+`PyCrypto <https://www.dlitz.net/software/pycrypto/>`_ provides the low-level
+(C-based) encryption algorithms we need to implement the SSH protocol. There
+are a couple gotchas associated with installing PyCrypto: its compatibility
+with Python's package tools, and the fact that it is a C-based extension.
C extension
-----------
diff --git a/test.py b/test.py
index bd966d1e..2b3d4ed4 100755
--- a/test.py
+++ b/test.py
@@ -101,12 +101,12 @@ def main():
parser.add_option('-P', '--sftp-passwd', dest='password', type='string', default=default_passwd,
metavar='<password>',
help='[with -R] (optional) password to unlock the private key for remote sftp tests')
-
+
options, args = parser.parse_args()
-
+
# setup logging
paramiko.util.log_to_file('test.log')
-
+
if options.use_sftp:
from tests.test_sftp import SFTPTest
if options.use_loopback_sftp:
diff --git a/tests/test_kex.py b/tests/test_kex.py
index c522be46..56f1b7c7 100644
--- a/tests/test_kex.py
+++ b/tests/test_kex.py
@@ -21,7 +21,9 @@ Some unit tests for the key exchange protocols.
"""
from binascii import hexlify
+import os
import unittest
+
import paramiko.util
from paramiko.kex_group1 import KexGroup1
from paramiko.kex_gex import KexGex
@@ -29,9 +31,8 @@ from paramiko import Message
from paramiko.common import byte_chr
-class FakeRng (object):
- def read(self, n):
- return byte_chr(0xcc) * n
+def dummy_urandom(n):
+ return byte_chr(0xcc) * n
class FakeKey (object):
@@ -41,7 +42,7 @@ class FakeKey (object):
def asbytes(self):
return b'fake-key'
- def sign_ssh_data(self, rng, H):
+ def sign_ssh_data(self, H):
return b'fake-sig'
@@ -53,8 +54,7 @@ class FakeModulusPack (object):
return self.G, self.P
-class FakeTransport (object):
- rng = FakeRng()
+class FakeTransport(object):
local_version = 'SSH-2.0-paramiko_1.0'
remote_version = 'SSH-2.0-lame'
local_kex_init = 'local-kex-init'
@@ -91,10 +91,11 @@ class KexTest (unittest.TestCase):
K = 14730343317708716439807310032871972459448364195094179797249681733965528989482751523943515690110179031004049109375612685505881911274101441415545039654102474376472240501616988799699744135291070488314748284283496055223852115360852283821334858541043710301057312858051901453919067023103730011648890038847384890504
def setUp(self):
- pass
+ self._original_urandom = os.urandom
+ os.urandom = dummy_urandom
def tearDown(self):
- pass
+ os.urandom = self._original_urandom
def test_1_group1_client(self):
transport = FakeTransport()
diff --git a/tests/test_packetizer.py b/tests/test_packetizer.py
index d4d5544e..a8c0f973 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 6ff68fc2..1468ee27 100644
--- a/tests/test_pkey.py
+++ b/tests/test_pkey.py
@@ -20,11 +20,13 @@
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 +92,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)
@@ -166,7 +167,7 @@ class KeyTest (unittest.TestCase):
def test_8_sign_rsa(self):
# verify that the rsa private key can sign and verify
key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
- msg = key.sign_ssh_data(rng, b'ice weasels')
+ msg = key.sign_ssh_data(b'ice weasels')
self.assertTrue(type(msg) is Message)
msg.rewind()
self.assertEqual('ssh-rsa', msg.get_text())
@@ -179,7 +180,7 @@ class KeyTest (unittest.TestCase):
def test_9_sign_dss(self):
# verify that the dss private key can sign and verify
key = DSSKey.from_private_key_file(test_path('test_dss.key'))
- msg = key.sign_ssh_data(rng, b'ice weasels')
+ msg = key.sign_ssh_data(b'ice weasels')
self.assertTrue(type(msg) is Message)
msg.rewind()
self.assertEqual('ssh-dss', msg.get_text())
@@ -193,13 +194,13 @@ class KeyTest (unittest.TestCase):
def test_A_generate_rsa(self):
key = RSAKey.generate(1024)
- msg = key.sign_ssh_data(rng, b'jerri blank')
+ msg = key.sign_ssh_data(b'jerri blank')
msg.rewind()
self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
def test_B_generate_dss(self):
key = DSSKey.generate(1024)
- msg = key.sign_ssh_data(rng, b'jerri blank')
+ msg = key.sign_ssh_data(b'jerri blank')
msg.rewind()
self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
@@ -240,7 +241,7 @@ class KeyTest (unittest.TestCase):
def test_13_sign_ecdsa(self):
# verify that the rsa private key can sign and verify
key = ECDSAKey.from_private_key_file(test_path('test_ecdsa.key'))
- msg = key.sign_ssh_data(rng, b'ice weasels')
+ msg = key.sign_ssh_data(b'ice weasels')
self.assertTrue(type(msg) is Message)
msg.rewind()
self.assertEqual('ecdsa-sha2-nistp256', msg.get_text())
diff --git a/tests/test_util.py b/tests/test_util.py
index ecf8db72..44fb8ab5 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')
@@ -153,12 +154,6 @@ class UtilTest(ParamikoTest):
finally:
os.unlink('hostfile.temp')
- def test_6_random(self):
- from paramiko.common import rng
- # just verify that we can pull out 32 bytes and not get an exception.
- x = rng.read(32)
- self.assertEqual(len(x), 32)
-
def test_7_host_config_expose_issue_33(self):
test_config_file = """
Host www13.*
diff --git a/tox.ini b/tox.ini
index 55e3fe64..43c391dd 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py25,py26,py27,py32,py33
+envlist = py26,py27,py32,py33
[testenv]
commands = pip install --use-mirrors -q -r tox-requirements.txt