summaryrefslogtreecommitdiff
path: root/chromium/net/cert/x509_util_nss_unittest.cc
blob: 0ad5ecddfaa99ba0600093d41539f64f93402622 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/cert/x509_util.h"
#include "net/cert/x509_util_nss.h"

#include <cert.h>
#include <secoid.h>

#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "crypto/ec_private_key.h"
#include "crypto/scoped_nss_types.h"
#include "crypto/signature_verifier.h"
#include "net/cert/x509_certificate.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

CERTCertificate* CreateNSSCertHandleFromBytes(const char* data, size_t length) {
  SECItem der_cert;
  der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
  der_cert.len  = length;
  der_cert.type = siDERCertBuffer;

  // Parse into a certificate structure.
  return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL,
                                 PR_FALSE, PR_TRUE);
}

#if !defined(OS_WIN) && !defined(OS_MACOSX)
void VerifyCertificateSignature(const std::string& der_cert,
                                const std::vector<uint8>& der_spki) {
  crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));

  CERTSignedData sd;
  memset(&sd, 0, sizeof(sd));

  SECItem der_cert_item = {
    siDERCertBuffer,
    reinterpret_cast<unsigned char*>(const_cast<char*>(der_cert.data())),
    static_cast<unsigned int>(der_cert.size())
  };
  SECStatus rv = SEC_ASN1DecodeItem(arena.get(), &sd,
                                    SEC_ASN1_GET(CERT_SignedDataTemplate),
                                    &der_cert_item);
  ASSERT_EQ(SECSuccess, rv);

  // The CERTSignedData.signatureAlgorithm is decoded, but SignatureVerifier
  // wants the DER encoded form, so re-encode it again.
  SECItem* signature_algorithm = SEC_ASN1EncodeItem(
      arena.get(),
      NULL,
      &sd.signatureAlgorithm,
      SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
  ASSERT_TRUE(signature_algorithm);

  crypto::SignatureVerifier verifier;
  bool ok = verifier.VerifyInit(
      signature_algorithm->data,
      signature_algorithm->len,
      sd.signature.data,
      sd.signature.len / 8,  // Signature is a BIT STRING, convert to bytes.
      &der_spki[0],
      der_spki.size());

  ASSERT_TRUE(ok);
  verifier.VerifyUpdate(sd.data.data,
                        sd.data.len);

  ok = verifier.VerifyFinal();
  EXPECT_TRUE(ok);
}
#endif  // !defined(OS_WIN) && !defined(OS_MACOSX)

void VerifyDomainBoundCert(const std::string& domain,
                           const std::string& der_cert) {
  // Origin Bound Cert OID.
  static const char oid_string[] = "1.3.6.1.4.1.11129.2.1.6";

  // Create object neccessary for extension lookup call.
  SECItem extension_object = {
    siAsciiString,
    (unsigned char*)domain.data(),
    static_cast<unsigned int>(domain.size())
  };

  // IA5Encode and arena allocate SECItem.
  PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  SECItem* expected = SEC_ASN1EncodeItem(arena,
                                         NULL,
                                         &extension_object,
                                         SEC_ASN1_GET(SEC_IA5StringTemplate));

  ASSERT_NE(static_cast<SECItem*>(NULL), expected);

  // Create OID SECItem.
  SECItem ob_cert_oid = { siDEROID, NULL, 0 };
  SECStatus ok = SEC_StringToOID(arena, &ob_cert_oid,
                                 oid_string, 0);

  ASSERT_EQ(SECSuccess, ok);

  SECOidTag ob_cert_oid_tag = SECOID_FindOIDTag(&ob_cert_oid);

  ASSERT_NE(SEC_OID_UNKNOWN, ob_cert_oid_tag);

  // This test is run on Mac and Win where X509Certificate::os_cert_handle isn't
  // an NSS type, so we have to manually create a NSS certificate object so we
  // can use CERT_FindCertExtension.  We also check the subject and validity
  // times using NSS since X509Certificate will fail with EC certs on OSX 10.5
  // (http://crbug.com/101231).
  CERTCertificate* nss_cert = CreateNSSCertHandleFromBytes(
      der_cert.data(), der_cert.size());

  char* common_name = CERT_GetCommonName(&nss_cert->subject);
  ASSERT_TRUE(common_name);
  EXPECT_STREQ("anonymous.invalid", common_name);
  PORT_Free(common_name);
  EXPECT_EQ(SECSuccess, CERT_CertTimesValid(nss_cert));

  // Lookup Origin Bound Cert extension in generated cert.
  SECItem actual = { siBuffer, NULL, 0 };
  ok = CERT_FindCertExtension(nss_cert,
                              ob_cert_oid_tag,
                              &actual);
  CERT_DestroyCertificate(nss_cert);
  ASSERT_EQ(SECSuccess, ok);

  // Compare expected and actual extension values.
  PRBool result = SECITEM_ItemsAreEqual(expected, &actual);
  ASSERT_TRUE(result);

  // Do Cleanup.
  SECITEM_FreeItem(&actual, PR_FALSE);
  PORT_FreeArena(arena, PR_FALSE);
}

}  // namespace

// This test creates a domain-bound cert and an EC private key and
// then verifies the content of the certificate.
TEST(X509UtilNSSTest, CreateKeyAndDomainBoundCertEC) {
  // Create a sample ASCII weborigin.
  std::string domain = "weborigin.com";
  base::Time now = base::Time::Now();

  scoped_ptr<crypto::ECPrivateKey> private_key;
  std::string der_cert;
  ASSERT_TRUE(x509_util::CreateKeyAndDomainBoundCertEC(
      domain, 1,
      now,
      now + base::TimeDelta::FromDays(1),
      &private_key,
      &der_cert));

  VerifyDomainBoundCert(domain, der_cert);

#if !defined(OS_WIN) && !defined(OS_MACOSX)
  // signature_verifier_win and signature_verifier_mac can't handle EC certs.
  std::vector<uint8> spki;
  ASSERT_TRUE(private_key->ExportPublicKey(&spki));
  VerifyCertificateSignature(der_cert, spki);
#endif
}

}  // namespace net