summaryrefslogtreecommitdiff
path: root/paramiko/ecdsakey.py
diff options
context:
space:
mode:
Diffstat (limited to 'paramiko/ecdsakey.py')
-rw-r--r--paramiko/ecdsakey.py181
1 files changed, 181 insertions, 0 deletions
diff --git a/paramiko/ecdsakey.py b/paramiko/ecdsakey.py
new file mode 100644
index 0000000..ac840ab
--- /dev/null
+++ b/paramiko/ecdsakey.py
@@ -0,0 +1,181 @@
+# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
+#
+# 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{ECDSAKey}
+"""
+
+import binascii
+from ecdsa import SigningKey, VerifyingKey, der, curves
+from ecdsa.util import number_to_string, sigencode_string, sigencode_strings, sigdecode_strings
+from Crypto.Hash import SHA256, MD5
+from Crypto.Cipher import DES3
+
+from paramiko.common import *
+from paramiko import util
+from paramiko.message import Message
+from paramiko.ber import BER, BERException
+from paramiko.pkey import PKey
+from paramiko.ssh_exception import SSHException
+
+
+class ECDSAKey (PKey):
+ """
+ Representation of an ECDSA key which can be used to sign and verify SSH2
+ data.
+ """
+
+ def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None):
+ self.verifying_key = None
+ self.signing_key = None
+ if file_obj is not None:
+ self._from_private_key(file_obj, password)
+ return
+ 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.verifying_key, self.signing_key = vals
+ else:
+ if msg is None:
+ raise SSHException('Key object may not be empty')
+ if msg.get_string() != 'ecdsa-sha2-nistp256':
+ raise SSHException('Invalid key')
+ curvename = msg.get_string()
+ if curvename != 'nistp256':
+ raise SSHException("Can't handle curve of type %s" % curvename)
+
+ pointinfo = msg.get_string()
+ if pointinfo[0] != "\x04":
+ raise SSHException('Point compression is being used: %s'%
+ binascii.hexlify(pointinfo))
+ self.verifying_key = VerifyingKey.from_string(pointinfo[1:],
+ curve=curves.NIST256p)
+ self.size = 256
+
+ def __str__(self):
+ key = self.verifying_key
+ m = Message()
+ m.add_string('ecdsa-sha2-nistp256')
+ m.add_string('nistp256')
+
+ point_str = "\x04" + key.to_string()
+
+ m.add_string(point_str)
+ return str(m)
+
+ def __hash__(self):
+ h = hash(self.get_name())
+ h = h * 37 + hash(self.verifying_key.pubkey.point.x())
+ h = h * 37 + hash(self.verifying_key.pubkey.point.y())
+ return hash(h)
+
+ def get_name(self):
+ return 'ecdsa-sha2-nistp256'
+
+ def get_bits(self):
+ return self.size
+
+ def can_sign(self):
+ return self.signing_key is not None
+
+ def sign_ssh_data(self, rpool, data):
+ digest = SHA256.new(data).digest()
+ sig = self.signing_key.sign_digest(digest, entropy=rpool.read,
+ sigencode=self._sigencode)
+ m = Message()
+ m.add_string('ecdsa-sha2-nistp256')
+ m.add_string(sig)
+ return m
+
+ def verify_ssh_sig(self, data, msg):
+ if msg.get_string() != 'ecdsa-sha2-nistp256':
+ return False
+ sig = msg.get_string()
+
+ # verify the signature by SHA'ing the data and encrypting it
+ # using the public key.
+ hash_obj = SHA256.new(data).digest()
+ return self.verifying_key.verify_digest(sig, hash_obj,
+ sigdecode=self._sigdecode)
+
+ def write_private_key_file(self, filename, password=None):
+ key = self.signing_key or self.verifying_key
+ self._write_private_key_file('EC', filename, key.to_der(), password)
+
+ def write_private_key(self, file_obj, password=None):
+ key = self.signing_key or self.verifying_key
+ self._write_private_key('EC', file_obj, key.to_der(), password)
+
+ def generate(bits, progress_func=None):
+ """
+ Generate a new private RSA 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{RSAKey}
+ """
+ signing_key = ECDSA.generate()
+ key = ECDSAKey(vals=(signing_key, signing_key.get_verifying_key()))
+ return key
+ generate = staticmethod(generate)
+
+
+ ### internals...
+
+
+ def _from_private_key_file(self, filename, password):
+ data = self._read_private_key_file('EC', filename, password)
+ self._decode_key(data)
+
+ def _from_private_key(self, file_obj, password):
+ data = self._read_private_key('EC', file_obj, password)
+ self._decode_key(data)
+
+ ALLOWED_PADDINGS = ['\x01', '\x02\x02', '\x03\x03\x03', '\x04\x04\x04\x04',
+ '\x05\x05\x05\x05\x05', '\x06\x06\x06\x06\x06\x06',
+ '\x07\x07\x07\x07\x07\x07\x07']
+ def _decode_key(self, data):
+ s, padding = der.remove_sequence(data)
+ if padding:
+ if padding not in self.ALLOWED_PADDINGS:
+ raise ValueError, "weird padding: %s" % (binascii.hexlify(empty))
+ data = data[:-len(padding)]
+ key = SigningKey.from_der(data)
+ self.signing_key = key
+ self.verifying_key = key.get_verifying_key()
+ self.size = 256
+
+ def _sigencode(self, r, s, order):
+ msg = Message()
+ msg.add_mpint(r)
+ msg.add_mpint(s)
+ return str(msg)
+
+ def _sigdecode(self, sig, order):
+ msg = Message(sig)
+ r = msg.get_mpint()
+ s = msg.get_mpint()
+ return (r, s)