2009-05-17 Denis Bernard <
[email protected]>
* SFEpython25-crypto.spec:
* SFEpython25-paramiko.spec,
patches/python-paramiko-01-arc4_and_ctr.patch: Python 2.5, bump to
paramiko 1.7.4, patch to implement AEC-CTR and arcfour ciphers for
OpenSolaris interoperability.
diff -ur paramiko-1.7.4.orig/paramiko/transport.py paramiko-1.7.4/paramiko/transport.py
--- paramiko-1.7.4.orig/paramiko/transport.py 2008-07-07 02:12:55.000000000 +0100
+++ paramiko-1.7.4/paramiko/transport.py 2009-05-15 12:44:50.322005250 +0100
@@ -50,8 +50,12 @@
# i believe this on the standards track.
# PyCrypt compiled for Win32 can be downloaded from the HashTar homepage:
# http://nitace.bsd.uchicago.edu:8080/hashtar
-from Crypto.Cipher import Blowfish, AES, DES3
+from Crypto.Cipher import Blowfish, AES, DES3, ARC4
from Crypto.Hash import SHA, MD5
+try:
+ from Crypto.Util import Counter
+except ImportError:
+ from paramiko.util import Counter
# for thread cleanup
@@ -196,17 +200,22 @@
_PROTO_ID = '2.0'
_CLIENT_ID = 'paramiko_1.7.4'
- _preferred_ciphers = ( 'aes128-cbc', 'blowfish-cbc', 'aes256-cbc', '3des-cbc' )
+ _preferred_ciphers = ( 'aes128-ctr', 'aes256-ctr', 'aes128-cbc', 'blowfish-cbc', 'aes256-cbc', '3des-cbc',
+ 'arcfour128', 'arcfour256' )
_preferred_macs = ( 'hmac-sha1', 'hmac-md5', 'hmac-sha1-96', 'hmac-md5-96' )
_preferred_keys = ( 'ssh-rsa', 'ssh-dss' )
_preferred_kex = ( 'diffie-hellman-group1-sha1', 'diffie-hellman-group-exchange-sha1' )
_preferred_compression = ( 'none', )
_cipher_info = {
+ 'aes128-ctr': { 'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 16 },
+ 'aes256-ctr': { 'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 32 },
'blowfish-cbc': { 'class': Blowfish, 'mode': Blowfish.MODE_CBC, 'block-size': 8, 'key-size': 16 },
'aes128-cbc': { 'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 16 },
'aes256-cbc': { 'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 32 },
'3des-cbc': { 'class': DES3, 'mode': DES3.MODE_CBC, 'block-size': 8, 'key-size': 24 },
+ 'arcfour128': { 'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 16 },
+ 'arcfour256': { 'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 32 },
}
_mac_info = {
@@ -1446,7 +1455,19 @@
def _get_cipher(self, name, key, iv):
if name not in self._cipher_info:
raise SSHException('Unknown client cipher ' + name)
- return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv)
+ if name in ('arcfour128', 'arcfour256'):
+ # arcfour cipher
+ cipher = self._cipher_info[name]['class'].new(key)
+ # as per RFC 4345, the first 1536 bytes of keystream
+ # generated by the cipher MUST be discarded
+ cipher.encrypt(" " * 1536)
+ return cipher
+ elif name.endswith("-ctr"):
+ # CTR modes, we need a counter
+ return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv,
+ Counter.new(nbits=self._cipher_info[name]['block-size']*8, initial_value=util.inflate_long(iv, True)))
+ else:
+ return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv)
def _set_x11_handler(self, handler):
# only called if a channel has turned on x11 forwarding
diff -ur paramiko-1.7.4.orig/paramiko/util.py paramiko-1.7.4/paramiko/util.py
--- paramiko-1.7.4.orig/paramiko/util.py 2007-02-13 02:59:59.000000000 +0000
+++ paramiko-1.7.4/paramiko/util.py 2009-05-16 17:27:21.266831004 +0100
@@ -22,6 +22,7 @@
from __future__ import generators
+import array
from binascii import hexlify, unhexlify
import sys
import struct
@@ -267,4 +268,33 @@
l.addFilter(_pfilter)
return l
+class Counter(object):
+ """Stateful counter for CTR mode crypto"""
+ def __init__(self, nbits, initial_value, overflow):
+ self.blocksize = nbits / 8
+ self.overflow = overflow
+ # start with value - 1 so we don't have to store intermediate values when counting
+ # could the iv be 0?
+ if initial_value != 0:
+ self.value = deflate_long(initial_value - 1, add_sign_padding=False)
+ self.value = '\x00' * (self.blocksize-len(self.value)) + self.value
+ else:
+ self.value = '\xFF' * self.blocksize
+ self.value = array.array('c', self.value)
+
+ def __call__(self):
+ """Increament the counter and return the new value"""
+ i = self.blocksize - 1
+ while i > -1:
+ c = self.value[i] = chr((ord(self.value[i]) + 1) % 256)
+ if c != '\x00':
+ return self.value.tostring()
+ i -= 1
+ # counter reset
+ self.value = array.array('c', Util.number.long_to_bytes(self.overflow, self.blocksize))
+ return self.value.tostring()
+
+ def new(cls, nbits, initial_value=1L, overflow=0L):
+ return cls(nbits, initial_value=initial_value, overflow=overflow)
+ new = classmethod(new)