blob: ae81c8235328af0c1075ba7ea7d97567d349304a [file] [log] [blame]
Jeremy Clark670a52f2020-08-29 21:48:26 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2013-2020 Regents of the University of California.
4 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
22#include "ndn-cxx/security/detail/certificate-bundle-decoder.hpp"
23
24#include "tests/boost-test.hpp"
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -050025#include "tests/key-chain-fixture.hpp"
Jeremy Clark670a52f2020-08-29 21:48:26 -040026
27namespace ndn {
28namespace security {
29namespace detail {
30namespace tests {
31
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -050032class CertificateBundleDecoderFixture : public ndn::tests::KeyChainFixture
Jeremy Clark670a52f2020-08-29 21:48:26 -040033{
34protected:
35 CertificateBundleDecoderFixture()
36 {
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -050037 auto id1 = m_keyChain.createIdentity("/hello/world1");
Jeremy Clark670a52f2020-08-29 21:48:26 -040038 auto cert1 = id1.getDefaultKey().getDefaultCertificate();
39 certBlock1 = cert1.wireEncode();
40 m_certs.push_back(certBlock1);
41
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -050042 auto id2 = m_keyChain.createIdentity("/hello/world2");
Jeremy Clark670a52f2020-08-29 21:48:26 -040043 auto cert2 = id2.getDefaultKey().getDefaultCertificate();
44 certBlock2 = cert2.wireEncode();
45 m_certs.push_back(certBlock2);
46
47 cbd.onCertDecoded.connect([this] (const Certificate& receivedCert) {
48 BOOST_CHECK_EQUAL(receivedCert.wireEncode(), m_certs.at(nCertsCompleted));
49 ++nCertsCompleted;
50 });
51 }
52
53protected:
54 CertificateBundleDecoder cbd;
55 Block certBlock1;
56 Block certBlock2;
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -050057 size_t nCertsCompleted = 0;
Jeremy Clark670a52f2020-08-29 21:48:26 -040058
59private:
60 std::vector<Block> m_certs;
61};
62
63BOOST_AUTO_TEST_SUITE(Security)
64BOOST_FIXTURE_TEST_SUITE(TestCertificateBundleDecoder, CertificateBundleDecoderFixture)
65
66BOOST_AUTO_TEST_CASE(EmptySegment)
67{
68 BOOST_CHECK_EQUAL(cbd.hasError(), false);
69 cbd.append(Block(tlv::Content));
70 BOOST_CHECK_EQUAL(cbd.hasError(), false);
71 BOOST_CHECK_EQUAL(nCertsCompleted, 0);
72}
73
74BOOST_AUTO_TEST_CASE(OneCertOneSegment)
75{
76 // Segment contains full certificate
77 Data d;
78 d.setContent(certBlock1);
79
80 cbd.append(d.getContent());
81 BOOST_CHECK_EQUAL(cbd.hasError(), false);
82 BOOST_CHECK_EQUAL(nCertsCompleted, 1);
83}
84
85BOOST_AUTO_TEST_CASE(TwoCertsOneSegment)
86{
87 // Segment contains two full certificates
88 auto buf = std::make_shared<Buffer>(certBlock1.begin(), certBlock1.end());
89 buf->insert(buf->end(), certBlock2.begin(), certBlock2.end());
90 Data d;
91 d.setContent(std::move(buf));
92
93 cbd.append(d.getContent());
94 BOOST_CHECK_EQUAL(cbd.hasError(), false);
95 BOOST_CHECK_EQUAL(nCertsCompleted, 2);
96}
97
98BOOST_AUTO_TEST_CASE(TwoCertsMultipleSegments)
99{
100 // First segment contains first 250 bytes of cert1
101 Data d;
102 d.setContent(certBlock1.wire(), 250);
103
104 // Second segment contains the rest of cert1 and the first 100 bytes of cert2
105 auto buf = std::make_shared<Buffer>(certBlock1.begin() + 250, certBlock1.end());
106 buf->insert(buf->end(), certBlock2.begin(), certBlock2.begin() + 100);
107 Data d2;
108 d2.setContent(std::move(buf));
109
110 // Third segment contains the rest of cert2
111 Data d3;
112 d3.setContent(std::make_shared<Buffer>(certBlock2.begin() + 100, certBlock2.end()));
113
114 cbd.append(d.getContent());
115 BOOST_CHECK_EQUAL(cbd.hasError(), false);
116 BOOST_CHECK_EQUAL(nCertsCompleted, 0);
117
118 cbd.append(d2.getContent());
119 BOOST_CHECK_EQUAL(cbd.hasError(), false);
120 BOOST_CHECK_EQUAL(nCertsCompleted, 1);
121
122 cbd.append(d3.getContent());
123 BOOST_CHECK_EQUAL(cbd.hasError(), false);
124 BOOST_CHECK_EQUAL(nCertsCompleted, 2);
125}
126
127BOOST_AUTO_TEST_CASE(InvalidCert)
128{
129 // First segment contains all of cert1
130 Data d;
131 d.setContent(certBlock1);
132
133 const uint8_t buf[] = {
134 0x06, 0x20, // Data
135 0x07, 0x11, // Name
136 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, // GenericNameComponent 'hello'
137 0x08, 0x01, 0x31, // GenericNameComponent '1'
138 0x08, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, // GenericNameComponent 'world'
139 0x14, 0x00, // MetaInfo empty
140 0x15, 0x00, // Content empty
141 0x16, 0x05, // SignatureInfo
142 0x1b, 0x01, 0x01, // SignatureType RSA
143 0x1c, 0x00, // KeyLocator empty
144 0x17, 0x00 // SignatureValue empty
145 };
146 // Second segment contains non-Certificate data
147 Data d2;
148 d2.setContent(buf, sizeof(buf));
149
150 // Third segment contains all of cert2
151 Data d3;
152 d3.setContent(certBlock2);
153
154 cbd.append(d.getContent());
155 BOOST_CHECK_EQUAL(cbd.hasError(), false);
156 BOOST_CHECK_EQUAL(nCertsCompleted, 1);
157
158 BOOST_CHECK_EXCEPTION(cbd.append(d2.getContent()), tlv::Error, [] (const auto& e) {
159 return e.what() == "Name does not follow the naming convention for certificate"s;
160 });
161 BOOST_CHECK_EQUAL(cbd.hasError(), true);
162 BOOST_CHECK_EQUAL(nCertsCompleted, 1);
163
164 BOOST_CHECK_EXCEPTION(cbd.append(d3.getContent()), tlv::Error, [] (const auto& e) {
165 return e.what() == "Unrecoverable decoding error"s;
166 });
167 BOOST_CHECK_EQUAL(cbd.hasError(), true);
168 BOOST_CHECK_EQUAL(nCertsCompleted, 1);
169}
170
171BOOST_AUTO_TEST_CASE(UnrecognizedCritical)
172{
173 // First segment contains an unrecognized critical element
174 Data d;
175 d.setContent("050B07030102030A0404050607"_block);
176
177 // Second segment contains cert1
178 Data d2;
179 d2.setContent(certBlock1);
180
181 BOOST_CHECK_EXCEPTION(cbd.append(d.getContent()), tlv::Error, [] (const auto& e) {
182 return e.what() == "Unrecognized element of critical type 5"s;
183 });
184 BOOST_CHECK_EQUAL(cbd.hasError(), true);
185 BOOST_CHECK_EQUAL(nCertsCompleted, 0);
186
187 BOOST_CHECK_EXCEPTION(cbd.append(d2.getContent()), tlv::Error, [] (const auto& e) {
188 return e.what() == "Unrecoverable decoding error"s;
189 });
190 BOOST_CHECK_EQUAL(cbd.hasError(), true);
191 BOOST_CHECK_EQUAL(nCertsCompleted, 0);
192}
193
194BOOST_AUTO_TEST_CASE(UnrecognizedNonCritical)
195{
196 // First segment contains an unrecognized non-critical element
197 Data d;
198 d.setContent("4202CAFE"_block);
199
200 // Second segment contains cert1
201 Data d2;
202 d2.setContent(certBlock1);
203
204 cbd.append(d.getContent());
205 BOOST_CHECK_EQUAL(cbd.hasError(), false);
206 BOOST_CHECK_EQUAL(nCertsCompleted, 0);
207
208 cbd.append(d2.getContent());
209 BOOST_CHECK_EQUAL(cbd.hasError(), false);
210 BOOST_CHECK_EQUAL(nCertsCompleted, 1);
211}
212
213BOOST_AUTO_TEST_SUITE_END() // TestCertificateBundleEncoderDecoder
214BOOST_AUTO_TEST_SUITE_END() // Security
215
216} // namespace tests
217} // namespace detail
218} // namespace security
219} // namespace ndn