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