root/chrome/common/net/x509_certificate_model_nss.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Stringize
  2. HashCert
  3. ProcessSecAlgorithmInternal
  4. ProcessExtension
  5. GetCertNameOrNickname
  6. GetNickname
  7. GetTokenName
  8. GetVersion
  9. GetType
  10. GetEmailAddress
  11. GetUsageStrings
  12. GetKeyUsageString
  13. GetSerialNumberHexified
  14. GetIssuerCommonName
  15. GetIssuerOrgName
  16. GetIssuerOrgUnitName
  17. GetSubjectOrgName
  18. GetSubjectOrgUnitName
  19. GetSubjectCommonName
  20. GetTimes
  21. GetTitle
  22. GetIssuerName
  23. GetSubjectName
  24. GetEmailAddresses
  25. GetNicknameStringsFromCertList
  26. GetPkcs11Id
  27. GetExtensions
  28. HashCertSHA256
  29. HashCertSHA1
  30. GetCertChainFromCert
  31. DestroyCertChain
  32. GetDerString
  33. GetCMSString
  34. ProcessSecAlgorithmSignature
  35. ProcessSecAlgorithmSubjectPublicKey
  36. ProcessSecAlgorithmSignatureWrap
  37. ProcessSubjectPublicKeyInfo
  38. ProcessRawBitsSignatureWrap
  39. RegisterDynamicOids

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/net/x509_certificate_model.h"

#include <cert.h>
#include <cms.h>
#include <hasht.h>
#include <keyhi.h>  // SECKEY_DestroyPrivateKey
#include <keythi.h>  // SECKEYPrivateKey
#include <pk11pub.h>  // PK11_FindKeyByAnyCert
#include <seccomon.h>  // SECItem
#include <sechash.h>

#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h"
#include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h"
#include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h"
#include "crypto/nss_util.h"
#include "crypto/scoped_nss_types.h"
#include "net/cert/x509_certificate.h"

namespace psm = mozilla_security_manager;

namespace {

// Convert a char* return value from NSS into a std::string and free the NSS
// memory.  If the arg is NULL, an empty string will be returned instead.
std::string Stringize(char* nss_text, const std::string& alternative_text) {
  if (!nss_text)
    return alternative_text;

  std::string s = nss_text;
  PORT_Free(nss_text);
  return s;
}

// Hash a certificate using the given algorithm, return the result as a
// colon-seperated hex string.  The len specified is the number of bytes
// required for storing the raw fingerprint.
// (It's a bit redundant that the caller needs to specify len in addition to the
// algorithm, but given the limited uses, not worth fixing.)
std::string HashCert(CERTCertificate* cert, HASH_HashType algorithm, int len) {
  unsigned char fingerprint[HASH_LENGTH_MAX];

  DCHECK(NULL != cert->derCert.data);
  DCHECK_NE(0U, cert->derCert.len);
  DCHECK_LE(len, HASH_LENGTH_MAX);
  memset(fingerprint, 0, len);
  SECStatus rv = HASH_HashBuf(algorithm, fingerprint, cert->derCert.data,
                              cert->derCert.len);
  DCHECK_EQ(rv, SECSuccess);
  return x509_certificate_model::ProcessRawBytes(fingerprint, len);
}

std::string ProcessSecAlgorithmInternal(SECAlgorithmID* algorithm_id) {
  return psm::GetOIDText(&algorithm_id->algorithm);
}

std::string ProcessExtension(
    const std::string& critical_label,
    const std::string& non_critical_label,
    CERTCertExtension* extension) {
  std::string criticality =
      extension->critical.data && extension->critical.data[0] ?
          critical_label : non_critical_label;
  return criticality + "\n" +
      psm::ProcessExtensionData(SECOID_FindOIDTag(&extension->id),
                                &extension->value);
}

////////////////////////////////////////////////////////////////////////////////
// NSS certificate export functions.

struct NSSCMSMessageDeleter {
  inline void operator()(NSSCMSMessage* x) const {
    NSS_CMSMessage_Destroy(x);
  }
};
typedef scoped_ptr<NSSCMSMessage, NSSCMSMessageDeleter> ScopedNSSCMSMessage;

struct FreeNSSCMSSignedData {
  inline void operator()(NSSCMSSignedData* x) const {
    NSS_CMSSignedData_Destroy(x);
  }
};
typedef scoped_ptr<NSSCMSSignedData, FreeNSSCMSSignedData>
    ScopedNSSCMSSignedData;

}  // namespace

namespace x509_certificate_model {

using net::X509Certificate;
using std::string;

string GetCertNameOrNickname(X509Certificate::OSCertHandle cert_handle) {
  string name = ProcessIDN(
      Stringize(CERT_GetCommonName(&cert_handle->subject), std::string()));
  if (!name.empty())
    return name;
  return GetNickname(cert_handle);
}

string GetNickname(X509Certificate::OSCertHandle cert_handle) {
  string name;
  if (cert_handle->nickname) {
    name = cert_handle->nickname;
    // Hack copied from mozilla: Cut off text before first :, which seems to
    // just be the token name.
    size_t colon_pos = name.find(':');
    if (colon_pos != string::npos)
      name = name.substr(colon_pos + 1);
  }
  return name;
}

string GetTokenName(X509Certificate::OSCertHandle cert_handle) {
  return psm::GetCertTokenName(cert_handle);
}

string GetVersion(X509Certificate::OSCertHandle cert_handle) {
  // If the version field is omitted from the certificate, the default
  // value is v1(0).
  unsigned long version = 0;
  if (cert_handle->version.len == 0 ||
      SEC_ASN1DecodeInteger(&cert_handle->version, &version) == SECSuccess) {
    return base::UintToString(version + 1);
  }
  return std::string();
}

net::CertType GetType(X509Certificate::OSCertHandle cert_handle) {
    return psm::GetCertType(cert_handle);
}

string GetEmailAddress(X509Certificate::OSCertHandle cert_handle) {
  if (cert_handle->emailAddr)
    return cert_handle->emailAddr;
  return std::string();
}

void GetUsageStrings(X509Certificate::OSCertHandle cert_handle,
                     std::vector<string>* usages) {
  psm::GetCertUsageStrings(cert_handle, usages);
}

string GetKeyUsageString(X509Certificate::OSCertHandle cert_handle) {
  SECItem key_usage;
  key_usage.data = NULL;
  string key_usage_str;
  if (CERT_FindKeyUsageExtension(cert_handle, &key_usage) == SECSuccess) {
    key_usage_str = psm::ProcessKeyUsageBitString(&key_usage, ',');
    PORT_Free(key_usage.data);
  }
  return key_usage_str;
}

string GetSerialNumberHexified(X509Certificate::OSCertHandle cert_handle,
                               const string& alternative_text) {
  return Stringize(CERT_Hexify(&cert_handle->serialNumber, true),
                   alternative_text);
}

string GetIssuerCommonName(X509Certificate::OSCertHandle cert_handle,
                           const string& alternative_text) {
  return Stringize(CERT_GetCommonName(&cert_handle->issuer), alternative_text);
}

string GetIssuerOrgName(X509Certificate::OSCertHandle cert_handle,
                        const string& alternative_text) {
  return Stringize(CERT_GetOrgName(&cert_handle->issuer), alternative_text);
}

string GetIssuerOrgUnitName(X509Certificate::OSCertHandle cert_handle,
                            const string& alternative_text) {
  return Stringize(CERT_GetOrgUnitName(&cert_handle->issuer), alternative_text);
}

string GetSubjectOrgName(X509Certificate::OSCertHandle cert_handle,
                         const string& alternative_text) {
  return Stringize(CERT_GetOrgName(&cert_handle->subject), alternative_text);
}

string GetSubjectOrgUnitName(X509Certificate::OSCertHandle cert_handle,
                             const string& alternative_text) {
  return Stringize(CERT_GetOrgUnitName(&cert_handle->subject),
                   alternative_text);
}

string GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle,
                            const string& alternative_text) {
  return Stringize(CERT_GetCommonName(&cert_handle->subject), alternative_text);
}

bool GetTimes(X509Certificate::OSCertHandle cert_handle,
              base::Time* issued, base::Time* expires) {
  PRTime pr_issued, pr_expires;
  if (CERT_GetCertTimes(cert_handle, &pr_issued, &pr_expires) == SECSuccess) {
    *issued = crypto::PRTimeToBaseTime(pr_issued);
    *expires = crypto::PRTimeToBaseTime(pr_expires);
    return true;
  }
  return false;
}

string GetTitle(X509Certificate::OSCertHandle cert_handle) {
  return psm::GetCertTitle(cert_handle);
}

string GetIssuerName(X509Certificate::OSCertHandle cert_handle) {
  return psm::ProcessName(&cert_handle->issuer);
}

string GetSubjectName(X509Certificate::OSCertHandle cert_handle) {
  return psm::ProcessName(&cert_handle->subject);
}

void GetEmailAddresses(X509Certificate::OSCertHandle cert_handle,
                       std::vector<string>* email_addresses) {
  for (const char* addr = CERT_GetFirstEmailAddress(cert_handle);
       addr; addr = CERT_GetNextEmailAddress(cert_handle, addr)) {
    // The first email addr (from Subject) may be duplicated in Subject
    // Alternative Name, so check subsequent addresses are not equal to the
    // first one before adding to the list.
    if (!email_addresses->size() || (*email_addresses)[0] != addr)
      email_addresses->push_back(addr);
  }
}

void GetNicknameStringsFromCertList(
    const std::vector<scoped_refptr<X509Certificate> >& certs,
    const string& cert_expired,
    const string& cert_not_yet_valid,
    std::vector<string>* nick_names) {
  CERTCertList* cert_list = CERT_NewCertList();
  for (size_t i = 0; i < certs.size(); ++i) {
    CERT_AddCertToListTail(
        cert_list,
        CERT_DupCertificate(certs[i]->os_cert_handle()));
  }
  // Would like to use CERT_GetCertNicknameWithValidity on each cert
  // individually instead of having to build a CERTCertList for this, but that
  // function is not exported.
  CERTCertNicknames* cert_nicknames = CERT_NicknameStringsFromCertList(
      cert_list,
      const_cast<char*>(cert_expired.c_str()),
      const_cast<char*>(cert_not_yet_valid.c_str()));
  DCHECK_EQ(cert_nicknames->numnicknames,
            static_cast<int>(certs.size()));

  for (int i = 0; i < cert_nicknames->numnicknames; ++i)
    nick_names->push_back(cert_nicknames->nicknames[i]);

  CERT_FreeNicknames(cert_nicknames);
  CERT_DestroyCertList(cert_list);
}

// For background see this discussion on dev-tech-crypto.lists.mozilla.org:
// http://web.archiveorange.com/archive/v/6JJW7E40sypfZGtbkzxX
//
// NOTE: This function relies on the convention that the same PKCS#11 ID
// is shared between a certificate and its associated private and public
// keys.  I tried to implement this with PK11_GetLowLevelKeyIDForCert(),
// but that always returns NULL on Chrome OS for me.
std::string GetPkcs11Id(net::X509Certificate::OSCertHandle cert_handle) {
  std::string pkcs11_id;
  SECKEYPrivateKey *priv_key = PK11_FindKeyByAnyCert(cert_handle,
                                                     NULL /* wincx */);
  if (priv_key) {
    // Get the CKA_ID attribute for a key.
    SECItem* sec_item = PK11_GetLowLevelKeyIDForPrivateKey(priv_key);
    if (sec_item) {
      pkcs11_id = base::HexEncode(sec_item->data, sec_item->len);
      SECITEM_FreeItem(sec_item, PR_TRUE);
    }
    SECKEY_DestroyPrivateKey(priv_key);
  }
  return pkcs11_id;
}

void GetExtensions(
    const string& critical_label,
    const string& non_critical_label,
    X509Certificate::OSCertHandle cert_handle,
    Extensions* extensions) {
  if (cert_handle->extensions) {
    for (size_t i = 0; cert_handle->extensions[i] != NULL; ++i) {
      Extension extension;
      extension.name = psm::GetOIDText(&cert_handle->extensions[i]->id);
      extension.value = ProcessExtension(
          critical_label, non_critical_label, cert_handle->extensions[i]);
      extensions->push_back(extension);
    }
  }
}

string HashCertSHA256(X509Certificate::OSCertHandle cert_handle) {
  return HashCert(cert_handle, HASH_AlgSHA256, SHA256_LENGTH);
}

string HashCertSHA1(X509Certificate::OSCertHandle cert_handle) {
  return HashCert(cert_handle, HASH_AlgSHA1, SHA1_LENGTH);
}

void GetCertChainFromCert(X509Certificate::OSCertHandle cert_handle,
                          X509Certificate::OSCertHandles* cert_handles) {
  CERTCertList* cert_list =
      CERT_GetCertChainFromCert(cert_handle, PR_Now(), certUsageSSLServer);
  CERTCertListNode* node;
  for (node = CERT_LIST_HEAD(cert_list);
       !CERT_LIST_END(node, cert_list);
       node = CERT_LIST_NEXT(node)) {
    cert_handles->push_back(CERT_DupCertificate(node->cert));
  }
  CERT_DestroyCertList(cert_list);
}

void DestroyCertChain(X509Certificate::OSCertHandles* cert_handles) {
  for (X509Certificate::OSCertHandles::iterator i(cert_handles->begin());
       i != cert_handles->end(); ++i)
    CERT_DestroyCertificate(*i);
  cert_handles->clear();
}

string GetDerString(X509Certificate::OSCertHandle cert_handle) {
  return string(reinterpret_cast<const char*>(cert_handle->derCert.data),
                cert_handle->derCert.len);
}

string GetCMSString(const X509Certificate::OSCertHandles& cert_chain,
                    size_t start, size_t end) {
  crypto::ScopedPLArenaPool arena(PORT_NewArena(1024));
  DCHECK(arena.get());

  ScopedNSSCMSMessage message(NSS_CMSMessage_Create(arena.get()));
  DCHECK(message.get());

  // First, create SignedData with the certificate only (no chain).
  ScopedNSSCMSSignedData signed_data(NSS_CMSSignedData_CreateCertsOnly(
      message.get(), cert_chain[start], PR_FALSE));
  if (!signed_data.get()) {
    DLOG(ERROR) << "NSS_CMSSignedData_Create failed";
    return std::string();
  }
  // Add the rest of the chain (if any).
  for (size_t i = start + 1; i < end; ++i) {
    if (NSS_CMSSignedData_AddCertificate(signed_data.get(), cert_chain[i]) !=
        SECSuccess) {
      DLOG(ERROR) << "NSS_CMSSignedData_AddCertificate failed on " << i;
      return std::string();
    }
  }

  NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(message.get());
  if (NSS_CMSContentInfo_SetContent_SignedData(
      message.get(), cinfo, signed_data.get()) == SECSuccess) {
    ignore_result(signed_data.release());
  } else {
    DLOG(ERROR) << "NSS_CMSMessage_GetContentInfo failed";
    return std::string();
  }

  SECItem cert_p7 = { siBuffer, NULL, 0 };
  NSSCMSEncoderContext *ecx = NSS_CMSEncoder_Start(message.get(), NULL, NULL,
                                                   &cert_p7, arena.get(), NULL,
                                                   NULL, NULL, NULL, NULL,
                                                   NULL);
  if (!ecx) {
    DLOG(ERROR) << "NSS_CMSEncoder_Start failed";
    return std::string();
  }

  if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
    DLOG(ERROR) << "NSS_CMSEncoder_Finish failed";
    return std::string();
  }

  return string(reinterpret_cast<const char*>(cert_p7.data), cert_p7.len);
}

string ProcessSecAlgorithmSignature(X509Certificate::OSCertHandle cert_handle) {
  return ProcessSecAlgorithmInternal(&cert_handle->signature);
}

string ProcessSecAlgorithmSubjectPublicKey(
    X509Certificate::OSCertHandle cert_handle) {
  return ProcessSecAlgorithmInternal(
      &cert_handle->subjectPublicKeyInfo.algorithm);
}

string ProcessSecAlgorithmSignatureWrap(
    X509Certificate::OSCertHandle cert_handle) {
  return ProcessSecAlgorithmInternal(
      &cert_handle->signatureWrap.signatureAlgorithm);
}

string ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle) {
  return psm::ProcessSubjectPublicKeyInfo(&cert_handle->subjectPublicKeyInfo);
}

string ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle) {
  return ProcessRawBits(cert_handle->signatureWrap.signature.data,
                        cert_handle->signatureWrap.signature.len);
}

void RegisterDynamicOids() {
  psm::RegisterDynamicOids();
}

}  // namespace x509_certificate_model

/* [<][>][^][v][top][bottom][index][help] */