From 91ce93471aa8e9824a8ad298dfb11d05814937c7 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Fri, 14 Apr 2017 16:10:12 -0400 Subject: Clean up and document cryptech_backup. --- cryptech_backup | 113 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 35 deletions(-) diff --git a/cryptech_backup b/cryptech_backup index 18441a3..f14f119 100755 --- a/cryptech_backup +++ b/cryptech_backup @@ -1,18 +1,38 @@ #!/usr/bin/env python -# KEY SOURCE KEY BACKUP +""" +Securely back up private keys from one Cryptech HSM to another. + +This works by having the destination HSM (the one importing keys) +create an RSA keypair (the "KEKEK"), the public key of which can then +be imported into the source HSM (the one exporting keys) and used to +encrypt AES key encryption keys (KEKs) which in turn can be used to +wrap the private keys being transfered. Transfers are encoded in +JSON; the underlying ASN.1 formats are SubjectPublicKeyInfo (KEKEK +public key) and PKCS #8 EncryptedPrivateKeyInfo (everything else). + +NOTE WELL: while this process makes it POSSIBLE to back up keys +securely, it is not sufficient by itself: the operator MUST make +sure only to export keys using a KEKEK known to have been generated by +the target HSM. See the unit tests in the source repository for +an example of how to fake this in a few lines of Python. + +YOU HAVE BEEN WARNED. Be careful out there. +""" + +# Diagram of the trivial protocol we're using: +# +# SOURCE HSM DESTINATION HSM # # Generate and export KEKEK: # hal_rpc_pkey_generate_rsa() # hal_rpc_pkey_get_public_key() # -# Load KEKEK public <---------------- Export KEKEK public -# -# hal_rpc_pkey_load() -# hal_rpc_pkey_export() +# Load KEKEK public <--------- Export KEKEK public +# hal_rpc_pkey_load() +# hal_rpc_pkey_export() # # Export PKCS #8 and KEK ----------> Load PKCS #8 and KEK, import key -# # hal_rpc_pkey_import() import sys @@ -27,42 +47,63 @@ from cryptech.libhal import * def main(): parser = argparse.ArgumentParser( - formatter_class = argparse.ArgumentDefaultsHelpFormatter, + formatter_class = argparse.RawDescriptionHelpFormatter, description = __doc__) subparsers = parser.add_subparsers( title = "Commands (use \"--help\" after command name for help with individual commands)", metavar = "") + setup_parser = defcmd(subparsers, cmd_setup) + export_parser = defcmd(subparsers, cmd_export) + import_parser = defcmd(subparsers, cmd_import) + setup_mutex_group = setup_parser.add_mutually_exclusive_group() + + parser.add_argument( "-p", "--pin", - help = "wheel PIN") - - subparser = defcmd(subparsers, cmd_setup) - group = subparser.add_mutually_exclusive_group() - group.add_argument( - "-n", "--new", action = "store_true", - help = "force creation of new KEKEK") - group.add_argument( + help = "wheel PIN") + + + setup_mutex_group.add_argument( + "-n", "--new", + action = "store_true", + help = "force creation of new KEKEK") + + setup_mutex_group.add_argument( "-u", "--uuid", - help = "UUID of existing KEKEK to use") - subparser.add_argument( - "-k", "--keylen", type = int, default = 2048, - help = "length of new KEKEK if we need to create one") - subparser.add_argument( - "-o", "--output", type = argparse.FileType("w"), default = "-", - help = "output file") - - subparser = defcmd(subparsers, cmd_export) - subparser.add_argument( - "-i", "--input", type = argparse.FileType("r"), default = "-", - help = "input file") - subparser.add_argument( - "-o", "--output", type = argparse.FileType("w"), default = "-", - help = "output file") - - subparser = defcmd(subparsers, cmd_import) - subparser.add_argument( - "-i", "--input", type = argparse.FileType("r"), default = "-", - help = "input file") + help = "UUID of existing KEKEK to use") + + setup_parser.add_argument( + "-k", "--keylen", + type = int, + default = 2048, + help = "length of new KEKEK if we need to create one") + + setup_parser.add_argument( + "-o", "--output", + type = argparse.FileType("w"), + default = "-", + help = "output file") + + + export_parser.add_argument( + "-i", "--input", + type = argparse.FileType("r"), + default = "-", + help = "input file") + + export_parser.add_argument( + "-o", "--output", + type = argparse.FileType("w"), + default = "-", + help = "output file") + + + import_parser.add_argument( + "-i", "--input", + type = argparse.FileType("r"), + default = "-", + help = "input file") + args = parser.parse_args() @@ -138,6 +179,7 @@ def cmd_setup(args, hsm): result.update(comment = "KEKEK public key") json.dump(result, args.output, indent = 4, sort_keys = True) + args.output.write("\n") def key_flag_names(flags): @@ -195,6 +237,7 @@ def cmd_export(args, hsm): db.update(comment = "Cryptech Alpha encrypted key backup", keys = result) json.dump(db, args.output, indent = 4, sort_keys = True) + args.output.write("\n") def cmd_import(args, hsm): -- cgit v1.2.3