Switch from using PyCrypto's Random to using os.urandom.
There's several reasons for this change: 1) It's faster for reads up to 1024 bytes (nearly 10x faster for 16 byte reads) 2) It receives considerably more security review since it's in the kernel. 3) It's yet another step towards running on PyPy. 4) Using userspace CSPRNGs is considered something of an anti-pattern. See: http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/ http://webcache.googleusercontent.com/search?q=cache:2nTvpCgKZXIJ:www.2uo.de/myths-about-urandom/+&cd=3&hl=en&ct=clnk&gl=us
This commit is contained in:
parent
5a430def22
commit
6f211115f4
|
@ -364,7 +364,7 @@ class AgentKey(PKey):
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def sign_ssh_data(self, rng, data):
|
def sign_ssh_data(self, data):
|
||||||
msg = Message()
|
msg = Message()
|
||||||
msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST)
|
msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST)
|
||||||
msg.add_string(self.blob)
|
msg.add_string(self.blob)
|
||||||
|
|
|
@ -206,7 +206,7 @@ class AuthHandler (object):
|
||||||
m.add_string(self.private_key.get_name())
|
m.add_string(self.private_key.get_name())
|
||||||
m.add_string(self.private_key)
|
m.add_string(self.private_key)
|
||||||
blob = self._get_session_blob(self.private_key, 'ssh-connection', self.username)
|
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)
|
m.add_string(sig)
|
||||||
elif self.auth_method == 'keyboard-interactive':
|
elif self.auth_method == 'keyboard-interactive':
|
||||||
m.add_string('')
|
m.add_string('')
|
||||||
|
|
|
@ -21,9 +21,10 @@ Abstraction for an SSH2 channel.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import socket
|
|
||||||
|
|
||||||
from paramiko import util
|
from paramiko import util
|
||||||
from paramiko.common import cMSG_CHANNEL_REQUEST, cMSG_CHANNEL_WINDOW_ADJUST, \
|
from paramiko.common import cMSG_CHANNEL_REQUEST, cMSG_CHANNEL_WINDOW_ADJUST, \
|
||||||
|
@ -358,7 +359,7 @@ class Channel (object):
|
||||||
if auth_protocol is None:
|
if auth_protocol is None:
|
||||||
auth_protocol = 'MIT-MAGIC-COOKIE-1'
|
auth_protocol = 'MIT-MAGIC-COOKIE-1'
|
||||||
if auth_cookie is None:
|
if auth_cookie is None:
|
||||||
auth_cookie = binascii.hexlify(self.transport.rng.read(16))
|
auth_cookie = binascii.hexlify(os.urandom(16))
|
||||||
|
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(cMSG_CHANNEL_REQUEST)
|
m.add_byte(cMSG_CHANNEL_REQUEST)
|
||||||
|
|
|
@ -126,11 +126,6 @@ CONNECTION_FAILED_CODE = {
|
||||||
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
|
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
|
||||||
DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
|
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)
|
zero_byte = byte_chr(0)
|
||||||
one_byte = byte_chr(1)
|
one_byte = byte_chr(1)
|
||||||
four_byte = byte_chr(4)
|
four_byte = byte_chr(4)
|
||||||
|
|
|
@ -20,11 +20,13 @@
|
||||||
DSS keys.
|
DSS keys.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from Crypto.PublicKey import DSA
|
from Crypto.PublicKey import DSA
|
||||||
from Crypto.Hash import SHA
|
from Crypto.Hash import SHA
|
||||||
|
|
||||||
from paramiko import util
|
from paramiko import util
|
||||||
from paramiko.common import zero_byte, rng
|
from paramiko.common import zero_byte
|
||||||
from paramiko.py3compat import long
|
from paramiko.py3compat import long
|
||||||
from paramiko.ssh_exception import SSHException
|
from paramiko.ssh_exception import SSHException
|
||||||
from paramiko.message import Message
|
from paramiko.message import Message
|
||||||
|
@ -91,17 +93,17 @@ class DSSKey (PKey):
|
||||||
|
|
||||||
def get_bits(self):
|
def get_bits(self):
|
||||||
return self.size
|
return self.size
|
||||||
|
|
||||||
def can_sign(self):
|
def can_sign(self):
|
||||||
return self.x is not None
|
return self.x is not None
|
||||||
|
|
||||||
def sign_ssh_data(self, rng, data):
|
def sign_ssh_data(self, data):
|
||||||
digest = SHA.new(data).digest()
|
digest = SHA.new(data).digest()
|
||||||
dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x)))
|
dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x)))
|
||||||
# generate a suitable k
|
# generate a suitable k
|
||||||
qsize = len(util.deflate_long(self.q, 0))
|
qsize = len(util.deflate_long(self.q, 0))
|
||||||
while True:
|
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):
|
if (k > 2) and (k < self.q):
|
||||||
break
|
break
|
||||||
r, s = dss.sign(util.inflate_long(digest, 1), k)
|
r, s = dss.sign(util.inflate_long(digest, 1), k)
|
||||||
|
@ -163,7 +165,7 @@ class DSSKey (PKey):
|
||||||
by ``pyCrypto.PublicKey``).
|
by ``pyCrypto.PublicKey``).
|
||||||
:return: new `.DSSKey` private key
|
: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 = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y))
|
||||||
key.x = dsa.x
|
key.x = dsa.x
|
||||||
return key
|
return key
|
||||||
|
@ -174,11 +176,11 @@ class DSSKey (PKey):
|
||||||
def _from_private_key_file(self, filename, password):
|
def _from_private_key_file(self, filename, password):
|
||||||
data = self._read_private_key_file('DSA', filename, password)
|
data = self._read_private_key_file('DSA', filename, password)
|
||||||
self._decode_key(data)
|
self._decode_key(data)
|
||||||
|
|
||||||
def _from_private_key(self, file_obj, password):
|
def _from_private_key(self, file_obj, password):
|
||||||
data = self._read_private_key('DSA', file_obj, password)
|
data = self._read_private_key('DSA', file_obj, password)
|
||||||
self._decode_key(data)
|
self._decode_key(data)
|
||||||
|
|
||||||
def _decode_key(self, data):
|
def _decode_key(self, data):
|
||||||
# private key file contains:
|
# private key file contains:
|
||||||
# DSAPrivateKey = { version = 0, p, q, g, y, x }
|
# DSAPrivateKey = { version = 0, p, q, g, y, x }
|
||||||
|
|
|
@ -21,11 +21,14 @@ L{ECDSAKey}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
from ecdsa import SigningKey, VerifyingKey, der, curves
|
import os
|
||||||
from Crypto.Hash import SHA256
|
|
||||||
from ecdsa.test_pyecdsa import ECDSA
|
|
||||||
from paramiko.common import four_byte, one_byte
|
|
||||||
|
|
||||||
|
from ecdsa import SigningKey, VerifyingKey, der, curves
|
||||||
|
from ecdsa.test_pyecdsa import ECDSA
|
||||||
|
|
||||||
|
from Crypto.Hash import SHA256
|
||||||
|
|
||||||
|
from paramiko.common import four_byte, one_byte
|
||||||
from paramiko.message import Message
|
from paramiko.message import Message
|
||||||
from paramiko.pkey import PKey
|
from paramiko.pkey import PKey
|
||||||
from paramiko.py3compat import byte_chr, u
|
from paramiko.py3compat import byte_chr, u
|
||||||
|
@ -97,9 +100,9 @@ class ECDSAKey (PKey):
|
||||||
def can_sign(self):
|
def can_sign(self):
|
||||||
return self.signing_key is not None
|
return self.signing_key is not None
|
||||||
|
|
||||||
def sign_ssh_data(self, rpool, data):
|
def sign_ssh_data(self, data):
|
||||||
digest = SHA256.new(data).digest()
|
digest = SHA256.new(data).digest()
|
||||||
sig = self.signing_key.sign_digest(digest, entropy=rpool.read,
|
sig = self.signing_key.sign_digest(digest, entropy=os.urandom,
|
||||||
sigencode=self._sigencode)
|
sigencode=self._sigencode)
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_string('ecdsa-sha2-nistp256')
|
m.add_string('ecdsa-sha2-nistp256')
|
||||||
|
|
|
@ -18,8 +18,10 @@
|
||||||
|
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
|
import os
|
||||||
|
|
||||||
from Crypto.Hash import SHA, HMAC
|
from Crypto.Hash import SHA, HMAC
|
||||||
from paramiko.common import rng
|
|
||||||
from paramiko.py3compat import b, u, encodebytes, decodebytes
|
from paramiko.py3compat import b, u, encodebytes, decodebytes
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -262,7 +264,7 @@ class HostKeys (MutableMapping):
|
||||||
:return: the hashed hostname as a `str`
|
:return: the hashed hostname as a `str`
|
||||||
"""
|
"""
|
||||||
if salt is None:
|
if salt is None:
|
||||||
salt = rng.read(SHA.digest_size)
|
salt = os.urandom(SHA.digest_size)
|
||||||
else:
|
else:
|
||||||
if salt.startswith('|1|'):
|
if salt.startswith('|1|'):
|
||||||
salt = salt.split('|')[2]
|
salt = salt.split('|')[2]
|
||||||
|
|
|
@ -22,6 +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.
|
client side, and a B{lot} more on the server side.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from Crypto.Hash import SHA
|
from Crypto.Hash import SHA
|
||||||
|
|
||||||
from paramiko import util
|
from paramiko import util
|
||||||
|
@ -101,7 +103,7 @@ class KexGex (object):
|
||||||
qhbyte <<= 1
|
qhbyte <<= 1
|
||||||
qmask >>= 1
|
qmask >>= 1
|
||||||
while True:
|
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_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:]
|
||||||
x = util.inflate_long(x_bytes, 1)
|
x = util.inflate_long(x_bytes, 1)
|
||||||
if (x > 1) and (x < q):
|
if (x > 1) and (x < q):
|
||||||
|
@ -206,7 +208,7 @@ class KexGex (object):
|
||||||
H = SHA.new(hm.asbytes()).digest()
|
H = SHA.new(hm.asbytes()).digest()
|
||||||
self.transport._set_K_H(K, H)
|
self.transport._set_K_H(K, H)
|
||||||
# sign it
|
# 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
|
# send reply
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(c_MSG_KEXDH_GEX_REPLY)
|
m.add_byte(c_MSG_KEXDH_GEX_REPLY)
|
||||||
|
@ -215,7 +217,7 @@ class KexGex (object):
|
||||||
m.add_string(sig)
|
m.add_string(sig)
|
||||||
self.transport._send_message(m)
|
self.transport._send_message(m)
|
||||||
self.transport._activate_outbound()
|
self.transport._activate_outbound()
|
||||||
|
|
||||||
def _parse_kexdh_gex_reply(self, m):
|
def _parse_kexdh_gex_reply(self, m):
|
||||||
host_key = m.get_string()
|
host_key = m.get_string()
|
||||||
self.f = m.get_mpint()
|
self.f = m.get_mpint()
|
||||||
|
|
|
@ -21,6 +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.
|
1024 bit key halves, using a known "p" prime and "g" generator.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from Crypto.Hash import SHA
|
from Crypto.Hash import SHA
|
||||||
|
|
||||||
from paramiko import util
|
from paramiko import util
|
||||||
|
@ -82,7 +84,7 @@ class KexGroup1(object):
|
||||||
# potential x where the first 63 bits are 1, because some of those will be
|
# 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).
|
# larger than q (but this is a tiny tiny subset of potential x).
|
||||||
while 1:
|
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:]
|
x_bytes = byte_mask(x_bytes[0], 0x7f) + x_bytes[1:]
|
||||||
if (x_bytes[:8] != b7fffffffffffffff and
|
if (x_bytes[:8] != b7fffffffffffffff and
|
||||||
x_bytes[:8] != b0000000000000000):
|
x_bytes[:8] != b0000000000000000):
|
||||||
|
@ -127,7 +129,7 @@ class KexGroup1(object):
|
||||||
H = SHA.new(hm.asbytes()).digest()
|
H = SHA.new(hm.asbytes()).digest()
|
||||||
self.transport._set_K_H(K, H)
|
self.transport._set_K_H(K, H)
|
||||||
# sign it
|
# 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
|
# send reply
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(c_MSG_KEXDH_REPLY)
|
m.add_byte(c_MSG_KEXDH_REPLY)
|
||||||
|
|
|
@ -21,6 +21,7 @@ Packet handling
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import threading
|
import threading
|
||||||
|
@ -28,7 +29,7 @@ import time
|
||||||
|
|
||||||
from paramiko import util
|
from paramiko import util
|
||||||
from paramiko.common import linefeed_byte, cr_byte_value, asbytes, MSG_NAMES, \
|
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.py3compat import u, byte_ord
|
||||||
from paramiko.ssh_exception import SSHException, ProxyCommandFailure
|
from paramiko.ssh_exception import SSHException, ProxyCommandFailure
|
||||||
from paramiko.message import Message
|
from paramiko.message import Message
|
||||||
|
@ -455,7 +456,7 @@ class Packetizer (object):
|
||||||
# don't waste random bytes for the padding
|
# don't waste random bytes for the padding
|
||||||
packet += (zero_byte * padding)
|
packet += (zero_byte * padding)
|
||||||
else:
|
else:
|
||||||
packet += rng.read(padding)
|
packet += os.urandom(padding)
|
||||||
return packet
|
return packet
|
||||||
|
|
||||||
def _trigger_rekey(self):
|
def _trigger_rekey(self):
|
||||||
|
|
|
@ -28,7 +28,7 @@ from Crypto.Hash import MD5
|
||||||
from Crypto.Cipher import DES3, AES
|
from Crypto.Cipher import DES3, AES
|
||||||
|
|
||||||
from paramiko import util
|
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.py3compat import u, encodebytes, decodebytes, b
|
||||||
from paramiko.ssh_exception import SSHException, PasswordRequiredException
|
from paramiko.ssh_exception import SSHException, PasswordRequiredException
|
||||||
|
|
||||||
|
@ -138,12 +138,11 @@ class PKey (object):
|
||||||
"""
|
"""
|
||||||
return u(encodebytes(self.asbytes())).replace('\n', '')
|
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`
|
Sign a blob of data with this private key, and return a `.Message`
|
||||||
representing an SSH signature message.
|
representing an SSH signature message.
|
||||||
|
|
||||||
:param .Crypto.Util.rng.RandomPool rng: a secure random number generator.
|
|
||||||
:param str data: the data to sign.
|
:param str data: the data to sign.
|
||||||
:return: an SSH signature `message <.Message>`.
|
:return: an SSH signature `message <.Message>`.
|
||||||
"""
|
"""
|
||||||
|
@ -331,11 +330,11 @@ class PKey (object):
|
||||||
keysize = self._CIPHER_TABLE[cipher_name]['keysize']
|
keysize = self._CIPHER_TABLE[cipher_name]['keysize']
|
||||||
blocksize = self._CIPHER_TABLE[cipher_name]['blocksize']
|
blocksize = self._CIPHER_TABLE[cipher_name]['blocksize']
|
||||||
mode = self._CIPHER_TABLE[cipher_name]['mode']
|
mode = self._CIPHER_TABLE[cipher_name]['mode']
|
||||||
salt = rng.read(16)
|
salt = os.urandom(16)
|
||||||
key = util.generate_key_bytes(MD5, salt, password, keysize)
|
key = util.generate_key_bytes(MD5, salt, password, keysize)
|
||||||
if len(data) % blocksize != 0:
|
if len(data) % blocksize != 0:
|
||||||
n = blocksize - len(data) % blocksize
|
n = blocksize - len(data) % blocksize
|
||||||
#data += rng.read(n)
|
#data += os.urandom(n)
|
||||||
# that would make more sense ^, but it confuses openssh.
|
# that would make more sense ^, but it confuses openssh.
|
||||||
data += zero_byte * n
|
data += zero_byte * n
|
||||||
data = cipher.new(key, mode, salt).encrypt(data)
|
data = cipher.new(key, mode, salt).encrypt(data)
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
Utility functions for dealing with primes.
|
Utility functions for dealing with primes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from Crypto.Util import number
|
from Crypto.Util import number
|
||||||
|
|
||||||
from paramiko import util
|
from paramiko import util
|
||||||
|
@ -27,12 +29,12 @@ from paramiko.py3compat import byte_mask, long
|
||||||
from paramiko.ssh_exception import SSHException
|
from paramiko.ssh_exception import SSHException
|
||||||
|
|
||||||
|
|
||||||
def _generate_prime(bits, rng):
|
def _generate_prime(bits):
|
||||||
"""primtive attempt at prime generation"""
|
"""primtive attempt at prime generation"""
|
||||||
hbyte_mask = pow(2, bits % 8) - 1
|
hbyte_mask = pow(2, bits % 8) - 1
|
||||||
while True:
|
while True:
|
||||||
# loop catches the case where we increment n into a higher bit-range
|
# loop catches the case where we increment n into a higher bit-range
|
||||||
x = rng.read((bits + 7) // 8)
|
x = os.urandom((bits + 7) // 8)
|
||||||
if hbyte_mask > 0:
|
if hbyte_mask > 0:
|
||||||
x = byte_mask(x[0], hbyte_mask) + x[1:]
|
x = byte_mask(x[0], hbyte_mask) + x[1:]
|
||||||
n = util.inflate_long(x, 1)
|
n = util.inflate_long(x, 1)
|
||||||
|
@ -45,7 +47,7 @@ def _generate_prime(bits, rng):
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def _roll_random(rng, n):
|
def _roll_random(n):
|
||||||
"""returns a random # from 0 to N-1"""
|
"""returns a random # from 0 to N-1"""
|
||||||
bits = util.bit_length(n - 1)
|
bits = util.bit_length(n - 1)
|
||||||
byte_count = (bits + 7) // 8
|
byte_count = (bits + 7) // 8
|
||||||
|
@ -58,7 +60,7 @@ def _roll_random(rng, n):
|
||||||
# fits, so i can't guarantee that this loop will ever finish, but the odds
|
# fits, so i can't guarantee that this loop will ever finish, but the odds
|
||||||
# of it looping forever should be infinitesimal.
|
# of it looping forever should be infinitesimal.
|
||||||
while True:
|
while True:
|
||||||
x = rng.read(byte_count)
|
x = os.urandom(byte_count)
|
||||||
if hbyte_mask > 0:
|
if hbyte_mask > 0:
|
||||||
x = byte_mask(x[0], hbyte_mask) + x[1:]
|
x = byte_mask(x[0], hbyte_mask) + x[1:]
|
||||||
num = util.inflate_long(x, 1)
|
num = util.inflate_long(x, 1)
|
||||||
|
@ -73,11 +75,10 @@ class ModulusPack (object):
|
||||||
on systems that have such a file.
|
on systems that have such a file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, rpool):
|
def __init__(self):
|
||||||
# pack is a hash of: bits -> [ (generator, modulus) ... ]
|
# pack is a hash of: bits -> [ (generator, modulus) ... ]
|
||||||
self.pack = {}
|
self.pack = {}
|
||||||
self.discarded = []
|
self.discarded = []
|
||||||
self.rng = rpool
|
|
||||||
|
|
||||||
def _parse_modulus(self, line):
|
def _parse_modulus(self, line):
|
||||||
timestamp, mod_type, tests, tries, size, generator, modulus = line.split()
|
timestamp, mod_type, tests, tries, size, generator, modulus = line.split()
|
||||||
|
@ -147,5 +148,5 @@ class ModulusPack (object):
|
||||||
if min > good:
|
if min > good:
|
||||||
good = bitsizes[-1]
|
good = bitsizes[-1]
|
||||||
# now pick a random modulus of this bitsize
|
# 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]
|
return self.pack[good][n]
|
||||||
|
|
|
@ -20,11 +20,13 @@
|
||||||
RSA keys.
|
RSA keys.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from Crypto.Hash import SHA
|
from Crypto.Hash import SHA
|
||||||
|
|
||||||
from paramiko import util
|
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.message import Message
|
||||||
from paramiko.ber import BER, BERException
|
from paramiko.ber import BER, BERException
|
||||||
from paramiko.pkey import PKey
|
from paramiko.pkey import PKey
|
||||||
|
@ -90,7 +92,7 @@ class RSAKey (PKey):
|
||||||
def can_sign(self):
|
def can_sign(self):
|
||||||
return self.d is not None
|
return self.d is not None
|
||||||
|
|
||||||
def sign_ssh_data(self, rpool, data):
|
def sign_ssh_data(self, data):
|
||||||
digest = SHA.new(data).digest()
|
digest = SHA.new(data).digest()
|
||||||
rsa = RSA.construct((long(self.n), long(self.e), long(self.d)))
|
rsa = RSA.construct((long(self.n), long(self.e), long(self.d)))
|
||||||
sig = util.deflate_long(rsa.sign(self._pkcs1imify(digest), bytes())[0], 0)
|
sig = util.deflate_long(rsa.sign(self._pkcs1imify(digest), bytes())[0], 0)
|
||||||
|
@ -125,7 +127,7 @@ class RSAKey (PKey):
|
||||||
|
|
||||||
def write_private_key_file(self, filename, password=None):
|
def write_private_key_file(self, filename, password=None):
|
||||||
self._write_private_key_file('RSA', filename, self._encode_key(), password)
|
self._write_private_key_file('RSA', filename, self._encode_key(), password)
|
||||||
|
|
||||||
def write_private_key(self, file_obj, password=None):
|
def write_private_key(self, file_obj, password=None):
|
||||||
self._write_private_key('RSA', file_obj, self._encode_key(), password)
|
self._write_private_key('RSA', file_obj, self._encode_key(), password)
|
||||||
|
|
||||||
|
@ -140,7 +142,7 @@ class RSAKey (PKey):
|
||||||
by ``pyCrypto.PublicKey``).
|
by ``pyCrypto.PublicKey``).
|
||||||
:return: new `.RSAKey` private key
|
: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 = RSAKey(vals=(rsa.e, rsa.n))
|
||||||
key.d = rsa.d
|
key.d = rsa.d
|
||||||
key.p = rsa.p
|
key.p = rsa.p
|
||||||
|
@ -162,11 +164,11 @@ class RSAKey (PKey):
|
||||||
def _from_private_key_file(self, filename, password):
|
def _from_private_key_file(self, filename, password):
|
||||||
data = self._read_private_key_file('RSA', filename, password)
|
data = self._read_private_key_file('RSA', filename, password)
|
||||||
self._decode_key(data)
|
self._decode_key(data)
|
||||||
|
|
||||||
def _from_private_key(self, file_obj, password):
|
def _from_private_key(self, file_obj, password):
|
||||||
data = self._read_private_key('RSA', file_obj, password)
|
data = self._read_private_key('RSA', file_obj, password)
|
||||||
self._decode_key(data)
|
self._decode_key(data)
|
||||||
|
|
||||||
def _decode_key(self, data):
|
def _decode_key(self, data):
|
||||||
# private key file contains:
|
# private key file contains:
|
||||||
# RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p }
|
# RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p }
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
Core protocol implementation
|
Core protocol implementation
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
@ -30,7 +31,7 @@ import paramiko
|
||||||
from paramiko import util
|
from paramiko import util
|
||||||
from paramiko.auth_handler import AuthHandler
|
from paramiko.auth_handler import AuthHandler
|
||||||
from paramiko.channel import Channel
|
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, \
|
cMSG_GLOBAL_REQUEST, DEBUG, MSG_KEXINIT, MSG_IGNORE, MSG_DISCONNECT, \
|
||||||
MSG_DEBUG, ERROR, WARNING, cMSG_UNIMPLEMENTED, INFO, cMSG_KEXINIT, \
|
MSG_DEBUG, ERROR, WARNING, cMSG_UNIMPLEMENTED, INFO, cMSG_KEXINIT, \
|
||||||
cMSG_NEWKEYS, MSG_NEWKEYS, cMSG_REQUEST_SUCCESS, cMSG_REQUEST_FAILURE, \
|
cMSG_NEWKEYS, MSG_NEWKEYS, cMSG_REQUEST_SUCCESS, cMSG_REQUEST_FAILURE, \
|
||||||
|
@ -57,7 +58,6 @@ from paramiko.ssh_exception import (SSHException, BadAuthenticationType,
|
||||||
ChannelException, ProxyCommandFailure)
|
ChannelException, ProxyCommandFailure)
|
||||||
from paramiko.util import retry_on_signal
|
from paramiko.util import retry_on_signal
|
||||||
|
|
||||||
from Crypto import Random
|
|
||||||
from Crypto.Cipher import Blowfish, AES, DES3, ARC4
|
from Crypto.Cipher import Blowfish, AES, DES3, ARC4
|
||||||
from Crypto.Hash import SHA, MD5
|
from Crypto.Hash import SHA, MD5
|
||||||
try:
|
try:
|
||||||
|
@ -192,7 +192,6 @@ class Transport (threading.Thread):
|
||||||
# okay, normal socket-ish flow here...
|
# okay, normal socket-ish flow here...
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self.setDaemon(True)
|
self.setDaemon(True)
|
||||||
self.rng = rng
|
|
||||||
self.sock = sock
|
self.sock = sock
|
||||||
# Python < 2.3 doesn't have the settimeout method - RogerB
|
# Python < 2.3 doesn't have the settimeout method - RogerB
|
||||||
try:
|
try:
|
||||||
|
@ -339,7 +338,6 @@ class Transport (threading.Thread):
|
||||||
# synchronous, wait for a result
|
# synchronous, wait for a result
|
||||||
self.completion_event = event = threading.Event()
|
self.completion_event = event = threading.Event()
|
||||||
self.start()
|
self.start()
|
||||||
Random.atfork()
|
|
||||||
while True:
|
while True:
|
||||||
event.wait(0.1)
|
event.wait(0.1)
|
||||||
if not self.active:
|
if not self.active:
|
||||||
|
@ -475,7 +473,7 @@ class Transport (threading.Thread):
|
||||||
|
|
||||||
.. note:: This has no effect when used in client mode.
|
.. 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
|
# places to look for the openssh "moduli" file
|
||||||
file_list = ['/etc/ssh/moduli', '/usr/local/etc/moduli']
|
file_list = ['/etc/ssh/moduli', '/usr/local/etc/moduli']
|
||||||
if filename is not None:
|
if filename is not None:
|
||||||
|
@ -732,8 +730,8 @@ class Transport (threading.Thread):
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(cMSG_IGNORE)
|
m.add_byte(cMSG_IGNORE)
|
||||||
if byte_count is None:
|
if byte_count is None:
|
||||||
byte_count = (byte_ord(rng.read(1)) % 32) + 10
|
byte_count = (byte_ord(os.urandom(1)) % 32) + 10
|
||||||
m.add_bytes(rng.read(byte_count))
|
m.add_bytes(os.urandom(byte_count))
|
||||||
self._send_user_message(m)
|
self._send_user_message(m)
|
||||||
|
|
||||||
def renegotiate_keys(self):
|
def renegotiate_keys(self):
|
||||||
|
@ -1402,10 +1400,6 @@ class Transport (threading.Thread):
|
||||||
# interpreter shutdown.
|
# interpreter shutdown.
|
||||||
self.sys = sys
|
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=True occurs before the thread is launched, to avoid a race
|
||||||
_active_threads.append(self)
|
_active_threads.append(self)
|
||||||
if self.server_mode:
|
if self.server_mode:
|
||||||
|
@ -1590,7 +1584,7 @@ class Transport (threading.Thread):
|
||||||
|
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(cMSG_KEXINIT)
|
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(self._preferred_kex)
|
||||||
m.add_list(available_server_keys)
|
m.add_list(available_server_keys)
|
||||||
m.add_list(self._preferred_ciphers)
|
m.add_list(self._preferred_ciphers)
|
||||||
|
|
6
test.py
6
test.py
|
@ -101,12 +101,12 @@ def main():
|
||||||
parser.add_option('-P', '--sftp-passwd', dest='password', type='string', default=default_passwd,
|
parser.add_option('-P', '--sftp-passwd', dest='password', type='string', default=default_passwd,
|
||||||
metavar='<password>',
|
metavar='<password>',
|
||||||
help='[with -R] (optional) password to unlock the private key for remote sftp tests')
|
help='[with -R] (optional) password to unlock the private key for remote sftp tests')
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
# setup logging
|
# setup logging
|
||||||
paramiko.util.log_to_file('test.log')
|
paramiko.util.log_to_file('test.log')
|
||||||
|
|
||||||
if options.use_sftp:
|
if options.use_sftp:
|
||||||
from tests.test_sftp import SFTPTest
|
from tests.test_sftp import SFTPTest
|
||||||
if options.use_loopback_sftp:
|
if options.use_loopback_sftp:
|
||||||
|
|
|
@ -21,7 +21,9 @@ Some unit tests for the key exchange protocols.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import paramiko.util
|
import paramiko.util
|
||||||
from paramiko.kex_group1 import KexGroup1
|
from paramiko.kex_group1 import KexGroup1
|
||||||
from paramiko.kex_gex import KexGex
|
from paramiko.kex_gex import KexGex
|
||||||
|
@ -29,9 +31,8 @@ from paramiko import Message
|
||||||
from paramiko.common import byte_chr
|
from paramiko.common import byte_chr
|
||||||
|
|
||||||
|
|
||||||
class FakeRng (object):
|
def dummy_urandom(n):
|
||||||
def read(self, n):
|
return byte_chr(0xcc) * n
|
||||||
return byte_chr(0xcc) * n
|
|
||||||
|
|
||||||
|
|
||||||
class FakeKey (object):
|
class FakeKey (object):
|
||||||
|
@ -41,7 +42,7 @@ class FakeKey (object):
|
||||||
def asbytes(self):
|
def asbytes(self):
|
||||||
return b'fake-key'
|
return b'fake-key'
|
||||||
|
|
||||||
def sign_ssh_data(self, rng, H):
|
def sign_ssh_data(self, H):
|
||||||
return b'fake-sig'
|
return b'fake-sig'
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,8 +54,7 @@ class FakeModulusPack (object):
|
||||||
return self.G, self.P
|
return self.G, self.P
|
||||||
|
|
||||||
|
|
||||||
class FakeTransport (object):
|
class FakeTransport(object):
|
||||||
rng = FakeRng()
|
|
||||||
local_version = 'SSH-2.0-paramiko_1.0'
|
local_version = 'SSH-2.0-paramiko_1.0'
|
||||||
remote_version = 'SSH-2.0-lame'
|
remote_version = 'SSH-2.0-lame'
|
||||||
local_kex_init = 'local-kex-init'
|
local_kex_init = 'local-kex-init'
|
||||||
|
@ -91,10 +91,11 @@ class KexTest (unittest.TestCase):
|
||||||
K = 14730343317708716439807310032871972459448364195094179797249681733965528989482751523943515690110179031004049109375612685505881911274101441415545039654102474376472240501616988799699744135291070488314748284283496055223852115360852283821334858541043710301057312858051901453919067023103730011648890038847384890504
|
K = 14730343317708716439807310032871972459448364195094179797249681733965528989482751523943515690110179031004049109375612685505881911274101441415545039654102474376472240501616988799699744135291070488314748284283496055223852115360852283821334858541043710301057312858051901453919067023103730011648890038847384890504
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
pass
|
self._original_urandom = os.urandom
|
||||||
|
os.urandom = dummy_urandom
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
os.urandom = self._original_urandom
|
||||||
|
|
||||||
def test_1_group1_client(self):
|
def test_1_group1_client(self):
|
||||||
transport = FakeTransport()
|
transport = FakeTransport()
|
||||||
|
|
|
@ -22,9 +22,10 @@ Some unit tests for public/private key objects.
|
||||||
|
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from paramiko import RSAKey, DSSKey, ECDSAKey, Message, util
|
from paramiko import RSAKey, DSSKey, ECDSAKey, Message, util
|
||||||
from paramiko.py3compat import StringIO, byte_chr, b, bytes
|
from paramiko.py3compat import StringIO, byte_chr, b, bytes
|
||||||
from paramiko.common import rng
|
|
||||||
from tests.util import test_path
|
from tests.util import test_path
|
||||||
|
|
||||||
# from openssh's ssh-keygen
|
# from openssh's ssh-keygen
|
||||||
|
@ -166,7 +167,7 @@ class KeyTest (unittest.TestCase):
|
||||||
def test_8_sign_rsa(self):
|
def test_8_sign_rsa(self):
|
||||||
# verify that the rsa private key can sign and verify
|
# verify that the rsa private key can sign and verify
|
||||||
key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
|
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)
|
self.assertTrue(type(msg) is Message)
|
||||||
msg.rewind()
|
msg.rewind()
|
||||||
self.assertEqual('ssh-rsa', msg.get_text())
|
self.assertEqual('ssh-rsa', msg.get_text())
|
||||||
|
@ -179,7 +180,7 @@ class KeyTest (unittest.TestCase):
|
||||||
def test_9_sign_dss(self):
|
def test_9_sign_dss(self):
|
||||||
# verify that the dss private key can sign and verify
|
# verify that the dss private key can sign and verify
|
||||||
key = DSSKey.from_private_key_file(test_path('test_dss.key'))
|
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)
|
self.assertTrue(type(msg) is Message)
|
||||||
msg.rewind()
|
msg.rewind()
|
||||||
self.assertEqual('ssh-dss', msg.get_text())
|
self.assertEqual('ssh-dss', msg.get_text())
|
||||||
|
@ -193,13 +194,13 @@ class KeyTest (unittest.TestCase):
|
||||||
|
|
||||||
def test_A_generate_rsa(self):
|
def test_A_generate_rsa(self):
|
||||||
key = RSAKey.generate(1024)
|
key = RSAKey.generate(1024)
|
||||||
msg = key.sign_ssh_data(rng, b'jerri blank')
|
msg = key.sign_ssh_data(b'jerri blank')
|
||||||
msg.rewind()
|
msg.rewind()
|
||||||
self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
|
self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
|
||||||
|
|
||||||
def test_B_generate_dss(self):
|
def test_B_generate_dss(self):
|
||||||
key = DSSKey.generate(1024)
|
key = DSSKey.generate(1024)
|
||||||
msg = key.sign_ssh_data(rng, b'jerri blank')
|
msg = key.sign_ssh_data(b'jerri blank')
|
||||||
msg.rewind()
|
msg.rewind()
|
||||||
self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
|
self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
|
||||||
|
|
||||||
|
@ -240,7 +241,7 @@ class KeyTest (unittest.TestCase):
|
||||||
def test_13_sign_ecdsa(self):
|
def test_13_sign_ecdsa(self):
|
||||||
# verify that the rsa private key can sign and verify
|
# verify that the rsa private key can sign and verify
|
||||||
key = ECDSAKey.from_private_key_file(test_path('test_ecdsa.key'))
|
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)
|
self.assertTrue(type(msg) is Message)
|
||||||
msg.rewind()
|
msg.rewind()
|
||||||
self.assertEqual('ecdsa-sha2-nistp256', msg.get_text())
|
self.assertEqual('ecdsa-sha2-nistp256', msg.get_text())
|
||||||
|
|
|
@ -153,12 +153,6 @@ class UtilTest(ParamikoTest):
|
||||||
finally:
|
finally:
|
||||||
os.unlink('hostfile.temp')
|
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):
|
def test_7_host_config_expose_issue_33(self):
|
||||||
test_config_file = """
|
test_config_file = """
|
||||||
Host www13.*
|
Host www13.*
|
||||||
|
|
Loading…
Reference in New Issue