- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / net / x509_certificate_model_nss.cc
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
25 namespace psm = mozilla_security_manager;
26
27 namespace {
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.
31 std::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.)
45 std::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
58 std::string ProcessSecAlgorithmInternal(SECAlgorithmID* algorithm_id) {
59   return psm::GetOIDText(&algorithm_id->algorithm);
60 }
61
62 std::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
77 class FreeNSSCMSMessage {
78  public:
79   inline void operator()(NSSCMSMessage* x) const {
80     NSS_CMSMessage_Destroy(x);
81   }
82 };
83 typedef scoped_ptr_malloc<NSSCMSMessage, FreeNSSCMSMessage>
84     ScopedNSSCMSMessage;
85
86 class FreeNSSCMSSignedData {
87  public:
88   inline void operator()(NSSCMSSignedData* x) const {
89     NSS_CMSSignedData_Destroy(x);
90   }
91 };
92 typedef scoped_ptr_malloc<NSSCMSSignedData, FreeNSSCMSSignedData>
93     ScopedNSSCMSSignedData;
94
95 }  // namespace
96
97 namespace x509_certificate_model {
98
99 using net::X509Certificate;
100 using std::string;
101
102 string 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
110 string 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
123 string GetTokenName(X509Certificate::OSCertHandle cert_handle) {
124   return psm::GetCertTokenName(cert_handle);
125 }
126
127 string 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
138 net::CertType GetType(X509Certificate::OSCertHandle cert_handle) {
139     return psm::GetCertType(cert_handle);
140 }
141
142 string GetEmailAddress(X509Certificate::OSCertHandle cert_handle) {
143   if (cert_handle->emailAddr)
144     return cert_handle->emailAddr;
145   return std::string();
146 }
147
148 void GetUsageStrings(X509Certificate::OSCertHandle cert_handle,
149                      std::vector<string>* usages) {
150   psm::GetCertUsageStrings(cert_handle, usages);
151 }
152
153 string 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
164 string GetSerialNumberHexified(X509Certificate::OSCertHandle cert_handle,
165                                const string& alternative_text) {
166   return Stringize(CERT_Hexify(&cert_handle->serialNumber, true),
167                    alternative_text);
168 }
169
170 string GetIssuerCommonName(X509Certificate::OSCertHandle cert_handle,
171                            const string& alternative_text) {
172   return Stringize(CERT_GetCommonName(&cert_handle->issuer), alternative_text);
173 }
174
175 string GetIssuerOrgName(X509Certificate::OSCertHandle cert_handle,
176                         const string& alternative_text) {
177   return Stringize(CERT_GetOrgName(&cert_handle->issuer), alternative_text);
178 }
179
180 string GetIssuerOrgUnitName(X509Certificate::OSCertHandle cert_handle,
181                             const string& alternative_text) {
182   return Stringize(CERT_GetOrgUnitName(&cert_handle->issuer), alternative_text);
183 }
184
185 string GetSubjectOrgName(X509Certificate::OSCertHandle cert_handle,
186                          const string& alternative_text) {
187   return Stringize(CERT_GetOrgName(&cert_handle->subject), alternative_text);
188 }
189
190 string GetSubjectOrgUnitName(X509Certificate::OSCertHandle cert_handle,
191                              const string& alternative_text) {
192   return Stringize(CERT_GetOrgUnitName(&cert_handle->subject),
193                    alternative_text);
194 }
195
196 string GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle,
197                             const string& alternative_text) {
198   return Stringize(CERT_GetCommonName(&cert_handle->subject), alternative_text);
199 }
200
201 bool 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
212 string GetTitle(X509Certificate::OSCertHandle cert_handle) {
213   return psm::GetCertTitle(cert_handle);
214 }
215
216 string GetIssuerName(X509Certificate::OSCertHandle cert_handle) {
217   return psm::ProcessName(&cert_handle->issuer);
218 }
219
220 string GetSubjectName(X509Certificate::OSCertHandle cert_handle) {
221   return psm::ProcessName(&cert_handle->subject);
222 }
223
224 void 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
236 void 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.
271 std::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
287 void 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
303 string HashCertSHA256(X509Certificate::OSCertHandle cert_handle) {
304   return HashCert(cert_handle, HASH_AlgSHA256, SHA256_LENGTH);
305 }
306
307 string HashCertSHA1(X509Certificate::OSCertHandle cert_handle) {
308   return HashCert(cert_handle, HASH_AlgSHA1, SHA1_LENGTH);
309 }
310
311 void 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
324 void 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
331 string GetDerString(X509Certificate::OSCertHandle cert_handle) {
332   return string(reinterpret_cast<const char*>(cert_handle->derCert.data),
333                 cert_handle->derCert.len);
334 }
335
336 string 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
387 string ProcessSecAlgorithmSignature(X509Certificate::OSCertHandle cert_handle) {
388   return ProcessSecAlgorithmInternal(&cert_handle->signature);
389 }
390
391 string ProcessSecAlgorithmSubjectPublicKey(
392     X509Certificate::OSCertHandle cert_handle) {
393   return ProcessSecAlgorithmInternal(
394       &cert_handle->subjectPublicKeyInfo.algorithm);
395 }
396
397 string ProcessSecAlgorithmSignatureWrap(
398     X509Certificate::OSCertHandle cert_handle) {
399   return ProcessSecAlgorithmInternal(
400       &cert_handle->signatureWrap.signatureAlgorithm);
401 }
402
403 string ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle) {
404   return psm::ProcessSubjectPublicKeyInfo(&cert_handle->subjectPublicKeyInfo);
405 }
406
407 string ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle) {
408   return ProcessRawBits(cert_handle->signatureWrap.signature.data,
409                         cert_handle->signatureWrap.signature.len);
410 }
411
412 void RegisterDynamicOids() {
413   psm::RegisterDynamicOids();
414 }
415
416 }  // namespace x509_certificate_model