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