~nabijaczleweli/tzpfms

675a0c40b791c4c03992a8f9c45b8361482171c9 — наб a month ago cc4716c
Prompt for passphrase (or empty for none) in TPM1.X
M src/bin/zfs-tpm1x-change-key.cpp => src/bin/zfs-tpm1x-change-key.cpp +15 -5
@@ 58,13 58,23 @@ int main(int argc, char ** argv) {
				    Tspi_Policy_FlushSecret(parent_key_policy);
				    Tspi_Context_CloseObject(ctx, parent_key_policy);
			    }};
			    // TODO: this is where we'd prompt for a password if we supported it yet
			    fprintf(stderr, "Tspi_Policy_SetSecret(\"adenozynotrójfosforan\") = %s\n",
			            Trspi_Error_String(
			                Tspi_Policy_SetSecret(parent_key_policy, TSS_SECRET_MODE_PLAIN, strlen("adenozynotrójfosforan"), (BYTE *)"adenozynotrójfosforan")));

			    {
				    uint8_t * parent_key_passphrase{};
				    size_t parent_key_passphrase_len{};
				    TRY_MAIN(read_new_passphrase("wrapping key (or empty for none)", parent_key_passphrase, parent_key_passphrase_len));
				    quickscope_wrapper parent_key_passphrase_deleter{[&] { free(parent_key_passphrase); }};

				    if(parent_key_passphrase_len)
					    TRY_TPM1X("assign passphrase to parent_key key",
					              Tspi_Policy_SetSecret(parent_key_policy, TSS_SECRET_MODE_PLAIN, parent_key_passphrase_len, parent_key_passphrase));
				    else
					    TRY_TPM1X("assign default sealant key secret",
					              Tspi_Policy_SetSecret(parent_key_policy, TSS_SECRET_MODE_SHA1, sizeof(parent_key_secret), (BYTE *)parent_key_secret));
			    }

			    TRY_MAIN(try_srk("create sealant key (did you take ownership?)", srk_policy, [&] { return Tspi_Key_CreateKey(parent_key, srk, 0); }));
			    TRY_MAIN(try_policy_or_passphrase("create sealant key (did you take ownership?)", "SRK", srk_policy,
			                                      [&] { return Tspi_Key_CreateKey(parent_key, srk, 0); }));

			    TRY_TPM1X("load sealant key", Tspi_Key_LoadKey(parent_key, srk));


M src/bin/zfs-tpm1x-load-key.cpp => src/bin/zfs-tpm1x-load-key.cpp +7 -6
@@ 35,8 35,9 @@ int main(int argc, char ** argv) {
		    uint8_t wrap_key[WRAPPING_KEY_LEN]{};
		    TRY_MAIN(with_tpm1x_session([&](auto ctx, auto srk, auto srk_policy) {
			    TSS_HOBJECT parent_key{};
			    TRY_MAIN(try_srk("load sealant key from blob (did you take ownership?)", srk_policy,
			                     [&] { return Tspi_Context_LoadKeyByBlob(ctx, srk, handle.parent_key_blob_len, handle.parent_key_blob, &parent_key); }));
			    TRY_MAIN(try_policy_or_passphrase("load sealant key from blob (did you take ownership?)", "SRK", srk_policy, [&] {
				    return Tspi_Context_LoadKeyByBlob(ctx, srk, handle.parent_key_blob_len, handle.parent_key_blob, &parent_key);
			    }));
			    quickscope_wrapper parent_key_deleter{[&] { Tspi_Key_UnloadKey(parent_key); }};

			    TSS_HPOLICY parent_key_policy{};


@@ 46,9 47,8 @@ int main(int argc, char ** argv) {
				    Tspi_Policy_FlushSecret(parent_key_policy);
				    Tspi_Context_CloseObject(ctx, parent_key_policy);
			    }};
			    fprintf(stderr, "Tspi_Policy_SetSecret(\"adenozynotrójfosforan\") = %s\n",
			            Trspi_Error_String(
			                Tspi_Policy_SetSecret(parent_key_policy, TSS_SECRET_MODE_PLAIN, strlen("adenozynotrójfosforan"), (BYTE *)"adenozynotrójfosforan")));
			    TRY_TPM1X("assign default sealant key secret",
			              Tspi_Policy_SetSecret(parent_key_policy, TSS_SECRET_MODE_SHA1, sizeof(parent_key_secret), (BYTE *)parent_key_secret));


			    TSS_HOBJECT sealed_object{};


@@ 61,7 61,8 @@ int main(int argc, char ** argv) {

			    uint8_t * loaded_wrap_key{};
			    uint32_t loaded_wrap_key_len{};
			    TRY_TPM1X("unseal wrapping key", Tspi_Data_Unseal(sealed_object, parent_key, &loaded_wrap_key_len, &loaded_wrap_key));
			    TRY_MAIN(try_policy_or_passphrase("unseal wrapping key", "wrapping key", parent_key_policy,
			                                      [&] { return Tspi_Data_Unseal(sealed_object, parent_key, &loaded_wrap_key_len, &loaded_wrap_key); }));
			    if(loaded_wrap_key_len != sizeof(wrap_key)) {
				    fprintf(stderr, "Wrong sealed data length (%u != %zu):", loaded_wrap_key_len, sizeof(wrap_key));
				    for(auto i = 0u; i < loaded_wrap_key_len; ++i)

M src/fd.cpp => src/fd.cpp +111 -0
@@ 3,9 3,12 @@

#include "fd.hpp"

#include "main.hpp"

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>




@@ 55,3 58,111 @@ int write_exact(const char * path, const void * data, size_t len, mode_t mode) {

	return 0;
}


/// Adapted from src:zfs's lib/libzfs/libzfs_crypto.c#get_key_material_raw()
static int get_key_material_raw(const char * whom, bool again, bool newkey, uint8_t *& buf, size_t & len_out) {
	static int caught_interrupt;

	struct termios old_term;
	struct sigaction osigint, osigtstp;

	len_out = 0;

	auto from_tty = isatty(0);
	if(from_tty) {
		// Handle SIGINT and ignore SIGSTP.
		// This is necessary to restore the state of the terminal.
		struct sigaction act {};
		sigemptyset(&act.sa_mask);

		caught_interrupt = 0;
		act.sa_handler   = [](auto sig) { caught_interrupt = sig; };
		sigaction(SIGINT, &act, &osigint);

		act.sa_handler = SIG_IGN;
		sigaction(SIGTSTP, &act, &osigtstp);

		// Prompt for the key
		printf("%s %spassphrase for %s: ", again ? "Re-enter" : "Enter", newkey ? "new " : "", whom);
		fflush(stdout);

		// Disable the terminal echo for key input
		tcgetattr(0, &old_term);

		auto new_term = old_term;
		new_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
		TRY("disable echo", tcsetattr(0, TCSAFLUSH, &new_term));
	}
	quickscope_wrapper stdin_restorer{[&] {
		if(from_tty) {
			// Reset the terminal
			tcsetattr(0, TCSAFLUSH, &old_term);
			sigaction(SIGINT, &osigint, nullptr);
			sigaction(SIGTSTP, &osigtstp, nullptr);

			// If we caught a signal, re-throw it now
			if(caught_interrupt != 0)
				kill(getpid(), caught_interrupt);

			// Print the newline that was not echoed
			putchar('\n');
		}
	}};


	// Read the key material
	size_t buflen{};
	errno      = 0;
	auto bytes = getline((char **)&buf, &buflen, stdin);
	switch(bytes) {
		case -1:
			if(errno != 0)
				TRY("read in passphrase", bytes);
			else  // EOF
				bytes = 0;
			break;
		case 0:
			break;
		default:
			// Trim ending newline, if any
			if(buf[bytes - 1] == '\n') {
				buf[bytes - 1] = '\0';
				--bytes;
			}
			break;
	}

	len_out = bytes;
	return 0;
}

int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out) {
	return get_key_material_raw(whom, false, false, buf, len_out);
}

int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out) {
	uint8_t * first_passphrase{};
	size_t first_passphrase_len{};
	TRY_MAIN(get_key_material_raw(whom, false, true, first_passphrase, first_passphrase_len));
	quickscope_wrapper first_passphrase_deleter{[&] { free(first_passphrase); }};

	uint8_t * second_passphrase{};
	size_t second_passphrase_len{};
	TRY_MAIN(get_key_material_raw(whom, true, true, second_passphrase, second_passphrase_len));
	quickscope_wrapper second_passphrase_deleter{[&] { free(second_passphrase); }};

	if(second_passphrase_len != first_passphrase_len || memcmp(first_passphrase, second_passphrase, first_passphrase_len)) {
		fprintf(stderr, "Provided keys do not match.\n");
		return __LINE__;
	}

	if(second_passphrase_len) {
		buf               = second_passphrase;
		second_passphrase = nullptr;
	} else
		buf = nullptr;

	len_out = second_passphrase_len;
	return 0;
}

M src/fd.hpp => src/fd.hpp +3 -0
@@ 32,3 32,6 @@ extern int read_exact(const char * path, void * data, size_t len);

/// Write exactly len bytes from data into path, or error
extern int write_exact(const char * path, const void * data, size_t len, mode_t mode);

extern int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out);
extern int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out);

M src/tpm1x.cpp => src/tpm1x.cpp +1 -0
@@ 8,6 8,7 @@
#include <stdlib.h>


/// Used as secret for the sealed object itself
// I just got this out of /dev/random
static const constexpr uint8_t sealing_secret[TPM_SHA1_160_HASH_LEN]{0xB9, 0xEE, 0x71, 0x5D, 0xBE, 0x4B, 0x24, 0x3F, 0xAA, 0x81,
                                                                     0xEA, 0x04, 0x30, 0x6E, 0x06, 0x37, 0x10, 0x38, 0x3E, 0x35};

M src/tpm1x.hpp => src/tpm1x.hpp +27 -10
@@ 5,6 5,10 @@


#include "common.hpp"
#include "fd.hpp"
#include "main.hpp"

#include <stdlib.h>

#include <tss/platform.h>
#include <tss/tspi.h>


@@ 17,6 21,12 @@
#define TRY_TPM1X(what, ...) TRY_GENERIC(what, , != TPM_SUCCESS, _try_ret, __LINE__, Trspi_Error_String, __VA_ARGS__)


/// Used as default secret if passphrase wasn't provided for wrapping key for the sealed object
// I just got this out of /dev/random
static const constexpr uint8_t parent_key_secret[TPM_SHA1_160_HASH_LEN]{0xCE, 0x4C, 0xF6, 0x77, 0x87, 0x5B, 0x5E, 0xB8, 0x99, 0x35,
                                                                        0x91, 0xD5, 0xA9, 0xAF, 0x1E, 0xD2, 0x4A, 0x3A, 0x87, 0x36};


template <class F>
int with_tpm1x_session(F && func) {
	TSS_HCONTEXT ctx{};  // All memory lives as long as this does


@@ 44,15 54,26 @@ int with_tpm1x_session(F && func) {
	return func(ctx, srk, srk_policy);
}

/// Try to run func() with the current SRK policy (well-known by default); if it fails, prompt for password and reattempt.
/// Try to run func() with the current policy; if it fails, prompt for passphrase and reattempt up to three total times.
template <class F>
int try_srk(const char * what, TSS_HPOLICY srk_policy, F && func) {
int try_policy_or_passphrase(const char * what, const char * what_for, TSS_HPOLICY policy, F && func) {
	auto get_passphrase = [&] {
		BYTE * pass{};
		size_t pass_len{};
		TRY_MAIN(read_known_passphrase(what_for, pass, pass_len));
		quickscope_wrapper pass_deleter{[&] { free(pass); }};

		TRY_TPM1X("set passphrase secret on policy", Tspi_Policy_SetSecret(policy, TSS_SECRET_MODE_PLAIN, pass_len, pass));
		return 0;
	};

	auto err = func();
	// Equivalent to TSS_ERROR_LAYER(err) == TSS_LAYER_TPM && TSS_ERROR_CODE(err) == TPM_E_AUTHFAIL
	if((err & TSS_LAYER_TSP) == TSS_LAYER_TPM && (err & TSS_MAX_ERROR) == TPM_E_AUTHFAIL) {
		// TODO: read SRK password from stdin here
		TRY_TPM1X("set password secret on SRK policy", Tspi_Policy_SetSecret(srk_policy, TSS_SECRET_MODE_PLAIN, strlen("dupanina"), (BYTE *)"dupanina"));
	for(int i = 0; ((err & TSS_LAYER_TSP) == TSS_LAYER_TPM && (err & TSS_MAX_ERROR) == TPM_E_AUTHFAIL) && i < 3; ++i) {
		if(i)
			fprintf(stderr, "Couldn't %s: %s\n", what, Trspi_Error_String(err));

		TRY_MAIN(get_passphrase());
		err = func();
	}



@@ 80,9 101,5 @@ struct tpm1x_handle {
/// The stored handle is in the form [%X:%X] where the first blob is the parent key and the second is the sealed data.
extern int tpm1x_parse_handle(const char * dataset_name, char * handle_s, tpm1x_handle & handle);


/// Create sealed object, assign a policy and a known secret to it.
extern int tpm1x_prep_sealed_object(TSS_HCONTEXT ctx, TSS_HOBJECT & sealed_object, TSS_HPOLICY & sealed_object_policy);
// extern int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT & persistent_handle, const TPM2B_DATA & metadata, void * data,
//                      size_t data_len);
// extern int tpm2_unseal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle, void * data, size_t data_len);
// extern int tpm2_free_persistent(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle);