summaryrefslogtreecommitdiff
path: root/paramiko/dsskey.py
diff options
context:
space:
mode:
Diffstat (limited to 'paramiko/dsskey.py')
-rw-r--r--paramiko/dsskey.py176
1 files changed, 176 insertions, 0 deletions
diff --git a/paramiko/dsskey.py b/paramiko/dsskey.py
new file mode 100644
index 0000000..2b31372
--- /dev/null
+++ b/paramiko/dsskey.py
@@ -0,0 +1,176 @@
+# Copyright (C) 2003-2005 Robey Pointer <robey@lag.net>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+"""
+L{DSSKey}
+"""
+
+from Crypto.PublicKey import DSA
+from Crypto.Hash import SHA
+
+from paramiko.common import *
+from paramiko import util
+from paramiko.ssh_exception import SSHException
+from paramiko.message import Message
+from paramiko.ber import BER, BERException
+from paramiko.pkey import PKey
+
+
+class DSSKey (PKey):
+ """
+ Representation of a DSS key which can be used to sign an verify SSH2
+ data.
+ """
+
+ def __init__(self, msg=None, data=None, filename=None, password=None, vals=None):
+ if filename is not None:
+ self._from_private_key_file(filename, password)
+ return
+ if (msg is None) and (data is not None):
+ msg = Message(data)
+ if vals is not None:
+ self.p, self.q, self.g, self.y = vals
+ else:
+ if msg is None:
+ raise SSHException('Key object may not be empty')
+ if msg.get_string() != 'ssh-dss':
+ raise SSHException('Invalid key')
+ self.p = msg.get_mpint()
+ self.q = msg.get_mpint()
+ self.g = msg.get_mpint()
+ self.y = msg.get_mpint()
+ self.size = util.bit_length(self.p)
+
+ def __str__(self):
+ m = Message()
+ m.add_string('ssh-dss')
+ m.add_mpint(self.p)
+ m.add_mpint(self.q)
+ m.add_mpint(self.g)
+ m.add_mpint(self.y)
+ return str(m)
+
+ def __hash__(self):
+ h = hash(self.get_name())
+ h = h * 37 + hash(self.p)
+ h = h * 37 + hash(self.q)
+ h = h * 37 + hash(self.g)
+ h = h * 37 + hash(self.y)
+ # h might be a long by now...
+ return hash(h)
+
+ def get_name(self):
+ return 'ssh-dss'
+
+ def get_bits(self):
+ return self.size
+
+ def can_sign(self):
+ return hasattr(self, 'x')
+
+ def sign_ssh_data(self, rpool, data):
+ digest = SHA.new(data).digest()
+ dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x)))
+ # generate a suitable k
+ qsize = len(util.deflate_long(self.q, 0))
+ while True:
+ k = util.inflate_long(rpool.get_bytes(qsize), 1)
+ if (k > 2) and (k < self.q):
+ break
+ r, s = dss.sign(util.inflate_long(digest, 1), k)
+ m = Message()
+ m.add_string('ssh-dss')
+ # apparently, in rare cases, r or s may be shorter than 20 bytes!
+ rstr = util.deflate_long(r, 0)
+ sstr = util.deflate_long(s, 0)
+ if len(rstr) < 20:
+ rstr = '\x00' * (20 - len(rstr)) + rstr
+ if len(sstr) < 20:
+ sstr = '\x00' * (20 - len(sstr)) + sstr
+ m.add_string(rstr + sstr)
+ return m
+
+ def verify_ssh_sig(self, data, msg):
+ if len(str(msg)) == 40:
+ # spies.com bug: signature has no header
+ sig = str(msg)
+ else:
+ kind = msg.get_string()
+ if kind != 'ssh-dss':
+ return 0
+ sig = msg.get_string()
+
+ # pull out (r, s) which are NOT encoded as mpints
+ sigR = util.inflate_long(sig[:20], 1)
+ sigS = util.inflate_long(sig[20:], 1)
+ sigM = util.inflate_long(SHA.new(data).digest(), 1)
+
+ dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q)))
+ return dss.verify(sigM, (sigR, sigS))
+
+ def write_private_key_file(self, filename, password=None):
+ keylist = [ 0, self.p, self.q, self.g, self.y, self.x ]
+ try:
+ b = BER()
+ b.encode(keylist)
+ except BERException:
+ raise SSHException('Unable to create ber encoding of key')
+ self._write_private_key_file('DSA', filename, str(b), password)
+
+ def generate(bits=1024, progress_func=None):
+ """
+ Generate a new private DSS key. This factory function can be used to
+ generate a new host key or authentication key.
+
+ @param bits: number of bits the generated key should be.
+ @type bits: int
+ @param progress_func: an optional function to call at key points in
+ key generation (used by C{pyCrypto.PublicKey}).
+ @type progress_func: function
+ @return: new private key
+ @rtype: L{DSSKey}
+
+ @since: fearow
+ """
+ randpool.stir()
+ dsa = DSA.generate(bits, randpool.get_bytes, progress_func)
+ key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y))
+ key.x = dsa.x
+ return key
+ generate = staticmethod(generate)
+
+
+ ### internals...
+
+
+ def _from_private_key_file(self, filename, password):
+ # private key file contains:
+ # DSAPrivateKey = { version = 0, p, q, g, y, x }
+ data = self._read_private_key_file('DSA', filename, password)
+ try:
+ keylist = BER(data).decode()
+ except BERException, x:
+ raise SSHException('Unable to parse key file: ' + str(x))
+ if (type(keylist) is not list) or (len(keylist) < 6) or (keylist[0] != 0):
+ raise SSHException('not a valid DSA private key file (bad ber encoding)')
+ self.p = keylist[1]
+ self.q = keylist[2]
+ self.g = keylist[3]
+ self.y = keylist[4]
+ self.x = keylist[5]
+ self.size = util.bit_length(self.p)