/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * JARSIGN * * Routines used in signing archives. */ #include "jar.h" #include "jarint.h" #include "secpkcs7.h" #include "pk11func.h" #include "sechash.h" /* from libevent.h */ typedef void (*ETVoidPtrFunc) (void * data); /* key database wrapper */ /* static SECKEYKeyDBHandle *jar_open_key_database (void); */ /* CHUNQ is our bite size */ #define CHUNQ 64000 #define FILECHUNQ 32768 /* * J A R _ c a l c u l a t e _ d i g e s t * * Quick calculation of a digest for * the specified block of memory. Will calculate * for all supported algorithms, now MD5. * * This version supports huge pointers for WIN16. * */ JAR_Digest * PR_CALLBACK JAR_calculate_digest(void *data, long length) { PK11Context *md5 = 0; PK11Context *sha1 = 0; JAR_Digest *dig = PORT_ZNew(JAR_Digest); long chunq; unsigned int md5_length, sha1_length; if (dig == NULL) { /* out of memory allocating digest */ return NULL; } md5 = PK11_CreateDigestContext(SEC_OID_MD5); if (md5 == NULL) { return NULL; } sha1 = PK11_CreateDigestContext(SEC_OID_SHA1); if (sha1 == NULL) { PK11_DestroyContext(md5, PR_TRUE); return NULL; } if (length >= 0) { PK11_DigestBegin (md5); PK11_DigestBegin (sha1); do { chunq = length; PK11_DigestOp(md5, (unsigned char*)data, chunq); PK11_DigestOp(sha1, (unsigned char*)data, chunq); length -= chunq; data = ((char *) data + chunq); } while (length > 0); PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH); PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH); PK11_DestroyContext (md5, PR_TRUE); PK11_DestroyContext (sha1, PR_TRUE); } return dig; } /* * J A R _ d i g e s t _ f i l e * * Calculates the MD5 and SHA1 digests for a file * present on disk, and returns these in JAR_Digest struct. * */ int JAR_digest_file (char *filename, JAR_Digest *dig) { JAR_FILE fp; PK11Context *md5 = 0; PK11Context *sha1 = 0; unsigned char *buf = (unsigned char *) PORT_ZAlloc (FILECHUNQ); int num; unsigned int md5_length, sha1_length; if (buf == NULL) { /* out of memory */ return JAR_ERR_MEMORY; } if ((fp = JAR_FOPEN (filename, "rb")) == 0) { /* perror (filename); FIX XXX XXX XXX XXX XXX XXX */ PORT_Free (buf); return JAR_ERR_FNF; } md5 = PK11_CreateDigestContext (SEC_OID_MD5); sha1 = PK11_CreateDigestContext (SEC_OID_SHA1); if (md5 == NULL || sha1 == NULL) { if (md5) { PK11_DestroyContext(md5, PR_TRUE); } if (sha1) { PK11_DestroyContext(sha1, PR_TRUE); } /* can't generate digest contexts */ PORT_Free (buf); JAR_FCLOSE (fp); return JAR_ERR_GENERAL; } PK11_DigestBegin (md5); PK11_DigestBegin (sha1); while (1) { if ((num = JAR_FREAD (fp, buf, FILECHUNQ)) == 0) break; PK11_DigestOp (md5, buf, num); PK11_DigestOp (sha1, buf, num); } PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH); PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH); PK11_DestroyContext (md5, PR_TRUE); PK11_DestroyContext (sha1, PR_TRUE); PORT_Free (buf); JAR_FCLOSE (fp); return 0; } /* * J A R _ o p e n _ k e y _ d a t a b a s e * */ void* jar_open_key_database(void) { return NULL; } int jar_close_key_database(void *keydb) { /* We never do close it */ return 0; } /* * j a r _ c r e a t e _ p k 7 * */ static void jar_pk7_out (void *arg, const char *buf, unsigned long len) { JAR_FWRITE ((JAR_FILE) arg, buf, len); } int jar_create_pk7(CERTCertDBHandle *certdb, void *keydb, CERTCertificate *cert, char *password, JAR_FILE infp, JAR_FILE outfp) { SEC_PKCS7ContentInfo *cinfo; const SECHashObject *hashObj; void *mw = NULL; void *hashcx; unsigned int len; int status = 0; SECStatus rv; SECItem digest; unsigned char digestdata[32]; unsigned char buffer[4096]; if (outfp == NULL || infp == NULL || cert == NULL) return JAR_ERR_GENERAL; /* we sign with SHA */ hashObj = HASH_GetHashObject(HASH_AlgSHA1); hashcx = (* hashObj->create)(); if (hashcx == NULL) return JAR_ERR_GENERAL; (* hashObj->begin)(hashcx); while (1) { int nb = JAR_FREAD(infp, buffer, sizeof buffer); if (nb == 0) { /* eof */ break; } (* hashObj->update) (hashcx, buffer, nb); } (* hashObj->end)(hashcx, digestdata, &len, 32); (* hashObj->destroy)(hashcx, PR_TRUE); digest.data = digestdata; digest.len = len; /* signtool must use any old context it can find since it's calling from inside javaland. */ PORT_SetError (0); cinfo = SEC_PKCS7CreateSignedData(cert, certUsageObjectSigner, NULL, SEC_OID_SHA1, &digest, NULL, mw); if (cinfo == NULL) return JAR_ERR_PK7; rv = SEC_PKCS7IncludeCertChain(cinfo, NULL); if (rv != SECSuccess) { status = PORT_GetError(); SEC_PKCS7DestroyContentInfo(cinfo); return status; } /* Having this here forces signtool to always include signing time. */ rv = SEC_PKCS7AddSigningTime(cinfo); /* don't check error */ PORT_SetError(0); /* if calling from mozilla thread*/ rv = SEC_PKCS7Encode(cinfo, jar_pk7_out, outfp, NULL, NULL, mw); if (rv != SECSuccess) status = PORT_GetError(); SEC_PKCS7DestroyContentInfo (cinfo); if (rv != SECSuccess) { return ((status < 0) ? status : JAR_ERR_GENERAL); } return 0; }