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