| /* 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) { |
| PORT_ZFree(dig, sizeof(JAR_Digest)); |
| return NULL; |
| } |
| sha1 = PK11_CreateDigestContext(SEC_OID_SHA1); |
| if (sha1 == NULL) { |
| PK11_DestroyContext(md5, PR_TRUE); |
| /* added due to bug Bug 1250214 - prevent the 2nd memory leak */ |
| PORT_ZFree(dig, sizeof(JAR_Digest)); |
| 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; |
| } |