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 distrubuted 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  L{DSSKey}
     21  """
     22
     23  from Crypto.PublicKey import DSA
     24  from Crypto.Hash import SHA
     25
     26  from paramiko.common import *
     27  from paramiko import util
     28  from paramiko.ssh_exception import SSHException
     29  from paramiko.message import Message
     30  from paramiko.ber import BER, BERException
     31  from paramiko.pkey import PKey
     32
     33
     34 -class DSSKey (PKey):
     35      """
     36      Representation of a DSS key which can be used to sign an verify SSH2
     37      data.
     38      """
     39
     40 -    def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None):
     41          self.p = None
     42          self.q = None
     43          self.g = None
     44          self.y = None
     45          self.x = None
     46          if file_obj is not None:
     47              self._from_private_key(file_obj, password)
     48              return
     49          if filename is not None:
     50              self._from_private_key_file(filename, password)
     51              return
     52          if (msg is None) and (data is not None):
     53              msg = Message(data)
     54          if vals is not None:
     55              self.p, self.q, self.g, self.y = vals
     56          else:
     57              if msg is None:
     58                  raise SSHException('Key object may not be empty')
     59              if msg.get_string() != 'ssh-dss':
     60                  raise SSHException('Invalid key')
     61              self.p = msg.get_mpint()
     62              self.q = msg.get_mpint()
     63              self.g = msg.get_mpint()
     64              self.y = msg.get_mpint()
     65          self.size = util.bit_length(self.p)
     66
     67 -    def __str__(self):
     68          m = Message()
     69          m.add_string('ssh-dss')
     70          m.add_mpint(self.p)
     71          m.add_mpint(self.q)
     72          m.add_mpint(self.g)
     73          m.add_mpint(self.y)
     74          return str(m)
     75
     76 -    def __hash__(self):
     77          h = hash(self.get_name())
     78          h = h * 37 + hash(self.p)
     79          h = h * 37 + hash(self.q)
     80          h = h * 37 + hash(self.g)
     81          h = h * 37 + hash(self.y)
     82          # h might be a long by now...
     83          return hash(h)
     84
     85 -    def get_name(self):
     86          return 'ssh-dss'
     87
     88 -    def get_bits(self):
     89          return self.size
     90
     91 -    def can_sign(self):
     92          return self.x is not None
     93
     94 -    def sign_ssh_data(self, rpool, data):
     95          digest = SHA.new(data).digest()
     96          dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x)))
     97          # generate a suitable k
     98          qsize = len(util.deflate_long(self.q, 0))
     99          while True:
    100              k = util.inflate_long(rpool.get_bytes(qsize), 1)
    101              if (k > 2) and (k < self.q):
    102                  break
    103          r, s = dss.sign(util.inflate_long(digest, 1), k)
    104          m = Message()
    105          m.add_string('ssh-dss')
    106          # apparently, in rare cases, r or s may be shorter than 20 bytes!
    107          rstr = util.deflate_long(r, 0)
    108          sstr = util.deflate_long(s, 0)
    109          if len(rstr) < 20:
    110              rstr = '\x00' * (20 - len(rstr)) + rstr
    111          if len(sstr) < 20:
    112              sstr = '\x00' * (20 - len(sstr)) + sstr
    113          m.add_string(rstr + sstr)
    114          return m
    115
    116 -    def verify_ssh_sig(self, data, msg):
    117          if len(str(msg)) == 40:
    118              # spies.com bug: signature has no header
    119              sig = str(msg)
    120          else:
    121              kind = msg.get_string()
    122              if kind != 'ssh-dss':
    123                  return 0
    124              sig = msg.get_string()
    125
    126          # pull out (r, s) which are NOT encoded as mpints
    127          sigR = util.inflate_long(sig[:20], 1)
    128          sigS = util.inflate_long(sig[20:], 1)
    129          sigM = util.inflate_long(SHA.new(data).digest(), 1)
    130
    131          dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q)))
    132          return dss.verify(sigM, (sigR, sigS))
    133
    134 -    def _encode_key(self):
    135          if self.x is None:
    136              raise SSHException('Not enough key information')
    137          keylist = [ 0, self.p, self.q, self.g, self.y, self.x ]
    138          try:
    139              b = BER()
    140              b.encode(keylist)
    141          except BERException:
    142              raise SSHException('Unable to create ber encoding of key')
    143          return str(b)
    144
    145 -    def write_private_key_file(self, filename, password=None):
    146          self._write_private_key_file('DSA', filename, self._encode_key(), password)
    147
    148 -    def write_private_key(self, file_obj, password=None):
    149          self._write_private_key('DSA', file_obj, self._encode_key(), password)
    150
    151 -    def generate(bits=1024, progress_func=None):
    152          """
    153          Generate a new private DSS key.  This factory function can be used to
    154          generate a new host key or authentication key.
    155
    156          @param bits: number of bits the generated key should be.
    157          @type bits: int
    158          @param progress_func: an optional function to call at key points in
    159              key generation (used by C{pyCrypto.PublicKey}).
    160          @type progress_func: function
    161          @return: new private key
    162          @rtype: L{DSSKey}
    163          """
    164          randpool.stir()
    165          dsa = DSA.generate(bits, randpool.get_bytes, progress_func)
    166          key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y))
    167          key.x = dsa.x
    168          return key
    169      generate = staticmethod(generate)
    170
    171
    172      ###  internals...
    173
    174
    175 -    def _from_private_key_file(self, filename, password):
    176          data = self._read_private_key_file('DSA', filename, password)
    177          self._decode_key(data)
    178
    179 -    def _from_private_key(self, file_obj, password):
    180          data = self._read_private_key('DSA', file_obj, password)
    181          self._decode_key(data)
    182
    183 -    def _decode_key(self, data):
    184          # private key file contains:
    185          # DSAPrivateKey = { version = 0, p, q, g, y, x }
    186          try:
    187              keylist = BER(data).decode()
    188          except BERException, x:
    189              raise SSHException('Unable to parse key file: ' + str(x))
    190          if (type(keylist) is not list) or (len(keylist) < 6) or (keylist[0] != 0):
    191              raise SSHException('not a valid DSA private key file (bad ber encoding)')
    192          self.p = keylist[1]
    193          self.q = keylist[2]
    194          self.g = keylist[3]
    195          self.y = keylist[4]
    196          self.x = keylist[5]
    197          self.size = util.bit_length(self.p)
    198