|
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 |
|
12 #include "jar.h" |
|
13 #include "jarint.h" |
|
14 #include "secpkcs7.h" |
|
15 #include "pk11func.h" |
|
16 #include "sechash.h" |
|
17 |
|
18 /* from libevent.h */ |
|
19 typedef void (*ETVoidPtrFunc) (void * data); |
|
20 |
|
21 /* key database wrapper */ |
|
22 /* static SECKEYKeyDBHandle *jar_open_key_database (void); */ |
|
23 /* CHUNQ is our bite size */ |
|
24 |
|
25 #define CHUNQ 64000 |
|
26 #define FILECHUNQ 32768 |
|
27 |
|
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; |
|
46 |
|
47 if (dig == NULL) { |
|
48 /* out of memory allocating digest */ |
|
49 return NULL; |
|
50 } |
|
51 |
|
52 md5 = PK11_CreateDigestContext(SEC_OID_MD5); |
|
53 sha1 = PK11_CreateDigestContext(SEC_OID_SHA1); |
|
54 |
|
55 if (length >= 0) { |
|
56 PK11_DigestBegin (md5); |
|
57 PK11_DigestBegin (sha1); |
|
58 |
|
59 do { |
|
60 chunq = length; |
|
61 |
|
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); |
|
68 |
|
69 PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH); |
|
70 PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH); |
|
71 |
|
72 PK11_DestroyContext (md5, PR_TRUE); |
|
73 PK11_DestroyContext (sha1, PR_TRUE); |
|
74 } |
|
75 return dig; |
|
76 } |
|
77 |
|
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; |
|
94 |
|
95 if (buf == NULL) { |
|
96 /* out of memory */ |
|
97 return JAR_ERR_MEMORY; |
|
98 } |
|
99 |
|
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 } |
|
105 |
|
106 md5 = PK11_CreateDigestContext (SEC_OID_MD5); |
|
107 sha1 = PK11_CreateDigestContext (SEC_OID_SHA1); |
|
108 |
|
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 } |
|
115 |
|
116 PK11_DigestBegin (md5); |
|
117 PK11_DigestBegin (sha1); |
|
118 |
|
119 while (1) { |
|
120 if ((num = JAR_FREAD (fp, buf, FILECHUNQ)) == 0) |
|
121 break; |
|
122 |
|
123 PK11_DigestOp (md5, buf, num); |
|
124 PK11_DigestOp (sha1, buf, num); |
|
125 } |
|
126 |
|
127 PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH); |
|
128 PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH); |
|
129 |
|
130 PK11_DestroyContext (md5, PR_TRUE); |
|
131 PK11_DestroyContext (sha1, PR_TRUE); |
|
132 |
|
133 PORT_Free (buf); |
|
134 JAR_FCLOSE (fp); |
|
135 |
|
136 return 0; |
|
137 } |
|
138 |
|
139 /* |
|
140 * J A R _ o p e n _ k e y _ d a t a b a s e |
|
141 * |
|
142 */ |
|
143 |
|
144 void* |
|
145 jar_open_key_database(void) |
|
146 { |
|
147 return NULL; |
|
148 } |
|
149 |
|
150 int |
|
151 jar_close_key_database(void *keydb) |
|
152 { |
|
153 /* We never do close it */ |
|
154 return 0; |
|
155 } |
|
156 |
|
157 |
|
158 /* |
|
159 * j a r _ c r e a t e _ p k 7 |
|
160 * |
|
161 */ |
|
162 |
|
163 static void jar_pk7_out (void *arg, const char *buf, unsigned long len) |
|
164 { |
|
165 JAR_FWRITE ((JAR_FILE) arg, buf, len); |
|
166 } |
|
167 |
|
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]; |
|
183 |
|
184 if (outfp == NULL || infp == NULL || cert == NULL) |
|
185 return JAR_ERR_GENERAL; |
|
186 |
|
187 /* we sign with SHA */ |
|
188 hashObj = HASH_GetHashObject(HASH_AlgSHA1); |
|
189 |
|
190 hashcx = (* hashObj->create)(); |
|
191 if (hashcx == NULL) |
|
192 return JAR_ERR_GENERAL; |
|
193 |
|
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); |
|
204 |
|
205 digest.data = digestdata; |
|
206 digest.len = len; |
|
207 |
|
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; |
|
215 |
|
216 rv = SEC_PKCS7IncludeCertChain(cinfo, NULL); |
|
217 if (rv != SECSuccess) { |
|
218 status = PORT_GetError(); |
|
219 SEC_PKCS7DestroyContentInfo(cinfo); |
|
220 return status; |
|
221 } |
|
222 |
|
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); |
|
227 |
|
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 } |