Package paramiko :: Module dsskey
[frames] | no frames]

Source Code for Module paramiko.dsskey

  1  # Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com> 
  2  # 
  3  # This file is part of paramiko. 
  4  # 
  5  # Paramiko is free software; you can redistribute it and/or modify it under the 
  6  # terms of the GNU Lesser General Public License as published by the Free 
  7  # Software Foundation; either version 2.1 of the License, or (at your option) 
  8  # any later version. 
  9  # 
 10  # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 
 11  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 12  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 13  # details. 
 14  # 
 15  # You should have received a copy of the GNU Lesser General Public License 
 16  # along with Paramiko; if not, write to the Free Software Foundation, Inc., 
 17  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. 
 18   
 19  """ 
 20  DSS keys. 
 21  """ 
 22   
 23  import os 
 24  from hashlib import sha1 
 25   
 26  from Crypto.PublicKey import DSA 
 27   
 28  from paramiko import util 
 29  from paramiko.common import zero_byte 
 30  from paramiko.py3compat import long 
 31  from paramiko.ssh_exception import SSHException 
 32  from paramiko.message import Message 
 33  from paramiko.ber import BER, BERException 
 34  from paramiko.pkey import PKey 
 35   
 36   
37 -class DSSKey (PKey):
38 """ 39 Representation of a DSS key which can be used to sign an verify SSH2 40 data. 41 """ 42
43 - def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None):
44 self.p = None 45 self.q = None 46 self.g = None 47 self.y = None 48 self.x = None 49 if file_obj is not None: 50 self._from_private_key(file_obj, password) 51 return 52 if filename is not None: 53 self._from_private_key_file(filename, password) 54 return 55 if (msg is None) and (data is not None): 56 msg = Message(data) 57 if vals is not None: 58 self.p, self.q, self.g, self.y = vals 59 else: 60 if msg is None: 61 raise SSHException('Key object may not be empty') 62 if msg.get_text() != 'ssh-dss': 63 raise SSHException('Invalid key') 64 self.p = msg.get_mpint() 65 self.q = msg.get_mpint() 66 self.g = msg.get_mpint() 67 self.y = msg.get_mpint() 68 self.size = util.bit_length(self.p)
69
70 - def asbytes(self):
71 m = Message() 72 m.add_string('ssh-dss') 73 m.add_mpint(self.p) 74 m.add_mpint(self.q) 75 m.add_mpint(self.g) 76 m.add_mpint(self.y) 77 return m.asbytes()
78
79 - def __str__(self):
80 return self.asbytes()
81
82 - def __hash__(self):
83 h = hash(self.get_name()) 84 h = h * 37 + hash(self.p) 85 h = h * 37 + hash(self.q) 86 h = h * 37 + hash(self.g) 87 h = h * 37 + hash(self.y) 88 # h might be a long by now... 89 return hash(h)
90
91 - def get_name(self):
92 return 'ssh-dss'
93
94 - def get_bits(self):
95 return self.size
96
97 - def can_sign(self):
98 return self.x is not None
99
100 - def sign_ssh_data(self, data):
101 digest = sha1(data).digest() 102 dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x))) 103 # generate a suitable k 104 qsize = len(util.deflate_long(self.q, 0)) 105 while True: 106 k = util.inflate_long(os.urandom(qsize), 1) 107 if (k > 2) and (k < self.q): 108 break 109 r, s = dss.sign(util.inflate_long(digest, 1), k) 110 m = Message() 111 m.add_string('ssh-dss') 112 # apparently, in rare cases, r or s may be shorter than 20 bytes! 113 rstr = util.deflate_long(r, 0) 114 sstr = util.deflate_long(s, 0) 115 if len(rstr) < 20: 116 rstr = zero_byte * (20 - len(rstr)) + rstr 117 if len(sstr) < 20: 118 sstr = zero_byte * (20 - len(sstr)) + sstr 119 m.add_string(rstr + sstr) 120 return m
121
122 - def verify_ssh_sig(self, data, msg):
123 if len(msg.asbytes()) == 40: 124 # spies.com bug: signature has no header 125 sig = msg.asbytes() 126 else: 127 kind = msg.get_text() 128 if kind != 'ssh-dss': 129 return 0 130 sig = msg.get_binary() 131 132 # pull out (r, s) which are NOT encoded as mpints 133 sigR = util.inflate_long(sig[:20], 1) 134 sigS = util.inflate_long(sig[20:], 1) 135 sigM = util.inflate_long(sha1(data).digest(), 1) 136 137 dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q))) 138 return dss.verify(sigM, (sigR, sigS))
139
140 - def _encode_key(self):
141 if self.x is None: 142 raise SSHException('Not enough key information') 143 keylist = [0, self.p, self.q, self.g, self.y, self.x] 144 try: 145 b = BER() 146 b.encode(keylist) 147 except BERException: 148 raise SSHException('Unable to create ber encoding of key') 149 return b.asbytes()
150
151 - def write_private_key_file(self, filename, password=None):
152 self._write_private_key_file('DSA', filename, self._encode_key(), password)
153
154 - def write_private_key(self, file_obj, password=None):
155 self._write_private_key('DSA', file_obj, self._encode_key(), password)
156
157 - def generate(bits=1024, progress_func=None):
158 """ 159 Generate a new private DSS key. This factory function can be used to 160 generate a new host key or authentication key. 161 162 :param int bits: number of bits the generated key should be. 163 :param function progress_func: 164 an optional function to call at key points in key generation (used 165 by ``pyCrypto.PublicKey``). 166 :return: new `.DSSKey` private key 167 """ 168 dsa = DSA.generate(bits, os.urandom, progress_func) 169 key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y)) 170 key.x = dsa.x 171 return key
172 generate = staticmethod(generate) 173 174 ### internals... 175
176 - def _from_private_key_file(self, filename, password):
177 data = self._read_private_key_file('DSA', filename, password) 178 self._decode_key(data)
179
180 - def _from_private_key(self, file_obj, password):
181 data = self._read_private_key('DSA', file_obj, password) 182 self._decode_key(data)
183
184 - def _decode_key(self, data):
185 # private key file contains: 186 # DSAPrivateKey = { version = 0, p, q, g, y, x } 187 try: 188 keylist = BER(data).decode() 189 except BERException as e: 190 raise SSHException('Unable to parse key file: ' + str(e)) 191 if (type(keylist) is not list) or (len(keylist) < 6) or (keylist[0] != 0): 192 raise SSHException('not a valid DSA private key file (bad ber encoding)') 193 self.p = keylist[1] 194 self.q = keylist[2] 195 self.g = keylist[3] 196 self.y = keylist[4] 197 self.x = keylist[5] 198 self.size = util.bit_length(self.p)
199