nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / signtool / sign.c
blob3055a66923ea1ad4c367aa1e0c91a4fc30871046
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "signtool.h"
38 #include "zip.h"
39 #include "prmem.h"
40 #include "blapi.h"
41 #include "sechash.h" /* for HASH_GetHashObject() */
43 static int create_pk7 (char *dir, char *keyName, int *keyType);
44 static int jar_find_key_type (CERTCertificate *cert);
45 static int manifesto (char *dirname, char *install_script, PRBool recurse);
46 static int manifesto_fn(char *relpath, char *basedir, char *reldir,
47 char *filename, void *arg);
48 static int manifesto_xpi_fn(char *relpath, char *basedir, char *reldir,
49 char *filename, void *arg);
50 static int sign_all_arc_fn(char *relpath, char *basedir, char *reldir,
51 char *filename, void *arg);
52 static int add_meta (FILE *fp, char *name);
53 static int SignFile (FILE *outFile, FILE *inFile, CERTCertificate *cert);
54 static int generate_SF_file (char *manifile, char *who);
55 static int calculate_MD5_range (FILE *fp, long r1, long r2,
56 JAR_Digest *dig);
57 static void SignOut (void *arg, const char *buf, unsigned long len);
59 static char *metafile = NULL;
60 static int optimize = 0;
61 static FILE *mf;
62 static ZIPfile *zipfile = NULL;
64 /*
65 * S i g n A r c h i v e
67 * Sign an individual archive tree. A directory
68 * called META-INF is created underneath this.
71 int
72 SignArchive(char *tree, char *keyName, char *zip_file, int javascript,
73 char *meta_file, char *install_script, int _optimize, PRBool recurse)
75 int status;
76 char tempfn [FNSIZE], fullfn [FNSIZE];
77 int keyType = rsaKey;
79 metafile = meta_file;
80 optimize = _optimize;
82 /* To create XPI compatible Archive manifesto() must be run before
83 * the zipfile is opened. This is so the signed files are not added
84 * the archive before the crucial rsa/dsa file*/
85 if (xpi_arc) {
86 manifesto (tree, install_script, recurse);
89 if (zip_file) {
90 zipfile = JzipOpen(zip_file, NULL /*no comment*/);
93 /*Sign and add files to the archive normally with manifesto()*/
94 if (!xpi_arc) {
95 manifesto (tree, install_script, recurse);
98 if (keyName) {
99 status = create_pk7 (tree, keyName, &keyType);
100 if (status < 0) {
101 PR_fprintf(errorFD, "the tree \"%s\" was NOT SUCCESSFULLY SIGNED\n",
102 tree);
103 errorCount++;
104 exit (ERRX);
108 /* Add the rsa/dsa file as the first file in the archive. This is crucial
109 * for a XPInstall compatible archive */
110 if (xpi_arc) {
111 if (verbosity >= 0) {
112 PR_fprintf(outputFD, "%s \n", XPI_TEXT);
115 /* rsa/dsa to zip */
116 sprintf (tempfn, "META-INF/%s.%s", base, (keyType == dsaKey ?
117 "dsa" : "rsa"));
118 sprintf (fullfn, "%s/%s", tree, tempfn);
119 JzipAdd(fullfn, tempfn, zipfile, compression_level);
121 /* Loop through all files & subdirectories, add to archive */
122 foreach (tree, "", manifesto_xpi_fn, recurse, PR_FALSE /*include dirs */,
123 (void * )NULL);
125 /* mf to zip */
126 strcpy (tempfn, "META-INF/manifest.mf");
127 sprintf (fullfn, "%s/%s", tree, tempfn);
128 JzipAdd(fullfn, tempfn, zipfile, compression_level);
130 /* sf to zip */
131 sprintf (tempfn, "META-INF/%s.sf", base);
132 sprintf (fullfn, "%s/%s", tree, tempfn);
133 JzipAdd(fullfn, tempfn, zipfile, compression_level);
135 /* Add the rsa/dsa file to the zip archive normally */
136 if (!xpi_arc) {
137 /* rsa/dsa to zip */
138 sprintf (tempfn, "META-INF/%s.%s", base, (keyType == dsaKey ?
139 "dsa" : "rsa"));
140 sprintf (fullfn, "%s/%s", tree, tempfn);
141 JzipAdd(fullfn, tempfn, zipfile, compression_level);
144 JzipClose(zipfile);
146 if (verbosity >= 0) {
147 if (javascript) {
148 PR_fprintf(outputFD, "jarfile \"%s\" signed successfully\n",
149 zip_file);
150 } else {
151 PR_fprintf(outputFD, "tree \"%s\" signed successfully\n",
152 tree);
156 return 0;
160 typedef struct {
161 char *keyName;
162 int javascript;
163 char *metafile;
164 char *install_script;
165 int optimize;
166 } SignArcInfo;
169 * S i g n A l l A r c
171 * Javascript may generate multiple .arc directories, one
172 * for each jar archive needed. Sign them all.
176 SignAllArc(char *jartree, char *keyName, int javascript, char *metafile,
177 char *install_script, int optimize, PRBool recurse)
179 SignArcInfo info;
181 info.keyName = keyName;
182 info.javascript = javascript;
183 info.metafile = metafile;
184 info.install_script = install_script;
185 info.optimize = optimize;
187 return foreach(jartree, "", sign_all_arc_fn, recurse,
188 PR_TRUE /*include dirs*/, (void * )&info);
192 static int
193 sign_all_arc_fn(char *relpath, char *basedir, char *reldir, char *filename,
194 void *arg)
196 char *zipfile = NULL;
197 char *arc = NULL, *archive = NULL;
198 int retval = 0;
199 SignArcInfo * infop = (SignArcInfo * )arg;
201 /* Make sure there is one and only one ".arc" in the relative path,
202 * and that it is at the end of the path (don't sign .arcs within .arcs) */
203 if ( (PL_strcaserstr(relpath, ".arc") == relpath + strlen(relpath) -
204 4) &&
205 (PL_strcasestr(relpath, ".arc") == relpath + strlen(relpath) - 4) ) {
207 if (!infop) {
208 PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
209 errorCount++;
210 retval = -1;
211 goto finish;
213 archive = PR_smprintf("%s/%s", basedir, relpath);
215 zipfile = PL_strdup(archive);
216 arc = PORT_Strrchr (zipfile, '.');
218 if (arc == NULL) {
219 PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
220 errorCount++;
221 retval = -1;
222 goto finish;
225 PL_strcpy (arc, ".jar");
227 if (verbosity >= 0) {
228 PR_fprintf(outputFD, "\nsigning: %s\n", zipfile);
230 retval = SignArchive(archive, infop->keyName, zipfile,
231 infop->javascript, infop->metafile, infop->install_script,
232 infop->optimize, PR_TRUE /* recurse */);
234 finish:
235 if (archive)
236 PR_Free(archive);
237 if (zipfile)
238 PR_Free(zipfile);
240 return retval;
244 /*********************************************************************
246 * c r e a t e _ p k 7
248 static int
249 create_pk7 (char *dir, char *keyName, int *keyType)
251 int status = 0;
252 char *file_ext;
254 CERTCertificate * cert;
255 CERTCertDBHandle * db;
257 FILE * in, *out;
259 char sf_file [FNSIZE];
260 char pk7_file [FNSIZE];
262 /* open cert database */
263 db = CERT_GetDefaultCertDB();
265 if (db == NULL)
266 return - 1;
268 /* find cert */
269 /*cert = CERT_FindCertByNicknameOrEmailAddr(db, keyName);*/
270 cert = PK11_FindCertFromNickname(keyName, NULL /*wincx*/);
272 if (cert == NULL) {
273 SECU_PrintError ( PROGRAM_NAME,
274 "Cannot find the cert \"%s\"", keyName);
275 return -1;
279 /* determine the key type, which sets the extension for pkcs7 object */
281 *keyType = jar_find_key_type (cert);
282 file_ext = (*keyType == dsaKey) ? "dsa" : "rsa";
284 sprintf (sf_file, "%s/META-INF/%s.sf", dir, base);
285 sprintf (pk7_file, "%s/META-INF/%s.%s", dir, base, file_ext);
287 if ((in = fopen (sf_file, "rb")) == NULL) {
288 PR_fprintf(errorFD, "%s: Can't open %s for reading\n", PROGRAM_NAME,
289 sf_file);
290 errorCount++;
291 exit (ERRX);
294 if ((out = fopen (pk7_file, "wb")) == NULL) {
295 PR_fprintf(errorFD, "%s: Can't open %s for writing\n", PROGRAM_NAME,
296 sf_file);
297 errorCount++;
298 exit (ERRX);
301 status = SignFile (out, in, cert);
303 CERT_DestroyCertificate (cert);
304 fclose (in);
305 fclose (out);
307 if (status) {
308 PR_fprintf(errorFD, "%s: PROBLEM signing data (%s)\n",
309 PROGRAM_NAME, SECU_ErrorString ((int16) PORT_GetError()));
310 errorCount++;
311 return - 1;
314 return 0;
319 * j a r _ f i n d _ k e y _ t y p e
321 * Determine the key type for a given cert, which
322 * should be rsaKey or dsaKey. Any error return 0.
325 static int
326 jar_find_key_type (CERTCertificate *cert)
328 PK11SlotInfo * slot = NULL;
329 SECKEYPrivateKey * privk = NULL;
330 KeyType keyType;
332 /* determine its type */
333 PK11_FindObjectForCert (cert, /*wincx*/ NULL, &slot);
335 if (slot == NULL) {
336 PR_fprintf(errorFD, "warning - can't find slot for this cert\n");
337 warningCount++;
338 return 0;
341 privk = PK11_FindPrivateKeyFromCert (slot, cert, /*wincx*/ NULL);
342 PK11_FreeSlot (slot);
344 if (privk == NULL) {
345 PR_fprintf(errorFD, "warning - can't find private key for this cert\n");
346 warningCount++;
347 return 0;
350 keyType = privk->keyType;
351 SECKEY_DestroyPrivateKey (privk);
352 return keyType;
357 * m a n i f e s t o
359 * Run once for every subdirectory in which a
360 * manifest is to be created -- usually exactly once.
363 static int
364 manifesto (char *dirname, char *install_script, PRBool recurse)
366 char metadir [FNSIZE], sfname [FNSIZE];
368 /* Create the META-INF directory to hold signing info */
370 if (PR_Access (dirname, PR_ACCESS_READ_OK)) {
371 PR_fprintf(errorFD, "%s: unable to read your directory: %s\n",
372 PROGRAM_NAME, dirname);
373 errorCount++;
374 perror (dirname);
375 exit (ERRX);
378 if (PR_Access (dirname, PR_ACCESS_WRITE_OK)) {
379 PR_fprintf(errorFD, "%s: unable to write to your directory: %s\n",
380 PROGRAM_NAME, dirname);
381 errorCount++;
382 perror(dirname);
383 exit(ERRX);
386 sprintf (metadir, "%s/META-INF", dirname);
388 strcpy (sfname, metadir);
390 PR_MkDir (metadir, 0777);
392 strcat (metadir, "/");
393 strcat (metadir, MANIFEST);
395 if ((mf = fopen (metadir, "wb")) == NULL) {
396 perror (MANIFEST);
397 PR_fprintf(errorFD, "%s: Probably, the directory you are trying to"
398 " sign has\n", PROGRAM_NAME);
399 PR_fprintf(errorFD, "%s: permissions problems or may not exist.\n",
400 PROGRAM_NAME);
401 errorCount++;
402 exit (ERRX);
405 if (verbosity >= 0) {
406 PR_fprintf(outputFD, "Generating %s file..\n", metadir);
409 fprintf(mf, "Manifest-Version: 1.0\n");
410 fprintf (mf, "Created-By: %s\n", CREATOR);
411 fprintf (mf, "Comments: %s\n", BREAKAGE);
413 if (scriptdir) {
414 fprintf (mf, "Comments: --\n");
415 fprintf (mf, "Comments: --\n");
416 fprintf (mf, "Comments: -- This archive signs Javascripts which may not necessarily\n");
417 fprintf (mf, "Comments: -- be included in the physical jar file.\n");
418 fprintf (mf, "Comments: --\n");
419 fprintf (mf, "Comments: --\n");
422 if (install_script)
423 fprintf (mf, "Install-Script: %s\n", install_script);
425 if (metafile)
426 add_meta (mf, "+");
428 /* Loop through all files & subdirectories */
429 foreach (dirname, "", manifesto_fn, recurse, PR_FALSE /*include dirs */,
430 (void * )NULL);
432 fclose (mf);
434 strcat (sfname, "/");
435 strcat (sfname, base);
436 strcat (sfname, ".sf");
438 if (verbosity >= 0) {
439 PR_fprintf(outputFD, "Generating %s.sf file..\n", base);
441 generate_SF_file (metadir, sfname);
443 return 0;
448 * m a n i f e s t o _ x p i _ f n
450 * Called by pointer from SignArchive(), once for
451 * each file within the directory. This function
452 * is only used for adding to XPI compatible archive
455 static int manifesto_xpi_fn
456 (char *relpath, char *basedir, char *reldir, char *filename, void *arg)
458 char fullname [FNSIZE];
460 if (verbosity >= 0) {
461 PR_fprintf(outputFD, "--> %s\n", relpath);
464 /* extension matching */
465 if (extensionsGiven) {
466 char *ext = PL_strrchr(relpath, '.');
467 if (!ext)
468 return 0;
469 if (!PL_HashTableLookup(extensions, ext))
470 return 0;
472 sprintf (fullname, "%s/%s", basedir, relpath);
473 JzipAdd(fullname, relpath, zipfile, compression_level);
475 return 0;
480 * m a n i f e s t o _ f n
482 * Called by pointer from manifesto(), once for
483 * each file within the directory.
486 static int manifesto_fn
487 (char *relpath, char *basedir, char *reldir, char *filename, void *arg)
489 int use_js;
491 JAR_Digest dig;
492 char fullname [FNSIZE];
494 if (verbosity >= 0) {
495 PR_fprintf(outputFD, "--> %s\n", relpath);
498 /* extension matching */
499 if (extensionsGiven) {
500 char *ext = PL_strrchr(relpath, '.');
501 if (!ext)
502 return 0;
503 if (!PL_HashTableLookup(extensions, ext))
504 return 0;
507 sprintf (fullname, "%s/%s", basedir, relpath);
509 fprintf (mf, "\n");
511 use_js = 0;
513 if (scriptdir && !PORT_Strcmp (scriptdir, reldir))
514 use_js++;
516 /* sign non-.js files inside .arc directories using the javascript magic */
518 if ( (PL_strcaserstr(filename, ".js") != filename + strlen(filename) - 3)
519 && (PL_strcaserstr(reldir, ".arc") == reldir + strlen(filename) - 4))
520 use_js++;
522 if (use_js) {
523 fprintf (mf, "Name: %s\n", filename);
524 fprintf (mf, "Magic: javascript\n");
526 if (optimize == 0)
527 fprintf (mf, "javascript.id: %s\n", filename);
529 if (metafile)
530 add_meta (mf, filename);
531 } else {
532 fprintf (mf, "Name: %s\n", relpath);
533 if (metafile)
534 add_meta (mf, relpath);
537 JAR_digest_file (fullname, &dig);
540 if (optimize == 0) {
541 fprintf (mf, "Digest-Algorithms: MD5 SHA1\n");
542 fprintf (mf, "MD5-Digest: %s\n", BTOA_DataToAscii (dig.md5,
543 MD5_LENGTH));
546 fprintf (mf, "SHA1-Digest: %s\n", BTOA_DataToAscii (dig.sha1, SHA1_LENGTH));
548 if (!use_js) {
549 JzipAdd(fullname, relpath, zipfile, compression_level);
552 return 0;
557 * a d d _ m e t a
559 * Parse the metainfo file, and add any details
560 * necessary to the manifest file. In most cases you
561 * should be using the -i option (ie, for SmartUpdate).
564 static int add_meta (FILE *fp, char *name)
566 FILE * met;
567 char buf [BUFSIZ];
569 int place;
570 char *pattern, *meta;
572 int num = 0;
574 if ((met = fopen (metafile, "r")) != NULL) {
575 while (fgets (buf, BUFSIZ, met)) {
576 char *s;
578 for (s = buf; *s && *s != '\n' && *s != '\r'; s++)
580 *s = 0;
582 if (*buf == 0)
583 continue;
585 pattern = buf;
587 /* skip to whitespace */
588 for (s = buf; *s && *s != ' ' && *s != '\t'; s++)
591 /* terminate pattern */
592 if (*s == ' ' || *s == '\t')
593 *s++ = 0;
595 /* eat through whitespace */
596 while (*s == ' ' || *s == '\t')
597 s++;
599 meta = s;
601 /* this will eventually be regexp matching */
603 place = 0;
604 if (!PORT_Strcmp (pattern, name))
605 place = 1;
607 if (place) {
608 num++;
609 if (verbosity >= 0) {
610 PR_fprintf(outputFD, "[%s] %s\n", name, meta);
612 fprintf (fp, "%s\n", meta);
615 fclose (met);
616 } else {
617 PR_fprintf(errorFD, "%s: can't open metafile: %s\n", PROGRAM_NAME,
618 metafile);
619 errorCount++;
620 exit (ERRX);
623 return num;
627 /**********************************************************************
629 * S i g n F i l e
631 static int
632 SignFile (FILE *outFile, FILE *inFile, CERTCertificate *cert)
634 int nb;
635 char ibuf[4096], digestdata[32];
636 const SECHashObject *hashObj;
637 void *hashcx;
638 unsigned int len;
640 SECItem digest;
641 SEC_PKCS7ContentInfo * cinfo;
642 SECStatus rv;
644 if (outFile == NULL || inFile == NULL || cert == NULL)
645 return - 1;
647 /* XXX probably want to extend interface to allow other hash algorithms */
648 hashObj = HASH_GetHashObject(HASH_AlgSHA1);
650 hashcx = (*hashObj->create)();
651 if (hashcx == NULL)
652 return - 1;
654 (*hashObj->begin)(hashcx);
656 for (; ; ) {
657 if (feof(inFile))
658 break;
659 nb = fread(ibuf, 1, sizeof(ibuf), inFile);
660 if (nb == 0) {
661 if (ferror(inFile)) {
662 PORT_SetError(SEC_ERROR_IO);
663 (*hashObj->destroy)(hashcx, PR_TRUE);
664 return - 1;
666 /* eof */
667 break;
669 (*hashObj->update)(hashcx, (unsigned char *) ibuf, nb);
672 (*hashObj->end)(hashcx, (unsigned char *) digestdata, &len, 32);
673 (*hashObj->destroy)(hashcx, PR_TRUE);
675 digest.data = (unsigned char *) digestdata;
676 digest.len = len;
678 cinfo = SEC_PKCS7CreateSignedData
679 (cert, certUsageObjectSigner, NULL,
680 SEC_OID_SHA1, &digest, NULL, NULL);
682 if (cinfo == NULL)
683 return - 1;
685 rv = SEC_PKCS7IncludeCertChain (cinfo, NULL);
686 if (rv != SECSuccess) {
687 SEC_PKCS7DestroyContentInfo (cinfo);
688 return - 1;
691 if (no_time == 0) {
692 rv = SEC_PKCS7AddSigningTime (cinfo);
693 if (rv != SECSuccess) {
694 /* don't check error */
698 if (password) {
699 rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL,
700 (SECKEYGetPasswordKey) password_hardcode, NULL);
701 } else {
702 rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, NULL,
703 NULL);
707 SEC_PKCS7DestroyContentInfo (cinfo);
709 if (rv != SECSuccess)
710 return - 1;
712 return 0;
717 * g e n e r a t e _ S F _ f i l e
719 * From the supplied manifest file, calculates
720 * digests on the various sections, creating a .SF
721 * file in the process.
724 static int generate_SF_file (char *manifile, char *who)
726 FILE * sf;
727 FILE * mf;
728 long r1, r2, r3;
729 char whofile [FNSIZE];
730 char *buf, *name = NULL;
731 JAR_Digest dig;
732 int line = 0;
734 strcpy (whofile, who);
736 if ((mf = fopen (manifile, "rb")) == NULL) {
737 perror (manifile);
738 exit (ERRX);
741 if ((sf = fopen (whofile, "wb")) == NULL) {
742 perror (who);
743 exit (ERRX);
746 buf = (char *) PORT_ZAlloc (BUFSIZ);
748 if (buf)
749 name = (char *) PORT_ZAlloc (BUFSIZ);
751 if (buf == NULL || name == NULL)
752 out_of_memory();
754 fprintf (sf, "Signature-Version: 1.0\n");
755 fprintf (sf, "Created-By: %s\n", CREATOR);
756 fprintf (sf, "Comments: %s\n", BREAKAGE);
758 if (fgets (buf, BUFSIZ, mf) == NULL) {
759 PR_fprintf(errorFD, "%s: empty manifest file!\n", PROGRAM_NAME);
760 errorCount++;
761 exit (ERRX);
764 if (strncmp (buf, "Manifest-Version:", 17)) {
765 PR_fprintf(errorFD, "%s: not a manifest file!\n", PROGRAM_NAME);
766 errorCount++;
767 exit (ERRX);
770 fseek (mf, 0L, SEEK_SET);
772 /* Process blocks of headers, and calculate their hashen */
774 while (1) {
775 /* Beginning range */
776 r1 = ftell (mf);
778 if (fgets (name, BUFSIZ, mf) == NULL)
779 break;
781 line++;
783 if (r1 != 0 && strncmp (name, "Name:", 5)) {
784 PR_fprintf(errorFD,
785 "warning: unexpected input in manifest file \"%s\" at line %d:\n",
786 manifile, line);
787 PR_fprintf(errorFD, "%s\n", name);
788 warningCount++;
791 r2 = r1;
792 while (fgets (buf, BUFSIZ, mf)) {
793 if (*buf == 0 || *buf == '\n' || *buf == '\r')
794 break;
796 line++;
798 /* Ending range for hashing */
799 r2 = ftell (mf);
802 r3 = ftell (mf);
804 if (r1) {
805 fprintf (sf, "\n");
806 fprintf (sf, "%s", name);
809 calculate_MD5_range (mf, r1, r2, &dig);
811 if (optimize == 0) {
812 fprintf (sf, "Digest-Algorithms: MD5 SHA1\n");
813 fprintf (sf, "MD5-Digest: %s\n",
814 BTOA_DataToAscii (dig.md5, MD5_LENGTH));
817 fprintf (sf, "SHA1-Digest: %s\n",
818 BTOA_DataToAscii (dig.sha1, SHA1_LENGTH));
820 /* restore normalcy after changing offset position */
821 fseek (mf, r3, SEEK_SET);
824 PORT_Free (buf);
825 PORT_Free (name);
827 fclose (sf);
828 fclose (mf);
830 return 0;
835 * c a l c u l a t e _ M D 5 _ r a n g e
837 * Calculate the MD5 digest on a range of bytes in
838 * the specified fopen'd file. Returns base64.
841 static int
842 calculate_MD5_range (FILE *fp, long r1, long r2, JAR_Digest *dig)
844 int num;
845 int range;
846 unsigned char *buf;
848 MD5Context * md5 = 0;
849 SHA1Context * sha1 = 0;
851 unsigned int sha1_length, md5_length;
853 range = r2 - r1;
855 /* position to the beginning of range */
856 fseek (fp, r1, SEEK_SET);
858 buf = (unsigned char *) PORT_ZAlloc (range);
859 if (buf == NULL)
860 out_of_memory();
862 if ((num = fread (buf, 1, range, fp)) != range) {
863 PR_fprintf(errorFD, "%s: expected %d bytes, got %d\n", PROGRAM_NAME,
864 range, num);
865 errorCount++;
866 exit (ERRX);
869 md5 = MD5_NewContext();
870 sha1 = SHA1_NewContext();
872 if (md5 == NULL || sha1 == NULL) {
873 PR_fprintf(errorFD, "%s: can't generate digest context\n",
874 PROGRAM_NAME);
875 errorCount++;
876 exit (ERRX);
879 MD5_Begin (md5);
880 SHA1_Begin (sha1);
882 MD5_Update (md5, buf, range);
883 SHA1_Update (sha1, buf, range);
885 MD5_End (md5, dig->md5, &md5_length, MD5_LENGTH);
886 SHA1_End (sha1, dig->sha1, &sha1_length, SHA1_LENGTH);
888 MD5_DestroyContext (md5, PR_TRUE);
889 SHA1_DestroyContext (sha1, PR_TRUE);
891 PORT_Free (buf);
893 return 0;
897 static void SignOut (void *arg, const char *buf, unsigned long len)
899 fwrite (buf, len, 1, (FILE * ) arg);