Blame


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