mirror of
https://git.sr.ht/~nabijaczleweli/tzpfms
synced 2025-04-21 09:47:35 +03:00
Allow for setting passphrases on TPM2 keys. Handle max passphrase lengths
This commit is contained in:
parent
a558ca5b48
commit
201fdf2c0a
@ -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.
|
||||
|
19
src/fd.cpp
19
src/fd.cpp
@ -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();
|
||||
}
|
||||
|
||||
|
34
src/tpm2.cpp
34
src/tpm2.cpp
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user