2 * Sub-authentication Daemon: Temporary Tokens
4 * (c) 2017 Martin Mares <mj@ucw.cz>
8 #include <ucw/base64.h>
9 #include <ucw/string.h>
19 #define TEMP_KEY_SIZE 32
20 #define TEMP_NONCE_SIZE 16
21 #define TEMP_SIGN_SIZE 16 // SHA2-256 HMAC
23 static byte temp_key[TEMP_KEY_SIZE];
25 static void temp_sign(byte *sig_text, char *raw_token)
27 // Sign the nonce with the temp key using SHA2-256 HMAC
29 if (gcry_md_open(&md, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
30 die("gcry_md_open(CGRY_MD_SHA256) failed");
31 if (gcry_md_setkey(md, temp_key, TEMP_KEY_SIZE) != GPG_ERR_NO_ERROR)
32 die("gcry_md_setkey() failed");
34 gcry_md_write(md, raw_token, strlen(raw_token));
36 uint sig_len = base64_encode(sig_text, gcry_md_read(md, 0), TEMP_SIGN_SIZE);
37 // Since the length of the input is fixed, the number of padding characters
38 // at the end is also fixed. Hence we remove them for better readability
39 // of tokens. This is harmless as we never attempt to decode these strings.
40 while (sig_len > 0 && sig_text[sig_len-1] == '=')
42 sig_text[sig_len] = 0;
47 char *temp_generate(const char *zone, const char *login, uint validity, struct mempool *pool)
49 byte nonce[TEMP_NONCE_SIZE];
50 gcry_randomize(nonce, TEMP_NONCE_SIZE, GCRY_STRONG_RANDOM);
52 char nonce_text[2*TEMP_NONCE_SIZE + 1];
53 uint nonce_len = base64_encode(nonce_text, nonce, TEMP_NONCE_SIZE);
54 // See the note on base64 in temp_sign()
55 while (nonce_len > 0 && nonce_text[nonce_len-1] == '=')
57 nonce_text[nonce_len] = 0;
59 unsigned long long expire = time(NULL) + validity;
61 int expire_len = snprintf(expire_text, sizeof(expire_text), "%lld", expire);
62 ASSERT(expire_len < (int) sizeof(expire_text));
64 char *raw_token = mp_multicat(pool, zone, "-", login, "-", nonce_text, "-", expire_text, NULL);
66 char sig_text[2*TEMP_SIGN_SIZE + 1];
67 temp_sign(sig_text, raw_token);
69 return mp_multicat(pool, "t-", nonce_text, "-", expire_text, "-", sig_text, NULL);
72 const char *temp_check(const char *zone, const char *login, const char *token, struct mempool *pool)
74 // Split and check format
76 if (str_sepsplit(mp_strdup(pool, token), '-', fields, 4) != 4)
77 return "Wrong token format";
78 if (strcmp(fields[0], "t"))
79 return "Wrong token format";
82 char *raw_token = mp_multicat(pool, zone, "-", login, "-", fields[1], "-", fields[2], NULL);
83 char sig_text[2*TEMP_SIGN_SIZE + 1];
84 temp_sign(sig_text, raw_token);
85 if (strcmp(sig_text, fields[3]))
86 return "Wrong token signature";
89 unsigned long long expire;
90 if (sscanf(fields[2], "%lld", &expire) != 1)
91 return "Wrong token format";
92 if (expire < (unsigned long long) time(NULL))
93 return "Token expired";
99 const char *temp_shorten(const char *token, struct mempool *pool)
102 if (str_sepsplit(mp_strdup(pool, token), '-', fields, 4) != 4)
104 return mp_printf(pool, "%.8s", fields[1]);
111 int fd = open(temp_key_file, O_RDONLY);
114 int n = read(fd, temp_key, TEMP_KEY_SIZE);
115 if (n != TEMP_KEY_SIZE)
116 die("Cannot read temporary token key from %s", temp_key_file);
118 msg(L_INFO, "Loaded temporary token key from %s", temp_key_file);
123 gcry_randomize(temp_key, TEMP_KEY_SIZE, GCRY_STRONG_RANDOM);
124 msg(L_INFO, "Generated new temporary token key");
128 int fd = open(temp_key_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
130 die("Cannot create %s: %m", temp_key_file);
131 int n = write(fd, temp_key, TEMP_KEY_SIZE);
132 if (n != TEMP_KEY_SIZE)
133 die("Cannot write temporary token key");