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 "net/cert/x509_util.h"
6#include "net/cert/x509_util_nss.h"
7
8#include <cert.h>
9#include <secoid.h>
10
11#include "base/memory/ref_counted.h"
12#include "base/memory/scoped_ptr.h"
13#include "crypto/ec_private_key.h"
14#include "crypto/scoped_nss_types.h"
15#include "crypto/signature_verifier.h"
16#include "net/cert/x509_certificate.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace net {
20
21namespace {
22
23CERTCertificate* CreateNSSCertHandleFromBytes(const char* data, size_t length) {
24  SECItem der_cert;
25  der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
26  der_cert.len  = length;
27  der_cert.type = siDERCertBuffer;
28
29  // Parse into a certificate structure.
30  return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL,
31                                 PR_FALSE, PR_TRUE);
32}
33
34#if !defined(OS_WIN) && !defined(OS_MACOSX)
35void VerifyCertificateSignature(const std::string& der_cert,
36                                const std::vector<uint8>& der_spki) {
37  crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
38
39  CERTSignedData sd;
40  memset(&sd, 0, sizeof(sd));
41
42  SECItem der_cert_item = {
43    siDERCertBuffer,
44    reinterpret_cast<unsigned char*>(const_cast<char*>(der_cert.data())),
45    static_cast<unsigned int>(der_cert.size())
46  };
47  SECStatus rv = SEC_ASN1DecodeItem(arena.get(), &sd,
48                                    SEC_ASN1_GET(CERT_SignedDataTemplate),
49                                    &der_cert_item);
50  ASSERT_EQ(SECSuccess, rv);
51
52  // The CERTSignedData.signatureAlgorithm is decoded, but SignatureVerifier
53  // wants the DER encoded form, so re-encode it again.
54  SECItem* signature_algorithm = SEC_ASN1EncodeItem(
55      arena.get(),
56      NULL,
57      &sd.signatureAlgorithm,
58      SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
59  ASSERT_TRUE(signature_algorithm);
60
61  crypto::SignatureVerifier verifier;
62  bool ok = verifier.VerifyInit(
63      signature_algorithm->data,
64      signature_algorithm->len,
65      sd.signature.data,
66      sd.signature.len / 8,  // Signature is a BIT STRING, convert to bytes.
67      &der_spki[0],
68      der_spki.size());
69
70  ASSERT_TRUE(ok);
71  verifier.VerifyUpdate(sd.data.data,
72                        sd.data.len);
73
74  ok = verifier.VerifyFinal();
75  EXPECT_TRUE(ok);
76}
77#endif  // !defined(OS_WIN) && !defined(OS_MACOSX)
78
79void VerifyDomainBoundCert(const std::string& domain,
80                           const std::string& der_cert) {
81  // Origin Bound Cert OID.
82  static const char oid_string[] = "1.3.6.1.4.1.11129.2.1.6";
83
84  // Create object neccessary for extension lookup call.
85  SECItem extension_object = {
86    siAsciiString,
87    (unsigned char*)domain.data(),
88    static_cast<unsigned int>(domain.size())
89  };
90
91  // IA5Encode and arena allocate SECItem.
92  PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
93  SECItem* expected = SEC_ASN1EncodeItem(arena,
94                                         NULL,
95                                         &extension_object,
96                                         SEC_ASN1_GET(SEC_IA5StringTemplate));
97
98  ASSERT_NE(static_cast<SECItem*>(NULL), expected);
99
100  // Create OID SECItem.
101  SECItem ob_cert_oid = { siDEROID, NULL, 0 };
102  SECStatus ok = SEC_StringToOID(arena, &ob_cert_oid,
103                                 oid_string, 0);
104
105  ASSERT_EQ(SECSuccess, ok);
106
107  SECOidTag ob_cert_oid_tag = SECOID_FindOIDTag(&ob_cert_oid);
108
109  ASSERT_NE(SEC_OID_UNKNOWN, ob_cert_oid_tag);
110
111  // This test is run on Mac and Win where X509Certificate::os_cert_handle isn't
112  // an NSS type, so we have to manually create a NSS certificate object so we
113  // can use CERT_FindCertExtension.  We also check the subject and validity
114  // times using NSS since X509Certificate will fail with EC certs on OSX 10.5
115  // (http://crbug.com/101231).
116  CERTCertificate* nss_cert = CreateNSSCertHandleFromBytes(
117      der_cert.data(), der_cert.size());
118
119  char* common_name = CERT_GetCommonName(&nss_cert->subject);
120  ASSERT_TRUE(common_name);
121  EXPECT_STREQ("anonymous.invalid", common_name);
122  PORT_Free(common_name);
123  EXPECT_EQ(SECSuccess, CERT_CertTimesValid(nss_cert));
124
125  // Lookup Origin Bound Cert extension in generated cert.
126  SECItem actual = { siBuffer, NULL, 0 };
127  ok = CERT_FindCertExtension(nss_cert,
128                              ob_cert_oid_tag,
129                              &actual);
130  CERT_DestroyCertificate(nss_cert);
131  ASSERT_EQ(SECSuccess, ok);
132
133  // Compare expected and actual extension values.
134  PRBool result = SECITEM_ItemsAreEqual(expected, &actual);
135  ASSERT_TRUE(result);
136
137  // Do Cleanup.
138  SECITEM_FreeItem(&actual, PR_FALSE);
139  PORT_FreeArena(arena, PR_FALSE);
140}
141
142}  // namespace
143
144// This test creates a domain-bound cert and an EC private key and
145// then verifies the content of the certificate.
146TEST(X509UtilNSSTest, CreateKeyAndDomainBoundCertEC) {
147  // Create a sample ASCII weborigin.
148  std::string domain = "weborigin.com";
149  base::Time now = base::Time::Now();
150
151  scoped_ptr<crypto::ECPrivateKey> private_key;
152  std::string der_cert;
153  ASSERT_TRUE(x509_util::CreateKeyAndDomainBoundCertEC(
154      domain, 1,
155      now,
156      now + base::TimeDelta::FromDays(1),
157      &private_key,
158      &der_cert));
159
160  VerifyDomainBoundCert(domain, der_cert);
161
162#if !defined(OS_WIN) && !defined(OS_MACOSX)
163  // signature_verifier_win and signature_verifier_mac can't handle EC certs.
164  std::vector<uint8> spki;
165  ASSERT_TRUE(private_key->ExportPublicKey(&spki));
166  VerifyCertificateSignature(der_cert, spki);
167#endif
168}
169
170}  // namespace net
171