summaryrefslogtreecommitdiffstats
path: root/src/shared/recovery-key.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/recovery-key.c')
-rw-r--r--src/shared/recovery-key.c109
1 files changed, 109 insertions, 0 deletions
diff --git a/src/shared/recovery-key.c b/src/shared/recovery-key.c
new file mode 100644
index 0000000..6a2f4d0
--- /dev/null
+++ b/src/shared/recovery-key.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "memory-util.h"
+#include "random-util.h"
+#include "recovery-key.h"
+
+const char modhex_alphabet[16] = {
+ 'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v'
+};
+
+int decode_modhex_char(char x) {
+
+ for (size_t i = 0; i < ELEMENTSOF(modhex_alphabet); i++)
+ /* Check both upper and lowercase */
+ if (modhex_alphabet[i] == x || (modhex_alphabet[i] - 32) == x)
+ return i;
+
+ return -EINVAL;
+}
+
+int normalize_recovery_key(const char *password, char **ret) {
+ _cleanup_(erase_and_freep) char *mangled = NULL;
+ size_t l;
+
+ assert(password);
+ assert(ret);
+
+ l = strlen(password);
+ if (!IN_SET(l,
+ RECOVERY_KEY_MODHEX_RAW_LENGTH*2, /* syntax without dashes */
+ RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1)) /* syntax with dashes */
+ return -EINVAL;
+
+ mangled = new(char, RECOVERY_KEY_MODHEX_FORMATTED_LENGTH);
+ if (!mangled)
+ return -ENOMEM;
+
+ for (size_t i = 0, j = 0; i < RECOVERY_KEY_MODHEX_RAW_LENGTH; i++) {
+ size_t k;
+ int a, b;
+
+ if (l == RECOVERY_KEY_MODHEX_RAW_LENGTH*2)
+ /* Syntax without dashes */
+ k = i * 2;
+ else {
+ /* Syntax with dashes */
+ assert(l == RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1);
+ k = i * 2 + i / 4;
+
+ if (i > 0 && i % 4 == 0 && password[k-1] != '-')
+ return -EINVAL;
+ }
+
+ a = decode_modhex_char(password[k]);
+ if (a < 0)
+ return -EINVAL;
+ b = decode_modhex_char(password[k+1]);
+ if (b < 0)
+ return -EINVAL;
+
+ mangled[j++] = modhex_alphabet[a];
+ mangled[j++] = modhex_alphabet[b];
+
+ if (i % 4 == 3)
+ mangled[j++] = '-';
+ }
+
+ mangled[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] = 0;
+
+ *ret = TAKE_PTR(mangled);
+ return 0;
+}
+
+int make_recovery_key(char **ret) {
+ _cleanup_(erase_and_freep) char *formatted = NULL;
+ _cleanup_(erase_and_freep) uint8_t *key = NULL;
+ size_t j = 0;
+ int r;
+
+ assert(ret);
+
+ key = new(uint8_t, RECOVERY_KEY_MODHEX_RAW_LENGTH);
+ if (!key)
+ return -ENOMEM;
+
+ r = crypto_random_bytes(key, RECOVERY_KEY_MODHEX_RAW_LENGTH);
+ if (r < 0)
+ return r;
+
+ /* Let's now format it as 64 modhex chars, and after each 8 chars insert a dash */
+ formatted = new(char, RECOVERY_KEY_MODHEX_FORMATTED_LENGTH);
+ if (!formatted)
+ return -ENOMEM;
+
+ for (size_t i = 0; i < RECOVERY_KEY_MODHEX_RAW_LENGTH; i++) {
+ formatted[j++] = modhex_alphabet[key[i] >> 4];
+ formatted[j++] = modhex_alphabet[key[i] & 0xF];
+
+ if (i % 4 == 3)
+ formatted[j++] = '-';
+ }
+
+ assert(j == RECOVERY_KEY_MODHEX_FORMATTED_LENGTH);
+ assert(formatted[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] == '-');
+ formatted[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] = 0; /* replace final dash with a NUL */
+
+ *ret = TAKE_PTR(formatted);
+ return 0;
+}