chiark / gitweb /
d1107a952ef1a0e0ce9198a66ee17e0a6e61c31e
[secnet.git] / rsa.c
1 /***************************************************************************
2  *
3  *              Part II Project, "A secure, private IP network"
4  *              Stephen Early <sde1000@cam.ac.uk>
5  *   
6  *
7  *     $RCSfile: rsa.c,v $
8  *
9  *  Description: RSA signature making and checking functions
10  *
11  *    Copyright: (C) Stephen Early 1995
12  *
13  *    $Revision: 1.1 $
14  *
15  *        $Date: 1996/05/16 18:40:14 $
16  *
17  *       $State: Exp $
18  *
19  ***************************************************************************/
20
21 /* $Log: rsa.c,v $
22  * Revision 1.1  1996/05/16 18:40:14  sde1000
23  * Initial revision
24  *
25  */
26
27 #include <stdio.h>
28 #include <gmp.h>
29 #include "secnet.h"
30 #include "util.h"
31
32 #define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n"
33
34 struct rsapriv {
35     closure_t cl;
36     struct rsaprivkey_if ops;
37     struct cloc loc;
38     MP_INT d;
39     MP_INT n;
40 };
41 struct rsapub {
42     closure_t cl;
43     struct rsapubkey_if ops;
44     struct cloc loc;
45     MP_INT e;
46     MP_INT n;
47 };
48 /* Sign data. NB data must be smaller than modulus */
49
50 static char *hexchars="0123456789abcdef";
51
52 static string_t rsa_sign(void *sst, uint8_t *data, uint32_t datalen)
53 {
54     struct rsapriv *st=sst;
55     MP_INT a, b;
56     char buff[2048];
57     int msize, i;
58     string_t signature;
59
60     mpz_init(&a);
61     mpz_init(&b);
62
63     msize=mpz_sizeinbase(&st->n, 16);
64
65     strcpy(buff,"0001");
66
67     for (i=0; i<datalen; i++) {
68         buff[4+i*2]=hexchars[(data[i]&0xf0)>>4];
69         buff[5+i*2]=hexchars[data[i]&0xf];
70     }
71     buff[4+datalen*2]=0;
72
73     for (i=datalen*2+4; i<msize; i++)
74         buff[i]='f';
75
76     buff[msize]=0;
77
78     mpz_set_str(&a, buff, 16);
79
80     mpz_powm(&b, &a, &st->d, &st->n);
81
82     signature=write_mpstring(&b);
83
84     mpz_clear(&b);
85     mpz_clear(&a);
86     return signature;
87 }
88
89 static bool_t rsa_sig_check(void *sst, uint8_t *data, uint32_t datalen,
90                             string_t signature)
91 {
92     struct rsapub *st=sst;
93     MP_INT a, b, c;
94     char buff[2048];
95     int msize, i;
96     bool_t ok;
97
98     mpz_init(&a);
99     mpz_init(&b);
100     mpz_init(&c);
101
102     msize=mpz_sizeinbase(&st->n, 16);
103
104     strcpy(buff,"0001");
105
106     for (i=0; i<datalen; i++) {
107         buff[4+i*2]=hexchars[(data[i]&0xf0)>>4];
108         buff[5+i*2]=hexchars[data[i]&0xf];
109     }
110     buff[4+datalen*2]=0;
111
112     for (i=datalen*2+4; i<msize; i++)
113         buff[i]='f';
114
115     buff[msize]=0;
116
117     mpz_set_str(&a, buff, 16);
118
119     mpz_set_str(&b, signature, 16);
120
121     mpz_powm(&c, &b, &st->e, &st->n);
122
123     ok=(mpz_cmp(&a, &c)==0);
124
125     mpz_clear(&c);
126     mpz_clear(&b);
127     mpz_clear(&a);
128
129     return ok;
130 }
131
132 static list_t *rsapub_apply(closure_t *self, struct cloc loc, dict_t *context,
133                             list_t *args)
134 {
135     struct rsapub *st;
136     item_t *i;
137     string_t e,n;
138
139     st=safe_malloc(sizeof(*st),"rsapub_apply");
140     st->cl.description="rsapub";
141     st->cl.type=CL_RSAPUBKEY;
142     st->cl.apply=NULL;
143     st->cl.interface=&st->ops;
144     st->ops.st=st;
145     st->ops.check=rsa_sig_check;
146     st->loc=loc;
147
148     i=list_elem(args,0);
149     if (i) {
150         if (i->type!=t_string) {
151             cfgfatal(i->loc,"rsa-public","first argument must be a string");
152         }
153         e=i->data.string;
154         if (mpz_init_set_str(&st->e,e,10)!=0) {
155             cfgfatal(i->loc,"rsa-public","encryption key \"%s\" is not a "
156                      "decimal number string\n",e);
157         }
158     } else {
159         cfgfatal(loc,"rsa-public","you must provide an encryption key\n");
160     }
161     
162     i=list_elem(args,1);
163     if (i) {
164         if (i->type!=t_string) {
165             cfgfatal(i->loc,"rsa-public","second argument must be a string");
166         }
167         n=i->data.string;
168         if (mpz_init_set_str(&st->n,n,10)!=0) {
169             cfgfatal(i->loc,"rsa-public","modulus \"%s\" is not a decimal "
170                      "number string\n",n);
171         }
172     } else {
173         cfgfatal(loc,"rsa-public","you must provide a modulus\n");
174     }
175     return new_closure(&st->cl);
176 }
177
178 static uint32_t keyfile_get_int(FILE *f)
179 {
180     uint32_t r;
181     r=fgetc(f)<<24;
182     r|=fgetc(f)<<16;
183     r|=fgetc(f)<<8;
184     r|=fgetc(f);
185     return r;
186 }
187
188 static uint16_t keyfile_get_short(FILE *f)
189 {
190     uint16_t r;
191     r=fgetc(f)<<8;
192     r|=fgetc(f);
193     return r;
194 }
195
196 static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context,
197                              list_t *args)
198 {
199     struct rsapriv *st;
200     FILE *f;
201     string_t filename;
202     item_t *i;
203     long length;
204     uint8_t *b, *c;
205     int cipher_type;
206     MP_INT e,sig,plain,check;
207
208     st=safe_malloc(sizeof(*st),"rsapriv_apply");
209     st->cl.description="rsapriv";
210     st->cl.type=CL_RSAPRIVKEY;
211     st->cl.apply=NULL;
212     st->cl.interface=&st->ops;
213     st->ops.st=st;
214     st->ops.sign=rsa_sign;
215     st->loc=loc;
216
217     /* Argument is filename pointing to SSH1 private key file */
218     i=list_elem(args,0);
219     if (i) {
220         if (i->type!=t_string) {
221             cfgfatal(i->loc,"rsa-public","first argument must be a string");
222         }
223         filename=i->data.string;
224     } else {
225         filename=""; /* Make compiler happy */
226         cfgfatal(loc,"rsa-private","you must provide a filename\n");
227     }
228
229     f=fopen(filename,"rb");
230     if (!f) {
231         fatal_perror("rsa-private (%s:%d): cannot open file \"%s\"",
232                      loc.file,loc.line,filename);
233     }
234
235     /* Check that the ID string is correct */
236     length=strlen(AUTHFILE_ID_STRING)+1;
237     b=safe_malloc(length,"rsapriv_apply");
238     if (fread(b,length,1,f)!=1 || memcmp(b,AUTHFILE_ID_STRING,length)!=0) {
239         cfgfatal(loc,"rsa-private","file \"%s\" is not a "
240                  "SSH1 private keyfile\n",filename);
241     }
242     free(b);
243
244     cipher_type=fgetc(f);
245     keyfile_get_int(f); /* "Reserved data" */
246     if (cipher_type != 0) {
247         cfgfatal(loc,"rsa-private","we don't support encrypted keyfiles\n");
248     }
249
250     /* Read the public key */
251     keyfile_get_int(f); /* Not sure what this is */
252     length=(keyfile_get_short(f)+7)/8;
253     if (length>1024) {
254         cfgfatal(loc,"rsa-private","implausible length %ld for modulus\n",
255                  length);
256     }
257     b=safe_malloc(length,"rsapriv_apply");
258     if (fread(b,length,1,f) != 1) {
259         cfgfatal(loc,"rsa-private","error reading modulus\n");
260     }
261     mpz_init(&st->n);
262     read_mpbin(&st->n,b,length);
263     free(b);
264     length=(keyfile_get_short(f)+7)/8;
265     if (length>1024) {
266         cfgfatal(loc,"rsa-private","implausible length %ld for e\n",length);
267     }
268     b=safe_malloc(length,"rsapriv_apply");
269     if (fread(b,length,1,f)!=1) {
270         cfgfatal(loc,"rsa-private","error reading e\n");
271     }
272     mpz_init(&e);
273     read_mpbin(&e,b,length);
274     free(b);
275     
276     length=keyfile_get_int(f);
277     if (length>1024) {
278         cfgfatal(loc,"rsa-private","implausibly long (%ld) key comment\n",
279                  length);
280     }
281     c=safe_malloc(length+1,"rsapriv_apply");
282     if (fread(c,length,1,f)!=1) {
283         cfgfatal(loc,"rsa-private","error reading key comment\n");
284     }
285     c[length]=0;
286
287     /* Check that the next two pairs of characters are identical - the
288        keyfile is not encrypted, so they should be */
289     if (keyfile_get_short(f) != keyfile_get_short(f)) {
290         cfgfatal(loc,"rsa-private","corrupt keyfile\n");
291     }
292
293     /* Read d */
294     length=(keyfile_get_short(f)+7)/8;
295     if (length>1024) {
296         cfgfatal(loc,"rsa-private","implausibly long (%ld) decryption key\n",
297                  length);
298     }
299     b=safe_malloc(length,"rsapriv_apply");
300     if (fread(b,length,1,f)!=1) {
301         cfgfatal(loc,"rsa-private","error reading decryption key\n");
302     }
303     mpz_init(&st->d);
304     read_mpbin(&st->d,b,length);
305     free(b);
306     
307     if (fclose(f)!=0) {
308         fatal_perror("rsa-private (%s:%d): fclose",loc.file,loc.line);
309     }
310
311     /* Now do trial signature/check to make sure it's a real keypair:
312        sign the comment string! */
313     mpz_init(&sig);
314     mpz_init(&plain);
315     mpz_init(&check);
316     read_mpbin(&plain,c,strlen(c));
317     mpz_powm(&sig, &plain, &st->d, &st->n);
318     mpz_powm(&check, &sig, &e, &st->n);
319     if (mpz_cmp(&plain,&check)!=0) {
320         cfgfatal(loc,"rsa-private","file \"%s\" does not contain a "
321                  "valid RSA key!\n",filename);
322     }
323     mpz_clear(&sig);
324     mpz_clear(&plain);
325     mpz_clear(&check);
326
327     free(c);
328     mpz_clear(&e);
329
330     return new_closure(&st->cl);
331 }
332
333 init_module rsa_module;
334 void rsa_module(dict_t *dict)
335 {
336     add_closure(dict,"rsa-private",rsapriv_apply);
337     add_closure(dict,"rsa-public",rsapub_apply);
338 }