File allow_specify_custom_secret.patch of Package tpm2-totp
From 097679eb2f1ac8dde37c7d94e51ebedfdd81d630 Mon Sep 17 00:00:00 2001
From: Dominik Heidler <dheidler@suse.de>
Date: Wed, 26 Jun 2024 22:15:52 +0200
Subject: [PATCH] Allow to specify custom secrets
Signed-off-by: Dominik Heidler <dheidler@suse.de>
---
man/tpm2-totp.1.md | 5 +++-
src/libtpm2-totp.c | 37 ++++++++++++++++-------------
src/tpm2-totp.c | 59 +++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 77 insertions(+), 24 deletions(-)
diff --git a/man/tpm2-totp.1.md b/man/tpm2-totp.1.md
index ad7c20c..833fcb2 100644
--- a/man/tpm2-totp.1.md
+++ b/man/tpm2-totp.1.md
@@ -24,7 +24,7 @@ options.
* `init`:
Generate and store a new TOTP secret.
- Possible options: `-b`, `-l`, `-N`, `-p`, `-P`, `-T`
+ Possible options: `-b`, `-l`, `-N`, `-p`, `-P`, `-T`, `-s`
* `show`:
Calculate and show a TOTP value.
@@ -53,6 +53,9 @@ options.
* `-l`, `--label`:
Label to use for display in the TOTP authenticator app (default: TPM2-TOTP)
+ * `-s`, `--secret`:
+ Specify a custom secret instead of randomly generationg one
+
* `-N <nvindex>`, `--nvindex <nvindex>`:
TPM NV index to store data (default: 0x018094AF)
diff --git a/src/libtpm2-totp.c b/src/libtpm2-totp.c
index 636d46f..c0ffa59 100644
--- a/src/libtpm2-totp.c
+++ b/src/libtpm2-totp.c
@@ -167,28 +167,30 @@ tpm2totp_generateKey(uint32_t pcrs, uint32_t banks, const char *password,
pcrsel.pcrSelections[i].pcrSelect[2] = pcrs >>16 & 0xff;
}
- *secret_size = 0;
- *secret = malloc(SECRETLEN);
- if (!*secret) {
- return -1;
- }
-
rc = Esys_Initialize(&ctx, tcti_context, NULL);
chkrc(rc, goto error);
rc = Esys_Startup(ctx, TPM2_SU_CLEAR);
if (rc != TPM2_RC_INITIALIZE) chkrc(rc, goto error);
- while (*secret_size < SECRETLEN) {
- dbg("Calling Esys_GetRandom for %zu bytes", SECRETLEN - *secret_size);
- rc = Esys_GetRandom(ctx,
- ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
- SECRETLEN - *secret_size, &t);
- chkrc(rc, goto error);
-
- memcpy(&(*secret)[*secret_size], &t->buffer[0], t->size);
- *secret_size += t->size;
- free(t);
+ if (!*secret) {
+ // generate random secret if not already set from caller
+ *secret_size = 0;
+ *secret = malloc(SECRETLEN);
+ if (!*secret) {
+ return -1;
+ }
+ while (*secret_size < SECRETLEN) {
+ dbg("Calling Esys_GetRandom for %zu bytes", SECRETLEN - *secret_size);
+ rc = Esys_GetRandom(ctx,
+ ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
+ SECRETLEN - *secret_size, &t);
+ chkrc(rc, goto error);
+
+ memcpy(&(*secret)[*secret_size], &t->buffer[0], t->size);
+ *secret_size += t->size;
+ free(t);
+ }
}
dbg("Calling Esys_CreatePrimary");
@@ -304,7 +306,8 @@ tpm2totp_generateKey(uint32_t pcrs, uint32_t banks, const char *password,
free(keyPublicSeal);
free(keyPrivateSeal);
Esys_Finalize(&ctx);
- free(*secret);
+ if (*secret)
+ free(*secret);
*secret = NULL;
*secret_size = 0;
return (rc)? (int)rc : -1;
diff --git a/src/tpm2-totp.c b/src/tpm2-totp.c
index 1b8eaed..cae38e1 100644
--- a/src/tpm2-totp.c
+++ b/src/tpm2-totp.c
@@ -37,6 +37,7 @@ char *help =
" -h, --help print help\n"
" -b, --banks Selected PCR banks (default: SHA1,SHA256)\n"
" -l, --label Label to use for display in the TOTP authenticator app (default: TPM2-TOTP)\n"
+ " -s, --secret Specify a custom secret instead of randomly generationg one\n"
" -N, --nvindex TPM NV index to store data (default: 0x018094AF)\n"
" -P, --password Password for recovery/resealing (default: None). Read from stdin if '-' (recommended).\n"
" -p, --pcrs Selected PCR registers (default: 0,2,4,6)\n"
@@ -45,7 +46,7 @@ char *help =
" -v, --verbose print verbose messages\n"
"\n";
-static const char *optstr = "hb:N:P:p:tT:l:v";
+static const char *optstr = "hb:N:P:p:tT:l:s:v";
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
@@ -56,6 +57,7 @@ static const struct option long_options[] = {
{"time", no_argument, 0, 't'},
{"tcti", required_argument, 0, 'T'},
{"label", required_argument, 0, 'l'},
+ {"secret", required_argument, 0, 's'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0 }
};
@@ -69,6 +71,7 @@ static struct opt {
int time;
char *tcti;
char *label;
+ char *secret;
int verbose;
} opt;
@@ -182,6 +185,7 @@ parse_opts(int argc, char **argv)
opt.time = 0;
opt.verbose = 0;
opt.label = "TPM2-TOTP";
+ opt.secret = 0;
/* parse the options */
int c;
@@ -251,6 +255,9 @@ parse_opts(int argc, char **argv)
case 'l':
opt.label = optarg;
break;
+ case 's':
+ opt.secret = optarg;
+ break;
case 'v':
opt.verbose = 1;
break;
@@ -294,7 +301,7 @@ parse_opts(int argc, char **argv)
static char *
base32enc(const uint8_t *in, size_t in_size) {
- static unsigned char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ static unsigned char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
size_t i = 0, j = 0;
size_t out_size = ((in_size + 4) / 5) * 8;
@@ -326,7 +333,44 @@ base32enc(const uint8_t *in, size_t in_size) {
r[i++] = '=';
}
r[i] = 0;
- return (char *)r;
+ return (char *)r;
+}
+
+int
+base32dec(const char *in, uint8_t **out, size_t *out_size) {
+ size_t in_len = strlen(in);
+ if (in_len % 8 != 0) {
+ fprintf(stderr, "Error: Invalid base32 length.\n");
+ return -1;
+ }
+ *out = malloc((in_len * 5) / 8);
+ *out = memset(*out, 0, (in_len * 5) / 8);
+ if (*out == NULL) return -1;
+ size_t i=0, j=0;
+ uint64_t data_block = 0;
+ uint8_t padding_bits = 0;
+ *out_size = 0;
+ // read 40 bit block
+ for (i = 0; in[i] != '\0'; i++) {
+ char c = in[i];
+ uint64_t v = 0;
+ if (c >= 'A' && c <= 'Z') v = c - 'A';
+ if (c >= '2' && c <= '7') v = c - '2' + 26;
+ if (c == '=') padding_bits += 5;
+ data_block |= ((v<<(64-5)) >> (5 * (i%8)));
+
+ if (i%8 == 7) {
+ // we got a full block
+ for (j = *out_size; j<*out_size+5; j++) {
+ if ((j%5+1)*8 + padding_bits > 5*8)
+ break;
+ (*out)[j] = (data_block >> (64-8*(j%5+1))) & 0xFF;
+ }
+ *out_size = j;
+ data_block = 0;
+ }
+ }
+ return 0;
}
char *
@@ -406,7 +450,7 @@ int
main(int argc, char **argv)
{
int rc;
- uint8_t *secret, *keyBlob, *newBlob;
+ uint8_t *secret = NULL, *keyBlob, *newBlob;
size_t secret_size, keyBlob_size, newBlob_size;
uint64_t totp;
time_t now;
@@ -426,6 +470,9 @@ main(int argc, char **argv)
switch(opt.cmd) {
case CMD_INIT:
+ if (opt.secret) {
+ if (base32dec(opt.secret, &secret, &secret_size) != 0) goto err;
+ }
rc = tpm2totp_generateKey(opt.pcrs, opt.banks, opt.password, tcti_context,
&secret, &secret_size,
@@ -436,7 +483,7 @@ main(int argc, char **argv)
free(keyBlob);
chkrc(rc, goto err);
- if (tpm2totp_qrencode(opt.label, secret, secret_size) < 0)
+ if (tpm2totp_qrencode(opt.label, secret, secret_size) < 0)
goto err;
break;
@@ -483,7 +530,7 @@ main(int argc, char **argv)
free(keyBlob);
chkrc(rc, goto err);
- if (tpm2totp_qrencode(opt.label, secret, secret_size) < 0)
+ if (tpm2totp_qrencode(opt.label, secret, secret_size) < 0)
goto err;
break;