1 //========================================================================
2 //
3 // SignatureHandler.cc
4 //
5 // This file is licensed under the GPLv2 or later
6 //
7 // Copyright 2015, 2016 André Guerreiro <aguerreiro1985@gmail.com>
8 // Copyright 2015 André Esser <bepandre@hotmail.com>
9 // Copyright 2015, 2016 Albert Astals Cid <aacid@kde.org>
10 // Copyright 2015 Markus Kilås <digital@markuspage.com>
11 //
12 //========================================================================
13
14 #include <config.h>
15
16 #include "SignatureHandler.h"
17 #include "goo/gmem.h"
18 #include <secmod.h>
19
20 #include <dirent.h>
21 #include <Error.h>
22
digestLength(SECOidTag digestAlgId)23 unsigned int SignatureHandler::digestLength(SECOidTag digestAlgId)
24 {
25 switch(digestAlgId){
26 case SEC_OID_SHA1:
27 return 20;
28 case SEC_OID_SHA256:
29 return 32;
30 case SEC_OID_SHA384:
31 return 48;
32 case SEC_OID_SHA512:
33 return 64;
34 default:
35 printf("ERROR: Unrecognized Hash ID\n");
36 return 0;
37 }
38 }
39
getSignerName()40 char *SignatureHandler::getSignerName()
41 {
42 if (!CMSSignerInfo)
43 return NULL;
44
45 CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB());
46 return CERT_GetCommonName(&cert->subject);
47 }
48
getSigningTime()49 time_t SignatureHandler::getSigningTime()
50 {
51 PRTime sTime; // time in microseconds since the epoch
52
53 if (NSS_CMSSignerInfo_GetSigningTime (CMSSignerInfo, &sTime) != SECSuccess)
54 return 0;
55
56 return (time_t) sTime/1000000;
57 }
58
59
getDefaultFirefoxCertDB_Linux()60 GooString *SignatureHandler::getDefaultFirefoxCertDB_Linux()
61 {
62 GooString * finalPath = NULL;
63 DIR *toSearchIn;
64 struct dirent *subFolder;
65
66 GooString * homePath = new GooString(getenv("HOME"));
67 homePath = homePath->append("/.mozilla/firefox/");
68
69 if ((toSearchIn = opendir(homePath->getCString())) == NULL) {
70 error(errInternal, 0, "couldn't find default Firefox Folder");
71 delete homePath;
72 return NULL;
73 }
74 do {
75 if ((subFolder = readdir(toSearchIn)) != NULL) {
76 if (strstr(subFolder->d_name, "default") != NULL) {
77 finalPath = homePath->append(subFolder->d_name);
78 closedir(toSearchIn);
79 return finalPath;
80 }
81 }
82 } while (subFolder != NULL);
83
84 closedir(toSearchIn);
85 delete homePath;
86 return NULL;
87 }
88
89 /**
90 * Initialise NSS
91 */
init_nss()92 void SignatureHandler::init_nss()
93 {
94 GooString *certDBPath = getDefaultFirefoxCertDB_Linux();
95 if (certDBPath == NULL) {
96 NSS_Init("sql:/etc/pki/nssdb");
97 } else {
98 NSS_Init(certDBPath->getCString());
99 }
100 //Make sure NSS root certificates module is loaded
101 SECMOD_AddNewModule("Root Certs", "libnssckbi.so", 0, 0);
102
103 delete certDBPath;
104 }
105
106
SignatureHandler(unsigned char * p7,int p7_length)107 SignatureHandler::SignatureHandler(unsigned char *p7, int p7_length)
108 : CMSMessage(NULL),
109 CMSSignedData(NULL),
110 CMSSignerInfo(NULL),
111 temp_certs(NULL)
112 {
113 init_nss();
114 CMSitem.data = p7;
115 CMSitem.len = p7_length;
116 CMSMessage = CMS_MessageCreate(&CMSitem);
117 CMSSignedData = CMS_SignedDataCreate(CMSMessage);
118 CMSSignerInfo = CMS_SignerInfoCreate(CMSSignedData);
119 hash_context = initHashContext();
120 }
121
initHashContext()122 HASHContext * SignatureHandler::initHashContext()
123 {
124
125 SECItem usedAlgorithm = NSS_CMSSignedData_GetDigestAlgs(CMSSignedData)[0]->algorithm;
126 hash_length = digestLength(SECOID_FindOIDTag(&usedAlgorithm));
127 HASH_HashType hashType;
128 hashType = HASH_GetHashTypeByOidTag(SECOID_FindOIDTag(&usedAlgorithm));
129 return HASH_Create(hashType);
130 }
131
updateHash(unsigned char * data_block,int data_len)132 void SignatureHandler::updateHash(unsigned char * data_block, int data_len)
133 {
134 HASH_Update(hash_context, data_block, data_len);
135 }
136
~SignatureHandler()137 SignatureHandler::~SignatureHandler()
138 {
139 SECITEM_FreeItem(&CMSitem, PR_FALSE);
140 if (CMSSignerInfo)
141 NSS_CMSSignerInfo_Destroy(CMSSignerInfo);
142 if (CMSSignedData)
143 NSS_CMSSignedData_Destroy(CMSSignedData);
144 if (CMSMessage)
145 NSS_CMSMessage_Destroy(CMSMessage);
146
147 if (hash_context)
148 HASH_Destroy(hash_context);
149
150 free(temp_certs);
151
152 if (NSS_Shutdown()!=SECSuccess)
153 fprintf(stderr, "Detail: %s\n", PR_ErrorToString(PORT_GetError(), PR_LANGUAGE_I_DEFAULT));
154 }
155
CMS_MessageCreate(SECItem * cms_item)156 NSSCMSMessage *SignatureHandler::CMS_MessageCreate(SECItem * cms_item)
157 {
158 if (cms_item->data){
159 return NSS_CMSMessage_CreateFromDER(cms_item, NULL, NULL /* Content callback */
160 , NULL, NULL /*Password callback*/
161 , NULL, NULL /*Decrypt callback*/);
162 } else {
163 return NULL;
164 }
165 }
166
CMS_SignedDataCreate(NSSCMSMessage * cms_msg)167 NSSCMSSignedData *SignatureHandler::CMS_SignedDataCreate(NSSCMSMessage * cms_msg)
168 {
169 if (!NSS_CMSMessage_IsSigned(cms_msg)) {
170 error(errInternal, 0, "Input couldn't be parsed as a CMS signature");
171 return NULL;
172 }
173
174 NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(cms_msg, 0);
175 if (!cinfo) {
176 error(errInternal, 0, "Error in NSS_CMSMessage_ContentLevel");
177 return NULL;
178 }
179
180 NSSCMSSignedData *signedData = (NSSCMSSignedData*) NSS_CMSContentInfo_GetContent(cinfo);
181 if (!signedData) {
182 error(errInternal, 0, "CError in NSS_CMSContentInfo_GetContent()");
183 return NULL;
184 }
185
186 if (signedData->rawCerts)
187 {
188 size_t i;
189 for (i = 0; signedData->rawCerts[i]; ++i) {} // just count the length of the certificate chain
190
191 // tempCerts field needs to be filled for complete memory release by NSSCMSSignedData_Destroy
192 signedData->tempCerts = (CERTCertificate **) gmallocn( i+1, sizeof(CERTCertificate *));
193 memset(signedData->tempCerts, 0, (i+1) * sizeof(CERTCertificate *));
194 // store the adresses of these temporary certificates for future release
195 for (i = 0; signedData->rawCerts[i]; ++i)
196 signedData->tempCerts[i] = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), signedData->rawCerts[i], NULL, 0, 0);
197
198 temp_certs = signedData->tempCerts;
199 return signedData;
200 } else {
201 return NULL;
202 }
203 }
204
CMS_SignerInfoCreate(NSSCMSSignedData * cms_sig_data)205 NSSCMSSignerInfo *SignatureHandler::CMS_SignerInfoCreate(NSSCMSSignedData * cms_sig_data)
206 {
207 NSSCMSSignerInfo *signerInfo = NSS_CMSSignedData_GetSignerInfo(cms_sig_data, 0);
208 if (!signerInfo) {
209 printf("Error in NSS_CMSSignedData_GetSignerInfo()\n");
210 return NULL;
211 } else {
212 return signerInfo;
213 }
214 }
215
validateSignature()216 NSSCMSVerificationStatus SignatureHandler::validateSignature()
217 {
218 unsigned char *digest_buffer = NULL;
219
220 if (!CMSSignedData)
221 return NSSCMSVS_MalformedSignature;
222
223 digest_buffer = (unsigned char *)PORT_Alloc(hash_length);
224 unsigned int result_len = 0;
225
226 HASH_End(hash_context, digest_buffer, &result_len, hash_length);
227
228 SECItem digest;
229 digest.data = digest_buffer;
230 digest.len = hash_length;
231
232 if ((NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == NULL)
233 CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
234
235 SECItem * content_info_data = CMSSignedData->contentInfo.content.data;
236 if (content_info_data != NULL && content_info_data->data != NULL)
237 {
238 /*
239 This means it's not a detached type signature
240 so the digest is contained in SignedData->contentInfo
241 */
242 if (memcmp(digest.data, content_info_data->data, hash_length) == 0
243 && digest.len == content_info_data->len)
244 {
245 PORT_Free(digest_buffer);
246 return NSSCMSVS_GoodSignature;
247 }
248 else
249 {
250 PORT_Free(digest_buffer);
251 return NSSCMSVS_DigestMismatch;
252 }
253
254 }
255 else if (NSS_CMSSignerInfo_Verify(CMSSignerInfo, &digest, NULL) != SECSuccess)
256 {
257
258 PORT_Free(digest_buffer);
259 return CMSSignerInfo->verificationStatus;
260 }
261 else
262 {
263 PORT_Free(digest_buffer);
264 return NSSCMSVS_GoodSignature;
265 }
266 }
267
validateCertificate()268 SECErrorCodes SignatureHandler::validateCertificate()
269 {
270 SECErrorCodes retVal;
271 CERTCertificate *cert;
272
273 if (!CMSSignerInfo)
274 return (SECErrorCodes) -1; //error code to avoid matching error codes defined in SECErrorCodes
275
276 if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == NULL)
277 CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
278
279 CERTValInParam inParams[2];
280 inParams[0].type = cert_pi_revocationFlags;
281 inParams[0].value.pointer.revocation = CERT_GetClassicOCSPEnabledSoftFailurePolicy();
282 inParams[1].type = cert_pi_end;
283
284 CERT_PKIXVerifyCert(cert, certificateUsageEmailSigner, inParams, NULL,
285 CMSSignerInfo->cmsg->pwfn_arg);
286
287 retVal = (SECErrorCodes) PORT_GetError();
288
289 if (cert)
290 CERT_DestroyCertificate(cert);
291
292 return retVal;
293 }
294
295
NSS_SigTranslate(NSSCMSVerificationStatus nss_code)296 SignatureValidationStatus SignatureHandler::NSS_SigTranslate(NSSCMSVerificationStatus nss_code)
297 {
298 switch(nss_code)
299 {
300 case NSSCMSVS_GoodSignature:
301 return SIGNATURE_VALID;
302
303 case NSSCMSVS_BadSignature:
304 return SIGNATURE_INVALID;
305
306 case NSSCMSVS_DigestMismatch:
307 return SIGNATURE_DIGEST_MISMATCH;
308
309 case NSSCMSVS_ProcessingError:
310 return SIGNATURE_DECODING_ERROR;
311
312 default:
313 return SIGNATURE_GENERIC_ERROR;
314 }
315 }
316
NSS_CertTranslate(SECErrorCodes nss_code)317 CertificateValidationStatus SignatureHandler::NSS_CertTranslate(SECErrorCodes nss_code)
318 {
319 // 0 not defined in SECErrorCodes, it means success for this purpose.
320 if (nss_code == (SECErrorCodes) 0)
321 return CERTIFICATE_TRUSTED;
322
323 switch(nss_code)
324 {
325 case SEC_ERROR_UNKNOWN_ISSUER:
326 return CERTIFICATE_UNKNOWN_ISSUER;
327
328 case SEC_ERROR_UNTRUSTED_ISSUER:
329 return CERTIFICATE_UNTRUSTED_ISSUER;
330
331 case SEC_ERROR_REVOKED_CERTIFICATE:
332 return CERTIFICATE_REVOKED;
333
334 case SEC_ERROR_EXPIRED_CERTIFICATE:
335 return CERTIFICATE_EXPIRED;
336
337 default:
338 return CERTIFICATE_GENERIC_ERROR;
339 }
340 }
341