chiark / gitweb /
Import release 0.1.15
[secnet.git] / rsa.c
1 /* CRT work by Simon Tatham */
2
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
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
12 struct rsapriv {
13     closure_t cl;
14     struct rsaprivkey_if ops;
15     struct cloc loc;
16     MP_INT n;
17     MP_INT p, dp;
18     MP_INT q, dq;
19     MP_INT w;
20 };
21 struct 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
30 static const char *hexchars="0123456789abcdef";
31
32 static string_t rsa_sign(void *sst, uint8_t *data, uint32_t datalen)
33 {
34     struct rsapriv *st=sst;
35     MP_INT a, b, u, v, tmp, tmp2;
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
45     if (datalen*2+4>=msize) {
46         fatal("rsa_sign: message too big");
47     }
48
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;
56     
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
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);
97
98     signature=write_mpstring(&b);
99
100     mpz_clear(&b);
101     mpz_clear(&a);
102     return signature;
103 }
104
105 static rsa_checksig_fn rsa_sig_check;
106 static bool_t rsa_sig_check(void *sst, uint8_t *data, uint32_t datalen,
107                             cstring_t signature)
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
149 static 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
195 static uint32_t keyfile_get_int(struct cloc loc, FILE *f)
196 {
197     uint32_t r;
198     r=fgetc(f)<<24;
199     r|=fgetc(f)<<16;
200     r|=fgetc(f)<<8;
201     r|=fgetc(f);
202     cfgfile_postreadcheck(loc,f);
203     return r;
204 }
205
206 static uint16_t keyfile_get_short(struct cloc loc, FILE *f)
207 {
208     uint16_t r;
209     r=fgetc(f)<<8;
210     r|=fgetc(f);
211     cfgfile_postreadcheck(loc,f);
212     return r;
213 }
214
215 static 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;
220     cstring_t filename;
221     item_t *i;
222     long length;
223     uint8_t *b, *c;
224     int cipher_type;
225     MP_INT e,d,iqmp,tmp,tmp2,tmp3;
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 {
244         filename=NULL; /* Make compiler happy */
245         cfgfatal(loc,"rsa-private","you must provide a filename\n");
246     }
247
248     f=fopen(filename,"rb");
249     if (!f) {
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         }
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) {
265         cfgfatal_maybefile(f,loc,"rsa-private","failed to read magic ID"
266                            " string from SSH1 private keyfile \"%s\"\n",
267                            filename);
268     }
269     free(b);
270
271     cipher_type=fgetc(f);
272     keyfile_get_int(loc,f); /* "Reserved data" */
273     if (cipher_type != 0) {
274         cfgfatal(loc,"rsa-private","we don't support encrypted keyfiles\n");
275     }
276
277     /* Read the public key */
278     keyfile_get_int(loc,f); /* Not sure what this is */
279     length=(keyfile_get_short(loc,f)+7)/8;
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) {
286         cfgfatal_maybefile(f,loc,"rsa-private","error reading modulus");
287     }
288     mpz_init(&st->n);
289     read_mpbin(&st->n,b,length);
290     free(b);
291     length=(keyfile_get_short(loc,f)+7)/8;
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) {
297         cfgfatal_maybefile(f,loc,"rsa-private","error reading e\n");
298     }
299     mpz_init(&e);
300     read_mpbin(&e,b,length);
301     free(b);
302     
303     length=keyfile_get_int(loc,f);
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) {
310         cfgfatal_maybefile(f,loc,"rsa-private","error reading key comment\n");
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 */
316
317     if (keyfile_get_short(loc,f) != keyfile_get_short(loc,f)) {
318         cfgfatal(loc,"rsa-private","corrupt keyfile\n");
319     }
320
321     /* Read d */
322     length=(keyfile_get_short(loc,f)+7)/8;
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) {
329         cfgfatal_maybefile(f,loc,"rsa-private",
330                            "error reading decryption key\n");
331     }
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);
376     free(b);
377     
378     if (fclose(f)!=0) {
379         fatal_perror("rsa-private (%s:%d): fclose",loc.file,loc.line);
380     }
381
382     /*
383      * Now verify the validity of the key, and set up the auxiliary
384      * values for fast CRT signing.
385      */
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 {
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) {
442             cfgfatal(loc,"rsa-private","file \"%s\" does not contain a "
443                      "valid RSA key!\n",filename);
444         }
445         mpz_clear(&tmp);
446         mpz_clear(&tmp2);
447         mpz_clear(&tmp3);
448     }
449
450     free(c);
451     mpz_clear(&e);
452     mpz_clear(&d);
453     mpz_clear(&iqmp);
454
455 assume_valid:
456     return new_closure(&st->cl);
457 }
458
459 init_module rsa_module;
460 void rsa_module(dict_t *dict)
461 {
462     add_closure(dict,"rsa-private",rsapriv_apply);
463     add_closure(dict,"rsa-public",rsapub_apply);
464 }