Unlocking works, too! Let's, as they say, go.

This commit is contained in:
наб 2020-10-17 23:14:08 +02:00
parent f1ce88d4b7
commit 84c1385485
No known key found for this signature in database
GPG Key ID: BCFD0B018D2658F1
9 changed files with 166 additions and 302 deletions

View File

@ -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;
}));

View File

@ -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;
}));

View File

@ -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>

View File

@ -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) {

View File

@ -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__)) \

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);