def tpm12_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm): """ Create certificates for the TPM 1.2 and write them into NVRAM """ ret = call_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm) if ret != 0: return 1 for entry in [(SETUP_EK_CERT_F, "ek.cert"), (SETUP_PLATFORM_CERT_F, "platform.cert")]: if flags & entry[0]: certfile = os.path.join(certsdir, entry[1]) data, ret = read_file(certfile) if ret != 0: logerr(LOGFILE, "%s file could not be read\n" % certfile) return 1 if entry[0] == SETUP_EK_CERT_F: ret = swtpm.write_ek_cert_nvram(data) if ret == 0: logit(LOGFILE, "Successfully created NVRAM area for EK certificate.\n") else: ret = swtpm.write_platform_cert_nvram(data) if ret == 0: logit(LOGFILE, "Successfully created NVRAM area for Platform certificate.\n") remove_file(certfile) if ret != 0: return 1 return 0
def write_ek_cert_nvram(self, isecc, rsa_keysize, lock_nvram, ekcert): """ Write the given ekcert into an NVRAM area appropriate for the key type and size """ if not isecc: if rsa_keysize == 2048: nvindex = TPM2_NV_INDEX_RSA2048_EKCERT elif rsa_keysize == 3072: nvindex = TPM2_NV_INDEX_RSA3072_HI_EKCERT keytype = "RSA %d" % rsa_keysize else: nvindex = TPM2_NV_INDEX_ECC_SECP384R1_HI_EKCERT keytype = "ECC" nvindexattrs = TPMA_NV_PLATFORMCREATE | \ TPMA_NV_AUTHREAD | \ TPMA_NV_OWNERREAD | \ TPMA_NV_PPREAD | \ TPMA_NV_PPWRITE | \ TPMA_NV_NO_DA | \ TPMA_NV_WRITEDEFINE ret = self.write_nvram(nvindex, nvindexattrs, ekcert, lock_nvram, "EK Certificate") if ret == 0: logit( self.logfile, "Successfully created NVRAM area 0x%x for %s EK certificate.\n" % (nvindex, keytype)) else: logerr( self.logfile, "Could not create NVRAM area 0x%x for %s EK certificate.\n" % (nvindex, keytype)) return ret
def create_ek(self, isecc, rsa_keysize, allowsigning, decryption, lock_nvram): """ Create an ECC or RSA EK """ if isecc: tpm2_ek_handle = TPM2_EK_ECC_SECP384R1_HANDLE keytype = "ECC" nvindex = TPM2_NV_INDEX_ECC_SECP384R1_HI_EKTEMPLATE else: if rsa_keysize == 2048: tpm2_ek_handle = TPM2_EK_RSA_HANDLE nvindex = TPM2_NV_INDEX_RSA2048_EKTEMPLATE elif rsa_keysize == 3072: tpm2_ek_handle = TPM2_EK_RSA3072_HANDLE nvindex = TPM2_NV_INDEX_RSA3072_HI_EKTEMPLATE keytype = "RSA %d" % rsa_keysize if isecc: ek_template, ekparam, handle, ret = \ self.createprimary_ek_ecc_nist_p384(allowsigning, decryption) else: ek_template, ekparam, handle, ret = \ self.createprimary_ek_rsa(rsa_keysize, allowsigning, decryption) if ret == 0: ret = self.evictcontrol(handle, tpm2_ek_handle) if ret != 0: logerr(self.logfile, "create_ek failed\n") return "", 1 logit( self.logfile, "Successfully created %s EK with handle 0x%x.\n" % (keytype, tpm2_ek_handle)) if allowsigning: nvindexattrs = TPMA_NV_PLATFORMCREATE | \ TPMA_NV_AUTHREAD | \ TPMA_NV_OWNERREAD | \ TPMA_NV_PPREAD | \ TPMA_NV_PPWRITE | \ TPMA_NV_NO_DA | \ TPMA_NV_WRITEDEFINE ret = self.write_nvram(nvindex, nvindexattrs, ek_template, lock_nvram, "EK template") if ret == 0: logit( self.logfile, "Successfully created NVRAM area 0x%x for %s EK template.\n" % (nvindex, keytype)) return ekparam, ret
def check_state_overwrite(flags, tpm_state_path): """ Check whether we are allowed to overwrite existing state """ if flags & SETUP_TPM2_F: statefile = "tpm2-00.permall" else: statefile = "tpm-00.permall" if os.access(os.path.join(tpm_state_path, statefile), os.R_OK|os.W_OK): if flags & SETUP_STATE_NOT_OVERWRITE_F: logit(LOGFILE, "Not overwriting existing state file.\n") return 2 if flags & SETUP_STATE_OVERWRITE_F: return 0 logerr(LOGFILE, "Found existing TPM state file %s.\n" % statefile) return 1 return 0
def create_spk(self, isecc, rsa_keysize): """ Create either an ECC or RSA storage primary key """ if isecc: _, _, handle, ret = self.createprimary_spk_ecc_nist_p384() else: _, _, handle, ret = self.createprimary_spk_rsa(rsa_keysize) if ret != 0: return 1 ret = self.evictcontrol(handle, TPM2_SPK_HANDLE) if ret == 0: logit( self.logfile, "Successfully created storage primary key with handle 0x%x.\n" % TPM2_SPK_HANDLE) return ret
def init_tpm2(flags, swtpm_prg_l, config_file, tpm2_state_path, vmid, pcr_banks, swtpm_keyopt, fds_to_pass, rsa_keysize): """ Initialize a TPM 2.0: create keys and certificate """ certsdir = tpm2_state_path swtpm = Swtpm2(swtpm_prg_l.copy(), tpm2_state_path, swtpm_keyopt, LOGFILE, fds_to_pass) ret = swtpm.start() if ret != 0: logerr(LOGFILE, "Could not start the TPM 2.\n") return 1 if ret == 0: ret = swtpm.run_swtpm_bios() if ret == 0 and flags & SETUP_CREATE_SPK_F: ret = swtpm.create_spk(flags & SETUP_TPM2_ECC_F, rsa_keysize) if ret == 0: ret = tpm2_create_eks_and_certs(flags, config_file, certsdir, vmid, rsa_keysize, swtpm) if ret == 0 and pcr_banks != "-": all_pcr_banks, ret = swtpm.get_all_pcr_banks() if ret == 0: active_pcr_banks, ret = swtpm.set_active_pcr_banks( pcr_banks.split(","), all_pcr_banks) if ret == 0: logit( LOGFILE, "Successfully activated PCR banks %s among %s.\n" % (",".join(active_pcr_banks), ",".join(all_pcr_banks))) if ret == 0: swtpm.shutdown() swtpm.destroy() return ret
def write_platform_cert_nvram(self, lock_nvram, platformcert): """ Write the platform certificate into an NVRAM area """ nvindex = TPM2_NV_INDEX_PLATFORMCERT nvindexattrs = TPMA_NV_PLATFORMCREATE | \ TPMA_NV_AUTHREAD | \ TPMA_NV_OWNERREAD | \ TPMA_NV_PPREAD | \ TPMA_NV_PPWRITE | \ TPMA_NV_NO_DA | \ TPMA_NV_WRITEDEFINE ret = self.write_nvram(nvindex, nvindexattrs, platformcert, lock_nvram, "Platform Certificate") if ret == 0: logit( self.logfile, "Successfully created NVRAM area 0x%x for platform certificate.\n" % nvindex) else: logerr( self.logfile, "Could not create NVRAM area 0x%x for platform certificate.\n" % nvindex) return ret
def init_tpm(flags, swtpm_prg_l, config_file, tpm_state_path, ownerpass, srkpass, vmid, swtpm_keyopt, fds_to_pass): """ Initialize a TPM 1.2: create keys and certificate and take ownership """ certsdir = tpm_state_path swtpm = Swtpm12(swtpm_prg_l.copy(), tpm_state_path, swtpm_keyopt, LOGFILE, fds_to_pass) ret = swtpm.start() if ret != 0: logerr(LOGFILE, "Could not start the TPM 2.\n") return 1 # We will have to call swtpm.destroy() at the end if ret == 0: ret = swtpm.run_swtpm_bios() if ret == 0 and flags & SETUP_CREATE_EK_F: pubek, ret = swtpm.create_endorsement_key_pair() if ret == 0: logit(LOGFILE, "Successfully created EK.\n") if ret == 0 and flags & SETUP_TAKEOWN_F: ret = tpm12_take_ownership(flags, ownerpass, srkpass, pubek, swtpm) if ret == 0: logit(LOGFILE, "Successfully took ownership of the TPM.\n") if ret == 0 and flags & SETUP_EK_CERT_F: ekparam = pubek.hex() ret = tpm12_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm) if ret == 0 and flags & SETUP_LOCK_NVRAM_F: ret = swtpm.nv_lock() if ret == 0: logit(LOGFILE, "Successfully locked NVRAM access.\n") swtpm.destroy() return ret
def main(): """ main function - parses command line parameters and low level dealing with them """ global LOGFILE # pylint: disable=W0603 swtpm_prg = distutils.spawn.find_executable("swtpm") if swtpm_prg: swtpm_prg += " socket" try: opts, _ = getopt.getopt(sys.argv[1:], "h?", ["tpm-state=", "tpmstate=", "tpm=", "swtpm_ioctl=", "tpm2", "ecc", "createek", "create-spk", "take-ownership", "ownerpass="******"owner-well-known", "srkpass="******"srk-well-known", "create-ek-cert", "create-platform-cert", "lock-nvram", "display", "config=", "vmid=", "keyfile=", "keyfile-fd=", "pwdfile=", "pwdfile-fd=", "cipher=", "runas=", "logfile=", "overwrite", "not-overwrite", "allow-signing", "decryption", "pcr-banks=", "rsa-keysize=", "tcsd-system-ps-file", "version", "print-capabilities", "help"]) except getopt.GetoptError as err: print(err) usage(sys.argv[0]) sys.exit(1) flags = 0 tpm_state_path = "" config_file = DEFAULT_CONFIG_FILE ownerpass = None got_ownerpass = False srkpass = None got_srkpass = False vmid = "" pcr_banks = "" printcapabilities = False keyfile = "" keyfile_fd = "" pwdfile = "" pwdfile_fd = "" cipher = "aes-128-cbc" rsa_keysize_str = "%d" % DEFAULT_RSA_KEYSIZE swtpm_keyopt = "" fds_to_pass = [] runas = "" for opt, arg in opts: if opt in ['--tpm-state', '--tpmstate']: tpm_state_path = arg elif opt == '--tpm': swtpm_prg = arg elif opt == '--swtpm_ioctl': print("Warning: --swtpm_ioctl is deprecated and has no effect.") elif opt == '--tpm2': flags |= SETUP_TPM2_F elif opt == '--ecc': flags |= SETUP_TPM2_ECC_F elif opt == '--createek': flags |= SETUP_CREATE_EK_F elif opt == '--create-spk': flags |= SETUP_CREATE_SPK_F elif opt == '--take-ownership': flags |= SETUP_CREATE_EK_F | SETUP_TAKEOWN_F elif opt == '--ownerpass': ownerpass = arg got_ownerpass = True elif opt == '--owner-well-known': flags |= SETUP_OWNERPASS_ZEROS_F elif opt == '--srkpass': srkpass = arg got_srkpass = True elif opt == '--srk-well-known': flags |= SETUP_SRKPASS_ZEROS_F elif opt == '--create-ek-cert': flags |= SETUP_CREATE_EK_F | SETUP_EK_CERT_F elif opt == '--create-platform-cert': flags |= SETUP_CREATE_EK_F | SETUP_PLATFORM_CERT_F elif opt == '--lock-nvram': flags |= SETUP_LOCK_NVRAM_F elif opt == '--display': flags |= SETUP_DISPLAY_RESULTS_F elif opt == '--config': config_file = arg elif opt == '--vmid': vmid = arg elif opt == '--keyfile': keyfile = arg elif opt == '--keyfile-fd': keyfile_fd = arg elif opt == '--pwdfile': pwdfile = arg elif opt == '--pwdfile-fd': pwdfile_fd = arg elif opt == '--cipher': cipher = arg elif opt == '--runas': runas = arg elif opt == '--logfile': LOGFILE = arg elif opt == '--overwrite': flags |= SETUP_STATE_OVERWRITE_F elif opt == '--not-overwrite': flags |= SETUP_STATE_NOT_OVERWRITE_F elif opt == '--allow-signing': flags |= SETUP_ALLOW_SIGNING_F elif opt == '--decryption': flags |= SETUP_DECRYPTION_F elif opt == '--pcr-banks': pcr_banks = pcr_banks + "," + arg elif opt == '--rsa-keysize': rsa_keysize_str = arg elif opt == '--tcsd-system-ps-file': print("Warning: --tcsd-system-ps-file is deprecated and has no effect.") elif opt == '--version': versioninfo() sys.exit(0) elif opt == '--print-capabilities': printcapabilities = True elif opt in ['--help', '-h', '-?']: usage(sys.argv[0]) sys.exit(0) else: sys.stderr.write("Unknown option %s\n" % opt) usage(sys.argv[0]) sys.exit(1) if not swtpm_prg: logerr(LOGFILE, "Default TPM 'swtpm' could not be found and was not provided using --tpm\n.") sys.exit(1) swtpm_prg_l = swtpm_prg.split() if not distutils.spawn.find_executable(swtpm_prg_l[0]): logerr(LOGFILE, "TPM at %s is not an executable.\n" % (" ".join(swtpm_prg_l))) sys.exit(1) if printcapabilities: ret = print_capabilities(swtpm_prg_l) sys.exit(ret) if runas: ret = change_process_owner(runas) if ret != 0: sys.exit(1) if not got_ownerpass: flags |= SETUP_OWNERPASS_ZEROS_F if not got_srkpass: flags |= SETUP_SRKPASS_ZEROS_F # sequeeze ',' and remove leading and trailing ',' pcr_banks = re.sub(r',$', '', re.sub(r'^,', '', re.sub(r',,+', ',', pcr_banks))) if len(pcr_banks) == 0: pcr_banks = DEFAULT_PCR_BANKS # set owner password to default if user didn't provide any password wish # and wants to take ownership if flags & SETUP_TAKEOWN_F and \ flags & SETUP_OWNERPASS_ZEROS_F and \ not got_srkpass: srkpass = DEFAULT_SRK_PASSWORD if len(LOGFILE) > 0: if os.path.islink(LOGFILE): sys.stderr.write("Logfile must not be a symlink.\n") sys.exit(1) try: fobj = open(LOGFILE, "a") # do not truncate fobj.close() except PermissionError: sys.stderr.write("Cannot write to logfile %s.\n", LOGFILE) sys.exit(1) # Check tpm_state_path directory and access rights if len(tpm_state_path) == 0: logerr(LOGFILE, "--tpm-state must be provided\n") sys.exit(1) if not os.path.isdir(tpm_state_path): logerr(LOGFILE, "User %s cannot access directory %s. Make sure it exists and is a directory.\n" % (getpass.getuser(), tpm_state_path)) sys.exit(1) if not os.access(tpm_state_path, os.R_OK): logerr(LOGFILE, "Need read rights on directory %s for user %s.\n" % (tpm_state_path, getpass.getuser())) sys.exit(1) if not os.access(tpm_state_path, os.W_OK): logerr(LOGFILE, "Need write rights on directory %s for user %s.\n" % (tpm_state_path, getpass.getuser())) sys.exit(1) if flags & SETUP_TPM2_F: if flags & SETUP_TAKEOWN_F: logerr(LOGFILE, "Taking ownership is not supported for TPM 2.\n") sys.exit(1) else: if flags & SETUP_TPM2_ECC_F: logerr(LOGFILE, "--ecc requires --tpm2.\n") sys.exit(1) if flags & SETUP_CREATE_SPK_F: logerr(LOGFILE, "--create-spk requires --tpm2.\n") sys.exit(1) ret = check_state_overwrite(flags, tpm_state_path) if ret == 1: sys.exit(1) elif ret == 2: sys.exit(0) files = glob.glob(os.path.join(tpm_state_path, "*permall")) files.extend(glob.glob(os.path.join(tpm_state_path, "*volatilestate"))) files.extend(glob.glob(os.path.join(tpm_state_path, "*savestate"))) try: for fil in files: os.remove(fil) except Exception as err: logerr(LOGFILE, "Could not remove previous state files. Need execute access rights on the " "directory %s.\n" % tpm_state_path) sys.exit(1) lockfile = os.path.join(tpm_state_path, ".lock") if os.path.exists(lockfile) and not os.access(lockfile, os.W_OK | os.R_OK): logerr(LOGFILE, "User %s cannot read/write %s.\n" % (getpass.getuser(), lockfile)) sys.exit(1) if not os.access(config_file, os.R_OK): logerr(LOGFILE, "User %s cannot read %s.\n" % (getpass.getuser(), config_file)) sys.exit(1) if len(cipher) > 0: if cipher not in ['aes-128-cbc', 'aes-cbc', 'aes-256-cbc']: logerr(LOGFILE, "Unsupported cipher %s.\n" % cipher) sys.exit(1) cipher = ",mode=%s" % cipher if len(keyfile) > 0: if not os.access(keyfile, os.R_OK): logerr(LOGFILE, "User %s cannot read keyfile %s.\n" % (getpass.getuser(), keyfile)) sys.exit(1) swtpm_keyopt = "file=%s%s" % (keyfile, cipher) logit(LOGFILE, " The TPM's state will be encrypted with a provided key.\n") elif len(pwdfile) > 0: if not os.access(pwdfile, os.R_OK): logerr(LOGFILE, "User %s canot read passphrase file %s.\n" % \ (getpass.getuser(), keyfile)) sys.exit(1) swtpm_keyopt = "pwdfile=%s%s" % (pwdfile, cipher) logit(LOGFILE, " The TPM's state will be encrypted using a key derived from a passphrase.\n") elif len(keyfile_fd) > 0: if not keyfile_fd.isnumeric(): logerr(LOGFILE, "--keyfile-fd parameter $keyfile_fd is not a valid file descriptor.\n") sys.exit(1) fds_to_pass.append(int(keyfile_fd)) swtpm_keyopt = "fd=%s%s" % (keyfile_fd, cipher) logit(LOGFILE, " The TPM's state will be encrypted with a provided key (fd).\n") elif len(pwdfile_fd) > 0: if not pwdfile_fd.isnumeric(): logerr(LOGFILE, "--pwdfile-fd parameter $pwdfile_fd is not a valid file descriptor.\n") sys.exit(1) fds_to_pass.append(int(pwdfile_fd)) swtpm_keyopt = "pwdfd=%s%s" % (pwdfile_fd, cipher) logit(LOGFILE, " The TPM's state will be encrypted using a key derived from a passphrase (fd).\n") if rsa_keysize_str == "max": keysizes, ret = get_rsa_keysizes(flags, swtpm_prg_l) if ret != 0: sys.exit(1) if len(keysizes) > 0: rsa_keysize_str = keysizes[-1] else: rsa_keysize_str = "2048" if rsa_keysize_str in ["2048", "3072"]: rsa_keysize = int(rsa_keysize_str) supported_keysizes, ret = get_rsa_keysizes(flags, swtpm_prg_l) if ret != 0: sys.exit(1) if rsa_keysize not in supported_keysizes and rsa_keysize != 2048: logerr(LOGFILE, "%s bit RSA keys are not supported by libtpms.\n" % rsa_keysize_str) sys.exit(1) else: logerr(LOGFILE, "Unsupported RSA key size %s.\n" % rsa_keysize_str) sys.exit(1) user = getpass.getuser() try: group = grp.getgrnam(user)[0] except KeyError: group = "<unknown>" tzinfo = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo logit(LOGFILE, "Starting vTPM manufacturing as %s:%s @ %s\n" % (user, group, datetime.datetime.now(tz=tzinfo).strftime("%a %d %h %Y %I:%M:%S %p %Z"))) if not flags & SETUP_TPM2_F: ret = init_tpm(flags, swtpm_prg_l, config_file, tpm_state_path, ownerpass, srkpass, vmid, swtpm_keyopt, fds_to_pass) else: ret = init_tpm2(flags, swtpm_prg_l, config_file, tpm_state_path, vmid, pcr_banks, swtpm_keyopt, fds_to_pass, rsa_keysize) if ret == 0: logit(LOGFILE, "Successfully authored TPM state.\n") else: logerr(LOGFILE, "An error occurred. Authoring the TPM state failed.\n") delete_state(flags, tpm_state_path) logit(LOGFILE, "Ending vTPM manufacturing @ %s\n" % datetime.datetime.now(tz=tzinfo).strftime("%a %d %h %Y %I:%M:%S %p %Z")) sys.exit(ret)
def call_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm): """ Call an external tool to create the certificates """ params, ret = tpm_get_specs_and_attributes(swtpm) if ret != 0: return 1 lines, ret = read_file_lines(config_file) if ret != 0: return ret create_certs_tool = get_config_value(lines, "create_certs_tool") create_certs_tool_config = get_config_value(lines, "create_certs_tool_config") create_certs_tool_options = get_config_value(lines, "create_certs_tool_options") ret = 0 if create_certs_tool: ret = 1 if flags & SETUP_TPM2_F: params.extend(["--tpm2"]) cmd = [create_certs_tool, "--type", "_", "--ek", ekparam, "--dir", certsdir] if len(LOGFILE) > 0: cmd.extend(["--logfile", LOGFILE]) if len(vmid) > 0: cmd.extend(["--vmid", vmid]) cmd.extend(params) if create_certs_tool_config: cmd.extend(["--configfile", create_certs_tool_config]) if create_certs_tool_options: cmd.extend(["--optsfile", create_certs_tool_options]) try: i = create_certs_tool.rindex(os.sep) prgname = create_certs_tool[i + 1:] except ValueError: prgname = create_certs_tool for entry in [(SETUP_EK_CERT_F, "ek"), (SETUP_PLATFORM_CERT_F, "platform")]: if flags & entry[0]: try: cmd[2] = entry[1] logit(LOGFILE, " Invoking %s\n" % (" ".join(cmd))) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = proc.communicate(timeout=30) for line in stdout.decode().split("\n"): if len(line) > 0: logit(LOGFILE, "%s: %s\n" % (prgname, line)) ret = proc.returncode if ret != 0: logerr(LOGFILE, "Error returned from command\n") return 1 except FileNotFoundError as err: logerr(LOGFILE, "Could not execute %s: %s\n" % (create_certs_tool, str(err))) return 1 except subprocess.TimeoutExpired as err: logerr(LOGFILE, "%s did not finish in time: %s\n" % (create_certs_tool, str(err))) return 1 except Exception as err: logerr(LOGFILE, "An error occurred running %s: %s\n" % (create_certs_tool, str(err))) return ret