Merge branch 'master' into ecdsa-deterministic

Conflicts:
	paramiko/ecdsakey.py
This commit is contained in:
Alex Gaynor 2014-04-14 11:06:44 -04:00
commit 91ab5f0c75
19 changed files with 69 additions and 77 deletions

View File

@ -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)

View File

@ -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('')

View File

@ -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)

View File

@ -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)

View File

@ -20,12 +20,13 @@
DSS keys.
"""
import os
from hashlib import sha1
from Crypto.PublicKey import DSA
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
@ -92,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):
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)
@ -164,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
@ -175,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 }

View File

@ -98,7 +98,7 @@ class ECDSAKey (PKey):
def can_sign(self):
return self.signing_key is not None
def sign_ssh_data(self, rpool, data):
def sign_ssh_data(self, data):
sig = self.signing_key.sign_deterministic(
data, sigencode=self._sigencode, hashfunc=sha256)
m = Message()

View File

@ -18,10 +18,11 @@
import binascii
import os
from hashlib import sha1
from hmac import HMAC
from paramiko.common import rng
from paramiko.py3compat import b, u, encodebytes, decodebytes
try:
@ -264,7 +265,7 @@ class HostKeys (MutableMapping):
:return: the hashed hostname as a `str`
"""
if salt is None:
salt = rng.read(sha1().digest_size)
salt = os.urandom(sha1().digest_size)
else:
if salt.startswith('|1|'):
salt = salt.split('|')[2]

View File

@ -22,6 +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.
"""
import os
from hashlib import sha1
from paramiko import util
@ -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):
@ -206,7 +207,7 @@ class KexGex (object):
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()

View File

@ -21,6 +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.
"""
import os
from hashlib import sha1
from paramiko import util
@ -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):
@ -127,7 +128,7 @@ class KexGroup1(object):
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)

View File

@ -21,6 +21,7 @@ Packet handling
"""
import errno
import os
import socket
import struct
import threading
@ -29,7 +30,7 @@ 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
@ -450,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):

View File

@ -28,7 +28,7 @@ from hashlib 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
@ -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>`.
"""
@ -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)
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)

View File

@ -20,12 +20,14 @@
Utility functions for dealing with primes.
"""
import os
from paramiko import util
from paramiko.py3compat import byte_mask, long
from paramiko.ssh_exception import SSHException
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
@ -38,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)
@ -53,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()
@ -127,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]

View File

@ -20,12 +20,13 @@
RSA keys.
"""
import os
from hashlib import sha1
from Crypto.PublicKey import RSA
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
@ -91,7 +92,7 @@ class RSAKey (PKey):
def can_sign(self):
return self.d is not None
def sign_ssh_data(self, rpool, data):
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)
@ -126,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)
@ -141,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
@ -163,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 }

View File

@ -20,6 +20,7 @@
Core protocol implementation
"""
import os
import socket
import sys
import threading
@ -31,7 +32,7 @@ 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, \
@ -58,7 +59,6 @@ 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
try:
from Crypto.Util import Counter
@ -192,7 +192,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:
@ -339,7 +338,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:
@ -475,7 +473,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:
@ -732,8 +730,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):
@ -1402,10 +1400,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:
@ -1590,7 +1584,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)

View File

@ -2,6 +2,8 @@
Changelog
=========
* :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

View File

@ -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:

View File

@ -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()

View File

@ -26,7 +26,6 @@ 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
@ -168,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())
@ -181,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())
@ -195,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))
@ -242,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())

View File

@ -154,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.*