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" + psm::ProcessExtensionData(extension);
70}
71
72std::string GetNickname(net::X509Certificate::OSCertHandle cert_handle) {
73  std::string name;
74  if (cert_handle->nickname) {
75    name = cert_handle->nickname;
76    // Hack copied from mozilla: Cut off text before first :, which seems to
77    // just be the token name.
78    size_t colon_pos = name.find(':');
79    if (colon_pos != std::string::npos)
80      name = name.substr(colon_pos + 1);
81  }
82  return name;
83}
84
85////////////////////////////////////////////////////////////////////////////////
86// NSS certificate export functions.
87
88struct NSSCMSMessageDeleter {
89  inline void operator()(NSSCMSMessage* x) const {
90    NSS_CMSMessage_Destroy(x);
91  }
92};
93typedef scoped_ptr<NSSCMSMessage, NSSCMSMessageDeleter> ScopedNSSCMSMessage;
94
95struct FreeNSSCMSSignedData {
96  inline void operator()(NSSCMSSignedData* x) const {
97    NSS_CMSSignedData_Destroy(x);
98  }
99};
100typedef scoped_ptr<NSSCMSSignedData, FreeNSSCMSSignedData>
101    ScopedNSSCMSSignedData;
102
103}  // namespace
104
105namespace x509_certificate_model {
106
107using net::X509Certificate;
108using std::string;
109
110string GetCertNameOrNickname(X509Certificate::OSCertHandle cert_handle) {
111  string name = ProcessIDN(
112      Stringize(CERT_GetCommonName(&cert_handle->subject), std::string()));
113  if (!name.empty())
114    return name;
115  return GetNickname(cert_handle);
116}
117
118string GetTokenName(X509Certificate::OSCertHandle cert_handle) {
119  return psm::GetCertTokenName(cert_handle);
120}
121
122string GetVersion(X509Certificate::OSCertHandle cert_handle) {
123  // If the version field is omitted from the certificate, the default
124  // value is v1(0).
125  unsigned long version = 0;
126  if (cert_handle->version.len == 0 ||
127      SEC_ASN1DecodeInteger(&cert_handle->version, &version) == SECSuccess) {
128    return base::UintToString(version + 1);
129  }
130  return std::string();
131}
132
133net::CertType GetType(X509Certificate::OSCertHandle cert_handle) {
134    return psm::GetCertType(cert_handle);
135}
136
137void GetUsageStrings(X509Certificate::OSCertHandle cert_handle,
138                     std::vector<string>* usages) {
139  psm::GetCertUsageStrings(cert_handle, usages);
140}
141
142string GetSerialNumberHexified(X509Certificate::OSCertHandle cert_handle,
143                               const string& alternative_text) {
144  return Stringize(CERT_Hexify(&cert_handle->serialNumber, true),
145                   alternative_text);
146}
147
148string GetIssuerCommonName(X509Certificate::OSCertHandle cert_handle,
149                           const string& alternative_text) {
150  return Stringize(CERT_GetCommonName(&cert_handle->issuer), alternative_text);
151}
152
153string GetIssuerOrgName(X509Certificate::OSCertHandle cert_handle,
154                        const string& alternative_text) {
155  return Stringize(CERT_GetOrgName(&cert_handle->issuer), alternative_text);
156}
157
158string GetIssuerOrgUnitName(X509Certificate::OSCertHandle cert_handle,
159                            const string& alternative_text) {
160  return Stringize(CERT_GetOrgUnitName(&cert_handle->issuer), alternative_text);
161}
162
163string GetSubjectOrgName(X509Certificate::OSCertHandle cert_handle,
164                         const string& alternative_text) {
165  return Stringize(CERT_GetOrgName(&cert_handle->subject), alternative_text);
166}
167
168string GetSubjectOrgUnitName(X509Certificate::OSCertHandle cert_handle,
169                             const string& alternative_text) {
170  return Stringize(CERT_GetOrgUnitName(&cert_handle->subject),
171                   alternative_text);
172}
173
174string GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle,
175                            const string& alternative_text) {
176  return Stringize(CERT_GetCommonName(&cert_handle->subject), alternative_text);
177}
178
179bool GetTimes(X509Certificate::OSCertHandle cert_handle,
180              base::Time* issued, base::Time* expires) {
181  PRTime pr_issued, pr_expires;
182  if (CERT_GetCertTimes(cert_handle, &pr_issued, &pr_expires) == SECSuccess) {
183    *issued = crypto::PRTimeToBaseTime(pr_issued);
184    *expires = crypto::PRTimeToBaseTime(pr_expires);
185    return true;
186  }
187  return false;
188}
189
190string GetTitle(X509Certificate::OSCertHandle cert_handle) {
191  return psm::GetCertTitle(cert_handle);
192}
193
194string GetIssuerName(X509Certificate::OSCertHandle cert_handle) {
195  return psm::ProcessName(&cert_handle->issuer);
196}
197
198string GetSubjectName(X509Certificate::OSCertHandle cert_handle) {
199  return psm::ProcessName(&cert_handle->subject);
200}
201
202void GetExtensions(
203    const string& critical_label,
204    const string& non_critical_label,
205    X509Certificate::OSCertHandle cert_handle,
206    Extensions* extensions) {
207  if (cert_handle->extensions) {
208    for (size_t i = 0; cert_handle->extensions[i] != NULL; ++i) {
209      Extension extension;
210      extension.name = psm::GetOIDText(&cert_handle->extensions[i]->id);
211      extension.value = ProcessExtension(
212          critical_label, non_critical_label, cert_handle->extensions[i]);
213      extensions->push_back(extension);
214    }
215  }
216}
217
218string HashCertSHA256(X509Certificate::OSCertHandle cert_handle) {
219  return HashCert(cert_handle, HASH_AlgSHA256, SHA256_LENGTH);
220}
221
222string HashCertSHA1(X509Certificate::OSCertHandle cert_handle) {
223  return HashCert(cert_handle, HASH_AlgSHA1, SHA1_LENGTH);
224}
225
226void GetCertChainFromCert(X509Certificate::OSCertHandle cert_handle,
227                          X509Certificate::OSCertHandles* cert_handles) {
228  CERTCertList* cert_list =
229      CERT_GetCertChainFromCert(cert_handle, PR_Now(), certUsageSSLServer);
230  CERTCertListNode* node;
231  for (node = CERT_LIST_HEAD(cert_list);
232       !CERT_LIST_END(node, cert_list);
233       node = CERT_LIST_NEXT(node)) {
234    cert_handles->push_back(CERT_DupCertificate(node->cert));
235  }
236  CERT_DestroyCertList(cert_list);
237}
238
239void DestroyCertChain(X509Certificate::OSCertHandles* cert_handles) {
240  for (X509Certificate::OSCertHandles::iterator i(cert_handles->begin());
241       i != cert_handles->end(); ++i)
242    CERT_DestroyCertificate(*i);
243  cert_handles->clear();
244}
245
246string GetDerString(X509Certificate::OSCertHandle cert_handle) {
247  return string(reinterpret_cast<const char*>(cert_handle->derCert.data),
248                cert_handle->derCert.len);
249}
250
251string GetCMSString(const X509Certificate::OSCertHandles& cert_chain,
252                    size_t start, size_t end) {
253  crypto::ScopedPLArenaPool arena(PORT_NewArena(1024));
254  DCHECK(arena.get());
255
256  ScopedNSSCMSMessage message(NSS_CMSMessage_Create(arena.get()));
257  DCHECK(message.get());
258
259  // First, create SignedData with the certificate only (no chain).
260  ScopedNSSCMSSignedData signed_data(NSS_CMSSignedData_CreateCertsOnly(
261      message.get(), cert_chain[start], PR_FALSE));
262  if (!signed_data.get()) {
263    DLOG(ERROR) << "NSS_CMSSignedData_Create failed";
264    return std::string();
265  }
266  // Add the rest of the chain (if any).
267  for (size_t i = start + 1; i < end; ++i) {
268    if (NSS_CMSSignedData_AddCertificate(signed_data.get(), cert_chain[i]) !=
269        SECSuccess) {
270      DLOG(ERROR) << "NSS_CMSSignedData_AddCertificate failed on " << i;
271      return std::string();
272    }
273  }
274
275  NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(message.get());
276  if (NSS_CMSContentInfo_SetContent_SignedData(
277      message.get(), cinfo, signed_data.get()) == SECSuccess) {
278    ignore_result(signed_data.release());
279  } else {
280    DLOG(ERROR) << "NSS_CMSMessage_GetContentInfo failed";
281    return std::string();
282  }
283
284  SECItem cert_p7 = { siBuffer, NULL, 0 };
285  NSSCMSEncoderContext *ecx = NSS_CMSEncoder_Start(message.get(), NULL, NULL,
286                                                   &cert_p7, arena.get(), NULL,
287                                                   NULL, NULL, NULL, NULL,
288                                                   NULL);
289  if (!ecx) {
290    DLOG(ERROR) << "NSS_CMSEncoder_Start failed";
291    return std::string();
292  }
293
294  if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
295    DLOG(ERROR) << "NSS_CMSEncoder_Finish failed";
296    return std::string();
297  }
298
299  return string(reinterpret_cast<const char*>(cert_p7.data), cert_p7.len);
300}
301
302string ProcessSecAlgorithmSignature(X509Certificate::OSCertHandle cert_handle) {
303  return ProcessSecAlgorithmInternal(&cert_handle->signature);
304}
305
306string ProcessSecAlgorithmSubjectPublicKey(
307    X509Certificate::OSCertHandle cert_handle) {
308  return ProcessSecAlgorithmInternal(
309      &cert_handle->subjectPublicKeyInfo.algorithm);
310}
311
312string ProcessSecAlgorithmSignatureWrap(
313    X509Certificate::OSCertHandle cert_handle) {
314  return ProcessSecAlgorithmInternal(
315      &cert_handle->signatureWrap.signatureAlgorithm);
316}
317
318string ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle) {
319  return psm::ProcessSubjectPublicKeyInfo(&cert_handle->subjectPublicKeyInfo);
320}
321
322string ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle) {
323  return ProcessRawBits(cert_handle->signatureWrap.signature.data,
324                        cert_handle->signatureWrap.signature.len);
325}
326
327}  // namespace x509_certificate_model
328