Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /*
6 * JARSIGN
7 *
8 * Routines used in signing archives.
9 */
12 #include "jar.h"
13 #include "jarint.h"
14 #include "secpkcs7.h"
15 #include "pk11func.h"
16 #include "sechash.h"
18 /* from libevent.h */
19 typedef void (*ETVoidPtrFunc) (void * data);
21 /* key database wrapper */
22 /* static SECKEYKeyDBHandle *jar_open_key_database (void); */
23 /* CHUNQ is our bite size */
25 #define CHUNQ 64000
26 #define FILECHUNQ 32768
28 /*
29 * J A R _ c a l c u l a t e _ d i g e s t
30 *
31 * Quick calculation of a digest for
32 * the specified block of memory. Will calculate
33 * for all supported algorithms, now MD5.
34 *
35 * This version supports huge pointers for WIN16.
36 *
37 */
38 JAR_Digest * PR_CALLBACK
39 JAR_calculate_digest(void *data, long length)
40 {
41 PK11Context *md5 = 0;
42 PK11Context *sha1 = 0;
43 JAR_Digest *dig = PORT_ZNew(JAR_Digest);
44 long chunq;
45 unsigned int md5_length, sha1_length;
47 if (dig == NULL) {
48 /* out of memory allocating digest */
49 return NULL;
50 }
52 md5 = PK11_CreateDigestContext(SEC_OID_MD5);
53 sha1 = PK11_CreateDigestContext(SEC_OID_SHA1);
55 if (length >= 0) {
56 PK11_DigestBegin (md5);
57 PK11_DigestBegin (sha1);
59 do {
60 chunq = length;
62 PK11_DigestOp(md5, (unsigned char*)data, chunq);
63 PK11_DigestOp(sha1, (unsigned char*)data, chunq);
64 length -= chunq;
65 data = ((char *) data + chunq);
66 }
67 while (length > 0);
69 PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH);
70 PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH);
72 PK11_DestroyContext (md5, PR_TRUE);
73 PK11_DestroyContext (sha1, PR_TRUE);
74 }
75 return dig;
76 }
78 /*
79 * J A R _ d i g e s t _ f i l e
80 *
81 * Calculates the MD5 and SHA1 digests for a file
82 * present on disk, and returns these in JAR_Digest struct.
83 *
84 */
85 int
86 JAR_digest_file (char *filename, JAR_Digest *dig)
87 {
88 JAR_FILE fp;
89 PK11Context *md5 = 0;
90 PK11Context *sha1 = 0;
91 unsigned char *buf = (unsigned char *) PORT_ZAlloc (FILECHUNQ);
92 int num;
93 unsigned int md5_length, sha1_length;
95 if (buf == NULL) {
96 /* out of memory */
97 return JAR_ERR_MEMORY;
98 }
100 if ((fp = JAR_FOPEN (filename, "rb")) == 0) {
101 /* perror (filename); FIX XXX XXX XXX XXX XXX XXX */
102 PORT_Free (buf);
103 return JAR_ERR_FNF;
104 }
106 md5 = PK11_CreateDigestContext (SEC_OID_MD5);
107 sha1 = PK11_CreateDigestContext (SEC_OID_SHA1);
109 if (md5 == NULL || sha1 == NULL) {
110 /* can't generate digest contexts */
111 PORT_Free (buf);
112 JAR_FCLOSE (fp);
113 return JAR_ERR_GENERAL;
114 }
116 PK11_DigestBegin (md5);
117 PK11_DigestBegin (sha1);
119 while (1) {
120 if ((num = JAR_FREAD (fp, buf, FILECHUNQ)) == 0)
121 break;
123 PK11_DigestOp (md5, buf, num);
124 PK11_DigestOp (sha1, buf, num);
125 }
127 PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH);
128 PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH);
130 PK11_DestroyContext (md5, PR_TRUE);
131 PK11_DestroyContext (sha1, PR_TRUE);
133 PORT_Free (buf);
134 JAR_FCLOSE (fp);
136 return 0;
137 }
139 /*
140 * J A R _ o p e n _ k e y _ d a t a b a s e
141 *
142 */
144 void*
145 jar_open_key_database(void)
146 {
147 return NULL;
148 }
150 int
151 jar_close_key_database(void *keydb)
152 {
153 /* We never do close it */
154 return 0;
155 }
158 /*
159 * j a r _ c r e a t e _ p k 7
160 *
161 */
163 static void jar_pk7_out (void *arg, const char *buf, unsigned long len)
164 {
165 JAR_FWRITE ((JAR_FILE) arg, buf, len);
166 }
168 int
169 jar_create_pk7(CERTCertDBHandle *certdb, void *keydb, CERTCertificate *cert,
170 char *password, JAR_FILE infp, JAR_FILE outfp)
171 {
172 SEC_PKCS7ContentInfo *cinfo;
173 const SECHashObject *hashObj;
174 char *errstring;
175 void *mw = NULL;
176 void *hashcx;
177 unsigned int len;
178 int status = 0;
179 SECStatus rv;
180 SECItem digest;
181 unsigned char digestdata[32];
182 unsigned char buffer[4096];
184 if (outfp == NULL || infp == NULL || cert == NULL)
185 return JAR_ERR_GENERAL;
187 /* we sign with SHA */
188 hashObj = HASH_GetHashObject(HASH_AlgSHA1);
190 hashcx = (* hashObj->create)();
191 if (hashcx == NULL)
192 return JAR_ERR_GENERAL;
194 (* hashObj->begin)(hashcx);
195 while (1) {
196 int nb = JAR_FREAD(infp, buffer, sizeof buffer);
197 if (nb == 0) { /* eof */
198 break;
199 }
200 (* hashObj->update) (hashcx, buffer, nb);
201 }
202 (* hashObj->end)(hashcx, digestdata, &len, 32);
203 (* hashObj->destroy)(hashcx, PR_TRUE);
205 digest.data = digestdata;
206 digest.len = len;
208 /* signtool must use any old context it can find since it's
209 calling from inside javaland. */
210 PORT_SetError (0);
211 cinfo = SEC_PKCS7CreateSignedData(cert, certUsageObjectSigner, NULL,
212 SEC_OID_SHA1, &digest, NULL, mw);
213 if (cinfo == NULL)
214 return JAR_ERR_PK7;
216 rv = SEC_PKCS7IncludeCertChain(cinfo, NULL);
217 if (rv != SECSuccess) {
218 status = PORT_GetError();
219 SEC_PKCS7DestroyContentInfo(cinfo);
220 return status;
221 }
223 /* Having this here forces signtool to always include signing time. */
224 rv = SEC_PKCS7AddSigningTime(cinfo);
225 /* don't check error */
226 PORT_SetError(0);
228 /* if calling from mozilla thread*/
229 rv = SEC_PKCS7Encode(cinfo, jar_pk7_out, outfp, NULL, NULL, mw);
230 if (rv != SECSuccess)
231 status = PORT_GetError();
232 SEC_PKCS7DestroyContentInfo (cinfo);
233 if (rv != SECSuccess) {
234 errstring = JAR_get_error (status);
235 return ((status < 0) ? status : JAR_ERR_GENERAL);
236 }
237 return 0;
238 }