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;
openSUSE Build Service is sponsored by