diff options
Diffstat (limited to 'src/shared/recovery-key.c')
-rw-r--r-- | src/shared/recovery-key.c | 109 |
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; +} |