1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/common/net/x509_certificate_model.h"
6
7#include <cert.h>
8#include <cms.h>
9#include <hasht.h>
10#include <keyhi.h>  // SECKEY_DestroyPrivateKey
11#include <keythi.h>  // SECKEYPrivateKey
12#include <pk11pub.h>  // PK11_FindKeyByAnyCert
13#include <seccomon.h>  // SECItem
14#include <sechash.h>
15
16#include "base/logging.h"
17#include "base/string_number_conversions.h"
18#include "crypto/nss_util.h"
19#include "net/base/x509_certificate.h"
20#include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h"
21#include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h"
22#include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h"
23
24namespace psm = mozilla_security_manager;
25
26namespace {
27
28// Convert a char* return value from NSS into a std::string and free the NSS
29// memory.  If the arg is NULL, an empty string will be returned instead.
30std::string Stringize(char* nss_text, const std::string& alternative_text) {
31  if (!nss_text)
32    return alternative_text;
33
34  std::string s = nss_text;
35  PORT_Free(nss_text);
36  return s;
37}
38
39// Hash a certificate using the given algorithm, return the result as a
40// colon-seperated hex string.  The len specified is the number of bytes
41// required for storing the raw fingerprint.
42// (It's a bit redundant that the caller needs to specify len in addition to the
43// algorithm, but given the limited uses, not worth fixing.)
44std::string HashCert(CERTCertificate* cert, HASH_HashType algorithm, int len) {
45  unsigned char fingerprint[HASH_LENGTH_MAX];
46
47  DCHECK(NULL != cert->derCert.data);
48  DCHECK_NE(0U, cert->derCert.len);
49  DCHECK_LE(len, HASH_LENGTH_MAX);
50  memset(fingerprint, 0, len);
51  SECStatus rv = HASH_HashBuf(algorithm, fingerprint, cert->derCert.data,
52                              cert->derCert.len);
53  DCHECK_EQ(rv, SECSuccess);
54  return x509_certificate_model::ProcessRawBytes(fingerprint, len);
55}
56
57std::string ProcessSecAlgorithmInternal(SECAlgorithmID* algorithm_id) {
58  return psm::GetOIDText(&algorithm_id->algorithm);
59}
60
61std::string ProcessExtension(
62    const std::string& critical_label,
63    const std::string& non_critical_label,
64    CERTCertExtension* extension) {
65  std::string criticality =
66      extension->critical.data && extension->critical.data[0] ?
67          critical_label : non_critical_label;
68  return criticality + "\n" +
69      psm::ProcessExtensionData(SECOID_FindOIDTag(&extension->id),
70                                &extension->value);
71}
72
73////////////////////////////////////////////////////////////////////////////////
74// NSS certificate export functions.
75
76class FreeNSSCMSMessage {
77 public:
78  inline void operator()(NSSCMSMessage* x) const {
79    NSS_CMSMessage_Destroy(x);
80  }
81};
82typedef scoped_ptr_malloc<NSSCMSMessage, FreeNSSCMSMessage>
83    ScopedNSSCMSMessage;
84
85class FreeNSSCMSSignedData {
86 public:
87  inline void operator()(NSSCMSSignedData* x) const {
88    NSS_CMSSignedData_Destroy(x);
89  }
90};
91typedef scoped_ptr_malloc<NSSCMSSignedData, FreeNSSCMSSignedData>
92    ScopedNSSCMSSignedData;
93
94}  // namespace
95
96namespace x509_certificate_model {
97
98using net::X509Certificate;
99using std::string;
100
101string GetCertNameOrNickname(X509Certificate::OSCertHandle cert_handle) {
102  string name = ProcessIDN(Stringize(CERT_GetCommonName(&cert_handle->subject),
103                                     ""));
104  if (!name.empty())
105    return name;
106  return GetNickname(cert_handle);
107}
108
109string GetNickname(X509Certificate::OSCertHandle cert_handle) {
110  string name;
111  if (cert_handle->nickname) {
112    name = cert_handle->nickname;
113    // Hack copied from mozilla: Cut off text before first :, which seems to
114    // just be the token name.
115    size_t colon_pos = name.find(':');
116    if (colon_pos != string::npos)
117      name = name.substr(colon_pos + 1);
118  }
119  return name;
120}
121
122string GetTokenName(X509Certificate::OSCertHandle cert_handle) {
123  return psm::GetCertTokenName(cert_handle);
124}
125
126string GetVersion(X509Certificate::OSCertHandle cert_handle) {
127  unsigned long version = ULONG_MAX;
128  if (SEC_ASN1DecodeInteger(&cert_handle->version, &version) == SECSuccess &&
129      version != ULONG_MAX)
130    return base::UintToString(version + 1);
131  return "";
132}
133
134net::CertType GetType(X509Certificate::OSCertHandle cert_handle) {
135    return psm::GetCertType(cert_handle);
136}
137
138string GetEmailAddress(X509Certificate::OSCertHandle cert_handle) {
139  if (cert_handle->emailAddr)
140    return cert_handle->emailAddr;
141  return "";
142}
143
144void GetUsageStrings(X509Certificate::OSCertHandle cert_handle,
145                     std::vector<string>* usages) {
146  psm::GetCertUsageStrings(cert_handle, usages);
147}
148
149string GetKeyUsageString(X509Certificate::OSCertHandle cert_handle) {
150  SECItem key_usage;
151  key_usage.data = NULL;
152  string key_usage_str;
153  if (CERT_FindKeyUsageExtension(cert_handle, &key_usage) == SECSuccess) {
154    key_usage_str = psm::ProcessKeyUsageBitString(&key_usage, ',');
155    PORT_Free(key_usage.data);
156  }
157  return key_usage_str;
158}
159
160string GetSerialNumberHexified(X509Certificate::OSCertHandle cert_handle,
161                               const string& alternative_text) {
162  return Stringize(CERT_Hexify(&cert_handle->serialNumber, true),
163                   alternative_text);
164}
165
166string GetIssuerCommonName(X509Certificate::OSCertHandle cert_handle,
167                           const string& alternative_text) {
168  return Stringize(CERT_GetCommonName(&cert_handle->issuer), alternative_text);
169}
170
171string GetIssuerOrgName(X509Certificate::OSCertHandle cert_handle,
172                        const string& alternative_text) {
173  return Stringize(CERT_GetOrgName(&cert_handle->issuer), alternative_text);
174}
175
176string GetIssuerOrgUnitName(X509Certificate::OSCertHandle cert_handle,
177                            const string& alternative_text) {
178  return Stringize(CERT_GetOrgUnitName(&cert_handle->issuer), alternative_text);
179}
180
181string GetSubjectOrgName(X509Certificate::OSCertHandle cert_handle,
182                         const string& alternative_text) {
183  return Stringize(CERT_GetOrgName(&cert_handle->subject), alternative_text);
184}
185
186string GetSubjectOrgUnitName(X509Certificate::OSCertHandle cert_handle,
187                             const string& alternative_text) {
188  return Stringize(CERT_GetOrgUnitName(&cert_handle->subject),
189                   alternative_text);
190}
191
192string GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle,
193                            const string& alternative_text) {
194  return Stringize(CERT_GetCommonName(&cert_handle->subject), alternative_text);
195}
196
197bool GetTimes(X509Certificate::OSCertHandle cert_handle,
198              base::Time* issued, base::Time* expires) {
199  PRTime pr_issued, pr_expires;
200  if (CERT_GetCertTimes(cert_handle, &pr_issued, &pr_expires) == SECSuccess) {
201    *issued = crypto::PRTimeToBaseTime(pr_issued);
202    *expires = crypto::PRTimeToBaseTime(pr_expires);
203    return true;
204  }
205  return false;
206}
207
208string GetTitle(X509Certificate::OSCertHandle cert_handle) {
209  return psm::GetCertTitle(cert_handle);
210}
211
212string GetIssuerName(X509Certificate::OSCertHandle cert_handle) {
213  return psm::ProcessName(&cert_handle->issuer);
214}
215
216string GetSubjectName(X509Certificate::OSCertHandle cert_handle) {
217  return psm::ProcessName(&cert_handle->subject);
218}
219
220void GetEmailAddresses(X509Certificate::OSCertHandle cert_handle,
221                       std::vector<string>* email_addresses) {
222  for (const char* addr = CERT_GetFirstEmailAddress(cert_handle);
223       addr; addr = CERT_GetNextEmailAddress(cert_handle, addr)) {
224    // The first email addr (from Subject) may be duplicated in Subject
225    // Alternative Name, so check subsequent addresses are not equal to the
226    // first one before adding to the list.
227    if (!email_addresses->size() || (*email_addresses)[0] != addr)
228      email_addresses->push_back(addr);
229  }
230}
231
232void GetNicknameStringsFromCertList(
233    const std::vector<scoped_refptr<X509Certificate> >& certs,
234    const string& cert_expired,
235    const string& cert_not_yet_valid,
236    std::vector<string>* nick_names) {
237  CERTCertList* cert_list = CERT_NewCertList();
238  for (size_t i = 0; i < certs.size(); ++i) {
239    CERT_AddCertToListTail(
240        cert_list,
241        CERT_DupCertificate(certs[i]->os_cert_handle()));
242  }
243  // Would like to use CERT_GetCertNicknameWithValidity on each cert
244  // individually instead of having to build a CERTCertList for this, but that
245  // function is not exported.
246  CERTCertNicknames* cert_nicknames = CERT_NicknameStringsFromCertList(
247      cert_list,
248      const_cast<char*>(cert_expired.c_str()),
249      const_cast<char*>(cert_not_yet_valid.c_str()));
250  DCHECK_EQ(cert_nicknames->numnicknames,
251            static_cast<int>(certs.size()));
252
253  for (int i = 0; i < cert_nicknames->numnicknames; ++i)
254    nick_names->push_back(cert_nicknames->nicknames[i]);
255
256  CERT_FreeNicknames(cert_nicknames);
257  CERT_DestroyCertList(cert_list);
258}
259
260// For background see this discussion on dev-tech-crypto.lists.mozilla.org:
261// http://web.archiveorange.com/archive/v/6JJW7E40sypfZGtbkzxX
262//
263// NOTE: This function relies on the convention that the same PKCS#11 ID
264// is shared between a certificate and its associated private and public
265// keys.  I tried to implement this with PK11_GetLowLevelKeyIDForCert(),
266// but that always returns NULL on Chrome OS for me.
267std::string GetPkcs11Id(net::X509Certificate::OSCertHandle cert_handle) {
268  std::string pkcs11_id;
269  SECKEYPrivateKey *priv_key = PK11_FindKeyByAnyCert(cert_handle,
270                                                     NULL /* wincx */);
271  if (priv_key) {
272    // Get the CKA_ID attribute for a key.
273    SECItem* sec_item = PK11_GetLowLevelKeyIDForPrivateKey(priv_key);
274    if (sec_item) {
275      pkcs11_id = base::HexEncode(sec_item->data, sec_item->len);
276      SECITEM_FreeItem(sec_item, PR_TRUE);
277    }
278    SECKEY_DestroyPrivateKey(priv_key);
279  }
280  return pkcs11_id;
281}
282
283void GetExtensions(
284    const string& critical_label,
285    const string& non_critical_label,
286    X509Certificate::OSCertHandle cert_handle,
287    Extensions* extensions) {
288  if (cert_handle->extensions) {
289    for (size_t i = 0; cert_handle->extensions[i] != NULL; ++i) {
290      Extension extension;
291      extension.name = psm::GetOIDText(&cert_handle->extensions[i]->id);
292      extension.value = ProcessExtension(
293          critical_label, non_critical_label, cert_handle->extensions[i]);
294      extensions->push_back(extension);
295    }
296  }
297}
298
299string HashCertSHA256(X509Certificate::OSCertHandle cert_handle) {
300  return HashCert(cert_handle, HASH_AlgSHA256, SHA256_LENGTH);
301}
302
303string HashCertSHA1(X509Certificate::OSCertHandle cert_handle) {
304  return HashCert(cert_handle, HASH_AlgSHA1, SHA1_LENGTH);
305}
306
307void GetCertChainFromCert(X509Certificate::OSCertHandle cert_handle,
308                          X509Certificate::OSCertHandles* cert_handles) {
309  CERTCertList* cert_list =
310      CERT_GetCertChainFromCert(cert_handle, PR_Now(), certUsageSSLServer);
311  CERTCertListNode* node;
312  for (node = CERT_LIST_HEAD(cert_list);
313       !CERT_LIST_END(node, cert_list);
314       node = CERT_LIST_NEXT(node)) {
315    cert_handles->push_back(CERT_DupCertificate(node->cert));
316  }
317  CERT_DestroyCertList(cert_list);
318}
319
320void DestroyCertChain(X509Certificate::OSCertHandles* cert_handles) {
321  for (X509Certificate::OSCertHandles::iterator i(cert_handles->begin());
322       i != cert_handles->end(); ++i)
323    CERT_DestroyCertificate(*i);
324  cert_handles->clear();
325}
326
327string GetDerString(X509Certificate::OSCertHandle cert_handle) {
328  return string(reinterpret_cast<const char*>(cert_handle->derCert.data),
329                cert_handle->derCert.len);
330}
331
332string GetCMSString(const X509Certificate::OSCertHandles& cert_chain,
333                    size_t start, size_t end) {
334  ScopedPRArenaPool arena(PORT_NewArena(1024));
335  CHECK(arena.get());
336
337  ScopedNSSCMSMessage message(NSS_CMSMessage_Create(arena.get()));
338  CHECK(message.get());
339
340  // First, create SignedData with the certificate only (no chain).
341  ScopedNSSCMSSignedData signed_data(NSS_CMSSignedData_CreateCertsOnly(
342      message.get(), cert_chain[start], PR_FALSE));
343  if (!signed_data.get()) {
344    LOG(ERROR) << "NSS_CMSSignedData_Create failed";
345    return "";
346  }
347  // Add the rest of the chain (if any).
348  for (size_t i = start + 1; i < end; ++i) {
349    if (NSS_CMSSignedData_AddCertificate(signed_data.get(), cert_chain[i]) !=
350        SECSuccess) {
351      LOG(ERROR) << "NSS_CMSSignedData_AddCertificate failed on " << i;
352      return "";
353    }
354  }
355
356  NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(message.get());
357  if (NSS_CMSContentInfo_SetContent_SignedData(
358      message.get(), cinfo, signed_data.get()) == SECSuccess) {
359    ignore_result(signed_data.release());
360  } else {
361    LOG(ERROR) << "NSS_CMSMessage_GetContentInfo failed";
362    return "";
363  }
364
365  SECItem cert_p7 = { siBuffer, NULL, 0 };
366  NSSCMSEncoderContext *ecx = NSS_CMSEncoder_Start(message.get(), NULL, NULL,
367                                                   &cert_p7, arena.get(), NULL,
368                                                   NULL, NULL, NULL, NULL,
369                                                   NULL);
370  if (!ecx) {
371    LOG(ERROR) << "NSS_CMSEncoder_Start failed";
372    return "";
373  }
374
375  if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
376    LOG(ERROR) << "NSS_CMSEncoder_Finish failed";
377    return "";
378  }
379
380  return string(reinterpret_cast<const char*>(cert_p7.data), cert_p7.len);
381}
382
383string ProcessSecAlgorithmSignature(X509Certificate::OSCertHandle cert_handle) {
384  return ProcessSecAlgorithmInternal(&cert_handle->signature);
385}
386
387string ProcessSecAlgorithmSubjectPublicKey(
388    X509Certificate::OSCertHandle cert_handle) {
389  return ProcessSecAlgorithmInternal(
390      &cert_handle->subjectPublicKeyInfo.algorithm);
391}
392
393string ProcessSecAlgorithmSignatureWrap(
394    X509Certificate::OSCertHandle cert_handle) {
395  return ProcessSecAlgorithmInternal(
396      &cert_handle->signatureWrap.signatureAlgorithm);
397}
398
399string ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle) {
400  return psm::ProcessSubjectPublicKeyInfo(&cert_handle->subjectPublicKeyInfo);
401}
402
403string ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle) {
404  return ProcessRawBits(cert_handle->signatureWrap.signature.data,
405                        cert_handle->signatureWrap.signature.len);
406}
407
408void RegisterDynamicOids() {
409}
410
411}  // namespace x509_certificate_model
412