chiark / gitweb /
Import release 0.1.3
[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     if (datalen*2+4>=msize) {
66         fatal("rsa_sign: message too big\n");
67     }
68
69     strcpy(buff,"0001");
70
71     for (i=0; i<datalen; i++) {
72         buff[4+i*2]=hexchars[(data[i]&0xf0)>>4];
73         buff[5+i*2]=hexchars[data[i]&0xf];
74     }
75     buff[4+datalen*2]=0;
76     
77     for (i=datalen*2+4; i<msize; i++)
78         buff[i]='f';
79
80     buff[msize]=0;
81
82     mpz_set_str(&a, buff, 16);
83
84     mpz_powm(&b, &a, &st->d, &st->n);
85
86     signature=write_mpstring(&b);
87
88     mpz_clear(&b);
89     mpz_clear(&a);
90     return signature;
91 }
92
93 static bool_t rsa_sig_check(void *sst, uint8_t *data, uint32_t datalen,
94                             string_t signature)
95 {
96     struct rsapub *st=sst;
97     MP_INT a, b, c;
98     char buff[2048];
99     int msize, i;
100     bool_t ok;
101
102     mpz_init(&a);
103     mpz_init(&b);
104     mpz_init(&c);
105
106     msize=mpz_sizeinbase(&st->n, 16);
107
108     strcpy(buff,"0001");
109
110     for (i=0; i<datalen; i++) {
111         buff[4+i*2]=hexchars[(data[i]&0xf0)>>4];
112         buff[5+i*2]=hexchars[data[i]&0xf];
113     }
114     buff[4+datalen*2]=0;
115
116     for (i=datalen*2+4; i<msize; i++)
117         buff[i]='f';
118
119     buff[msize]=0;
120
121     mpz_set_str(&a, buff, 16);
122
123     mpz_set_str(&b, signature, 16);
124
125     mpz_powm(&c, &b, &st->e, &st->n);
126
127     ok=(mpz_cmp(&a, &c)==0);
128
129     mpz_clear(&c);
130     mpz_clear(&b);
131     mpz_clear(&a);
132
133     return ok;
134 }
135
136 static list_t *rsapub_apply(closure_t *self, struct cloc loc, dict_t *context,
137                             list_t *args)
138 {
139     struct rsapub *st;
140     item_t *i;
141     string_t e,n;
142
143     st=safe_malloc(sizeof(*st),"rsapub_apply");
144     st->cl.description="rsapub";
145     st->cl.type=CL_RSAPUBKEY;
146     st->cl.apply=NULL;
147     st->cl.interface=&st->ops;
148     st->ops.st=st;
149     st->ops.check=rsa_sig_check;
150     st->loc=loc;
151
152     i=list_elem(args,0);
153     if (i) {
154         if (i->type!=t_string) {
155             cfgfatal(i->loc,"rsa-public","first argument must be a string");
156         }
157         e=i->data.string;
158         if (mpz_init_set_str(&st->e,e,10)!=0) {
159             cfgfatal(i->loc,"rsa-public","encryption key \"%s\" is not a "
160                      "decimal number string\n",e);
161         }
162     } else {
163         cfgfatal(loc,"rsa-public","you must provide an encryption key\n");
164     }
165     
166     i=list_elem(args,1);
167     if (i) {
168         if (i->type!=t_string) {
169             cfgfatal(i->loc,"rsa-public","second argument must be a string");
170         }
171         n=i->data.string;
172         if (mpz_init_set_str(&st->n,n,10)!=0) {
173             cfgfatal(i->loc,"rsa-public","modulus \"%s\" is not a decimal "
174                      "number string\n",n);
175         }
176     } else {
177         cfgfatal(loc,"rsa-public","you must provide a modulus\n");
178     }
179     return new_closure(&st->cl);
180 }
181
182 static uint32_t keyfile_get_int(FILE *f)
183 {
184     uint32_t r;
185     r=fgetc(f)<<24;
186     r|=fgetc(f)<<16;
187     r|=fgetc(f)<<8;
188     r|=fgetc(f);
189     return r;
190 }
191
192 static uint16_t keyfile_get_short(FILE *f)
193 {
194     uint16_t r;
195     r=fgetc(f)<<8;
196     r|=fgetc(f);
197     return r;
198 }
199
200 static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context,
201                              list_t *args)
202 {
203     struct rsapriv *st;
204     FILE *f;
205     string_t filename;
206     item_t *i;
207     long length;
208     uint8_t *b, *c;
209     int cipher_type;
210     MP_INT e,sig,plain,check;
211
212     st=safe_malloc(sizeof(*st),"rsapriv_apply");
213     st->cl.description="rsapriv";
214     st->cl.type=CL_RSAPRIVKEY;
215     st->cl.apply=NULL;
216     st->cl.interface=&st->ops;
217     st->ops.st=st;
218     st->ops.sign=rsa_sign;
219     st->loc=loc;
220
221     /* Argument is filename pointing to SSH1 private key file */
222     i=list_elem(args,0);
223     if (i) {
224         if (i->type!=t_string) {
225             cfgfatal(i->loc,"rsa-public","first argument must be a string");
226         }
227         filename=i->data.string;
228     } else {
229         filename=""; /* Make compiler happy */
230         cfgfatal(loc,"rsa-private","you must provide a filename\n");
231     }
232
233     f=fopen(filename,"rb");
234     if (!f) {
235         if (just_check_config) {
236             Message(M_WARNING,"rsa-private (%s:%d): cannot open keyfile "
237                     "\"%s\"; assuming it's valid while we check the "
238                     "rest of the configuration\n",loc.file,loc.line,filename);
239             goto assume_valid;
240         } else {
241             fatal_perror("rsa-private (%s:%d): cannot open file \"%s\"",
242                          loc.file,loc.line,filename);
243         }
244     }
245
246     /* Check that the ID string is correct */
247     length=strlen(AUTHFILE_ID_STRING)+1;
248     b=safe_malloc(length,"rsapriv_apply");
249     if (fread(b,length,1,f)!=1 || memcmp(b,AUTHFILE_ID_STRING,length)!=0) {
250         cfgfatal(loc,"rsa-private","file \"%s\" is not a "
251                  "SSH1 private keyfile\n",filename);
252     }
253     free(b);
254
255     cipher_type=fgetc(f);
256     keyfile_get_int(f); /* "Reserved data" */
257     if (cipher_type != 0) {
258         cfgfatal(loc,"rsa-private","we don't support encrypted keyfiles\n");
259     }
260
261     /* Read the public key */
262     keyfile_get_int(f); /* Not sure what this is */
263     length=(keyfile_get_short(f)+7)/8;
264     if (length>1024) {
265         cfgfatal(loc,"rsa-private","implausible length %ld for modulus\n",
266                  length);
267     }
268     b=safe_malloc(length,"rsapriv_apply");
269     if (fread(b,length,1,f) != 1) {
270         cfgfatal(loc,"rsa-private","error reading modulus\n");
271     }
272     mpz_init(&st->n);
273     read_mpbin(&st->n,b,length);
274     free(b);
275     length=(keyfile_get_short(f)+7)/8;
276     if (length>1024) {
277         cfgfatal(loc,"rsa-private","implausible length %ld for e\n",length);
278     }
279     b=safe_malloc(length,"rsapriv_apply");
280     if (fread(b,length,1,f)!=1) {
281         cfgfatal(loc,"rsa-private","error reading e\n");
282     }
283     mpz_init(&e);
284     read_mpbin(&e,b,length);
285     free(b);
286     
287     length=keyfile_get_int(f);
288     if (length>1024) {
289         cfgfatal(loc,"rsa-private","implausibly long (%ld) key comment\n",
290                  length);
291     }
292     c=safe_malloc(length+1,"rsapriv_apply");
293     if (fread(c,length,1,f)!=1) {
294         cfgfatal(loc,"rsa-private","error reading key comment\n");
295     }
296     c[length]=0;
297
298     /* Check that the next two pairs of characters are identical - the
299        keyfile is not encrypted, so they should be */
300     if (keyfile_get_short(f) != keyfile_get_short(f)) {
301         cfgfatal(loc,"rsa-private","corrupt keyfile\n");
302     }
303
304     /* Read d */
305     length=(keyfile_get_short(f)+7)/8;
306     if (length>1024) {
307         cfgfatal(loc,"rsa-private","implausibly long (%ld) decryption key\n",
308                  length);
309     }
310     b=safe_malloc(length,"rsapriv_apply");
311     if (fread(b,length,1,f)!=1) {
312         cfgfatal(loc,"rsa-private","error reading decryption key\n");
313     }
314     mpz_init(&st->d);
315     read_mpbin(&st->d,b,length);
316     free(b);
317     
318     if (fclose(f)!=0) {
319         fatal_perror("rsa-private (%s:%d): fclose",loc.file,loc.line);
320     }
321
322     /* Now do trial signature/check to make sure it's a real keypair:
323        sign the comment string! */
324     i=list_elem(args,1);
325     if (i && i->type==t_bool && i->data.bool==False) {
326         Message(M_INFO,"rsa-private (%s:%d): skipping RSA key validity "
327                 "check\n",loc.file,loc.line);
328     } else {
329         mpz_init(&sig);
330         mpz_init(&plain);
331         mpz_init(&check);
332         read_mpbin(&plain,c,strlen(c));
333         mpz_powm(&sig, &plain, &st->d, &st->n);
334         mpz_powm(&check, &sig, &e, &st->n);
335         if (mpz_cmp(&plain,&check)!=0) {
336             cfgfatal(loc,"rsa-private","file \"%s\" does not contain a "
337                      "valid RSA key!\n",filename);
338         }
339         mpz_clear(&sig);
340         mpz_clear(&plain);
341         mpz_clear(&check);
342     }
343
344     free(c);
345     mpz_clear(&e);
346
347 assume_valid:
348     return new_closure(&st->cl);
349 }
350
351 init_module rsa_module;
352 void rsa_module(dict_t *dict)
353 {
354     add_closure(dict,"rsa-private",rsapriv_apply);
355     add_closure(dict,"rsa-public",rsapub_apply);
356 }