gecko-dev/security/nss/lib/jar/jarsign.c
2004-04-25 15:03:26 +00:00

375 lines
8.6 KiB
C

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1994-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* JARSIGN
*
* Routines used in signing archives.
*/
#define USE_MOZ_THREAD
#include "jar.h"
#include "jarint.h"
#ifdef USE_MOZ_THREAD
#include "jarevil.h"
#endif
#include "pk11func.h"
#include "sechash.h"
/* from libevent.h */
typedef void (*ETVoidPtrFunc) (void * data);
#ifdef MOZILLA_CLIENT_OLD
extern void ET_moz_CallFunction (ETVoidPtrFunc fn, void *data);
/* from proto.h */
/* extern MWContext *XP_FindSomeContext(void); */
extern void *XP_FindSomeContext(void);
#endif
/* 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 ZHUGEP *data, long length)
{
long chunq;
JAR_Digest *dig;
unsigned int md5_length, sha1_length;
PK11Context *md5 = 0;
PK11Context *sha1 = 0;
dig = (JAR_Digest *) PORT_ZAlloc (sizeof (JAR_Digest));
if (dig == NULL)
{
/* out of memory allocating digest */
return NULL;
}
#if defined(XP_WIN16)
PORT_Assert ( !IsBadHugeReadPtr(data, length) );
#endif
md5 = PK11_CreateDigestContext (SEC_OID_MD5);
sha1 = PK11_CreateDigestContext (SEC_OID_SHA1);
if (length >= 0)
{
PK11_DigestBegin (md5);
PK11_DigestBegin (sha1);
do {
chunq = length;
#ifdef XP_WIN16
if (length > CHUNQ) chunq = CHUNQ;
/*
* If the block of data crosses one or more segment
* boundaries then only pass the chunk of data in the
* first segment.
*
* This allows the data to be treated as FAR by the
* PK11_DigestOp(...) routine.
*
*/
if (OFFSETOF(data) + chunq >= 0x10000)
chunq = 0x10000 - OFFSETOF(data);
#endif
PK11_DigestOp (md5, (unsigned char*)data, chunq);
PK11_DigestOp (sha1, (unsigned char*)data, chunq);
length -= chunq;
data = ((char ZHUGEP *) 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;
int num;
unsigned char *buf;
PK11Context *md5 = 0;
PK11Context *sha1 = 0;
unsigned int md5_length, sha1_length;
buf = (unsigned char *) PORT_ZAlloc (FILECHUNQ);
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)
{
/* 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)
{
int nb;
unsigned char buffer [4096], digestdata[32];
const SECHashObject *hashObj;
void *hashcx;
unsigned int len;
int status = 0;
char *errstring;
SECItem digest;
SEC_PKCS7ContentInfo *cinfo;
SECStatus rv;
void /*MWContext*/ *mw;
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)
{
/* nspr2.0 doesn't support feof
if (feof (infp)) break; */
nb = JAR_FREAD (infp, buffer, sizeof (buffer));
if (nb == 0)
{
#if 0
if (ferror(infp))
{
/* PORT_SetError(SEC_ERROR_IO); */ /* FIX */
(* hashObj->destroy) (hashcx, PR_TRUE);
return JAR_ERR_GENERAL;
}
#endif
/* 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. */
#ifdef MOZILLA_CLIENT_OLD
mw = XP_FindSomeContext();
#else
mw = NULL;
#endif
PORT_SetError (0);
cinfo = SEC_PKCS7CreateSignedData
(cert, certUsageObjectSigner, NULL,
SEC_OID_SHA1, &digest, NULL, (void *) 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);
if (rv != SECSuccess)
{
/* don't check error */
}
PORT_SetError (0);
#ifdef USE_MOZ_THREAD
/* if calling from mozilla */
rv = jar_moz_encode
(cinfo, jar_pk7_out, outfp,
NULL, /* pwfn */ NULL, /* pwarg */ (void *) mw);
#else
/* if calling from mozilla thread*/
rv = SEC_PKCS7Encode
(cinfo, jar_pk7_out, outfp,
NULL, /* pwfn */ NULL, /* pwarg */ (void *) mw):
#endif
if (rv != SECSuccess)
status = PORT_GetError();
SEC_PKCS7DestroyContentInfo (cinfo);
if (rv != SECSuccess)
{
errstring = JAR_get_error (status);
/*XP_TRACE (("Jar signing failed (reason %d = %s)", status, errstring));*/
return status < 0 ? status : JAR_ERR_GENERAL;
}
return 0;
}