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