mirror of
https://git.sr.ht/~nabijaczleweli/tzpfms
synced 2025-04-15 09:40:32 +03:00
Unlocking works, too! Let's, as they say, go.
This commit is contained in:
parent
f1ce88d4b7
commit
84c1385485
@ -5,22 +5,20 @@
|
||||
// #include <sys/zio_crypt.h>
|
||||
#define WRAPPING_KEY_LEN 32
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <tss2/tss2_common.h>
|
||||
#include <tss2/tss2_esys.h>
|
||||
#include <tss2/tss2_rc.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../fd.hpp"
|
||||
#include "../main.hpp"
|
||||
#include "../parse.hpp"
|
||||
#include "../tpm2.hpp"
|
||||
#include "../zfs.hpp"
|
||||
|
||||
// ./src/swtpm/swtpm socket --server port=2321 --ctrl type=tcp,port=2322 --tpm2
|
||||
|
||||
|
||||
#define THIS_BACKEND "TPM2"
|
||||
|
||||
|
||||
template <class T>
|
||||
struct slice_iter {
|
||||
T * data;
|
||||
@ -54,7 +52,6 @@ int main(int argc, char ** argv) {
|
||||
}
|
||||
|
||||
|
||||
ESYS_CONTEXT * tpm2_ctx{};
|
||||
// https://software.intel.com/content/www/us/en/develop/articles/code-sample-protecting-secret-data-and-keys-using-intel-platform-trust-technology.html
|
||||
// tssstartup
|
||||
// tpm2_createprimary -Q --hierarchy=o --key-context=prim.ctx
|
||||
@ -66,225 +63,68 @@ int main(int argc, char ** argv) {
|
||||
//
|
||||
// tpm2_unseal -Q --object-context=0x81000000
|
||||
|
||||
|
||||
// https://trustedcomputinggroup.org/wp-content/uploads/TSS_ESAPI_v1p00_r05_pubrev.pdf
|
||||
// mainly "3.4. The ESAPI Session" and "3.5. ESAPI Use Model"
|
||||
// https://tpm2-tss.readthedocs.io/en/latest/group___e_s_y_s___c_o_n_t_e_x_t.html
|
||||
fprintf(stderr, "Esys_Initialize() = %s\n", Tss2_RC_Decode(Esys_Initialize(&tpm2_ctx, nullptr, nullptr)));
|
||||
quickscope_wrapper tpm2_ctx_deleter{[&] { Esys_Finalize(&tpm2_ctx); }};
|
||||
|
||||
fprintf(stderr, "Esys_Startup() = %s\n", Tss2_RC_Decode(Esys_Startup(tpm2_ctx, TPM2_SU_CLEAR)));
|
||||
|
||||
|
||||
ESYS_TR tpm2_session;
|
||||
quickscope_wrapper tpm2_session_deleter{
|
||||
[&] { fprintf(stderr, "Esys_FlushContext(tpm2_session) = %s\n", Tss2_RC_Decode(Esys_FlushContext(tpm2_ctx, tpm2_session))); }};
|
||||
|
||||
{
|
||||
// https://github.com/tpm2-software/tpm2-tss/blob/master/test/integration/esys-create-session-auth.int.c#L218
|
||||
TPMT_SYM_DEF session_key{};
|
||||
session_key.algorithm = TPM2_ALG_AES;
|
||||
session_key.keyBits.aes = 128;
|
||||
session_key.mode.aes = TPM2_ALG_CFB;
|
||||
fprintf(stderr, "Esys_StartAuthSession() = %s\n",
|
||||
Tss2_RC_Decode(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)));
|
||||
}
|
||||
|
||||
|
||||
uint8_t wrap_key[WRAPPING_KEY_LEN];
|
||||
{
|
||||
TPM2B_DIGEST * rand{};
|
||||
fprintf(stderr, "Esys_GetRandom() = %s\n",
|
||||
Tss2_RC_Decode(Esys_GetRandom(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, WRAPPING_KEY_LEN, &rand)));
|
||||
quickscope_wrapper rand_deleter{[=] { Esys_Free(rand); }};
|
||||
if(rand->size != WRAPPING_KEY_LEN)
|
||||
fprintf(stderr, "Wrong random size: %d\n", rand->size);
|
||||
memcpy(wrap_key, rand->buffer, WRAPPING_KEY_LEN);
|
||||
}
|
||||
|
||||
ESYS_TR primary_handle = ESYS_TR_NONE;
|
||||
quickscope_wrapper primary_handle_deleter{
|
||||
[&] { fprintf(stderr, "Esys_FlushContext(primary_handle) = %s\n", Tss2_RC_Decode(Esys_FlushContext(tpm2_ctx, primary_handle))); }};
|
||||
|
||||
TPM2B_DATA metadata{};
|
||||
const auto now = time(nullptr);
|
||||
const auto now_tm = localtime(&now);
|
||||
metadata.size = snprintf((char *)metadata.buffer, sizeof(metadata.buffer), "%s %d-%02d-%02dT%02d:%02d:%02d %s", zfs_get_name(dataset), //
|
||||
now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, //
|
||||
TZPFMS_VERSION) +
|
||||
1;
|
||||
metadata.size = metadata.size > sizeof(metadata.buffer) ? sizeof(metadata.buffer) : metadata.size;
|
||||
fprintf(stderr, "%d/%zu: \"%s\"\n", metadata.size, sizeof(metadata.buffer), metadata.buffer);
|
||||
|
||||
{
|
||||
const TPM2B_SENSITIVE_CREATE primary_sens{};
|
||||
|
||||
// Adapted from tpm2-tss-3.0.1/test/integration/esys-create-primary-hmac.int.c
|
||||
TPM2B_PUBLIC pub{};
|
||||
pub.publicArea.type = TPM2_ALG_RSA;
|
||||
pub.publicArea.nameAlg = TPM2_ALG_SHA1;
|
||||
pub.publicArea.objectAttributes = TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_RESTRICTED | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_FIXEDTPM |
|
||||
TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_SENSITIVEDATAORIGIN;
|
||||
pub.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM2_ALG_AES;
|
||||
pub.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128;
|
||||
pub.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM2_ALG_CFB;
|
||||
pub.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL;
|
||||
pub.publicArea.parameters.rsaDetail.keyBits = 2048;
|
||||
pub.publicArea.parameters.rsaDetail.exponent = 0;
|
||||
|
||||
const TPML_PCR_SELECTION pcrs{};
|
||||
|
||||
TPM2B_PUBLIC * public_ret{};
|
||||
TPM2B_CREATION_DATA * creation_data{};
|
||||
TPM2B_DIGEST * creation_hash{};
|
||||
TPMT_TK_CREATION * creation_ticket{};
|
||||
fprintf(stderr, "Esys_CreatePrimary() = %s\n",
|
||||
Tss2_RC_Decode(Esys_CreatePrimary(tpm2_ctx, ESYS_TR_RH_OWNER, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &primary_sens, &pub, &metadata, &pcrs,
|
||||
&primary_handle, &public_ret, &creation_data, &creation_hash, &creation_ticket)));
|
||||
quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }};
|
||||
quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }};
|
||||
quickscope_wrapper creation_data_deleter{[=] { Esys_Free(creation_data); }};
|
||||
quickscope_wrapper public_ret_deleter{[=] { Esys_Free(public_ret); }};
|
||||
|
||||
// TSS2_RC Esys_CertifyCreation ( ESYS_CONTEXT * esysContext,
|
||||
// ESYS_TR signHandle,
|
||||
// ESYS_TR objectHandle,
|
||||
// ESYS_TR shandle1,
|
||||
// ESYS_TR shandle2,
|
||||
// ESYS_TR shandle3,
|
||||
// const TPM2B_DATA * qualifyingData,
|
||||
// const TPM2B_DIGEST * creationHash,
|
||||
// const TPMT_SIG_SCHEME * inScheme,
|
||||
// const TPMT_TK_CREATION * creationTicket,
|
||||
// TPM2B_ATTEST ** certifyInfo,
|
||||
// TPMT_SIGNATURE ** signature
|
||||
// )
|
||||
}
|
||||
|
||||
|
||||
TPM2B_PRIVATE * sealant_private{};
|
||||
TPM2B_PUBLIC * sealant_public{};
|
||||
quickscope_wrapper sealant_public_deleter{[=] { Esys_Free(sealant_public); }};
|
||||
quickscope_wrapper sealant_private_deleter{[=] { Esys_Free(sealant_private); }};
|
||||
|
||||
/// This is the object with the actual sealed data in it
|
||||
{
|
||||
TPM2B_SENSITIVE_CREATE secret_sens{};
|
||||
secret_sens.sensitive.data.size = sizeof(wrap_key);
|
||||
memcpy(secret_sens.sensitive.data.buffer, wrap_key, secret_sens.sensitive.data.size);
|
||||
|
||||
// Same args as tpm2-tools' tpm2_create(1)
|
||||
TPM2B_PUBLIC pub{};
|
||||
pub.publicArea.type = TPM2_ALG_KEYEDHASH;
|
||||
pub.publicArea.nameAlg = TPM2_ALG_SHA256;
|
||||
pub.publicArea.objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_USERWITHAUTH;
|
||||
pub.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL;
|
||||
|
||||
const TPML_PCR_SELECTION pcrs{};
|
||||
|
||||
TPM2B_CREATION_DATA * creation_data{};
|
||||
TPM2B_DIGEST * creation_hash{};
|
||||
TPMT_TK_CREATION * creation_ticket{};
|
||||
fprintf(stderr, "Esys_Create() = %s\n",
|
||||
Tss2_RC_Decode(Esys_Create(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &secret_sens, &pub, &metadata, &pcrs,
|
||||
&sealant_private, &sealant_public, &creation_data, &creation_hash, &creation_ticket)));
|
||||
quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }};
|
||||
quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }};
|
||||
quickscope_wrapper creation_data_deleter{[=] { Esys_Free(creation_data); }};
|
||||
}
|
||||
|
||||
ESYS_TR sealed_handle = ESYS_TR_NONE;
|
||||
quickscope_wrapper sealed_handle_deleter{
|
||||
[&] { fprintf(stderr, "Esys_FlushContext(sealed_handle) = %s\n", Tss2_RC_Decode(Esys_FlushContext(tpm2_ctx, sealed_handle))); }};
|
||||
|
||||
/// Load the keyedhash/sealed object into a transient handle
|
||||
{
|
||||
fprintf(
|
||||
stderr, "Esys_Load() = %s\n",
|
||||
Tss2_RC_Decode(Esys_Load(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, sealant_private, sealant_public, &sealed_handle)));
|
||||
}
|
||||
|
||||
TPMI_DH_PERSISTENT persistent_handle{};
|
||||
|
||||
/// Find lowest unused persistent handle
|
||||
{
|
||||
TPMS_CAPABILITY_DATA * cap; // TODO: check for more data?
|
||||
fprintf(stderr, "Esys_GetCapability() = %s\n",
|
||||
Tss2_RC_Decode(Esys_GetCapability(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST,
|
||||
TPM2_MAX_CAP_HANDLES, nullptr, &cap)));
|
||||
quickscope_wrapper cap_deleter{[=] { Esys_Free(cap); }};
|
||||
|
||||
switch(cap->data.handles.count) {
|
||||
case 0:
|
||||
persistent_handle = TPM2_PERSISTENT_FIRST;
|
||||
break;
|
||||
case TPM2_MAX_CAP_HANDLES:
|
||||
break;
|
||||
default:
|
||||
for(TPM2_HC i = TPM2_PERSISTENT_FIRST; i <= TPM2_PERSISTENT_LAST && i <= TPM2_PLATFORM_PERSISTENT; ++i)
|
||||
if(std::find(std::begin(cap->data.handles.handle), std::end(cap->data.handles.handle), i) == std::end(cap->data.handles.handle)) {
|
||||
persistent_handle = i;
|
||||
break;
|
||||
}
|
||||
TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
|
||||
char *previous_backend{}, *previous_handle_s{};
|
||||
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_BACKEND, previous_backend));
|
||||
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_KEY, previous_handle_s));
|
||||
if(!!previous_backend ^ !!previous_handle_s)
|
||||
fprintf(stderr, "Inconsistent tzpfms metadata for %s: back-end is %s, but handle is %s?\n", zfs_get_name(dataset), previous_backend,
|
||||
previous_handle_s);
|
||||
else if(previous_backend && previous_handle_s) {
|
||||
if(strcmp(previous_backend, THIS_BACKEND))
|
||||
fprintf(stderr,
|
||||
"Dataset %s was encrypted with tzpfms back-end %s before, but we are %s. You will have to free handle %s for back-end %s manually!\n",
|
||||
zfs_get_name(dataset), previous_backend, THIS_BACKEND, previous_handle_s, previous_backend);
|
||||
else {
|
||||
TPMI_DH_PERSISTENT previous_handle{};
|
||||
if(parse_int(previous_handle_s, previous_handle))
|
||||
fprintf(stderr, "Couldn't parse previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c %s\" or equivalent!\n",
|
||||
zfs_get_name(dataset), previous_handle_s);
|
||||
else {
|
||||
if(tpm2_free_persistent(tpm2_ctx, tpm2_session, previous_handle))
|
||||
fprintf(stderr,
|
||||
"Couldn't free previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n",
|
||||
zfs_get_name(dataset), previous_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!persistent_handle)
|
||||
fprintf(stderr, "All %zu persistent handles allocated! We're fucked!\n", TPM2_MAX_CAP_HANDLES);
|
||||
}
|
||||
uint8_t wrap_key[WRAPPING_KEY_LEN];
|
||||
TPMI_DH_PERSISTENT persistent_handle{};
|
||||
|
||||
fprintf(stderr, "0x%x\n", persistent_handle);
|
||||
TRY_MAIN(tpm2_generate_rand(tpm2_ctx, wrap_key, sizeof(wrap_key)));
|
||||
if(backup)
|
||||
TRY_MAIN(write_exact(backup, wrap_key, sizeof(wrap_key), 0400));
|
||||
|
||||
/// Persist the loaded handle in the TPM — this will make it available as $persistent_handle until we explicitly evict it back to the transient store
|
||||
{
|
||||
// Can't be flushed (tpm:parameter(1):value is out of range or is not correct for the context), plus, that's kinda the point
|
||||
ESYS_TR new_handle;
|
||||
fprintf(stderr, "Esys_EvictControl() = %s\n",
|
||||
Tss2_RC_Decode(
|
||||
Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, sealed_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, persistent_handle, &new_handle)));
|
||||
}
|
||||
TRY_MAIN(tpm2_seal(tpm2_ctx, tpm2_session, persistent_handle, tpm2_creation_metadata(zfs_get_name(dataset)), wrap_key, sizeof(wrap_key)));
|
||||
bool ok = false; // Try to free the persistent handle if we're unsuccessful in actually using it later on
|
||||
quickscope_wrapper persistent_clearer{[&] {
|
||||
if(!ok && tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle))
|
||||
fprintf(stderr, "Couldn't free persistent handle. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n", persistent_handle);
|
||||
}};
|
||||
|
||||
/// Free the persistent slot
|
||||
/*{
|
||||
// Neither of these are flushable (tpm:parameter(1):value is out of range or is not correct for the context)
|
||||
ESYS_TR pandle;
|
||||
fprintf(stderr, "Esys_TR_FromTPMPublic() = %s\n",
|
||||
Tss2_RC_Decode(Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle - 1, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle)));
|
||||
TRY_MAIN(set_key_props(dataset, THIS_BACKEND, persistent_handle));
|
||||
|
||||
ESYS_TR new_handle;
|
||||
fprintf(stderr, "Esys_EvictControl() = %s\n",
|
||||
Tss2_RC_Decode(Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, 0, &new_handle)));
|
||||
}*/
|
||||
/// zfs_crypto_rewrap() with "prompt" reads from stdin, but not if it's a TTY;
|
||||
/// this user-proofs the set-up, and means we don't have to touch the filesysten:
|
||||
/// instead, get an FD, write the raw key data there, dup() it onto stdin,
|
||||
/// let libzfs read it, then restore stdin
|
||||
|
||||
int key_fd;
|
||||
TRY_MAIN(filled_fd(key_fd, wrap_key, WRAPPING_KEY_LEN));
|
||||
quickscope_wrapper key_fd_deleter{[=] { close(key_fd); }};
|
||||
|
||||
|
||||
// uint8_t wrap_key[WRAPPING_KEY_LEN];
|
||||
// TRY_MAIN(read_exact("/dev/random", wrap_key, sizeof(wrap_key)));
|
||||
if(backup)
|
||||
TRY_MAIN(write_exact(backup, wrap_key, sizeof(wrap_key), 0400));
|
||||
TRY_MAIN(with_stdin_at(key_fd, [&] {
|
||||
if(zfs_crypto_rewrap(dataset, TRY_PTR("get rewrap args", rewrap_args()), B_FALSE))
|
||||
return __LINE__; // Error printed by libzfs
|
||||
else
|
||||
printf("Key for %s changed\n", zfs_get_name(dataset));
|
||||
|
||||
char persistent_handle_s[2 + sizeof(persistent_handle) * 2 + 1];
|
||||
if(auto written = snprintf(persistent_handle_s, sizeof(persistent_handle_s), "0x%02X", persistent_handle);
|
||||
written < 0 || written >= static_cast<int>(sizeof(persistent_handle_s)))
|
||||
fprintf(stderr, "oops, truncated: %d/%zu\n", written, sizeof(persistent_handle_s));
|
||||
TRY_MAIN(zfs_prop_set(dataset, "xyz.nabijaczleweli:tzpfms.key", persistent_handle_s));
|
||||
|
||||
|
||||
/// zfs_crypto_rewrap() with "prompt" reads from stdin, but not if it's a TTY;
|
||||
/// this user-proofs the set-up, and means we don't have to touch the filesysten:
|
||||
/// instead, get an FD, write the raw key data there, dup() it onto stdin,
|
||||
/// let libzfs read it, then restore stdin
|
||||
|
||||
int key_fd;
|
||||
TRY_MAIN(filled_fd(key_fd, wrap_key, WRAPPING_KEY_LEN));
|
||||
quickscope_wrapper key_fd_deleter{[=] { close(key_fd); }};
|
||||
|
||||
|
||||
TRY_MAIN(with_stdin_at(key_fd, [&] {
|
||||
if(zfs_crypto_rewrap(dataset, TRY_PTR("get rewrap args", rewrap_args()), B_FALSE))
|
||||
return __LINE__; // Error printed by libzfs
|
||||
else
|
||||
printf("Key for %s changed\n", zfs_get_name(dataset));
|
||||
return 0;
|
||||
}));
|
||||
|
||||
ok = true;
|
||||
return 0;
|
||||
}));
|
||||
|
||||
|
@ -2,59 +2,19 @@
|
||||
|
||||
|
||||
#include <libzfs.h>
|
||||
// #include <sys/zio_crypt.h>
|
||||
#define WRAPPING_KEY_LEN 32
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../fd.hpp"
|
||||
#include "../main.hpp"
|
||||
#include "../parse.hpp"
|
||||
#include "../tpm2.hpp"
|
||||
#include "../zfs.hpp"
|
||||
|
||||
|
||||
static int hex_nibble(char c) {
|
||||
switch(c) {
|
||||
case '0':
|
||||
return 0x0;
|
||||
case '1':
|
||||
return 0x1;
|
||||
case '2':
|
||||
return 0x2;
|
||||
case '3':
|
||||
return 0x3;
|
||||
case '4':
|
||||
return 0x4;
|
||||
case '5':
|
||||
return 0x5;
|
||||
case '6':
|
||||
return 0x6;
|
||||
case '7':
|
||||
return 0x7;
|
||||
case '8':
|
||||
return 0x8;
|
||||
case '9':
|
||||
return 0x9;
|
||||
case 'A':
|
||||
case 'a':
|
||||
return 0xA;
|
||||
case 'B':
|
||||
case 'b':
|
||||
return 0xB;
|
||||
case 'C':
|
||||
case 'c':
|
||||
return 0xC;
|
||||
case 'D':
|
||||
case 'd':
|
||||
return 0xD;
|
||||
case 'E':
|
||||
case 'e':
|
||||
return 0xE;
|
||||
case 'F':
|
||||
case 'f':
|
||||
return 0xF;
|
||||
default:
|
||||
fprintf(stderr, "Character %c (0x%02X) not hex?\n", c, static_cast<int>(c));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#define THIS_BACKEND "TPM2"
|
||||
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
@ -62,23 +22,41 @@ int main(int argc, char ** argv) {
|
||||
return do_main(
|
||||
argc, argv, "n", [&](auto) { noop = B_TRUE; },
|
||||
[&](auto dataset) {
|
||||
char * stored_key_s{};
|
||||
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), "xyz.nabijaczleweli:tzpfms.key", stored_key_s));
|
||||
errno = EEXIST;
|
||||
TRY_PTR("find encryption key prop", stored_key_s);
|
||||
char *backend{}, *handle_s{};
|
||||
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_BACKEND, backend));
|
||||
fprintf(stderr, "backend=%s\n", backend);
|
||||
|
||||
auto stored_key_len = strlen(stored_key_s) / 2;
|
||||
auto stored_key = static_cast<uint8_t *>(TRY_PTR("stored_key", alloca(stored_key_len)));
|
||||
{
|
||||
auto cur = stored_key_s;
|
||||
for(auto kcur = stored_key; kcur < stored_key + stored_key_len; ++kcur) {
|
||||
*kcur = (hex_nibble(cur[0]) << 4) | hex_nibble(cur[1]);
|
||||
cur += 2;
|
||||
}
|
||||
if(!backend) {
|
||||
fprintf(stderr, "Dataset %s not encrypted with tzpfms!\n", zfs_get_name(dataset));
|
||||
return __LINE__;
|
||||
}
|
||||
if(strcmp(backend, THIS_BACKEND)) {
|
||||
fprintf(stderr, "Dataset %s encrypted with tzpfms back-end %s, but we are %s.\n", zfs_get_name(dataset), backend, THIS_BACKEND);
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_KEY, handle_s));
|
||||
if(!handle_s) {
|
||||
fprintf(stderr, "Dataset %s missing key data.\n", zfs_get_name(dataset));
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
TPMI_DH_PERSISTENT handle{};
|
||||
if(parse_int(handle_s, handle)) {
|
||||
fprintf(stderr, "Dataset %s's handle %s not valid.\n", zfs_get_name(dataset), handle_s);
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
|
||||
uint8_t wrap_key[WRAPPING_KEY_LEN];
|
||||
TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
|
||||
TRY_MAIN(tpm2_unseal(tpm2_ctx, tpm2_session, handle, wrap_key, sizeof(wrap_key)));
|
||||
return 0;
|
||||
}));
|
||||
|
||||
|
||||
int key_fd;
|
||||
TRY_MAIN(filled_fd(key_fd, (void *)stored_key, stored_key_len));
|
||||
TRY_MAIN(filled_fd(key_fd, (void *)wrap_key, sizeof(wrap_key)));
|
||||
quickscope_wrapper key_fd_deleter{[=] { close(key_fd); }};
|
||||
|
||||
|
||||
@ -86,7 +64,7 @@ int main(int argc, char ** argv) {
|
||||
if(zfs_crypto_load_key(dataset, noop, nullptr))
|
||||
return __LINE__; // Error printed by libzfs
|
||||
else
|
||||
printf("Key for %s loaded\n", zfs_get_name(dataset));
|
||||
printf("Key for %s %s\n", zfs_get_name(dataset), noop ? "OK" : "loaded");
|
||||
|
||||
return 0;
|
||||
}));
|
||||
|
@ -9,17 +9,17 @@
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define TRY_GENERIC(what, cond_pre, cond_post, err_src, err_ret, ...) \
|
||||
({ \
|
||||
auto _try_ret = (__VA_ARGS__); \
|
||||
if(cond_pre _try_ret cond_post) { \
|
||||
if constexpr(what != nullptr) \
|
||||
fprintf(stderr, "Couldn't %s: %s\n", static_cast<const char *>(what), strerror(err_src)); \
|
||||
return err_ret; \
|
||||
} \
|
||||
_try_ret; \
|
||||
#define TRY_GENERIC(what, cond_pre, cond_post, err_src, err_ret, strerr, ...) \
|
||||
({ \
|
||||
auto _try_ret = (__VA_ARGS__); \
|
||||
if(cond_pre _try_ret cond_post) { \
|
||||
if constexpr(what != nullptr) \
|
||||
fprintf(stderr, "Couldn't %s: %s\n", static_cast<const char *>(what), strerr(err_src)); \
|
||||
return err_ret; \
|
||||
} \
|
||||
_try_ret; \
|
||||
})
|
||||
#define TRY(what, ...) TRY_GENERIC(what, , == -1, errno, __LINE__, __VA_ARGS__)
|
||||
#define TRY(what, ...) TRY_GENERIC(what, , == -1, errno, __LINE__, strerror, __VA_ARGS__)
|
||||
|
||||
|
||||
template <class F>
|
||||
|
@ -44,7 +44,7 @@ int read_exact(const char * path, void * data, size_t len) {
|
||||
|
||||
|
||||
int write_exact(const char * path, const void * data, size_t len, mode_t mode) {
|
||||
auto outfd = TRY("open output file", open(path, O_WRONLY | O_CREAT | O_EXCL, mode));
|
||||
auto outfd = TRY("create output file", open(path, O_WRONLY | O_CREAT | O_EXCL, mode));
|
||||
quickscope_wrapper infd_deleter{[=] { close(outfd); }};
|
||||
|
||||
while(len) {
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define TRY_PTR(what, ...) TRY_GENERIC(what, !, , errno, __LINE__, __VA_ARGS__)
|
||||
#define TRY_PTR(what, ...) TRY_GENERIC(what, !, , errno, __LINE__, strerror, __VA_ARGS__)
|
||||
#define TRY_MAIN(...) \
|
||||
do { \
|
||||
if(auto _try_ret = (__VA_ARGS__)) \
|
||||
|
33
src/tpm2.cpp
33
src/tpm2.cpp
@ -26,7 +26,7 @@ TPM2B_DATA tpm2_creation_metadata(const char * dataset_name) {
|
||||
|
||||
int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length) {
|
||||
TPM2B_DIGEST * rand{};
|
||||
TRY_TPM2("Esys_GetRandom()", Esys_GetRandom(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, length, &rand));
|
||||
TRY_TPM2("get random data from TPM", Esys_GetRandom(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, length, &rand));
|
||||
quickscope_wrapper rand_deleter{[=] { Esys_Free(rand); }};
|
||||
|
||||
if(rand->size != length) {
|
||||
@ -41,7 +41,7 @@ int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length) {
|
||||
|
||||
static int tpm2_find_unused_persistent_non_platform(ESYS_CONTEXT * tpm2_ctx, TPMI_DH_PERSISTENT & persistent_handle) {
|
||||
TPMS_CAPABILITY_DATA * cap; // TODO: check for more data?
|
||||
TRY_TPM2("Esys_GetCapability()", Esys_GetCapability(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST,
|
||||
TRY_TPM2("Read used persistent TPM handles", Esys_GetCapability(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST,
|
||||
TPM2_MAX_CAP_HANDLES, nullptr, &cap));
|
||||
quickscope_wrapper cap_deleter{[=] { Esys_Free(cap); }};
|
||||
|
||||
@ -94,7 +94,7 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
|
||||
TPM2B_CREATION_DATA * creation_data{};
|
||||
TPM2B_DIGEST * creation_hash{};
|
||||
TPMT_TK_CREATION * creation_ticket{};
|
||||
TRY_TPM2("Esys_CreatePrimary()", Esys_CreatePrimary(tpm2_ctx, ESYS_TR_RH_OWNER, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &primary_sens, &pub, &metadata,
|
||||
TRY_TPM2("create primary encryption key", Esys_CreatePrimary(tpm2_ctx, ESYS_TR_RH_OWNER, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &primary_sens, &pub, &metadata,
|
||||
&pcrs, &primary_handle, &public_ret, &creation_data, &creation_hash, &creation_ticket));
|
||||
quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }};
|
||||
quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }};
|
||||
@ -140,7 +140,7 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
|
||||
TPM2B_CREATION_DATA * creation_data{};
|
||||
TPM2B_DIGEST * creation_hash{};
|
||||
TPMT_TK_CREATION * creation_ticket{};
|
||||
TRY_TPM2("Esys_Create()", Esys_Create(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &secret_sens, &pub, &metadata, &pcrs,
|
||||
TRY_TPM2("create key seal", Esys_Create(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &secret_sens, &pub, &metadata, &pcrs,
|
||||
&sealant_private, &sealant_public, &creation_data, &creation_hash, &creation_ticket));
|
||||
quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }};
|
||||
quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }};
|
||||
@ -151,7 +151,7 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
|
||||
quickscope_wrapper sealed_handle_deleter{[&] { Esys_FlushContext(tpm2_ctx, sealed_handle); }};
|
||||
|
||||
/// Load the sealed object (keyedhash) into a transient handle
|
||||
TRY_TPM2("Esys_Load()", Esys_Load(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, sealant_private, sealant_public, &sealed_handle));
|
||||
TRY_TPM2("load key seal", Esys_Load(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, sealant_private, sealant_public, &sealed_handle));
|
||||
|
||||
/// Find lowest unused persistent handle
|
||||
TRY_MAIN(tpm2_find_unused_persistent_non_platform(tpm2_ctx, persistent_handle));
|
||||
@ -160,20 +160,37 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT
|
||||
{
|
||||
// Can't be flushed (tpm:parameter(1):value is out of range or is not correct for the context), plus, that's kinda the point
|
||||
ESYS_TR new_handle;
|
||||
TRY_TPM2("Esys_EvictControl()",
|
||||
TRY_TPM2("persist key seal",
|
||||
Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, sealed_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, persistent_handle, &new_handle));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_unseal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle, void * data, size_t data_len) {
|
||||
// Entirely fake and not flushable (tpm:parameter(1):value is out of range or is not correct for the context)
|
||||
ESYS_TR 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{};
|
||||
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); }};
|
||||
|
||||
if(unsealed->size != data_len) {
|
||||
fprintf(stderr, "Unsealed data has wrong length %u, expected %zu!\n", unsealed->size, data_len);
|
||||
return __LINE__;
|
||||
}
|
||||
memcpy(data, unsealed->buffer, data_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_free_persistent(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle) {
|
||||
// Neither of these are flushable (tpm:parameter(1):value is out of range or is not correct for the context)
|
||||
ESYS_TR pandle;
|
||||
TRY_TPM2("Esys_TR_FromTPMPublic()", 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));
|
||||
|
||||
ESYS_TR new_handle;
|
||||
TRY_TPM2("Esys_EvictControl()", Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, 0, &new_handle));
|
||||
TRY_TPM2("unpersist object", Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, 0, &new_handle));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -46,4 +46,5 @@ extern TPM2B_DATA tpm2_creation_metadata(const char * dataset_name);
|
||||
|
||||
extern int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length);
|
||||
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);
|
||||
|
27
src/zfs.cpp
27
src/zfs.cpp
@ -9,7 +9,7 @@
|
||||
|
||||
// Funxion statics pull in libc++'s __cxa_guard_acquire()
|
||||
static nvlist_t * rrargs{};
|
||||
static quickscope_wrapper ret_deleter{[] { nvlist_free(rrargs); }};
|
||||
static quickscope_wrapper rrargs_deleter{[] { nvlist_free(rrargs); }};
|
||||
nvlist_t * rewrap_args() {
|
||||
if(!rrargs)
|
||||
if(auto err =
|
||||
@ -22,8 +22,8 @@ nvlist_t * rewrap_args() {
|
||||
}();
|
||||
err && rrargs) {
|
||||
nvlist_free(rrargs);
|
||||
rrargs = nullptr;
|
||||
errno = err;
|
||||
rrargs = nullptr;
|
||||
errno = err;
|
||||
}
|
||||
|
||||
return rrargs;
|
||||
@ -48,3 +48,24 @@ int lookup_userprop(nvlist_t * from, const char * name, char *& out) {
|
||||
TRY_LOOKUP("look up user property value", nvlist_lookup_string(vs, "value", &out));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int set_key_props(zfs_handle_t * on, const char * backend, uint32_t handle) {
|
||||
char handle_s[2 + sizeof(handle) * 2 + 1];
|
||||
if(auto written = snprintf(handle_s, sizeof(handle_s), "0x%02X", handle); written < 0 || written >= static_cast<int>(sizeof(handle_s))) {
|
||||
fprintf(stderr, "Truncated handle name? %d/%zu\n", written, sizeof(handle_s));
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
|
||||
nvlist_t * props{};
|
||||
quickscope_wrapper props_deleter{[&] { nvlist_free(props); }};
|
||||
|
||||
TRY_NVL("allocate key nvlist", nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
|
||||
TRY_NVL("add back-end to key nvlist", nvlist_add_string(props, PROPNAME_BACKEND, backend));
|
||||
TRY_NVL("add handle to key nvlist", nvlist_add_string(props, PROPNAME_KEY, handle_s));
|
||||
|
||||
TRY("set tzpfms.{backend,key}", zfs_prop_set_list(on, props));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
13
src/zfs.hpp
13
src/zfs.hpp
@ -4,14 +4,18 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <libzfs.h>
|
||||
#include <sys/nvpair.h>
|
||||
// #include <libzfs.h>
|
||||
// #include <sys/fs/zfs.h>
|
||||
//// #include <sys/zio_crypt.h>
|
||||
// #define WRAPPING_KEY_LEN 32
|
||||
|
||||
|
||||
#define TRY_NVL(what, ...) TRY_GENERIC(what, , , _try_ret, _try_ret, __VA_ARGS__)
|
||||
#define TRY_NVL(what, ...) TRY_GENERIC(what, , , _try_ret, _try_ret, strerror, __VA_ARGS__)
|
||||
|
||||
|
||||
#define PROPNAME_BACKEND "xyz.nabijaczleweli:tzpfms.backend"
|
||||
#define PROPNAME_KEY "xyz.nabijaczleweli:tzpfms.key"
|
||||
|
||||
|
||||
/// Static nvlist with {keyformat=raw, keylocation=prompt}
|
||||
@ -20,4 +24,7 @@ extern nvlist_t * rewrap_args();
|
||||
/// Extract user property name from ZFS property list from to out.
|
||||
///
|
||||
/// Returns success but does not touch out on not found.
|
||||
extern int lookup_userprop(nvlist_t * from, const char * name, char*&out);
|
||||
extern int lookup_userprop(nvlist_t * from, const char * name, char *& out);
|
||||
|
||||
/// Set required decoding props on the dataset
|
||||
extern int set_key_props(zfs_handle_t * on, const char * backend, uint32_t handle);
|
||||
|
Loading…
x
Reference in New Issue
Block a user