SignatureHandler.cc 10 KB
Newer Older
1 2 3 4 5 6
//========================================================================
//
// SignatureHandler.cc
//
// This file is licensed under the GPLv2 or later
//
Albert Astals Cid's avatar
Albert Astals Cid committed
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>
Markus Kilås's avatar
Markus Kilås committed
10
// Copyright 2015 Markus Kilås <digital@markuspage.com>
11
// Copyright 2017 Sebastian Rasmussen <sebras@gmail.com>
12
// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
13 14 15 16 17 18 19
//
//========================================================================

#include <config.h>

#include "SignatureHandler.h"
#include "goo/gmem.h"
20
#include <secmod.h>
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

#include <dirent.h>
#include <Error.h>

unsigned int SignatureHandler::digestLength(SECOidTag digestAlgId)
{
  switch(digestAlgId){
    case SEC_OID_SHA1:
      return 20;
    case SEC_OID_SHA256:
      return 32;
    case SEC_OID_SHA384:
      return 48;
    case SEC_OID_SHA512:
      return 64;
    default:
      printf("ERROR: Unrecognized Hash ID\n");
      return 0;
  }
}

char *SignatureHandler::getSignerName()
{
  if (!CMSSignerInfo)
45
      return nullptr;
46 47 48 49 50

  CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB());
  return CERT_GetCommonName(&cert->subject);
}

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
const char * SignatureHandler::getSignerSubjectDN()
{
  if (!CMSSignerInfo)
    return nullptr;

  CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB());
  if (!cert)
    return nullptr;
  return cert->subjectName;
}

HASH_HashType SignatureHandler::getHashAlgorithm()
{
  if (hash_context && hash_context->hashobj)
  {
    return hash_context->hashobj->type;
  }
  return HASH_AlgNULL;
}

71 72 73 74 75 76 77
time_t SignatureHandler::getSigningTime()
{
  PRTime sTime; // time in microseconds since the epoch

  if (NSS_CMSSignerInfo_GetSigningTime (CMSSignerInfo, &sTime) != SECSuccess)
    return 0;

78
  return static_cast<time_t>(sTime/1000000);
79 80 81 82 83
}


GooString *SignatureHandler::getDefaultFirefoxCertDB_Linux()
{
84
  GooString * finalPath = nullptr;
85 86 87 88 89 90
  DIR *toSearchIn;
  struct dirent *subFolder;

  GooString * homePath = new GooString(getenv("HOME"));
  homePath = homePath->append("/.mozilla/firefox/");

91
  if ((toSearchIn = opendir(homePath->getCString())) == nullptr) {
92 93
    error(errInternal, 0, "couldn't find default Firefox Folder");
    delete homePath;
94
    return nullptr;
95 96
  }
  do {
97 98
    if ((subFolder = readdir(toSearchIn)) != nullptr) {
      if (strstr(subFolder->d_name, "default") != nullptr) {
99 100 101 102 103
	finalPath = homePath->append(subFolder->d_name);
	closedir(toSearchIn);
	return finalPath;
      }
    }
104
  } while (subFolder != nullptr);
105

106
  closedir(toSearchIn);
107
  delete homePath;
108
  return nullptr;
109 110 111 112 113 114 115 116
}

/**
 * Initialise NSS
 */
void SignatureHandler::init_nss() 
{
  GooString *certDBPath = getDefaultFirefoxCertDB_Linux();
117
  if (certDBPath == nullptr) {
118 119 120 121
    NSS_Init("sql:/etc/pki/nssdb");
  } else {
    NSS_Init(certDBPath->getCString());
  }
122 123
  //Make sure NSS root certificates module is loaded
  SECMOD_AddNewModule("Root Certs", "libnssckbi.so", 0, 0);
124

125
  delete certDBPath;
126 127 128 129
}


SignatureHandler::SignatureHandler(unsigned char *p7, int p7_length)
130 131 132 133 134
 : hash_context(nullptr),
   CMSMessage(nullptr),
   CMSSignedData(nullptr),
   CMSSignerInfo(nullptr),
   temp_certs(nullptr)
135 136 137 138 139 140
{
  init_nss();
  CMSitem.data = p7;
  CMSitem.len = p7_length;
  CMSMessage = CMS_MessageCreate(&CMSitem);
  CMSSignedData = CMS_SignedDataCreate(CMSMessage);
141 142 143 144
  if (CMSSignedData) {
    CMSSignerInfo = CMS_SignerInfoCreate(CMSSignedData);
    hash_context = initHashContext();
  }
145 146 147 148 149 150 151 152 153 154
}

HASHContext * SignatureHandler::initHashContext()
{

  SECItem usedAlgorithm = NSS_CMSSignedData_GetDigestAlgs(CMSSignedData)[0]->algorithm;
  hash_length = digestLength(SECOID_FindOIDTag(&usedAlgorithm));
  HASH_HashType hashType;
  hashType    = HASH_GetHashTypeByOidTag(SECOID_FindOIDTag(&usedAlgorithm));
  return HASH_Create(hashType);
155 156
}

157 158
void SignatureHandler::updateHash(unsigned char * data_block, int data_len)
{
159 160 161
  if (hash_context) {
    HASH_Update(hash_context, data_block, data_len);
  }
162
}
163 164 165 166 167 168 169 170 171 172 173

SignatureHandler::~SignatureHandler()
{
  SECITEM_FreeItem(&CMSitem, PR_FALSE);
  if (CMSSignerInfo)
    NSS_CMSSignerInfo_Destroy(CMSSignerInfo);
  if (CMSSignedData)
    NSS_CMSSignedData_Destroy(CMSSignedData);
  if (CMSMessage)
    NSS_CMSMessage_Destroy(CMSMessage);

174 175 176
  if (hash_context)
    HASH_Destroy(hash_context);

177 178 179 180 181 182 183 184 185
  free(temp_certs);

  if (NSS_Shutdown()!=SECSuccess)
    fprintf(stderr, "Detail: %s\n", PR_ErrorToString(PORT_GetError(), PR_LANGUAGE_I_DEFAULT));
}

NSSCMSMessage *SignatureHandler::CMS_MessageCreate(SECItem * cms_item)
{
  if (cms_item->data){
186 187 188
    return NSS_CMSMessage_CreateFromDER(cms_item, nullptr, nullptr /* Content callback */
                        , nullptr, nullptr /*Password callback*/
                        , nullptr, nullptr /*Decrypt callback*/);
189
  } else {
190
    return nullptr;
191 192 193 194 195 196 197
  }
}

NSSCMSSignedData *SignatureHandler::CMS_SignedDataCreate(NSSCMSMessage * cms_msg)
{
  if (!NSS_CMSMessage_IsSigned(cms_msg)) {
    error(errInternal, 0, "Input couldn't be parsed as a CMS signature");
198
    return nullptr;
199 200 201 202 203
  }

  NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(cms_msg, 0);
  if (!cinfo) {
    error(errInternal, 0, "Error in NSS_CMSMessage_ContentLevel");
204
    return nullptr;
205 206 207 208 209
  }

  NSSCMSSignedData *signedData = (NSSCMSSignedData*) NSS_CMSContentInfo_GetContent(cinfo);
  if (!signedData) {
    error(errInternal, 0, "CError in NSS_CMSContentInfo_GetContent()");
210
    return nullptr;
211 212 213 214 215 216 217 218 219 220 221 222
  }

  if (signedData->rawCerts)
  {
    size_t i;
    for (i = 0; signedData->rawCerts[i]; ++i) {} // just count the length of the certificate chain

    // tempCerts field needs to be filled for complete memory release by NSSCMSSignedData_Destroy
    signedData->tempCerts = (CERTCertificate **) gmallocn( i+1, sizeof(CERTCertificate *));
    memset(signedData->tempCerts, 0, (i+1) * sizeof(CERTCertificate *));
    // store the adresses of these temporary certificates for future release
    for (i = 0; signedData->rawCerts[i]; ++i)
223
      signedData->tempCerts[i] = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), signedData->rawCerts[i], nullptr, 0, 0);
224 225 226 227

    temp_certs = signedData->tempCerts;
    return signedData;
  } else {
228
    return nullptr;
229 230 231 232 233 234 235 236
  }
}

NSSCMSSignerInfo *SignatureHandler::CMS_SignerInfoCreate(NSSCMSSignedData * cms_sig_data)
{
  NSSCMSSignerInfo *signerInfo = NSS_CMSSignedData_GetSignerInfo(cms_sig_data, 0);
  if (!signerInfo) {
    printf("Error in NSS_CMSSignedData_GetSignerInfo()\n");
237
    return nullptr;
238 239 240 241 242
  } else {
    return signerInfo;
  }
}

243
NSSCMSVerificationStatus SignatureHandler::validateSignature()
244
{
245
  unsigned char *digest_buffer = nullptr;
246 247 248 249 250

  if (!CMSSignedData)
    return NSSCMSVS_MalformedSignature;

  digest_buffer = (unsigned char *)PORT_Alloc(hash_length);
251
  unsigned int result_len = 0;
252

253
  HASH_End(hash_context, digest_buffer, &result_len, hash_length);
254 255 256 257 258

  SECItem digest;
  digest.data = digest_buffer;
  digest.len = hash_length;

259
  if ((NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr)
260 261
    CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;

262
  SECItem * content_info_data = CMSSignedData->contentInfo.content.data;
263
  if (content_info_data != nullptr && content_info_data->data != nullptr)
264 265 266 267 268
  {
    /*
      This means it's not a detached type signature
      so the digest is contained in SignedData->contentInfo
    */
269 270
    if (memcmp(digest.data, content_info_data->data, hash_length) == 0
        && digest.len == content_info_data->len)
271 272 273 274 275 276 277 278 279 280 281
    {
      PORT_Free(digest_buffer);
      return NSSCMSVS_GoodSignature;
    }
    else
    {
      PORT_Free(digest_buffer);
      return NSSCMSVS_DigestMismatch;
    }

  }
282
  else if (NSS_CMSSignerInfo_Verify(CMSSignerInfo, &digest, nullptr) != SECSuccess)
283 284
  {

285 286
    PORT_Free(digest_buffer);
    return CMSSignerInfo->verificationStatus;
287 288 289
  }
  else
  {
290 291 292 293 294
    PORT_Free(digest_buffer);
    return NSSCMSVS_GoodSignature;
  }
}

295
SECErrorCodes SignatureHandler::validateCertificate(time_t validation_time)
296 297 298 299 300 301 302
{
  SECErrorCodes retVal;
  CERTCertificate *cert;

  if (!CMSSignerInfo)
    return (SECErrorCodes) -1; //error code to avoid matching error codes defined in SECErrorCodes

303
  if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr)
304 305
    CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;

306 307 308 309
  PRTime vTime = 0; // time in microseconds since the epoch, special value 0 means now
  if (validation_time > 0)
    vTime = 1000000*(PRTime)validation_time;
  CERTValInParam inParams[3];
310 311
  inParams[0].type = cert_pi_revocationFlags;
  inParams[0].value.pointer.revocation = CERT_GetClassicOCSPEnabledSoftFailurePolicy();
312 313 314
  inParams[1].type = cert_pi_date;
  inParams[1].value.scalar.time = vTime;
  inParams[2].type = cert_pi_end;
315

316
  CERT_PKIXVerifyCert(cert, certificateUsageEmailSigner, inParams, nullptr,
317
                CMSSignerInfo->cmsg->pwfn_arg);
318

319
  retVal = (SECErrorCodes) PORT_GetError();
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

  if (cert)
    CERT_DestroyCertificate(cert);

  return retVal;
}


SignatureValidationStatus SignatureHandler::NSS_SigTranslate(NSSCMSVerificationStatus nss_code)
{
  switch(nss_code)
  {
    case NSSCMSVS_GoodSignature:
      return SIGNATURE_VALID;

    case NSSCMSVS_BadSignature:
      return SIGNATURE_INVALID;

      case NSSCMSVS_DigestMismatch:
      return SIGNATURE_DIGEST_MISMATCH;

    case NSSCMSVS_ProcessingError:
      return SIGNATURE_DECODING_ERROR;

    default:
      return SIGNATURE_GENERIC_ERROR;
  }
}

CertificateValidationStatus SignatureHandler::NSS_CertTranslate(SECErrorCodes nss_code)
{
  // 0 not defined in SECErrorCodes, it means success for this purpose.
  if (nss_code == (SECErrorCodes) 0)
    return CERTIFICATE_TRUSTED;

  switch(nss_code)
  {
    case SEC_ERROR_UNKNOWN_ISSUER:
358 359
      return CERTIFICATE_UNKNOWN_ISSUER;

Markus Kilås's avatar
Markus Kilås committed
360
    case SEC_ERROR_UNTRUSTED_ISSUER:
361
      return CERTIFICATE_UNTRUSTED_ISSUER;
362 363 364 365 366 367 368 369 370 371 372

    case SEC_ERROR_REVOKED_CERTIFICATE:
      return CERTIFICATE_REVOKED;

    case SEC_ERROR_EXPIRED_CERTIFICATE:
      return CERTIFICATE_EXPIRED;

    default:
      return CERTIFICATE_GENERIC_ERROR;
  }
}