Allow for setting passphrases on TPM2 keys. Handle max passphrase lengths

This commit is contained in:
наб 2020-10-27 20:02:17 +01:00
parent a558ca5b48
commit 201fdf2c0a
No known key found for this signature in database
GPG Key ID: BCFD0B018D2658F1
6 changed files with 67 additions and 24 deletions

View File

@ -16,7 +16,8 @@ If `dataset` was previously encrypted with tzpfms and the *TPM2* back-end was us
Otherwise, or in case of an error, data required for manual intervention will be printed to the standard error stream. Otherwise, or in case of an error, data required for manual intervention will be printed to the standard error stream.
Next, a new wrapping key is be generated on the TPM, optionally backed up (see [OPTIONS][]), Next, a new wrapping key is be generated on the TPM, optionally backed up (see [OPTIONS][]),
and sealed to a persistent object on the TPM under the owner hierarchy. and sealed to a persistent object on the TPM under the owner hierarchy;
the user is also prompted for an optional passphrase to protect the object with.
The following properties are set on `dataset`: The following properties are set on `dataset`:
@ -27,7 +28,7 @@ The following properties are set on `dataset`:
(namely zfs-tpm2-change-key(8), zfs-tpm2-load-key(8), and zfs-tpm2-clear-key(8)). (namely zfs-tpm2-change-key(8), zfs-tpm2-load-key(8), and zfs-tpm2-clear-key(8)).
`tzpfms.key` is an integer representing the sealed object; `tzpfms.key` is an integer representing the sealed object;
if needed, it can be passed to **tpm2_unseal(1) -c ${tzpfms.key}** or equivalent for back-up (see [OPTIONS][]). if needed, it can be passed to **tpm2_unseal(1) -c ${tzpfms.key} [-p ${password}]** or equivalent for back-up (see [OPTIONS][]).
If you have a sealed key you can access with that or equivalent tool and set both of these properties, it will funxion seamlessly. If you have a sealed key you can access with that or equivalent tool and set both of these properties, it will funxion seamlessly.
Finally, the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=raw dataset** is performed with the new key. Finally, the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=raw dataset** is performed with the new key.

View File

@ -137,16 +137,29 @@ static int get_key_material_raw(const char * whom, bool again, bool newkey, uint
return 0; return 0;
} }
int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out) { int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len) {
return get_key_material_raw(whom, false, false, buf, len_out); TRY_MAIN(get_key_material_raw(whom, false, false, buf, len_out));
if(len_out <= max_len)
return 0;
fprintf(stderr, "Passphrase too long: (max %zu)\n", max_len);
free(buf);
buf = nullptr;
len_out = 0;
return __LINE__;
} }
int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out) { int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len) {
uint8_t * first_passphrase{}; uint8_t * first_passphrase{};
size_t first_passphrase_len{}; size_t first_passphrase_len{};
TRY_MAIN(get_key_material_raw(whom, false, true, first_passphrase, 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); }}; quickscope_wrapper first_passphrase_deleter{[&] { free(first_passphrase); }};
if(first_passphrase_len > max_len) {
fprintf(stderr, "Passphrase too long: (max %zu)\n", max_len);
return __LINE__;
}
uint8_t * second_passphrase{}; uint8_t * second_passphrase{};
size_t second_passphrase_len{}; size_t second_passphrase_len{};
TRY_MAIN(get_key_material_raw(whom, true, true, second_passphrase, second_passphrase_len)); TRY_MAIN(get_key_material_raw(whom, true, true, second_passphrase, second_passphrase_len));

View File

@ -6,6 +6,7 @@
#include "common.hpp" #include "common.hpp"
#include <unistd.h> #include <unistd.h>
#include <stdint.h>
template <class F> template <class F>
@ -33,5 +34,8 @@ extern int read_exact(const char * path, void * data, size_t len);
/// Write exactly len bytes from data into path, or error /// 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 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); /// Prompt for passphrase for whom the user knows, up to max_len bytes
extern int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out); extern int read_known_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len = SIZE_MAX);
/// Prompt twive for passphrase for whom the user is setting
extern int read_new_passphrase(const char * whom, uint8_t *& buf, size_t & len_out, size_t max_len = SIZE_MAX);

View File

@ -64,23 +64,18 @@ int with_tpm1x_session(F && func) {
/// Try to run func() with the current policy; if it fails, prompt for passphrase and reattempt up to three total times. /// Try to run func() with the current policy; if it fails, prompt for passphrase and reattempt up to three total times.
template <class F> template <class F>
int try_policy_or_passphrase(const char * what, const char * what_for, TSS_HPOLICY 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(); auto err = func();
// Equivalent to TSS_ERROR_LAYER(err) == TSS_LAYER_TPM && TSS_ERROR_CODE(err) == TPM_E_AUTHFAIL // Equivalent to TSS_ERROR_LAYER(err) == TSS_LAYER_TPM && TSS_ERROR_CODE(err) == TPM_E_AUTHFAIL
for(int i = 0; ((err & TSS_LAYER_TSP) == TSS_LAYER_TPM && (err & TSS_MAX_ERROR) == TPM_E_AUTHFAIL) && i < 3; ++i) { for(int i = 0; ((err & TSS_LAYER_TSP) == TSS_LAYER_TPM && (err & TSS_MAX_ERROR) == TPM_E_AUTHFAIL) && i < 3; ++i) {
if(i) if(i)
fprintf(stderr, "Couldn't %s: %s\n", what, Trspi_Error_String(err)); fprintf(stderr, "Couldn't %s: %s\n", what, Trspi_Error_String(err));
TRY_MAIN(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));
err = func(); err = func();
} }

View File

@ -2,6 +2,7 @@
#include "tpm2.hpp" #include "tpm2.hpp"
#include "fd.hpp"
#include "main.hpp" #include "main.hpp"
#include "parse.hpp" #include "parse.hpp"
@ -84,7 +85,7 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
quickscope_wrapper primary_handle_deleter{[&] { Esys_FlushContext(tpm2_ctx, primary_handle); }}; quickscope_wrapper primary_handle_deleter{[&] { Esys_FlushContext(tpm2_ctx, primary_handle); }};
{ {
const TPM2B_SENSITIVE_CREATE primary_sens{}; const TPM2B_SENSITIVE_CREATE primary_sens{0, {{}, {8, "dupanina"}}};
// Adapted from tpm2-tss-3.0.1/test/integration/esys-create-primary-hmac.int.c // Adapted from tpm2-tss-3.0.1/test/integration/esys-create-primary-hmac.int.c
TPM2B_PUBLIC pub{}; TPM2B_PUBLIC pub{};
@ -140,6 +141,16 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
secret_sens.sensitive.data.size = data_len; secret_sens.sensitive.data.size = data_len;
memcpy(secret_sens.sensitive.data.buffer, data, secret_sens.sensitive.data.size); memcpy(secret_sens.sensitive.data.buffer, data, secret_sens.sensitive.data.size);
{
uint8_t * passphrase{};
size_t passphrase_len{};
TRY_MAIN(read_new_passphrase("wrapping key (or empty for none)", passphrase, passphrase_len, sizeof(TPM2B_SENSITIVE_CREATE::sensitive.userAuth.buffer)));
quickscope_wrapper passphrase_deleter{[&] { free(passphrase); }};
secret_sens.sensitive.userAuth.size = passphrase_len;
memcpy(secret_sens.sensitive.userAuth.buffer, passphrase, secret_sens.sensitive.userAuth.size);
}
// Same args as tpm2-tools' tpm2_create(1) // Same args as tpm2-tools' tpm2_create(1)
TPM2B_PUBLIC pub{}; TPM2B_PUBLIC pub{};
pub.publicArea.type = TPM2_ALG_KEYEDHASH; pub.publicArea.type = TPM2_ALG_KEYEDHASH;
@ -185,8 +196,27 @@ int tpm2_unseal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTEN
TRY_TPM2("convert persistent handle to object", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle)); TRY_TPM2("convert persistent handle to object", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle));
TPM2B_SENSITIVE_DATA * unsealed{}; TPM2B_SENSITIVE_DATA * unsealed{};
TRY_TPM2("unseal data", Esys_Unseal(tpm2_ctx, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &unsealed));
quickscope_wrapper unsealed_deleter{[=] { Esys_Free(unsealed); }}; quickscope_wrapper unsealed_deleter{[=] { Esys_Free(unsealed); }};
{
auto err = Esys_Unseal(tpm2_ctx, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &unsealed);
for(int i = 0; err == TPM2_RC_9 + TPM2_RC_AUTH_FAIL && i < 3; ++i) {
if(i)
fprintf(stderr, "Couldn't unseal wrapping key: %s\n", Tss2_RC_Decode(err));
uint8_t * pass{};
size_t pass_len{};
TRY_MAIN(read_known_passphrase("wrapping key", pass, pass_len, sizeof(TPM2B_AUTH::buffer)));
quickscope_wrapper pass_deleter{[&] { free(pass); }};
TPM2B_AUTH auth{};
auth.size = pass_len;
memcpy(auth.buffer, pass, auth.size);
TRY_TPM2("set passphrase for persistent object", Esys_TR_SetAuth(tpm2_ctx, pandle, &auth));
err = Esys_Unseal(tpm2_ctx, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &unsealed);
}
TRY_TPM2("unseal wrapping key", err);
}
if(unsealed->size != data_len) { if(unsealed->size != data_len) {
fprintf(stderr, "Unsealed data has wrong length %u, expected %zu!\n", unsealed->size, data_len); fprintf(stderr, "Unsealed data has wrong length %u, expected %zu!\n", unsealed->size, data_len);

View File

@ -21,10 +21,10 @@ int with_tpm2_session(F && func) {
// https://tpm2-tss.readthedocs.io/en/latest/group___e_s_y_s___c_o_n_t_e_x_t.html // https://tpm2-tss.readthedocs.io/en/latest/group___e_s_y_s___c_o_n_t_e_x_t.html
ESYS_CONTEXT * tpm2_ctx{}; ESYS_CONTEXT * tpm2_ctx{};
TRY_TPM2("Esys_Initialize()", Esys_Initialize(&tpm2_ctx, nullptr, nullptr)); TRY_TPM2("initialise TPM connection", Esys_Initialize(&tpm2_ctx, nullptr, nullptr));
quickscope_wrapper tpm2_ctx_deleter{[&] { Esys_Finalize(&tpm2_ctx); }}; quickscope_wrapper tpm2_ctx_deleter{[&] { Esys_Finalize(&tpm2_ctx); }};
TRY_TPM2("Esys_Startup()", Esys_Startup(tpm2_ctx, TPM2_SU_CLEAR)); TRY_TPM2("start TPM", Esys_Startup(tpm2_ctx, TPM2_SU_CLEAR));
ESYS_TR tpm2_session = ESYS_TR_NONE; ESYS_TR tpm2_session = ESYS_TR_NONE;
quickscope_wrapper tpm2_session_deleter{[&] { Esys_FlushContext(tpm2_ctx, tpm2_session); }}; quickscope_wrapper tpm2_session_deleter{[&] { Esys_FlushContext(tpm2_ctx, tpm2_session); }};
@ -35,8 +35,8 @@ int with_tpm2_session(F && func) {
session_key.algorithm = TPM2_ALG_AES; session_key.algorithm = TPM2_ALG_AES;
session_key.keyBits.aes = 128; session_key.keyBits.aes = 128;
session_key.mode.aes = TPM2_ALG_CFB; session_key.mode.aes = TPM2_ALG_CFB;
TRY_TPM2("Esys_StartAuthSession()", Esys_StartAuthSession(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, nullptr, TRY_TPM2("authenticate with TPM", Esys_StartAuthSession(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, nullptr,
TPM2_SE_HMAC, &session_key, TPM2_ALG_SHA512, &tpm2_session)); TPM2_SE_HMAC, &session_key, TPM2_ALG_SHA512, &tpm2_session));
} }
return func(tpm2_ctx, tpm2_session); return func(tpm2_ctx, tpm2_session);