Make fips algorithm test utilities use RESP_EOL for end of line character(s).
[openssl.git] / fips / dsa / fips_dssvs.c
1
2 #define OPENSSL_FIPSAPI
3 #include <openssl/opensslconf.h>
4
5 #ifndef OPENSSL_FIPS
6 #include <stdio.h>
7
8 int main(int argc, char **argv)
9 {
10     printf("No FIPS DSA support\n");
11     return(0);
12 }
13 #else
14
15 #include <openssl/bn.h>
16 #include <openssl/dsa.h>
17 #include <openssl/fips.h>
18 #include <openssl/err.h>
19 #include <openssl/evp.h>
20 #include <string.h>
21 #include <ctype.h>
22
23 #include "fips_utl.h"
24
25 static int parse_mod(char *line, int *pdsa2, int *pL, int *pN,
26                                 const EVP_MD **pmd)
27         {
28         char lbuf[10240];
29         char *keyword, *value;
30
31         char *p;
32         p = strchr(line, ',');
33         if (!p)
34                 {
35                 *pL = atoi(line);
36                 *pdsa2 = 0;
37                 *pN = 160;
38                 if (pmd)
39                         *pmd = EVP_sha1();
40                 return 1;
41                 }
42         *pdsa2 = 1;
43         *p = 0;
44         if (!parse_line2(&keyword, &value, lbuf, line, 0))
45                 return 0;
46         if (strcmp(keyword, "L"))
47                 return 0;
48         *pL = atoi(value);
49         strcpy(line, p + 1);
50         if (pmd)
51                 p = strchr(line, ',');
52         else
53                 p = strchr(line, ']');
54         if (!p)
55                 return 0;
56         *p = 0;
57         if (!parse_line2(&keyword, &value, lbuf, line, 0))
58                 return 0;
59         if (strcmp(keyword, "N"))
60                 return 0;
61         *pN = atoi(value);
62         if (!pmd)
63                 return 1;
64         strcpy(line, p + 1);
65         p = strchr(line, ']');
66         if (!p)
67                 return 0;
68         *p = 0;
69         p = line;
70         while(isspace(*p))
71                 p++;
72         if (!strcmp(p, "SHA-1"))
73                 *pmd = EVP_sha1();
74         else if (!strcmp(p, "SHA-224"))
75                 *pmd = EVP_sha224();
76         else if (!strcmp(p, "SHA-256"))
77                 *pmd = EVP_sha256();
78         else if (!strcmp(p, "SHA-384"))
79                 *pmd = EVP_sha384();
80         else if (!strcmp(p, "SHA-512"))
81                 *pmd = EVP_sha512();
82         else
83                 return 0;
84         return 1;
85         }
86
87 static void primes(FILE *in, FILE *out)
88     {
89     char buf[10240];
90     char lbuf[10240];
91     char *keyword, *value;
92
93     while(fgets(buf,sizeof buf,in) != NULL)
94         {
95         fputs(buf,out);
96         if (!parse_line(&keyword, &value, lbuf, buf))
97                 continue;
98         if(!strcmp(keyword,"Prime"))
99             {
100             BIGNUM *pp;
101
102             pp=BN_new();
103             do_hex2bn(&pp,value);
104             fprintf(out, "result= %c" RESP_EOL,
105                    BN_is_prime_ex(pp,20,NULL,NULL) ? 'P' : 'F');
106             }       
107         }
108     }
109
110 int dsa_builtin_paramgen(DSA *ret, size_t bits, size_t qbits,
111         const EVP_MD *evpmd, const unsigned char *seed_in, size_t seed_len,
112         unsigned char *seed_out,
113         int *counter_ret, unsigned long *h_ret, BN_GENCB *cb);
114 int dsa_builtin_paramgen2(DSA *ret, size_t L, size_t N,
115         const EVP_MD *evpmd, const unsigned char *seed_in, size_t seed_len,
116         int idx, unsigned char *seed_out,
117         int *counter_ret, unsigned long *h_ret, BN_GENCB *cb);
118
119 static void pqg(FILE *in, FILE *out)
120     {
121     char buf[1024];
122     char lbuf[1024];
123     char *keyword, *value;
124     int dsa2, L, N;
125     const EVP_MD *md = NULL;
126     BIGNUM *p = NULL, *q = NULL;
127     enum pqtype { PQG_NONE, PQG_PQ, PQG_G, PQG_GCANON}
128                 pqg_type = PQG_NONE;
129     int seedlen=-1, idxlen, idx = -1;
130     unsigned char seed[1024], idtmp[1024];
131
132     while(fgets(buf,sizeof buf,in) != NULL)
133         {
134         if (buf[0] == '[')
135                 {
136                 if (strstr(buf, "Probable"))
137                         pqg_type = PQG_PQ;
138                 else if (strstr(buf, "Unverifiable"))
139                         pqg_type = PQG_G;
140                 else if (strstr(buf, "Canonical"))
141                         pqg_type = PQG_GCANON;
142                 }
143         if (!parse_line(&keyword, &value, lbuf, buf))
144                 {
145                 fputs(buf,out);
146                 continue;
147                 }
148         if (strcmp(keyword, "Num"))
149                 fputs(buf,out);
150         if(!strcmp(keyword,"[mod"))
151             {
152             if (!parse_mod(value, &dsa2, &L, &N, &md))
153                 {
154                 fprintf(stderr, "Mod Parse Error\n");
155                 exit (1);
156                 }
157             }
158         else if(!strcmp(keyword,"N") 
159                 || (!strcmp(keyword, "Num") && pqg_type == PQG_PQ))
160             {
161             int n=atoi(value);
162
163             while(n--)
164                 {
165                 DSA *dsa;
166                 int counter;
167                 unsigned long h;
168                 dsa = FIPS_dsa_new();
169
170                 if (!dsa2 && !dsa_builtin_paramgen(dsa, L, N, md,
171                                                 NULL, 0, seed,
172                                                 &counter, &h, NULL))
173                         {
174                         fprintf(stderr, "Parameter Generation error\n");
175                         exit(1);
176                         }
177                 if (dsa2 && dsa_builtin_paramgen2(dsa, L, N, md,
178                                                 NULL, 0, -1, seed,
179                                                 &counter, &h, NULL) <= 0)
180                         {
181                         fprintf(stderr, "Parameter Generation error\n");
182                         exit(1);
183                         }
184  
185                 do_bn_print_name(out, "P",dsa->p);
186                 do_bn_print_name(out, "Q",dsa->q);
187                 if (!dsa2)
188                         do_bn_print_name(out, "G",dsa->g);
189                 OutputValue(dsa2 ? "domain_parameter_seed" : "Seed",
190                                 seed, M_EVP_MD_size(md), out, 0);
191                 if (!dsa2)
192                         {
193                         fprintf(out, "c = %d" RESP_EOL, counter);
194                         fprintf(out, "H = %lx" RESP_EOL RESP_EOL,h);
195                         }
196                 else
197                         {
198                         fprintf(out, "counter = %d" RESP_EOL RESP_EOL, counter);
199                         }
200                 }
201             }
202         else if(!strcmp(keyword,"P"))
203             p=hex2bn(value);
204         else if(!strcmp(keyword,"Q"))
205             q=hex2bn(value);
206         else if(!strcmp(keyword,"domain_parameter_seed"))
207             seedlen = hex2bin(value, seed);
208         else if(!strcmp(keyword,"firstseed"))
209             seedlen = hex2bin(value, seed);
210         else if(!strcmp(keyword,"pseed"))
211             seedlen += hex2bin(value, seed + seedlen);
212         else if(!strcmp(keyword,"qseed"))
213             seedlen += hex2bin(value, seed + seedlen);
214         else if(!strcmp(keyword,"index"))
215             {
216             idxlen = hex2bin(value, idtmp);
217             if (idxlen != 1)
218                 {
219                 fprintf(stderr, "Index value error\n");
220                 exit (1);
221                 }
222             idx = idtmp[0];
223             }
224         if ((idx >= 0 && pqg_type == PQG_GCANON) || (q && pqg_type == PQG_G))
225                 {
226                 DSA *dsa;
227                 dsa = FIPS_dsa_new();
228                 dsa->p = p;
229                 dsa->q = q;
230                 p = q = NULL;
231                 if (dsa_builtin_paramgen2(dsa, L, N, md,
232                                                 seed, seedlen, idx, NULL,
233                                                 NULL, NULL, NULL) <= 0)
234                         {
235                         fprintf(stderr, "Parameter Generation error\n");
236                         exit(1);
237                         }
238                 do_bn_print_name(out, "G",dsa->g);
239                 FIPS_dsa_free(dsa);
240                 idx = -1;
241                 }
242         }
243     }
244
245 static void pqgver(FILE *in, FILE *out)
246     {
247     char buf[1024];
248     char lbuf[1024];
249     char *keyword, *value;
250     BIGNUM *p = NULL, *q = NULL, *g = NULL;
251     int counter=-1, counter2;
252     unsigned long h=0, h2;
253     DSA *dsa=NULL;
254     int dsa2, L, N, part_test = 0;
255     const EVP_MD *md = NULL;
256     int seedlen=-1, idxlen, idx = -1;
257     unsigned char seed[1024], idtmp[1024];
258
259     while(fgets(buf,sizeof buf,in) != NULL)
260         {
261         if (!parse_line(&keyword, &value, lbuf, buf))
262                 {
263                 if (p && q)
264                         {
265                         part_test = 1;
266                         goto partial;
267                         }
268                 fputs(buf,out);
269                 continue;
270                 }
271         fputs(buf, out);
272         if(!strcmp(keyword,"[mod"))
273             {
274             if (!parse_mod(value, &dsa2, &L, &N, &md))
275                 {
276                 fprintf(stderr, "Mod Parse Error\n");
277                 exit (1);
278                 }
279             }
280         else if(!strcmp(keyword,"P"))
281             p=hex2bn(value);
282         else if(!strcmp(keyword,"Q"))
283             q=hex2bn(value);
284         else if(!strcmp(keyword,"G"))
285             g=hex2bn(value);
286         else if(!strcmp(keyword,"Seed")
287                 || !strcmp(keyword,"domain_parameter_seed"))
288             {
289             seedlen = hex2bin(value, seed);
290             if (!dsa2 && seedlen != 20)
291                 {
292                 fprintf(stderr, "Seed parse length error\n");
293                 exit (1);
294                 }
295             if (idx > 0)
296                 part_test = 1;
297             }
298         else if(!strcmp(keyword,"index"))
299             {
300             idxlen = hex2bin(value, idtmp);
301             if (idxlen != 1)
302                 {
303                 fprintf(stderr, "Index value error\n");
304                 exit (1);
305                 }
306             idx = idtmp[0];
307             }
308         else if(!strcmp(keyword,"c"))
309             counter = atoi(buf+4);
310         partial:
311         if(!strcmp(keyword,"H") || part_test)
312             {
313             if (!part_test)
314                 h = atoi(value);
315             if (!p || !q || (!g && !part_test))
316                 {
317                 fprintf(stderr, "Parse Error\n");
318                 exit (1);
319                 }
320             dsa = FIPS_dsa_new();
321             if (idx >= 0)
322                 {
323                 dsa->p = BN_dup(p);
324                 dsa->q = BN_dup(q);
325                 }
326             no_err = 1;
327             if (!dsa2 && !dsa_builtin_paramgen(dsa, L, N, md,
328                                         seed, seedlen, NULL,
329                                         &counter2, &h2, NULL))
330                         {
331                         fprintf(stderr, "Parameter Generation error\n");
332                         exit(1);
333                         }
334             if (dsa2 && dsa_builtin_paramgen2(dsa, L, N, md,
335                                         seed, seedlen, idx, NULL,
336                                         &counter2, &h2, NULL) < 0)
337                         {
338                         fprintf(stderr, "Parameter Generation error\n");
339                         exit(1);
340                         }
341             no_err = 0;
342             if (idx >= 0)
343                 {
344                 if (BN_cmp(dsa->g, g))
345                         fprintf(out, "Result = F" RESP_EOL);
346                 else
347                         fprintf(out, "Result = P" RESP_EOL);
348                 }
349             else if (BN_cmp(dsa->p, p) || BN_cmp(dsa->q, q) || 
350                 (!part_test &&
351                 ((BN_cmp(dsa->g, g) || (counter != counter2) || (h != h2)))))
352                 fprintf(out, "Result = F" RESP_EOL);
353             else
354                 fprintf(out, "Result = P" RESP_EOL);
355             BN_free(p);
356             BN_free(q);
357             BN_free(g);
358             p = NULL;
359             q = NULL;
360             g = NULL;
361             FIPS_dsa_free(dsa);
362             dsa = NULL;
363             if (part_test)
364                 {
365                 if (idx == -1)
366                         fputs(buf,out);
367                 part_test = 0;
368                 }
369             idx = -1;
370             }
371         }
372     }
373
374 /* Keypair verification routine. NB: this isn't part of the standard FIPS140-2
375  * algorithm tests. It is an additional test to perform sanity checks on the
376  * output of the KeyPair test.
377  */
378
379 static int dss_paramcheck(int L, int N, BIGNUM *p, BIGNUM *q, BIGNUM *g,
380                                                         BN_CTX *ctx)
381     {
382     BIGNUM *rem = NULL;
383     if (BN_num_bits(p) != L)
384         return 0;
385     if (BN_num_bits(q) != N)
386         return 0;
387     if (BN_is_prime_ex(p, BN_prime_checks, ctx, NULL) != 1)
388         return 0;
389     if (BN_is_prime_ex(q, BN_prime_checks, ctx, NULL) != 1)
390         return 0;
391     rem = BN_new();
392     if (!BN_mod(rem, p, q, ctx) || !BN_is_one(rem)
393         || (BN_cmp(g, BN_value_one()) <= 0)
394         || !BN_mod_exp(rem, g, q, p, ctx) || !BN_is_one(rem))
395         {
396         BN_free(rem);
397         return 0;
398         }
399     /* Todo: check g */
400     BN_free(rem);
401     return 1;
402     }
403
404 static void keyver(FILE *in, FILE *out)
405     {
406     char buf[1024];
407     char lbuf[1024];
408     char *keyword, *value;
409     BIGNUM *p = NULL, *q = NULL, *g = NULL, *X = NULL, *Y = NULL;
410     BIGNUM *Y2;
411     BN_CTX *ctx = NULL;
412     int dsa2, L, N;
413     int paramcheck = 0;
414
415     ctx = BN_CTX_new();
416     Y2 = BN_new();
417
418     while(fgets(buf,sizeof buf,in) != NULL)
419         {
420         if (!parse_line(&keyword, &value, lbuf, buf))
421                 {
422                 fputs(buf,out);
423                 continue;
424                 }
425         if(!strcmp(keyword,"[mod"))
426             {
427             if (p)
428                 BN_free(p);
429             p = NULL;
430             if (q)
431                 BN_free(q);
432             q = NULL;
433             if (g)
434                 BN_free(g);
435             g = NULL;
436             paramcheck = 0;
437             if (!parse_mod(value, &dsa2, &L, &N, NULL))
438                 {
439                 fprintf(stderr, "Mod Parse Error\n");
440                 exit (1);
441                 }
442             }
443         else if(!strcmp(keyword,"P"))
444             p=hex2bn(value);
445         else if(!strcmp(keyword,"Q"))
446             q=hex2bn(value);
447         else if(!strcmp(keyword,"G"))
448             g=hex2bn(value);
449         else if(!strcmp(keyword,"X"))
450             X=hex2bn(value);
451         else if(!strcmp(keyword,"Y"))
452             {
453             Y=hex2bn(value);
454             if (!p || !q || !g || !X || !Y)
455                 {
456                 fprintf(stderr, "Parse Error\n");
457                 exit (1);
458                 }
459             do_bn_print_name(out, "P",p);
460             do_bn_print_name(out, "Q",q);
461             do_bn_print_name(out, "G",g);
462             do_bn_print_name(out, "X",X);
463             do_bn_print_name(out, "Y",Y);
464             if (!paramcheck)
465                 {
466                 if (dss_paramcheck(L, N, p, q, g, ctx))
467                         paramcheck = 1;
468                 else
469                         paramcheck = -1;
470                 }
471             if (paramcheck != 1)
472                 fprintf(out, "Result = F" RESP_EOL);
473             else
474                 {
475                 if (!BN_mod_exp(Y2, g, X, p, ctx) || BN_cmp(Y2, Y))
476                         fprintf(out, "Result = F" RESP_EOL);
477                 else
478                         fprintf(out, "Result = P" RESP_EOL);
479                 }
480             BN_free(X);
481             BN_free(Y);
482             X = NULL;
483             Y = NULL;
484             }
485         }
486         if (p)
487             BN_free(p);
488         if (q)
489             BN_free(q);
490         if (g)
491             BN_free(g);
492         if (Y2)
493             BN_free(Y2);
494     }
495
496 static void keypair(FILE *in, FILE *out)
497     {
498     char buf[1024];
499     char lbuf[1024];
500     char *keyword, *value;
501     int dsa2, L, N;
502
503     while(fgets(buf,sizeof buf,in) != NULL)
504         {
505         if (!parse_line(&keyword, &value, lbuf, buf))
506                 {
507                 continue;
508                 }
509         if(!strcmp(keyword,"[mod"))
510             {
511             if (!parse_mod(value, &dsa2, &L, &N, NULL))
512                 {
513                 fprintf(stderr, "Mod Parse Error\n");
514                 exit (1);
515                 }
516             fputs(buf,out);
517             }
518         else if(!strcmp(keyword,"N"))
519             {
520             DSA *dsa;
521             int n=atoi(value);
522
523             dsa = FIPS_dsa_new();
524             if (!dsa2 && !dsa_builtin_paramgen(dsa, L, N, NULL, NULL, 0,
525                                                 NULL, NULL, NULL, NULL))
526                         {
527                         fprintf(stderr, "Parameter Generation error\n");
528                         exit(1);
529                         }
530             if (dsa2 && dsa_builtin_paramgen2(dsa, L, N, NULL, NULL, 0, -1,
531                                                 NULL, NULL, NULL, NULL) <= 0)
532                         {
533                         fprintf(stderr, "Parameter Generation error\n");
534                         exit(1);
535                         }
536             do_bn_print_name(out, "P",dsa->p);
537             do_bn_print_name(out, "Q",dsa->q);
538             do_bn_print_name(out, "G",dsa->g);
539             fputs(RESP_EOL, out);
540
541             while(n--)
542                 {
543                 if (!DSA_generate_key(dsa))
544                         exit(1);
545
546                 do_bn_print_name(out, "X",dsa->priv_key);
547                 do_bn_print_name(out, "Y",dsa->pub_key);
548                 fputs(RESP_EOL, out);
549                 }
550             }
551         }
552     }
553
554 static void siggen(FILE *in, FILE *out)
555     {
556     char buf[1024];
557     char lbuf[1024];
558     char *keyword, *value;
559     int dsa2, L, N;
560     const EVP_MD *md = NULL;
561     DSA *dsa=NULL;
562
563     while(fgets(buf,sizeof buf,in) != NULL)
564         {
565         if (!parse_line(&keyword, &value, lbuf, buf))
566                 {
567                 fputs(buf,out);
568                 continue;
569                 }
570         fputs(buf,out);
571         if(!strcmp(keyword,"[mod"))
572             {
573             if (!parse_mod(value, &dsa2, &L, &N, &md))
574                 {
575                 fprintf(stderr, "Mod Parse Error\n");
576                 exit (1);
577                 }
578             if (dsa)
579                 FIPS_dsa_free(dsa);
580             dsa = FIPS_dsa_new();
581             if (!dsa2 && !dsa_builtin_paramgen(dsa, L, N, md, NULL, 0,
582                                                 NULL, NULL, NULL, NULL))
583                         {
584                         fprintf(stderr, "Parameter Generation error\n");
585                         exit(1);
586                         }
587             if (dsa2 && dsa_builtin_paramgen2(dsa, L, N, md, NULL, 0, -1,
588                                                 NULL, NULL, NULL, NULL) <= 0)
589                         {
590                         fprintf(stderr, "Parameter Generation error\n");
591                         exit(1);
592                         }
593             do_bn_print_name(out, "P",dsa->p);
594             do_bn_print_name(out, "Q",dsa->q);
595             do_bn_print_name(out, "G",dsa->g);
596             fputs(RESP_EOL, out);
597             }
598         else if(!strcmp(keyword,"Msg"))
599             {
600             unsigned char msg[1024];
601             int n;
602             EVP_MD_CTX mctx;
603             DSA_SIG *sig;
604             FIPS_md_ctx_init(&mctx);
605
606             n=hex2bin(value,msg);
607
608             if (!DSA_generate_key(dsa))
609                 exit(1);
610             do_bn_print_name(out, "Y",dsa->pub_key);
611
612             FIPS_digestinit(&mctx, md);
613             FIPS_digestupdate(&mctx, msg, n);
614             sig = FIPS_dsa_sign_ctx(dsa, &mctx);
615
616             do_bn_print_name(out, "R",sig->r);
617             do_bn_print_name(out, "S",sig->s);
618             fputs(RESP_EOL, out);
619             FIPS_dsa_sig_free(sig);
620             FIPS_md_ctx_cleanup(&mctx);
621             }
622         }
623         if (dsa)
624                 FIPS_dsa_free(dsa);
625     }
626
627 static void sigver(FILE *in, FILE *out)
628     {
629     DSA *dsa=NULL;
630     char buf[1024];
631     char lbuf[1024];
632     unsigned char msg[1024];
633     char *keyword, *value;
634     int n=0;
635     int dsa2, L, N;
636     const EVP_MD *md = NULL;
637     DSA_SIG sg, *sig = &sg;
638
639     sig->r = NULL;
640     sig->s = NULL;
641
642     while(fgets(buf,sizeof buf,in) != NULL)
643         {
644         if (!parse_line(&keyword, &value, lbuf, buf))
645                 {
646                 fputs(buf,out);
647                 continue;
648                 }
649         fputs(buf,out);
650         if(!strcmp(keyword,"[mod"))
651             {
652             if (!parse_mod(value, &dsa2, &L, &N, &md))
653                 {
654                 fprintf(stderr, "Mod Parse Error\n");
655                 exit (1);
656                 }
657             if (dsa)
658                 FIPS_dsa_free(dsa);
659             dsa = FIPS_dsa_new();
660             }
661         else if(!strcmp(keyword,"P"))
662             dsa->p=hex2bn(value);
663         else if(!strcmp(keyword,"Q"))
664             dsa->q=hex2bn(value);
665         else if(!strcmp(keyword,"G"))
666             dsa->g=hex2bn(value);
667         else if(!strcmp(keyword,"Msg"))
668             n=hex2bin(value,msg);
669         else if(!strcmp(keyword,"Y"))
670             dsa->pub_key=hex2bn(value);
671         else if(!strcmp(keyword,"R"))
672             sig->r=hex2bn(value);
673         else if(!strcmp(keyword,"S"))
674             {
675             EVP_MD_CTX mctx;
676             int r;
677             FIPS_md_ctx_init(&mctx);
678             sig->s=hex2bn(value);
679
680             FIPS_digestinit(&mctx, md);
681             FIPS_digestupdate(&mctx, msg, n);
682             no_err = 1;
683             r = FIPS_dsa_verify_ctx(dsa, &mctx, sig);
684             no_err = 0;
685             FIPS_md_ctx_cleanup(&mctx);
686         
687             fprintf(out, "Result = %c" RESP_EOL RESP_EOL, r == 1 ? 'P' : 'F');
688             }
689         }
690     }
691
692 int main(int argc,char **argv)
693     {
694     FILE *in, *out;
695     if (argc == 4)
696         {
697         in = fopen(argv[2], "r");
698         if (!in)
699                 {
700                 fprintf(stderr, "Error opening input file\n");
701                 exit(1);
702                 }
703         out = fopen(argv[3], "w");
704         if (!out)
705                 {
706                 fprintf(stderr, "Error opening output file\n");
707                 exit(1);
708                 }
709         }
710     else if (argc == 2)
711         {
712         in = stdin;
713         out = stdout;
714         }
715     else
716         {
717         fprintf(stderr,"%s [prime|pqg|pqgver|keypair|keyver|siggen|sigver]\n",argv[0]);
718         exit(1);
719         }
720     fips_algtest_init();
721     if(!strcmp(argv[1],"prime"))
722         primes(in, out);
723     else if(!strcmp(argv[1],"pqg"))
724         pqg(in, out);
725     else if(!strcmp(argv[1],"pqgver"))
726         pqgver(in, out);
727     else if(!strcmp(argv[1],"keypair"))
728         keypair(in, out);
729     else if(!strcmp(argv[1],"keyver"))
730         keyver(in, out);
731     else if(!strcmp(argv[1],"siggen"))
732         siggen(in, out);
733     else if(!strcmp(argv[1],"sigver"))
734         sigver(in, out);
735     else
736         {
737         fprintf(stderr,"Don't know how to %s.\n",argv[1]);
738         exit(1);
739         }
740
741     if (argc == 4)
742         {
743         fclose(in);
744         fclose(out);
745         }
746
747     return 0;
748     }
749
750 #endif