Blob


1 /*
2 * Copyright (c) 2022 Omar Polo <op@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <ctype.h>
18 #include <err.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <unistd.h>
26 #include <openssl/evp.h>
27 #include <openssl/hmac.h>
29 #ifndef __OpenBSD__
30 # define pledge(a, b) (0)
31 #endif
33 #ifndef __dead
34 # define __dead __attribute__((noreturn))
35 #endif
37 #if defined(__FreeBSD__)
38 # include <sys/endian.h>
39 #elif defined(__APPLE__)
40 # include <machine/endian.h>
41 # include <libkern/OSByteOrder.h>
42 # define htobe64(x) OSSwapHostToBigInt64(x)
43 # define be32toh(x) OSSwapBigToHostInt32(x)
44 #else
45 # include <endian.h>
46 #endif
48 static __dead void
49 usage(void)
50 {
51 fprintf(stderr, "usage: %s\n", getprogname());
52 exit(1);
53 }
55 static int
56 b32c(unsigned char c)
57 {
58 if (c >= 'A' && c <= 'Z')
59 return (c - 'A');
60 if (c >= '2' && c <= '7')
61 return (c - '2' + 26);
62 errno = EINVAL;
63 return (-1);
64 }
66 static size_t
67 b32decode(const char *s, char *q, size_t qlen)
68 {
69 int i, val[8];
70 char *t = q;
72 while (*s != '\0') {
73 memset(val, 0, sizeof(val));
74 for (i = 0; i < 8; ++i) {
75 if (*s == '\0')
76 break;
77 if ((val[i] = b32c(*s)) == -1)
78 return (0);
79 s++;
80 }
82 if (qlen < 5) {
83 errno = ENOSPC;
84 return (0);
85 }
86 qlen -= 5;
88 *q++ = (val[0] << 3) | (val[1] >> 2);
89 *q++ = ((val[1] & 0x03) << 6) | (val[2] << 1) | (val[3] >> 4);
90 *q++ = ((val[3] & 0x0F) << 4) | (val[4] >> 1);
91 *q++ = ((val[4] & 0x01) << 7) | (val[5] << 2) | (val[6] >> 3);
92 *q++ = ((val[6] & 0x07) << 5) | val[7];
93 }
95 return (q - t);
96 }
98 int
99 main(int argc, char **argv)
101 char buf[1024];
102 size_t buflen;
103 unsigned char md[EVP_MAX_MD_SIZE];
104 unsigned int mdlen;
105 char *s, *q, *line = NULL;
106 size_t linesize = 0;
107 ssize_t linelen;
108 uint64_t ct;
109 uint32_t hash;
110 uint8_t off;
111 int ch;
113 if (pledge("stdio", NULL) == -1)
114 err(1, "pledge");
116 while ((ch = getopt(argc, argv, "")) != -1) {
117 switch (ch) {
118 default:
119 usage();
122 argc -= optind;
123 argv += optind;
125 if (argc != 0)
126 usage();
128 linelen = getline(&line, &linesize, stdin);
129 if (linelen == -1) {
130 if (ferror(stdin))
131 err(1, "getline");
132 errx(1, "no secret provided");
134 for (s = q = line; *s != '\0'; ++s) {
135 if (isspace((unsigned char)*s)) {
136 linelen--;
137 continue;
139 *q++ = *s;
141 *q = '\0';
142 if (linelen < 1)
143 errx(1, "no secret provided");
145 if ((buflen = b32decode(line, buf, sizeof(buf))) == 0)
146 err(1, "can't base32 decode the secret");
148 ct = htobe64(time(NULL) / 30);
150 HMAC(EVP_sha1(), buf, buflen, (unsigned char *)&ct, sizeof(ct),
151 md, &mdlen);
153 off = md[mdlen - 1] & 0x0F;
155 memcpy(&hash, md + off, sizeof(hash));
156 hash = be32toh(hash);
157 printf("%06d\n", (hash & 0x7FFFFFFF) % 1000000);
159 free(line);
160 return (0);