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

@ -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.
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`:
@ -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)).
`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.
Finally, the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=raw dataset** is performed with the new key.

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

@ -6,6 +6,7 @@
#include "common.hpp"
#include <unistd.h>
#include <stdint.h>
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
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);
/// Prompt for passphrase for whom the user knows, up to max_len bytes
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);

@ -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.
template <class F>
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
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());
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();
}

@ -2,6 +2,7 @@
#include "tpm2.hpp"
#include "fd.hpp"
#include "main.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); }};
{
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
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;
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)
TPM2B_PUBLIC pub{};
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));
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); }};
{
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) {
fprintf(stderr, "Unsealed data has wrong length %u, expected %zu!\n", unsealed->size, data_len);

@ -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
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); }};
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;
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.keyBits.aes = 128;
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,
TPM2_SE_HMAC, &session_key, TPM2_ALG_SHA512, &tpm2_session));
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));
}
return func(tpm2_ctx, tpm2_session);