token.c (3051B)
1 #include <string.h> 2 #include <stdlib.h> 3 #include <errno.h> 4 #include <stdio.h> 5 #include "token.h" 6 #include "util.h" 7 8 const char *digest_names[] = { 9 "SHA1", 10 "SHA224", 11 "SHA256", 12 "SHA384", 13 "SHA512", 14 }; 15 16 static inline uint8_t dehex(const uint8_t *s) 17 { 18 static const uint8_t vals[256] = { 19 ['0'] = 1, ['1'] = 2, ['2'] = 3, ['3'] = 4, ['4'] = 5, 20 ['5'] = 6, ['6'] = 7, ['7'] = 8, ['8'] = 9, ['9'] = 10, 21 ['A'] = 11, ['B'] = 12, ['C'] = 13, ['D'] = 14, ['E'] = 15, 22 ['F'] = 16, ['a'] = 11, ['b'] = 12, ['c'] = 13, ['d'] = 14, 23 ['e'] = 15, ['f'] = 16, 24 }; 25 26 if (!vals[s[0]] || !vals[s[1]]) 27 return 0; 28 29 return (vals[s[0]] - 1) << 4 | 30 (vals[s[1]] - 1); 31 } 32 33 static struct bytes uridecode(struct bytes data, bool getarg) 34 { 35 uint8_t *w = data.data; 36 const uint8_t *r = w; 37 38 while (r < data.end) { 39 if (*r == '%') { 40 if (r + 2 >= data.end) 41 return (struct bytes){ 0 }; 42 *w++ = dehex(++r); 43 if (!w[-1]) 44 return (struct bytes){ 0 }; 45 r += 2; 46 } else if (getarg && *r == '+') { 47 *w++ = ' '; 48 r++; 49 } else 50 *w++ = *r++; 51 } 52 53 return (struct bytes){ data.data, w }; 54 } 55 56 57 static uint8_t get_digest(const char *s, size_t len) 58 { 59 size_t i; 60 61 for (i = 0; i < sizeof(digest_names) / sizeof(*digest_names); i++) 62 if (!strncmp(s, digest_names[i], len) && 63 !digest_names[i][len]) 64 return i; 65 66 fprintf(stderr, "Unknown digest \"%.*s\", assuming %s\n", 67 (int)len, s, digest_names[DIGEST_SHA1]); 68 return DIGEST_SHA1; 69 } 70 71 static uint8_t strtou8(const char *s, char **end) { 72 unsigned long v = strtoul(s, end, 10); 73 if (v > UINT8_MAX || v == 0) { 74 errno = ERANGE; 75 return 0; 76 } 77 return v; 78 } 79 80 struct token token_parse_uri(char *data) { 81 struct token rv = { .t0 = 0, .period = 30, .digits = 6, .digest = DIGEST_SHA1, .valid = false }; 82 char *str; 83 char *i; 84 char *v; 85 86 if (!(str = if_prefix(data, "otpauth://totp/"))) 87 return rv; 88 89 i = strchr(str, '?'); 90 if (!i) 91 return rv; 92 93 rv.desc = uridecode(bytesec(str, i), false); 94 95 if ((v = memchr(rv.desc.data, ':', bytes_len(rv.desc)))) { 96 rv.issuer = bytesec(rv.desc.data, v++); 97 rv.desc = bytesec(v, rv.desc.end); 98 } 99 100 while (*i++) { 101 if ((v = if_prefix(i, "secret="))) { 102 if (bytes_len(rv.key)) 103 croak("Multiple secrets in URI"); 104 i = v + strcspn(v, "&"); 105 rv.key = debase32(bytesec(v, i)); 106 } else if ((v = if_prefix(i, "digits="))) { 107 if (!(rv.digits = strtou8(v, &i))) 108 return rv; 109 } else if ((v = if_prefix(i, "period="))) { 110 if (!(rv.period = strtou8(v, &i))) 111 return rv; 112 } else if ((v = if_prefix(i, "issuer="))) { 113 i = v + strcspn(v, "&"); 114 struct bytes newiss = uridecode(bytesec(v, i), true); 115 if (bytes_len(rv.issuer) && !bytesequal(rv.issuer, newiss)) { 116 errno = EINVAL; 117 return rv; 118 } 119 rv.issuer = newiss; 120 } else if ((v = if_prefix(i, "algorithm="))) { 121 i = v + strcspn(v, "&"); 122 rv.digest = get_digest(v, i - v); 123 } else { 124 i += strcspn(i, "&"); 125 } 126 127 if (!i && *i != '&') { 128 errno = EINVAL; 129 return rv; 130 } 131 } 132 133 if (bytes_len(rv.key) && bytes_len(rv.desc)) 134 rv.valid = true; 135 else 136 errno = EINVAL; 137 138 return rv; 139 }