keys: Add in pushkey and setkey to help with key management

pushkey allows someone to push their OpenSSH public key into a device's
~/.ssh/authorized_keys file, and setkey allows one to import their PEM-format
OpenSSH private key into MDT's keyfile.

Change-Id: I5301c6d8fb6365e6ac60d396aba2225e861cee66
diff --git a/mdt/keys.py b/mdt/keys.py
index 309862a..964ee78 100644
--- a/mdt/keys.py
+++ b/mdt/keys.py
@@ -19,6 +19,7 @@
 import platform
 import shutil
 import subprocess
+import sys
 
 import paramiko
 from paramiko.ssh_exception import SSHException, PasswordRequiredException
@@ -66,6 +67,28 @@
         else:
             return True
 
+    def importKey(self, keyfile):
+        try:
+            self.pkey = RSAKey.from_private_key_file(keyfile)
+        except IOError as e:
+            print("Unable to read private key from file: {0}".format(e))
+            return False
+        except PasswordRequiredException as e:
+            print("Unable to load in private key: {0}".format(e))
+            return False
+        except SSHException as e:
+            print("Unable to import private key: {0}".format(e))
+            print("Note: Only OpenSSH keys generated using ssh-keygen in PEM format are supported.")
+            return False
+
+        try:
+            self.pkey.write_private_key_file(KEYFILE_PATH)
+        except IOError as e:
+            print("Unable to write private key to disk: {0}".format(e))
+            return False
+        else:
+            return True
+
     def key(self):
         return self.pkey
 
@@ -107,6 +130,9 @@
             print("Can't copy {0}: no such file or directory.".format(source_keyfile))
             return 1
 
-        shutil.copy(source_keyfile, KEYFILE_PATH)
+        keystore = Keystore()
+        if not keystore.importKey(source_keyfile):
+            return 1
+
         print("Key {0} imported.".format(source_keyfile))
         return 0
diff --git a/mdt/main.py b/mdt/main.py
index d31f32d..e0b7862 100755
--- a/mdt/main.py
+++ b/mdt/main.py
@@ -96,6 +96,7 @@
     'install': files.InstallCommand(),
     'pull': files.PullCommand(),
     'push': files.PushCommand(),
+    'pushkey': shell.PushKeyCommand(),
     'reboot': shell.RebootCommand(),
     'reboot-bootloader': shell.RebootBootloaderCommand(),
     'set': config.SetCommand(),
diff --git a/mdt/shell.py b/mdt/shell.py
index df4061a..4ef1441 100644
--- a/mdt/shell.py
+++ b/mdt/shell.py
@@ -15,6 +15,7 @@
 '''
 
 
+import os
 import sys
 
 from mdt import command
@@ -104,3 +105,47 @@
         channel = client.shellExec("sudo reboot-bootloader")
         cons = console.Console(channel, sys.stdin)
         return cons.run()
+
+
+class PushKeyCommand(command.NetworkCommand):
+    '''Usage: mdt pushkey [<path-to-ssh-public-key>]
+
+Copies an SSH public key provided to the device's ~/.ssh/authorized_keys
+file. If no public key is provided, attempts to push MDTs previously generated
+public key from ~/.config/mdt/keys/mdt.key.
+'''
+
+    def runWithClient(self, client, args):
+        key_to_push = None
+
+        if len(args) == 1:
+            # The key was most likely pushed by the NetworkCommand substrate. We
+            # can simply return here.
+            print("MDT Key pushed.")
+            return 0
+
+        if len(args) != 2:
+            print("Usage: mdt pushkey [<path-to-public-key>]")
+            return 1
+
+        source_keyfile = args[1]
+        if not os.path.exists(source_keyfile):
+            print("Can't copy {0}: no such file or directory.".format(source_keyfile))
+            return 1
+
+        source_key = ''
+        with open(args[1], 'rb') as fp:
+            source_key = fp.read()
+
+        sftp = client.openSftp()
+        try:
+            sftp.chdir('/home/mendel/.ssh')
+        except FileNotFoundError as e:
+            sftp.mkdir('/home/mendel/.ssh', mode=0o700)
+
+        with sftp.open('/home/mendel/.ssh/authorized_keys', 'a+b') as fp:
+            fp.write('\r\n')
+            fp.write(source_key)
+
+        print("Key {0} pushed.".format(source_keyfile))
+        return 0