rsa.c: Factor out constructing the EMSA-PKCS1 message representative.
[secnet] / rsa.c
CommitLineData
3b83c932
SE
1/* This file is part of secnet, and is distributed under the terms of
2 the GNU General Public License version 2 or later.
3
4 Copyright (C) 1995-2002 Stephen Early
5 Copyright (C) 2001 Simon Tatham
6 Copyright (C) 2002 Ian Jackson
7 */
fe5e9cc4 8
2fe58dfd 9#include <stdio.h>
3b83c932 10#include <string.h>
2fe58dfd
SE
11#include <gmp.h>
12#include "secnet.h"
13#include "util.h"
14
15#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n"
16
fe5e9cc4
SE
17#define mpp(s,n) do { char *p = mpz_get_str(NULL,16,n); printf("%s 0x%sL\n", s, p); free(p); } while (0)
18
2fe58dfd
SE
19struct rsapriv {
20 closure_t cl;
21 struct rsaprivkey_if ops;
22 struct cloc loc;
2fe58dfd 23 MP_INT n;
fe5e9cc4
SE
24 MP_INT p, dp;
25 MP_INT q, dq;
26 MP_INT w;
2fe58dfd
SE
27};
28struct rsapub {
29 closure_t cl;
30 struct rsapubkey_if ops;
31 struct cloc loc;
32 MP_INT e;
33 MP_INT n;
34};
35/* Sign data. NB data must be smaller than modulus */
36
fe5e9cc4 37static const char *hexchars="0123456789abcdef";
2fe58dfd 38
2cd2cf05
MW
39static void emsa_pkcs1(MP_INT *n, MP_INT *m,
40 const uint8_t *data, int32_t datalen)
2fe58dfd 41{
2fe58dfd
SE
42 char buff[2048];
43 int msize, i;
2fe58dfd 44
3b83c932
SE
45 /* RSA PKCS#1 v1.5 signature padding:
46 *
47 * <------------ msize hex digits ---------->
48 *
49 * 00 01 ff ff .... ff ff 00 vv vv vv .... vv
50 *
51 * <--- datalen -->
52 * bytes
53 * = datalen*2 hex digits
54 *
55 * NB that according to PKCS#1 v1.5 we're supposed to include a
56 * hash function OID in the data. We don't do that (because we
57 * don't have the hash function OID to hand here), thus violating
58 * the spec in a way that affects interop but not security.
59 *
60 * -iwj 17.9.2002
61 */
62
2cd2cf05 63 msize=mpz_sizeinbase(n, 16);
2fe58dfd 64
3b83c932 65 if (datalen*2+6>=msize) {
4f5e39ec 66 fatal("rsa_sign: message too big");
3454dce4
SE
67 }
68
2fe58dfd
SE
69 strcpy(buff,"0001");
70
71 for (i=0; i<datalen; i++) {
3b83c932
SE
72 buff[msize+(-datalen+i)*2]=hexchars[(data[i]&0xf0)>>4];
73 buff[msize+(-datalen+i)*2+1]=hexchars[data[i]&0xf];
2fe58dfd 74 }
3454dce4 75
3b83c932
SE
76 buff[msize-datalen*2-2]= '0';
77 buff[msize-datalen*2-1]= '0';
78
79 for (i=4; i<msize-datalen*2-2; i++)
80 buff[i]='f';
2fe58dfd
SE
81
82 buff[msize]=0;
83
2cd2cf05
MW
84 mpz_set_str(m, buff, 16);
85}
86
87static string_t rsa_sign(void *sst, uint8_t *data, int32_t datalen)
88{
89 struct rsapriv *st=sst;
90 MP_INT a, b, u, v, tmp, tmp2;
91 string_t signature;
92
93 mpz_init(&a);
94 mpz_init(&b);
95
96 /* Construct the message representative. */
97 emsa_pkcs1(&st->n, &a, data, datalen);
2fe58dfd 98
fe5e9cc4
SE
99 /*
100 * Produce an RSA signature (a^d mod n) using the Chinese
101 * Remainder Theorem. We compute:
102 *
103 * u = a^dp mod p (== a^d mod p, since dp == d mod (p-1))
104 * v = a^dq mod q (== a^d mod q, similarly)
105 *
106 * We also know w == iqmp * q, which has the property that w ==
107 * 0 mod q and w == 1 mod p. So (1-w) has the reverse property
108 * (congruent to 0 mod p and to 1 mod q). Hence we now compute
109 *
110 * b = w * u + (1-w) * v
111 * = w * (u-v) + v
112 *
113 * so that b is congruent to a^d both mod p and mod q. Hence b,
114 * reduced mod n, is the required signature.
115 */
116 mpz_init(&tmp);
117 mpz_init(&tmp2);
118 mpz_init(&u);
119 mpz_init(&v);
120
121 mpz_powm(&u, &a, &st->dp, &st->p);
122 mpz_powm(&v, &a, &st->dq, &st->q);
123 mpz_sub(&tmp, &u, &v);
124 mpz_mul(&tmp2, &tmp, &st->w);
125 mpz_add(&tmp, &tmp2, &v);
126 mpz_mod(&b, &tmp, &st->n);
127
128 mpz_clear(&tmp);
129 mpz_clear(&tmp2);
130 mpz_clear(&u);
131 mpz_clear(&v);
2fe58dfd
SE
132
133 signature=write_mpstring(&b);
134
135 mpz_clear(&b);
136 mpz_clear(&a);
137 return signature;
138}
139
fe5e9cc4 140static rsa_checksig_fn rsa_sig_check;
1caa23ff 141static bool_t rsa_sig_check(void *sst, uint8_t *data, int32_t datalen,
fe5e9cc4 142 cstring_t signature)
2fe58dfd
SE
143{
144 struct rsapub *st=sst;
145 MP_INT a, b, c;
2fe58dfd
SE
146 bool_t ok;
147
148 mpz_init(&a);
149 mpz_init(&b);
150 mpz_init(&c);
151
2cd2cf05 152 emsa_pkcs1(&st->n, &a, data, datalen);
2fe58dfd
SE
153
154 mpz_set_str(&b, signature, 16);
155
156 mpz_powm(&c, &b, &st->e, &st->n);
157
158 ok=(mpz_cmp(&a, &c)==0);
159
160 mpz_clear(&c);
161 mpz_clear(&b);
162 mpz_clear(&a);
163
164 return ok;
165}
166
167static list_t *rsapub_apply(closure_t *self, struct cloc loc, dict_t *context,
168 list_t *args)
169{
170 struct rsapub *st;
171 item_t *i;
172 string_t e,n;
173
174 st=safe_malloc(sizeof(*st),"rsapub_apply");
175 st->cl.description="rsapub";
176 st->cl.type=CL_RSAPUBKEY;
177 st->cl.apply=NULL;
178 st->cl.interface=&st->ops;
179 st->ops.st=st;
180 st->ops.check=rsa_sig_check;
181 st->loc=loc;
182
183 i=list_elem(args,0);
184 if (i) {
185 if (i->type!=t_string) {
39a6b1e2 186 cfgfatal(i->loc,"rsa-public","first argument must be a string\n");
2fe58dfd
SE
187 }
188 e=i->data.string;
189 if (mpz_init_set_str(&st->e,e,10)!=0) {
190 cfgfatal(i->loc,"rsa-public","encryption key \"%s\" is not a "
191 "decimal number string\n",e);
192 }
193 } else {
194 cfgfatal(loc,"rsa-public","you must provide an encryption key\n");
195 }
196
197 i=list_elem(args,1);
198 if (i) {
199 if (i->type!=t_string) {
39a6b1e2 200 cfgfatal(i->loc,"rsa-public","second argument must be a string\n");
2fe58dfd
SE
201 }
202 n=i->data.string;
203 if (mpz_init_set_str(&st->n,n,10)!=0) {
204 cfgfatal(i->loc,"rsa-public","modulus \"%s\" is not a decimal "
205 "number string\n",n);
206 }
207 } else {
208 cfgfatal(loc,"rsa-public","you must provide a modulus\n");
209 }
210 return new_closure(&st->cl);
211}
212
4f5e39ec 213static uint32_t keyfile_get_int(struct cloc loc, FILE *f)
2fe58dfd
SE
214{
215 uint32_t r;
216 r=fgetc(f)<<24;
217 r|=fgetc(f)<<16;
218 r|=fgetc(f)<<8;
219 r|=fgetc(f);
4f5e39ec 220 cfgfile_postreadcheck(loc,f);
2fe58dfd
SE
221 return r;
222}
223
4f5e39ec 224static uint16_t keyfile_get_short(struct cloc loc, FILE *f)
2fe58dfd
SE
225{
226 uint16_t r;
227 r=fgetc(f)<<8;
228 r|=fgetc(f);
4f5e39ec 229 cfgfile_postreadcheck(loc,f);
2fe58dfd
SE
230 return r;
231}
232
233static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context,
234 list_t *args)
235{
236 struct rsapriv *st;
237 FILE *f;
fe5e9cc4 238 cstring_t filename;
2fe58dfd
SE
239 item_t *i;
240 long length;
241 uint8_t *b, *c;
242 int cipher_type;
fe5e9cc4 243 MP_INT e,d,iqmp,tmp,tmp2,tmp3;
3b83c932 244 bool_t valid;
2fe58dfd
SE
245
246 st=safe_malloc(sizeof(*st),"rsapriv_apply");
247 st->cl.description="rsapriv";
248 st->cl.type=CL_RSAPRIVKEY;
249 st->cl.apply=NULL;
250 st->cl.interface=&st->ops;
251 st->ops.st=st;
252 st->ops.sign=rsa_sign;
253 st->loc=loc;
254
255 /* Argument is filename pointing to SSH1 private key file */
256 i=list_elem(args,0);
257 if (i) {
258 if (i->type!=t_string) {
39a6b1e2 259 cfgfatal(i->loc,"rsa-public","first argument must be a string\n");
2fe58dfd
SE
260 }
261 filename=i->data.string;
262 } else {
fe5e9cc4 263 filename=NULL; /* Make compiler happy */
2fe58dfd
SE
264 cfgfatal(loc,"rsa-private","you must provide a filename\n");
265 }
266
267 f=fopen(filename,"rb");
268 if (!f) {
baa06aeb
SE
269 if (just_check_config) {
270 Message(M_WARNING,"rsa-private (%s:%d): cannot open keyfile "
271 "\"%s\"; assuming it's valid while we check the "
272 "rest of the configuration\n",loc.file,loc.line,filename);
273 goto assume_valid;
274 } else {
275 fatal_perror("rsa-private (%s:%d): cannot open file \"%s\"",
276 loc.file,loc.line,filename);
277 }
2fe58dfd
SE
278 }
279
280 /* Check that the ID string is correct */
281 length=strlen(AUTHFILE_ID_STRING)+1;
282 b=safe_malloc(length,"rsapriv_apply");
283 if (fread(b,length,1,f)!=1 || memcmp(b,AUTHFILE_ID_STRING,length)!=0) {
4f5e39ec
SE
284 cfgfatal_maybefile(f,loc,"rsa-private","failed to read magic ID"
285 " string from SSH1 private keyfile \"%s\"\n",
286 filename);
2fe58dfd
SE
287 }
288 free(b);
289
290 cipher_type=fgetc(f);
4f5e39ec 291 keyfile_get_int(loc,f); /* "Reserved data" */
2fe58dfd
SE
292 if (cipher_type != 0) {
293 cfgfatal(loc,"rsa-private","we don't support encrypted keyfiles\n");
294 }
295
296 /* Read the public key */
4f5e39ec
SE
297 keyfile_get_int(loc,f); /* Not sure what this is */
298 length=(keyfile_get_short(loc,f)+7)/8;
2fe58dfd
SE
299 if (length>1024) {
300 cfgfatal(loc,"rsa-private","implausible length %ld for modulus\n",
301 length);
302 }
303 b=safe_malloc(length,"rsapriv_apply");
304 if (fread(b,length,1,f) != 1) {
39a6b1e2 305 cfgfatal_maybefile(f,loc,"rsa-private","error reading modulus\n");
2fe58dfd
SE
306 }
307 mpz_init(&st->n);
308 read_mpbin(&st->n,b,length);
309 free(b);
4f5e39ec 310 length=(keyfile_get_short(loc,f)+7)/8;
2fe58dfd
SE
311 if (length>1024) {
312 cfgfatal(loc,"rsa-private","implausible length %ld for e\n",length);
313 }
314 b=safe_malloc(length,"rsapriv_apply");
315 if (fread(b,length,1,f)!=1) {
4f5e39ec 316 cfgfatal_maybefile(f,loc,"rsa-private","error reading e\n");
2fe58dfd
SE
317 }
318 mpz_init(&e);
319 read_mpbin(&e,b,length);
320 free(b);
321
4f5e39ec 322 length=keyfile_get_int(loc,f);
2fe58dfd
SE
323 if (length>1024) {
324 cfgfatal(loc,"rsa-private","implausibly long (%ld) key comment\n",
325 length);
326 }
327 c=safe_malloc(length+1,"rsapriv_apply");
328 if (fread(c,length,1,f)!=1) {
4f5e39ec 329 cfgfatal_maybefile(f,loc,"rsa-private","error reading key comment\n");
2fe58dfd
SE
330 }
331 c[length]=0;
332
333 /* Check that the next two pairs of characters are identical - the
334 keyfile is not encrypted, so they should be */
4f5e39ec
SE
335
336 if (keyfile_get_short(loc,f) != keyfile_get_short(loc,f)) {
2fe58dfd
SE
337 cfgfatal(loc,"rsa-private","corrupt keyfile\n");
338 }
339
340 /* Read d */
4f5e39ec 341 length=(keyfile_get_short(loc,f)+7)/8;
2fe58dfd
SE
342 if (length>1024) {
343 cfgfatal(loc,"rsa-private","implausibly long (%ld) decryption key\n",
344 length);
345 }
346 b=safe_malloc(length,"rsapriv_apply");
347 if (fread(b,length,1,f)!=1) {
4f5e39ec
SE
348 cfgfatal_maybefile(f,loc,"rsa-private",
349 "error reading decryption key\n");
2fe58dfd 350 }
fe5e9cc4
SE
351 mpz_init(&d);
352 read_mpbin(&d,b,length);
353 free(b);
354 /* Read iqmp (inverse of q mod p) */
355 length=(keyfile_get_short(loc,f)+7)/8;
356 if (length>1024) {
357 cfgfatal(loc,"rsa-private","implausibly long (%ld)"
358 " iqmp auxiliary value\n", length);
359 }
360 b=safe_malloc(length,"rsapriv_apply");
361 if (fread(b,length,1,f)!=1) {
362 cfgfatal_maybefile(f,loc,"rsa-private",
363 "error reading decryption key\n");
364 }
365 mpz_init(&iqmp);
366 read_mpbin(&iqmp,b,length);
367 free(b);
368 /* Read q (the smaller of the two primes) */
369 length=(keyfile_get_short(loc,f)+7)/8;
370 if (length>1024) {
371 cfgfatal(loc,"rsa-private","implausibly long (%ld) q value\n",
372 length);
373 }
374 b=safe_malloc(length,"rsapriv_apply");
375 if (fread(b,length,1,f)!=1) {
376 cfgfatal_maybefile(f,loc,"rsa-private",
377 "error reading q value\n");
378 }
379 mpz_init(&st->q);
380 read_mpbin(&st->q,b,length);
381 free(b);
382 /* Read p (the larger of the two primes) */
383 length=(keyfile_get_short(loc,f)+7)/8;
384 if (length>1024) {
385 cfgfatal(loc,"rsa-private","implausibly long (%ld) p value\n",
386 length);
387 }
388 b=safe_malloc(length,"rsapriv_apply");
389 if (fread(b,length,1,f)!=1) {
390 cfgfatal_maybefile(f,loc,"rsa-private",
391 "error reading p value\n");
392 }
393 mpz_init(&st->p);
394 read_mpbin(&st->p,b,length);
2fe58dfd
SE
395 free(b);
396
397 if (fclose(f)!=0) {
398 fatal_perror("rsa-private (%s:%d): fclose",loc.file,loc.line);
399 }
400
fe5e9cc4
SE
401 /*
402 * Now verify the validity of the key, and set up the auxiliary
403 * values for fast CRT signing.
404 */
3b83c932 405 valid=False;
70dc107b 406 i=list_elem(args,1);
3b83c932
SE
407 mpz_init(&tmp);
408 mpz_init(&tmp2);
409 mpz_init(&tmp3);
70dc107b
SE
410 if (i && i->type==t_bool && i->data.bool==False) {
411 Message(M_INFO,"rsa-private (%s:%d): skipping RSA key validity "
412 "check\n",loc.file,loc.line);
413 } else {
fe5e9cc4
SE
414 /* Verify that p*q is equal to n. */
415 mpz_mul(&tmp, &st->p, &st->q);
416 if (mpz_cmp(&tmp, &st->n) != 0)
417 goto done_checks;
418
419 /*
420 * Verify that d*e is congruent to 1 mod (p-1), and mod
421 * (q-1). This is equivalent to it being congruent to 1 mod
104e8e74
MW
422 * lambda(n) = lcm(p-1,q-1). The usual `textbook' condition,
423 * that d e == 1 (mod (p-1)(q-1)) is sufficient, but not
424 * actually necessary.
fe5e9cc4
SE
425 */
426 mpz_mul(&tmp, &d, &e);
427 mpz_sub_ui(&tmp2, &st->p, 1);
428 mpz_mod(&tmp3, &tmp, &tmp2);
429 if (mpz_cmp_si(&tmp3, 1) != 0)
430 goto done_checks;
431 mpz_sub_ui(&tmp2, &st->q, 1);
432 mpz_mod(&tmp3, &tmp, &tmp2);
433 if (mpz_cmp_si(&tmp3, 1) != 0)
434 goto done_checks;
435
436 /* Verify that q*iqmp is congruent to 1 mod p. */
437 mpz_mul(&tmp, &st->q, &iqmp);
438 mpz_mod(&tmp2, &tmp, &st->p);
439 if (mpz_cmp_si(&tmp2, 1) != 0)
440 goto done_checks;
2fe58dfd 441 }
3b83c932
SE
442 /* Now we know the key is valid (or we don't care). */
443 valid = True;
444
445 /*
446 * Now we compute auxiliary values dp, dq and w to allow us
447 * to use the CRT optimisation when signing.
448 *
449 * dp == d mod (p-1) so that a^dp == a^d mod p, for all a
450 * dq == d mod (q-1) similarly mod q
451 * w == iqmp * q so that w == 0 mod q, and w == 1 mod p
452 */
453 mpz_init(&st->dp);
454 mpz_init(&st->dq);
455 mpz_init(&st->w);
456 mpz_sub_ui(&tmp, &st->p, 1);
457 mpz_mod(&st->dp, &d, &tmp);
458 mpz_sub_ui(&tmp, &st->q, 1);
459 mpz_mod(&st->dq, &d, &tmp);
460 mpz_mul(&st->w, &iqmp, &st->q);
461
462done_checks:
463 if (!valid) {
464 cfgfatal(loc,"rsa-private","file \"%s\" does not contain a "
465 "valid RSA key!\n",filename);
466 }
467 mpz_clear(&tmp);
468 mpz_clear(&tmp2);
469 mpz_clear(&tmp3);
2fe58dfd
SE
470
471 free(c);
472 mpz_clear(&e);
fe5e9cc4
SE
473 mpz_clear(&d);
474 mpz_clear(&iqmp);
2fe58dfd 475
baa06aeb 476assume_valid:
2fe58dfd
SE
477 return new_closure(&st->cl);
478}
479
2fe58dfd
SE
480void rsa_module(dict_t *dict)
481{
482 add_closure(dict,"rsa-private",rsapriv_apply);
483 add_closure(dict,"rsa-public",rsapub_apply);
484}