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