patches/python-paramiko-01-arc4_and_ctr.patch
author dnsbrnrd
Sun, 17 May 2009 15:27:29 +0000
changeset 1879 165510f78f50
permissions -rw-r--r--
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)