Commit | Line | Data |
---|---|---|
410c8acf | 1 | /* -*-c-*- |
2 | * | |
410c8acf | 3 | * Various handy server-only utilities |
4 | * | |
5 | * (c) 2001 Straylight/Edgeware | |
6 | */ | |
7 | ||
e04c2d50 | 8 | /*----- Licensing notice --------------------------------------------------* |
410c8acf | 9 | * |
10 | * This file is part of Trivial IP Encryption (TrIPE). | |
11 | * | |
11ad66c2 MW |
12 | * TrIPE is free software: you can redistribute it and/or modify it under |
13 | * the terms of the GNU General Public License as published by the Free | |
14 | * Software Foundation; either version 3 of the License, or (at your | |
15 | * option) any later version. | |
e04c2d50 | 16 | * |
11ad66c2 MW |
17 | * TrIPE is distributed in the hope that it will be useful, but WITHOUT |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
e04c2d50 | 21 | * |
410c8acf | 22 | * You should have received a copy of the GNU General Public License |
11ad66c2 | 23 | * along with TrIPE. If not, see <https://www.gnu.org/licenses/>. |
410c8acf | 24 | */ |
25 | ||
410c8acf | 26 | /*----- Header files ------------------------------------------------------*/ |
27 | ||
28 | #include "tripe.h" | |
29 | ||
df9dfccf | 30 | /*----- Global variables --------------------------------------------------*/ |
31 | ||
a4b808b0 | 32 | octet buf_i[PKBUFSZ], buf_o[PKBUFSZ], buf_t[PKBUFSZ], buf_u[PKBUFSZ]; |
df9dfccf | 33 | |
82ba25cf | 34 | /*----- Sequence numbers --------------------------------------------------*/ |
52b86648 | 35 | |
37941236 | 36 | /* --- @seq_reset@ --- * |
37 | * | |
38 | * Arguments: @seqwin *s@ = sequence-checking window | |
39 | * | |
40 | * Returns: --- | |
41 | * | |
42 | * Use: Resets a sequence number window. | |
43 | */ | |
44 | ||
45 | void seq_reset(seqwin *s) { s->seq = 0; s->win = 0; } | |
46 | ||
47 | /* --- @seq_check@ --- * | |
48 | * | |
49 | * Arguments: @seqwin *s@ = sequence-checking window | |
50 | * @uint32 q@ = sequence number to check | |
f43df819 | 51 | * @const char *service@ = service to report message from |
37941236 | 52 | * |
f43df819 | 53 | * Returns: Zero on success, nonzero if the sequence number was bad. |
37941236 | 54 | * |
55 | * Use: Checks a sequence number against the window, updating things | |
56 | * as necessary. | |
57 | */ | |
58 | ||
f43df819 | 59 | int seq_check(seqwin *s, uint32 q, const char *service) |
37941236 | 60 | { |
61 | uint32 qbit; | |
62 | uint32 n; | |
63 | ||
f43df819 MW |
64 | if (q < s->seq) { |
65 | a_warn(service, "replay", "old-sequence", A_END); | |
66 | return (-1); | |
67 | } | |
37941236 | 68 | if (q >= s->seq + SEQ_WINSZ) { |
69 | n = q - (s->seq + SEQ_WINSZ - 1); | |
70 | if (n < SEQ_WINSZ) | |
71 | s->win >>= n; | |
72 | else | |
73 | s->win = 0; | |
74 | s->seq += n; | |
75 | } | |
76 | qbit = 1 << (q - s->seq); | |
f43df819 MW |
77 | if (s->win & qbit) { |
78 | a_warn(service, "replay", "duplicated-sequence", A_END); | |
79 | return (-1); | |
80 | } | |
37941236 | 81 | s->win |= qbit; |
82 | return (0); | |
83 | } | |
84 | ||
e9fcf28e MW |
85 | /*----- Rate limiting -----------------------------------------------------*/ |
86 | ||
87 | /* --- @ratelim_init@ --- * | |
88 | * | |
89 | * Arguments: @ratelim *r@ = rate-limiting state to fill in | |
90 | * @unsigned persec@ = credit to accumulate per second | |
91 | * @unsigned max@ = maximum credit to retain | |
92 | * | |
93 | * Returns: --- | |
94 | * | |
95 | * Use: Initialize a rate-limiting state. | |
96 | */ | |
97 | ||
98 | void ratelim_init(ratelim *r, unsigned persec, unsigned max) | |
99 | { | |
100 | r->n = r->max = max; | |
101 | r->persec = persec; | |
102 | gettimeofday(&r->when, 0); | |
103 | } | |
104 | ||
105 | /* --- @ratelim_withdraw@ --- * | |
106 | * | |
107 | * Arguments: @ratelim *r@ = rate-limiting state | |
108 | * @unsigned n@ = credit to withdraw | |
109 | * | |
110 | * Returns: Zero if successful; @-1@ if there is unsufficient credit | |
111 | * | |
112 | * Use: Updates the state with any accumulated credit. Then, if | |
113 | * there there are more than @n@ credits available, withdraw @n@ | |
114 | * and return successfully; otherwise, report failure. | |
115 | */ | |
116 | ||
117 | int ratelim_withdraw(ratelim *r, unsigned n) | |
118 | { | |
119 | struct timeval now, delta; | |
120 | unsigned long d; | |
121 | ||
122 | gettimeofday(&now, 0); | |
123 | TV_SUB(&delta, &now, &r->when); | |
124 | d = (unsigned long)r->persec*delta.tv_sec + | |
125 | (unsigned long)r->persec*delta.tv_usec/MILLION; | |
126 | if (d < r->max - r->n) r->n += d; | |
127 | else r->n = r->max; | |
128 | r->when = now; | |
129 | ||
130 | if (n > r->n) return (-1); | |
131 | else { r->n -= n; return (0); } | |
132 | } | |
133 | ||
0c1cede3 MW |
134 | /*----- Crypto ------------------------------------------------------------*/ |
135 | ||
136 | /* --- @ies_encrypt@ --- * | |
137 | * | |
138 | * Arguments: @kdata *kpub@ = recipient's public key | |
139 | * @unsigned ty@ = message type octet | |
140 | * @buf *b@ = input message buffer | |
141 | * @buf *bb@ = output buffer for the ciphertext | |
142 | * | |
143 | * Returns: On error, returns a @KSERR_...@ code or breaks the buffer; | |
144 | * on success, returns zero and the buffer is good. | |
145 | * | |
146 | * Use: Encrypts a message for a recipient, given their public key. | |
147 | * This does not (by itself) provide forward secrecy or sender | |
148 | * authenticity. The ciphertext is self-delimiting (unlike | |
149 | * @ks_encrypt@). | |
150 | */ | |
151 | ||
152 | int ies_encrypt(kdata *kpub, unsigned ty, buf *b, buf *bb) | |
153 | { | |
154 | dhgrp *g = kpub->grp; | |
155 | dhsc *u = g->ops->randsc(g); | |
156 | dhge *U = g->ops->mul(g, u, 0), *Z = g->ops->mul(g, u, kpub->K); | |
157 | bulkalgs *algs = kpub->algs.bulk; | |
158 | octet *len; | |
159 | bulkctx *bulk; | |
160 | deriveargs a; | |
161 | size_t n; | |
162 | buf bk; | |
163 | int rc = 0; | |
164 | ||
165 | IF_TRACING(T_CRYPTO, { | |
166 | trace(T_CRYPTO, | |
167 | "crypto: encrypting IES message (type 0x%02x) for recipient `%s'", | |
168 | ty, kpub->tag); | |
169 | trace_block(T_CRYPTO, "crypto: plaintext message", BCUR(b), BLEFT(b)); | |
170 | }) | |
171 | ||
172 | a.hc = kpub->algs.h; a.what = "tripe:ecies-"; a.f = DF_OUT; | |
173 | buf_init(&bk, buf_u, sizeof(buf_u)); a.k = BBASE(&bk); | |
174 | g->ops->stge(g, &bk, U, DHFMT_HASH); a.x = a.y = BLEN(&bk); | |
175 | g->ops->stge(g, &bk, Z, DHFMT_HASH); a.z = BLEN(&bk); | |
176 | assert(BOK(&bk)); | |
177 | T( trace_block(T_CRYPTO, "crypto: KEM clue", a.k, a.x); | |
178 | trace_block(T_CRYPTO, "crypto: shared secret", a.k + a.y, a.z - a.y); ) | |
179 | ||
180 | len = BCUR(bb); buf_get(bb, 2); | |
181 | bulk = algs->ops->genkeys(algs, &a); | |
182 | bulk->ops = algs->ops; | |
183 | g->ops->stge(g, bb, U, DHFMT_VAR); if (BBAD(bb)) goto end; | |
184 | rc = bulk->ops->encrypt(bulk, ty, b, bb, 0); | |
185 | if (rc || BBAD(bb)) goto end; | |
186 | n = BCUR(bb) - len - 2; assert(n <= MASK16); STORE16(len, n); | |
187 | ||
188 | end: | |
189 | bulk->ops->freectx(bulk); | |
190 | g->ops->freesc(g, u); | |
191 | g->ops->freege(g, U); | |
192 | g->ops->freege(g, Z); | |
193 | return (rc); | |
194 | } | |
195 | ||
196 | /* --- @ies_decrypt@ --- * | |
197 | * | |
198 | * Arguments: @kdata *kpub@ = private key key | |
199 | * @unsigned ty@ = message type octet | |
200 | * @buf *b@ = input ciphertext buffer | |
201 | * @buf *bb@ = output buffer for the message | |
202 | * | |
203 | * Returns: On error, returns a @KSERR_...@ code; on success, returns | |
204 | * zero and the buffer is good. | |
205 | * | |
206 | * Use: Decrypts a message encrypted using @ies_encrypt@, given our | |
207 | * private key. | |
208 | */ | |
209 | ||
210 | int ies_decrypt(kdata *kpriv, unsigned ty, buf *b, buf *bb) | |
211 | { | |
212 | dhgrp *g = kpriv->grp; | |
213 | bulkalgs *algs = kpriv->algs.bulk; | |
214 | bulkctx *bulk = 0; | |
215 | T( const octet *m; ) | |
216 | dhge *U = 0, *Z = 0; | |
217 | deriveargs a; | |
218 | uint32 seq; | |
219 | buf bk, bc; | |
220 | int rc; | |
221 | ||
222 | IF_TRACING(T_CRYPTO, { | |
223 | trace(T_CRYPTO, | |
224 | "crypto: decrypting IES message (type 0x%02x) to recipient `%s'", | |
225 | ty, kpriv->tag); | |
226 | trace_block(T_CRYPTO, "crypto: ciphertext message", BCUR(b), BLEFT(b)); | |
227 | }) | |
228 | ||
229 | if (buf_getbuf16(b, &bc) || | |
230 | (U = g->ops->ldge(g, &bc, DHFMT_VAR)) == 0 || | |
231 | g->ops->checkge(g, U)) | |
232 | { rc = KSERR_MALFORMED; goto end; } | |
233 | Z = g->ops->mul(g, kpriv->k, U); | |
234 | ||
235 | a.hc = kpriv->algs.h; a.what = "tripe:ecies-"; a.f = DF_IN; | |
236 | buf_init(&bk, buf_u, sizeof(buf_u)); a.k = BBASE(&bk); a.x = 0; | |
237 | g->ops->stge(g, &bk, U, DHFMT_HASH); a.y = BLEN(&bk); | |
238 | g->ops->stge(g, &bk, Z, DHFMT_HASH); a.z = BLEN(&bk); | |
239 | T( trace_block(T_CRYPTO, "crypto: KEM clue", a.k + a.x, a.y - a.x); | |
240 | trace_block(T_CRYPTO, "crypto: shared secret", a.k + a.y, a.z - a.y); ) | |
241 | assert(BOK(&bk)); | |
242 | ||
243 | bulk = algs->ops->genkeys(algs, &a); | |
244 | bulk->ops = algs->ops; | |
245 | T( m = BCUR(bb); ) | |
246 | rc = bulk->ops->decrypt(bulk, ty, &bc, bb, &seq); | |
247 | if (rc) goto end; | |
248 | if (seq) { rc = KSERR_SEQ; goto end; } | |
249 | assert(BOK(bb)); | |
250 | T( trace_block(T_CRYPTO, "crypto: decrypted message", m, BCUR(bb) - m); ) | |
251 | ||
252 | end: | |
253 | if (bulk) bulk->ops->freectx(bulk); | |
254 | g->ops->freege(g, U); | |
255 | g->ops->freege(g, Z); | |
256 | return (rc); | |
257 | } | |
258 | ||
82ba25cf MW |
259 | /*----- Random odds and sods ----------------------------------------------*/ |
260 | ||
261 | /* --- @timestr@ --- * | |
262 | * | |
263 | * Arguments: @time_t t@ = a time to convert | |
264 | * | |
265 | * Returns: A pointer to a textual representation of the time. | |
266 | * | |
267 | * Use: Converts a time to a textual representation. Corrupts | |
268 | * @buf_u@. | |
269 | */ | |
270 | ||
271 | const char *timestr(time_t t) | |
272 | { | |
273 | struct tm *tm; | |
274 | if (!t) | |
275 | return ("NEVER"); | |
276 | tm = localtime(&t); | |
277 | strftime((char *)buf_u, sizeof(buf_u), "%Y-%m-%dT%H:%M:%S", tm); | |
278 | return ((const char *)buf_u); | |
279 | } | |
280 | ||
281 | /* --- @mystrieq@ --- * | |
282 | * | |
283 | * Arguments: @const char *x, *y@ = two strings | |
284 | * | |
285 | * Returns: True if @x@ and @y are equal, up to case. | |
286 | */ | |
287 | ||
288 | int mystrieq(const char *x, const char *y) | |
289 | { | |
290 | for (;;) { | |
291 | if (!*x && !*y) return (1); | |
292 | if (tolower((unsigned char)*x) != tolower((unsigned char)*y)) | |
293 | return (0); | |
294 | x++; y++; | |
295 | } | |
296 | } | |
297 | ||
5d06f63e MW |
298 | /*----- Address handling --------------------------------------------------*/ |
299 | ||
300 | const struct addrfam aftab[] = { | |
a8211197 MW |
301 | #ifdef HAVE_LIBADNS |
302 | # define DEF(af, qf) { AF_##af, #af, adns_qf_##qf }, | |
303 | #else | |
304 | # define DEF(af, qf) { AF_##af, #af }, | |
305 | #endif | |
5d06f63e MW |
306 | ADDRFAM(DEF) |
307 | #undef DEF | |
308 | }; | |
309 | ||
310 | /* --- @afix@ --- * | |
311 | * | |
312 | * Arguments: @int af@ = an address family code | |
313 | * | |
314 | * Returns: The index of the address family's record in @aftab@, or @-1@. | |
315 | */ | |
316 | ||
317 | int afix(int af) | |
318 | { | |
319 | int i; | |
320 | ||
321 | for (i = 0; i < NADDRFAM; i++) | |
322 | if (af == aftab[i].af) return (i); | |
323 | return (-1); | |
324 | } | |
325 | ||
cb2c2bfc MW |
326 | /* --- @addrsz@ --- * |
327 | * | |
328 | * Arguments: @const addr *a@ = a network address | |
329 | * | |
330 | * Returns: The size of the address, for passing into the sockets API. | |
331 | */ | |
332 | ||
333 | socklen_t addrsz(const addr *a) | |
334 | { | |
335 | switch (a->sa.sa_family) { | |
336 | case AF_INET: return (sizeof(a->sin)); | |
47828bd9 | 337 | case AF_INET6: return (sizeof(a->sin6)); |
cb2c2bfc MW |
338 | default: abort(); |
339 | } | |
340 | } | |
341 | ||
d98625f4 MW |
342 | /* --- @getport@, @setport@ --- * |
343 | * | |
344 | * Arguments: @addr *a@ = a network address | |
345 | * @unsigned port@ = port number to set | |
346 | * | |
347 | * Returns: --- | |
348 | * | |
349 | * Use: Retrieves or sets the port number in an address structure. | |
350 | */ | |
351 | ||
352 | unsigned getport(addr *a) | |
353 | { | |
354 | switch (a->sa.sa_family) { | |
355 | case AF_INET: return (ntohs(a->sin.sin_port)); break; | |
47828bd9 | 356 | case AF_INET6: return (ntohs(a->sin6.sin6_port)); break; |
d98625f4 MW |
357 | default: abort(); |
358 | } | |
359 | } | |
360 | ||
361 | void setport(addr *a, unsigned port) | |
362 | { | |
363 | switch (a->sa.sa_family) { | |
364 | case AF_INET: a->sin.sin_port = htons(port); break; | |
47828bd9 | 365 | case AF_INET6: a->sin6.sin6_port = htons(port); break; |
cb2c2bfc MW |
366 | default: abort(); |
367 | } | |
368 | } | |
369 | ||
410c8acf | 370 | /*----- That's all, folks -------------------------------------------------*/ |