Skip to content

Commit 4bb3c1e

Browse files
Address issue with embedded certificates in JWT x5c (#6440)
Co-authored-by: Max <maxtropets@gmail.com>
1 parent 4575118 commit 4bb3c1e

6 files changed

Lines changed: 219 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## [5.0.4]
9+
10+
[5.0.4]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.4
11+
12+
### Bug fix
13+
14+
- JWT authentication correctly parses certificates that contain other certificates (#6440)
15+
816
## [5.0.3]
917

1018
[5.0.3]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.3

src/crypto/openssl/openssl_wrappers.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,16 +255,35 @@ namespace ccf::crypto
255255
{}
256256
};
257257

258+
static const char pem_prefix[] = "-----BEGIN CERTIFICATE-----\n";
259+
// -1 for the null terminator
260+
static constexpr size_t pem_prefix_len = sizeof(pem_prefix) - 1;
261+
262+
// Check BIO starts with PEM prefix before attempting to read it as PEM
263+
// because PEM_read_bio_X509 is permissive and will skip over non-PEM data,
264+
// which may for example result in a DER containing nested PEM being read
265+
// as the nested certificate.
266+
inline X509* read_pem(BIO* mem)
267+
{
268+
std::vector<char> buf(pem_prefix_len);
269+
auto read = BIO_read(mem, buf.data(), pem_prefix_len);
270+
BIO_reset(mem);
271+
if (
272+
read != pem_prefix_len ||
273+
std::memcmp(buf.data(), pem_prefix, read) != 0)
274+
{
275+
return nullptr;
276+
}
277+
return PEM_read_bio_X509(mem, NULL, NULL, NULL);
278+
};
279+
258280
struct Unique_X509 : public Unique_SSL_OBJECT<X509, X509_new, X509_free>
259281
{
260282
using Unique_SSL_OBJECT::Unique_SSL_OBJECT;
261283
// p == nullptr is OK (e.g. wrong format)
262284
Unique_X509(BIO* mem, bool pem, bool check_null = false) :
263285
Unique_SSL_OBJECT(
264-
pem ? PEM_read_bio_X509(mem, NULL, NULL, NULL) :
265-
d2i_X509_bio(mem, NULL),
266-
X509_free,
267-
check_null)
286+
pem ? read_pem(mem) : d2i_X509_bio(mem, NULL), X509_free, check_null)
268287
{}
269288
Unique_X509(X509* cert, bool check_null) :
270289
Unique_SSL_OBJECT(cert, X509_free, check_null)

src/crypto/openssl/verifier.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ namespace ccf::crypto
116116
{
117117
Unique_BIO tcbio(*pem);
118118
Unique_X509 tc(tcbio, true);
119+
if (tc == nullptr)
120+
{
121+
LOG_DEBUG_FMT("Failed to load certificate from PEM: {}", pem->str());
122+
return false;
123+
}
124+
119125
CHECK1(X509_STORE_add_cert(store, tc));
120126
}
121127

@@ -124,6 +130,11 @@ namespace ccf::crypto
124130
{
125131
Unique_BIO certbio(*pem);
126132
Unique_X509 cert(certbio, true);
133+
if (cert == nullptr)
134+
{
135+
LOG_DEBUG_FMT("Failed to load certificate from PEM: {}", pem->str());
136+
return false;
137+
}
127138

128139
CHECK1(sk_X509_push(chain_stack, cert));
129140
CHECK1(X509_up_ref(cert));

src/crypto/pem.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,15 @@ namespace ccf::crypto
4242
auto next_separator_start = pem.find(separator);
4343
while (next_separator_start != std::string_view::npos)
4444
{
45-
pems.emplace_back(std::string(
46-
pem.substr(separator_end, next_separator_start + separator.size())));
45+
// Trim whitespace between certificates
46+
while (separator_end < next_separator_start &&
47+
std::isspace(pem[separator_end]))
48+
{
49+
++separator_end;
50+
}
51+
pems.emplace_back(std::string(pem.substr(
52+
separator_end,
53+
(next_separator_start - separator_end) + separator.size())));
4754
separator_end = next_separator_start + separator.size();
4855
next_separator_start = pem.find(separator, separator_end);
4956
}

src/crypto/test/crypto.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,127 @@ static const string contents_ =
4444

4545
vector<uint8_t> contents(contents_.begin(), contents_.end());
4646

47+
static const string nested_cert =
48+
"MIIV1zCCFL+"
49+
"gAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDDChodHRwczovL3NoYXJlZGV1czIuZXV"
50+
"zMi5hdHRlc3QuYXp1cmUubmV0MCIYDzIwMTkwNTAxMDAwMDAwWhgPMjA1MDEyMzEyMzU5NTlaMDM"
51+
"xMTAvBgNVBAMMKGh0dHBzOi8vc2hhcmVkZXVzMi5ldXMyLmF0dGVzdC5henVyZS5uZXQwggEiMA0"
52+
"GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDY0GsRB3BdTNXLan5JnuwGPFtV3iJMY0RAm78638L"
53+
"Q0LNcgNPoMwQB5VktKhZZxbqhdDzWH7JBa3D6MVb9I+"
54+
"AbgUZIvVSdU7xlqTzS2Gi9CTR1tkOj72Wyg6c59d89QvRP0CAe2omlSve0J/"
55+
"JFEt0LQyAXW0DKNlsyPxsd7ZmYn0YtMlPm/0TSLmXdLhZljna8zNlpWl/"
56+
"HD7T+zm1HNyg8aoisw6df/uS/mPuyKypko2rp8/7gwe8tv+1fIcKRboXNfyZSXDJE3ME/"
57+
"dHjFpcG/KTMkxoCIJb9iv9PHJx2ebCxNHuF7VDvyrXYqdiou9RWOD+/f39FYZJsWdo/"
58+
"VhfkfAgMBAAGjghLwMIIS7DAJBgNVHRMEAjAAMB0GA1UdDgQWBBRLSJIoQYE9YTEPZ30bgjdlv/"
59+
"RNDzAfBgNVHSMEGDAWgBRLSJIoQYE9YTEPZ30bgjdlv/"
60+
"RNDzCCEp0GCSsGAQQBgjdpAQSCEo4BAAAAAgAAAH4SAAAAAAAAAwACAAAAAAAKAA8Ak5pyM/"
61+
"ecTKmUCg2zlX8GBz6f+cAQUwPfmJD+H0OHgqMAAAAADg4QD///"
62+
"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAA"
63+
"AMG+d2W08VnHBjXWJzQgwpztMaXmeuK7Kha4P/"
64+
"IN14L3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0ul+"
65+
"6IIVxz5nh9xWOZTagW7ts54B+749ql/"
66+
"ZKevZLgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
67+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAA"
68+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnSkA919"
69+
"dcepZaKaCsfznfAwh2Hn98t7XPq5Jdg9cJrQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
70+
"AAyhAAABbJ695qIni/27w8wj0BRxIueJMn4SZTntdR7/"
71+
"e+s5ajJc+jMXwish9akKmwKqeRdyX3cDnkAjPvY0AjYi/"
72+
"39FZtwI3hoTxkyWE3Vpk8IdKJU+oomqS8snlNp+oT+"
73+
"ClCyILcP78X1k0xk5vi2OO44ktNBTyHIVWAKSSdxNj39TBxDg4QD///"
74+
"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQAAAAAAAADnAAAAAAAA"
75+
"AB7AKOTzYYZbiudS8D7kBDlbIscxEdPw8/"
76+
"tDnGuibpX2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMT1d115ZQPpYTf3fGioKaAF"
77+
"asje1wFAsIGwlEkMV7/"
78+
"wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
79+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACgAAAAAAAAAAAAAAAAAAA"
80+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABvByKZT5Gm6A9i+"
81+
"eXoH22RqqvB4tf80tEosVAMAK0h0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaWdf+"
82+
"dceUZCkBvD8ZTZQDgzklLWu5NJKI+"
83+
"QZb3tC4f7ORUBfklfihcUZXLT3Uc4L8jaXnpDYbMplAIsUMueifCAAAAECAwQFBgcICQoLDA0ODx"
84+
"AREhMUFRYXGBkaGxwdHh8FAGIOAAAtLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJRTlEQ0"
85+
"NCSm1nQXdJQkFnSVZBTkxaR05BSUVTOVN3QVA4ZGFocnlTN0daamVqTUFvR0NDcUdTTTQ5QkFNQw"
86+
"pNSEF4SWpBZ0JnTlZCQU1NR1VsdWRHVnNJRk5IV0NCUVEwc2dVR3hoZEdadmNtMGdRMEV4R2pBWU"
87+
"JnTlZCQW9NCkVVbHVkR1ZzSUVOdmNuQnZjbUYwYVc5dU1SUXdFZ1lEVlFRSERBdFRZVzUwWVNCRG"
88+
"JHRnlZVEVMTUFrR0ExVUUKQ0F3Q1EwRXhDekFKQmdOVkJBWVRBbFZUTUI0WERUSTBNRFF3TmpFMU"
89+
"5EZzFNVm9YRFRNeE1EUXdOakUxTkRnMQpNVm93Y0RFaU1DQUdBMVVFQXd3WlNXNTBaV3dnVTBkWU"
90+
"lGQkRTeUJEWlhKMGFXWnBZMkYwWlRFYU1CZ0dBMVVFCkNnd1JTVzUwWld3Z1EyOXljRzl5WVhScG"
91+
"IyNHhGREFTQmdOVkJBY01DMU5oYm5SaElFTnNZWEpoTVFzd0NRWUQKVlFRSURBSkRRVEVMTUFrR0"
92+
"ExVUVCaE1DVlZNd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRQQpjR2ZYVWpWME"
93+
"RJUDJMajNUY0pXaHJCMmhTbmlVYkRoWVVOSWozL1pLelRMcGcwUXBzS1NHbkd5amlYRFo5cEg1Cm"
94+
"IzbE1yMndJMFpBbFBRcCsyVVV0bzRJRERqQ0NBd293SHdZRFZSMGpCQmd3Rm9BVWxXOWR6YjBiNG"
95+
"VsQVNjblUKOURQT0FWY0wzbFF3YXdZRFZSMGZCR1F3WWpCZ29GNmdYSVphYUhSMGNITTZMeTloY0"
96+
"drdWRISjFjM1JsWkhObApjblpwWTJWekxtbHVkR1ZzTG1OdmJTOXpaM2d2WTJWeWRHbG1hV05oZE"
97+
"dsdmJpOTJNeTl3WTJ0amNtdy9ZMkU5CmNHeGhkR1p2Y20wbVpXNWpiMlJwYm1jOVpHVnlNQjBHQT"
98+
"FVZERnUVdCQlRlWjU1cXR4OEpVMmI4WkFkaTh4aysKQkhReXlUQU9CZ05WSFE4QkFmOEVCQU1DQn"
99+
"NBd0RBWURWUjBUQVFIL0JBSXdBRENDQWpzR0NTcUdTSWI0VFFFTgpBUVNDQWl3d2dnSW9NQjRHQ2"
100+
"lxR1NJYjRUUUVOQVFFRUVQVndZZHdoWU1HbHB4Z2dOK0xnaDBFd2dnRmxCZ29xCmhraUcrRTBCRF"
101+
"FFQ01JSUJWVEFRQmdzcWhraUcrRTBCRFFFQ0FRSUJEakFRQmdzcWhraUcrRTBCRFFFQ0FnSUIKRG"
102+
"pBUUJnc3Foa2lHK0UwQkRRRUNBd0lCQXpBUUJnc3Foa2lHK0UwQkRRRUNCQUlCQXpBUkJnc3Foa2"
103+
"lHK0UwQgpEUUVDQlFJQ0FQOHdFUVlMS29aSWh2aE5BUTBCQWdZQ0FnRC9NQkFHQ3lxR1NJYjRUUU"
104+
"VOQVFJSEFnRUJNQkFHCkN5cUdTSWI0VFFFTkFRSUlBZ0VBTUJBR0N5cUdTSWI0VFFFTkFRSUpBZ0"
105+
"VBTUJBR0N5cUdTSWI0VFFFTkFRSUsKQWdFQU1CQUdDeXFHU0liNFRRRU5BUUlMQWdFQU1CQUdDeX"
106+
"FHU0liNFRRRU5BUUlNQWdFQU1CQUdDeXFHU0liNApUUUVOQVFJTkFnRUFNQkFHQ3lxR1NJYjRUUU"
107+
"VOQVFJT0FnRUFNQkFHQ3lxR1NJYjRUUUVOQVFJUEFnRUFNQkFHCkN5cUdTSWI0VFFFTkFRSVFBZ0"
108+
"VBTUJBR0N5cUdTSWI0VFFFTkFRSVJBZ0VOTUI4R0N5cUdTSWI0VFFFTkFRSVMKQkJBT0RnTUQvLz"
109+
"hCQUFBQUFBQUFBQUFBTUJBR0NpcUdTSWI0VFFFTkFRTUVBZ0FBTUJRR0NpcUdTSWI0VFFFTgpBUV"
110+
"FFQmdCZ2FnQUFBREFQQmdvcWhraUcrRTBCRFFFRkNnRUJNQjRHQ2lxR1NJYjRUUUVOQVFZRUVDVU"
111+
"JVNGp5CmZ0cnVoMmNvdGVnQXlOSXdSQVlLS29aSWh2aE5BUTBCQnpBMk1CQUdDeXFHU0liNFRRRU"
112+
"5BUWNCQVFIL01CQUcKQ3lxR1NJYjRUUUVOQVFjQ0FRRUFNQkFHQ3lxR1NJYjRUUUVOQVFjREFRRU"
113+
"FNQW9HQ0NxR1NNNDlCQU1DQTBrQQpNRVlDSVFDeW9USFpyR3BoSVBnMHczNWJucjJTR3kyMk16T1"
114+
"ZGODRONUhTR3JPL3B2d0loQVA4WmxOYW9aV2hBCmhibVIyUzNVSHg1SjFSS216bzIwKzZJWmpuM3"
115+
"lScjhaCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS"
116+
"0tCk1JSUNsakNDQWoyZ0F3SUJBZ0lWQUpWdlhjMjlHK0hwUUVuSjFQUXp6Z0ZYQzk1VU1Bb0dDQ3"
117+
"FHU000OUJBTUMKTUdneEdqQVlCZ05WQkFNTUVVbHVkR1ZzSUZOSFdDQlNiMjkwSUVOQk1Sb3dHQV"
118+
"lEVlFRS0RCRkpiblJsYkNCRApiM0p3YjNKaGRHbHZiakVVTUJJR0ExVUVCd3dMVTJGdWRHRWdRMn"
119+
"hoY21FeEN6QUpCZ05WQkFnTUFrTkJNUXN3CkNRWURWUVFHRXdKVlV6QWVGdzB4T0RBMU1qRXhNRF"
120+
"V3TVRCYUZ3MHpNekExTWpFeE1EVXdNVEJhTUhBeElqQWcKQmdOVkJBTU1HVWx1ZEdWc0lGTkhXQ0"
121+
"JRUTBzZ1VHeGhkR1p2Y20wZ1EwRXhHakFZQmdOVkJBb01FVWx1ZEdWcwpJRU52Y25CdmNtRjBhVz"
122+
"l1TVJRd0VnWURWUVFIREF0VFlXNTBZU0JEYkdGeVlURUxNQWtHQTFVRUNBd0NRMEV4CkN6QUpCZ0"
123+
"5WQkFZVEFsVlRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVOU0IvN3QyMWxYU0"
124+
"8KMkN1enB4dzc0ZUpCNzJFeURHZ1c1clhDdHgydFZUTHE2aEtrNnorVWlSWkNucVI3cHNPdmdxRm"
125+
"VTeGxtVGxKbAplVG1pMldZejNxT0J1ekNCdURBZkJnTlZIU01FR0RBV2dCUWlaUXpXV3AwMGlmT0"
126+
"R0SlZTdjFBYk9TY0dyREJTCkJnTlZIUjhFU3pCSk1FZWdSYUJEaGtGb2RIUndjem92TDJObGNuUn"
127+
"BabWxqWVhSbGN5NTBjblZ6ZEdWa2MyVnkKZG1salpYTXVhVzUwWld3dVkyOXRMMGx1ZEdWc1UwZF"
128+
"lVbTl2ZEVOQkxtUmxjakFkQmdOVkhRNEVGZ1FVbFc5ZAp6YjBiNGVsQVNjblU5RFBPQVZjTDNsUX"
129+
"dEZ1lEVlIwUEFRSC9CQVFEQWdFR01CSUdBMVVkRXdFQi93UUlNQVlCCkFmOENBUUF3Q2dZSUtvWk"
130+
"l6ajBFQXdJRFJ3QXdSQUlnWHNWa2kwdytpNlZZR1czVUYvMjJ1YVhlMFlKRGoxVWUKbkErVGpEMW"
131+
"FpNWNDSUNZYjFTQW1ENXhrZlRWcHZvNFVveWlTWXhyRFdMbVVSNENJOU5LeWZQTisKLS0tLS1FTk"
132+
"QgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJQ2p6Q0NBal"
133+
"NnQXdJQkFnSVVJbVVNMWxxZE5JbnpnN1NWVXI5UUd6a25CcXd3Q2dZSUtvWkl6ajBFQXdJdwphRE"
134+
"VhTUJnR0ExVUVBd3dSU1c1MFpXd2dVMGRZSUZKdmIzUWdRMEV4R2pBWUJnTlZCQW9NRVVsdWRHVn"
135+
"NJRU52CmNuQnZjbUYwYVc5dU1SUXdFZ1lEVlFRSERBdFRZVzUwWVNCRGJHRnlZVEVMTUFrR0ExVU"
136+
"VDQXdDUTBFeEN6QUoKQmdOVkJBWVRBbFZUTUI0WERURTRNRFV5TVRFd05EVXhNRm9YRFRRNU1USX"
137+
"pNVEl6TlRrMU9Wb3dhREVhTUJnRwpBMVVFQXd3UlNXNTBaV3dnVTBkWUlGSnZiM1FnUTBFeEdqQV"
138+
"lCZ05WQkFvTUVVbHVkR1ZzSUVOdmNuQnZjbUYwCmFXOXVNUlF3RWdZRFZRUUhEQXRUWVc1MFlTQk"
139+
"RiR0Z5WVRFTE1Ba0dBMVVFQ0F3Q1EwRXhDekFKQmdOVkJBWVQKQWxWVE1Ga3dFd1lIS29aSXpqME"
140+
"NBUVlJS29aSXpqMERBUWNEUWdBRUM2bkV3TURJWVpPai9pUFdzQ3phRUtpNwoxT2lPU0xSRmhXR2"
141+
"pibkJWSmZWbmtZNHUzSWprRFlZTDBNeE80bXFzeVlqbEJhbFRWWXhGUDJzSkJLNXpsS09CCnV6Q0"
142+
"J1REFmQmdOVkhTTUVHREFXZ0JRaVpReldXcDAwaWZPRHRKVlN2MUFiT1NjR3JEQlNCZ05WSFI4RV"
143+
"N6QkoKTUVlZ1JhQkRoa0ZvZEhSd2N6b3ZMMk5sY25ScFptbGpZWFJsY3k1MGNuVnpkR1ZrYzJWeW"
144+
"RtbGpaWE11YVc1MApaV3d1WTI5dEwwbHVkR1ZzVTBkWVVtOXZkRU5CTG1SbGNqQWRCZ05WSFE0RU"
145+
"ZnUVVJbVVNMWxxZE5JbnpnN1NWClVyOVFHemtuQnF3d0RnWURWUjBQQVFIL0JBUURBZ0VHTUJJR0"
146+
"ExVWRFd0VCL3dRSU1BWUJBZjhDQVFFd0NnWUkKS29aSXpqMEVBd0lEU1FBd1JnSWhBT1cvNVFrUi"
147+
"tTOUNpU0RjTm9vd0x1UFJMc1dHZi9ZaTdHU1g5NEJnd1R3ZwpBaUVBNEowbHJIb01zK1hvNW8vc1"
148+
"g2TzlRV3hIUkF2WlVHT2RSUTdjdnFSWGFxST0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQoAMA"
149+
"0GCSqGSIb3DQEBCwUAA4IBAQAWPfe1yj4TfaxWipdcjCX+"
150+
"NBJQtQOvhu6TbkzwWczIkvcCQ8O6dzsnMDFkxVkZ2ZlcsufSaB74VS//3BzOh/PLWpSX/"
151+
"TaQHxFKhcK5RxlEq0O/oINnJ7fMhKlrd/hyoD/"
152+
"P2bSLej5zdh63JciGxNGXkanchgQ8qNxXhs9oRUJINYYinFfRsD3OzX6dsHLPVshkdOZFpM9DgP2"
153+
"QozqQJ1GC4tAKwbktxU0Ai3BecoPFzYVIygGLY1BAGd112C6cktj7YZTWE/"
154+
"tCSD+uXWyQieBu5zUN7H/PcxY9VBT/fOkBfaaL+JcpG4/tGrbTTbZUUclzKVQ/5XP6bOa1t6r/"
155+
"zN/W";
156+
157+
static const string pem_key_for_nested_cert =
158+
"-----BEGIN PUBLIC "
159+
"KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2NBrEQdwXUzVy2p+"
160+
"SZ7s\nBjxbVd4iTGNEQJu/Ot/C0NCzXIDT6DMEAeVZLSoWWcW6oXQ81h+yQWtw+jFW/"
161+
"SPg\nG4FGSL1UnVO8Zak80thovQk0dbZDo+"
162+
"9lsoOnOfXfPUL0T9AgHtqJpUr3tCfyRRLd\nC0MgF1tAyjZbMj8bHe2ZmJ9GLTJT5v9E0i5l3S4W"
163+
"ZY52vMzZaVpfxw+0/s5tRzco\nPGqIrMOnX/7kv5j7sisqZKNq6fP+4MHvLb/"
164+
"tXyHCkW6FzX8mUlwyRNzBP3R4xaXB\nvykzJMaAiCW/Yr/"
165+
"TxycdnmwsTR7he1Q78q12KnYqLvUVjg/v39/RWGSbFnaP1YX5\nHwIDAQAB\n-----END "
166+
"PUBLIC KEY-----\n";
167+
47168
template <typename T>
48169
void corrupt(T& buf)
49170
{
@@ -69,6 +190,19 @@ ccf::crypto::Pem generate_self_signed_cert(
69190
kp, name, {}, valid_from, certificate_validity_period_days);
70191
}
71192

193+
TEST_CASE("Check verifier handles nested certs for both PEM and DER inputs")
194+
{
195+
auto cert_der = ccf::crypto::raw_from_b64(nested_cert);
196+
auto cert_pem = fmt::format(
197+
"-----BEGIN CERTIFICATE-----\n{}\n-----END CERTIFICATE-----", nested_cert);
198+
auto der_verifier = make_verifier(cert_der);
199+
auto pem_verifier = make_verifier(cert_pem);
200+
auto pem_key_from_der = der_verifier->public_key_pem();
201+
auto pem_key_from_pem = pem_verifier->public_key_pem();
202+
CHECK(pem_key_from_der.str() == pem_key_from_pem.str());
203+
CHECK(pem_key_from_der.str() == pem_key_for_nested_cert);
204+
}
205+
72206
TEST_CASE("Sign, verify, with KeyPair")
73207
{
74208
for (const auto curve : supported_curves)

src/crypto/test/pem.cpp

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,36 @@
1010
using namespace std;
1111
using namespace ccf::crypto;
1212

13+
void check_bundles(
14+
const std::string& single_cert,
15+
const Pem& cert_pem,
16+
bool lr_before = false,
17+
bool lr_after = false)
18+
{
19+
for (size_t count : {1, 2, 3, 10})
20+
{
21+
std::string certs;
22+
for (size_t i = 0; i < count; ++i)
23+
{
24+
if (lr_before)
25+
{
26+
certs += "\n";
27+
}
28+
certs += single_cert;
29+
if (lr_after)
30+
{
31+
certs += "\n";
32+
}
33+
}
34+
auto bundle = split_x509_cert_bundle(certs);
35+
REQUIRE(bundle.size() == count);
36+
for (const auto& pem : bundle)
37+
{
38+
REQUIRE(pem == cert_pem);
39+
}
40+
}
41+
}
42+
1343
TEST_CASE("Split x509 cert bundle")
1444
{
1545
REQUIRE(split_x509_cert_bundle("") == std::vector<Pem>{});
@@ -31,14 +61,11 @@ TEST_CASE("Split x509 cert bundle")
3161
"\n-----END CERTIFICATE-----";
3262
auto bundle = split_x509_cert_bundle(single_cert);
3363
const auto cert_pem = Pem(single_cert);
34-
REQUIRE(bundle.size() == 1);
35-
REQUIRE(bundle[0] == cert_pem);
3664

37-
const std::string two_certs = single_cert + single_cert;
38-
bundle = split_x509_cert_bundle(two_certs);
39-
REQUIRE(bundle.size() == 2);
40-
REQUIRE(bundle[0] == cert_pem);
41-
REQUIRE(bundle[1] == cert_pem);
65+
check_bundles(single_cert, cert_pem);
66+
check_bundles(single_cert, cert_pem, true);
67+
check_bundles(single_cert, cert_pem, false, true);
68+
check_bundles(single_cert, cert_pem, true, true);
4269

4370
std::string bundle_with_invalid_suffix = single_cert + "ignored suffix";
4471
bundle = split_x509_cert_bundle(bundle_with_invalid_suffix);

0 commit comments

Comments
 (0)