Skip to content

Commit d40437c

Browse files
Copilotromanett
andcommitted
Add comprehensive tests for trust list size validation
Add TrustListValidationTest with tests for: - Normal-sized trust lists (verify basic functionality) - Trust lists exceeding 16MB limit (verify BadEncodingLimitsExceeded error) - Trust lists just under limit (~15MB) (verify boundary condition) Co-authored-by: romanett <[email protected]>
1 parent f46df1e commit d40437c

1 file changed

Lines changed: 216 additions & 0 deletions

File tree

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/* ========================================================================
2+
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
3+
*
4+
* OPC Foundation MIT License 1.00
5+
*
6+
* Permission is hereby granted, free of charge, to any person
7+
* obtaining a copy of this software and associated documentation
8+
* files (the "Software"), to deal in the Software without
9+
* restriction, including without limitation the rights to use,
10+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the
12+
* Software is furnished to do so, subject to the following
13+
* conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be
16+
* included in all copies or substantial portions of the Software.
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24+
* OTHER DEALINGS IN THE SOFTWARE.
25+
*
26+
* The complete license agreement can be found here:
27+
* http://opcfoundation.org/License/MIT/1.00/
28+
* ======================================================================*/
29+
30+
using System;
31+
using System.Security.Cryptography.X509Certificates;
32+
using System.Threading.Tasks;
33+
using NUnit.Framework;
34+
using Opc.Ua.Security.Certificates;
35+
using Opc.Ua.Test;
36+
using Opc.Ua.Tests;
37+
using Assert = NUnit.Framework.Legacy.ClassicAssert;
38+
39+
namespace Opc.Ua.Gds.Tests
40+
{
41+
[TestFixture]
42+
[Category("GDSPush")]
43+
[Category("GDS")]
44+
[SetCulture("en-us")]
45+
[SetUICulture("en-us")]
46+
[NonParallelizable]
47+
public class TrustListValidationTest
48+
{
49+
private GlobalDiscoveryTestServer m_server;
50+
private ServerConfigurationPushTestClient m_pushClient;
51+
private ITelemetryContext m_telemetry;
52+
53+
[OneTimeSetUp]
54+
public async Task OneTimeSetUpAsync()
55+
{
56+
// Start GDS server
57+
m_telemetry = NUnitTelemetryContext.Create();
58+
m_server = await TestUtils.StartGDSAsync(true, CertificateStoreType.Directory).ConfigureAwait(false);
59+
60+
// Load client
61+
m_pushClient = new ServerConfigurationPushTestClient(true, m_telemetry);
62+
await m_pushClient.LoadClientConfigurationAsync(m_server.BasePort).ConfigureAwait(false);
63+
64+
// Set admin credentials and connect
65+
m_pushClient.PushClient.AdminCredentials = m_pushClient.SysAdminUser;
66+
await m_pushClient.ConnectAsync(SecurityPolicies.Aes256_Sha256_RsaPss).ConfigureAwait(false);
67+
}
68+
69+
[OneTimeTearDown]
70+
public async Task OneTimeTearDownAsync()
71+
{
72+
try
73+
{
74+
await m_pushClient.DisconnectClientAsync().ConfigureAwait(false);
75+
await m_server.StopServerAsync().ConfigureAwait(false);
76+
}
77+
catch
78+
{
79+
}
80+
finally
81+
{
82+
m_pushClient?.Dispose();
83+
m_pushClient = null;
84+
m_server = null;
85+
}
86+
}
87+
88+
/// <summary>
89+
/// Test that normal-sized trust lists work correctly.
90+
/// </summary>
91+
[Test]
92+
[Order(100)]
93+
public async Task NormalSizeTrustListAsync()
94+
{
95+
// Create a normal-sized trust list
96+
var normalTrustList = new TrustListDataType
97+
{
98+
SpecifiedLists = (uint)TrustListMasks.All,
99+
TrustedCertificates = new ByteStringCollection(),
100+
TrustedCrls = new ByteStringCollection(),
101+
IssuerCertificates = new ByteStringCollection(),
102+
IssuerCrls = new ByteStringCollection()
103+
};
104+
105+
// Add a reasonable number of certificates (10)
106+
for (int i = 0; i < 10; i++)
107+
{
108+
using X509Certificate2 cert = CertificateFactory
109+
.CreateCertificate($"urn:test:cert{i}", $"NormalCert{i}", $"CN=NormalCert{i}, O=OPC Foundation", null)
110+
.CreateForRSA();
111+
normalTrustList.TrustedCertificates.Add(cert.RawData);
112+
}
113+
114+
// This should succeed
115+
bool requireReboot = await m_pushClient.PushClient.UpdateTrustListAsync(normalTrustList).ConfigureAwait(false);
116+
Assert.False(requireReboot);
117+
118+
// Read it back to verify
119+
TrustListDataType readTrustList = await m_pushClient.PushClient.ReadTrustListAsync().ConfigureAwait(false);
120+
Assert.IsNotNull(readTrustList);
121+
Assert.AreEqual(normalTrustList.TrustedCertificates.Count, readTrustList.TrustedCertificates.Count);
122+
}
123+
124+
/// <summary>
125+
/// Test that writing a trust list exceeding the size limit fails.
126+
/// </summary>
127+
[Test]
128+
[Order(200)]
129+
public async Task WriteTrustListExceedsSizeLimitAsync()
130+
{
131+
// Create a trust list that will definitely exceed 16MB when encoded
132+
var oversizedTrustList = new TrustListDataType
133+
{
134+
SpecifiedLists = (uint)TrustListMasks.All,
135+
TrustedCertificates = new ByteStringCollection(),
136+
TrustedCrls = new ByteStringCollection(),
137+
IssuerCertificates = new ByteStringCollection(),
138+
IssuerCrls = new ByteStringCollection()
139+
};
140+
141+
// Generate a large number of certificates to exceed 16MB
142+
// Each 4096-bit RSA cert is roughly 2KB, so we need about 9000+ certs
143+
TestContext.Out.WriteLine("Generating large trust list...");
144+
for (int i = 0; i < 9000; i++)
145+
{
146+
using X509Certificate2 cert = CertificateFactory
147+
.CreateCertificate($"urn:test:cert{i}", $"TestCert{i}", $"CN=TestCert{i}, O=OPC Foundation", null)
148+
.SetRSAKeySize(4096)
149+
.CreateForRSA();
150+
oversizedTrustList.TrustedCertificates.Add(cert.RawData);
151+
152+
if (i % 1000 == 0)
153+
{
154+
TestContext.Out.WriteLine($"Generated {i} certificates...");
155+
}
156+
}
157+
158+
TestContext.Out.WriteLine("Attempting to write oversized trust list...");
159+
160+
// This should throw ServiceResultException with BadEncodingLimitsExceeded
161+
var ex = Assert.ThrowsAsync<ServiceResultException>(async () =>
162+
{
163+
await m_pushClient.PushClient.UpdateTrustListAsync(oversizedTrustList).ConfigureAwait(false);
164+
});
165+
166+
Assert.IsNotNull(ex);
167+
Assert.AreEqual(StatusCodes.BadEncodingLimitsExceeded, ex.StatusCode);
168+
TestContext.Out.WriteLine($"Expected exception caught: {ex.Message}");
169+
}
170+
171+
/// <summary>
172+
/// Test boundary condition - trust list just under the limit.
173+
/// </summary>
174+
[Test]
175+
[Order(300)]
176+
public async Task TrustListJustUnderLimitAsync()
177+
{
178+
var boundaryTrustList = new TrustListDataType
179+
{
180+
SpecifiedLists = (uint)TrustListMasks.All,
181+
TrustedCertificates = new ByteStringCollection(),
182+
TrustedCrls = new ByteStringCollection(),
183+
IssuerCertificates = new ByteStringCollection(),
184+
IssuerCrls = new ByteStringCollection()
185+
};
186+
187+
// Add enough certificates to get close to but under the limit
188+
// Estimate: 4096-bit cert ~2KB, so ~7500 certs = ~15MB
189+
TestContext.Out.WriteLine("Generating trust list just under limit...");
190+
for (int i = 0; i < 7500; i++)
191+
{
192+
using X509Certificate2 cert = CertificateFactory
193+
.CreateCertificate($"urn:test:cert{i}", $"BoundaryCert{i}", $"CN=BoundaryCert{i}, O=OPC Foundation", null)
194+
.SetRSAKeySize(4096)
195+
.CreateForRSA();
196+
boundaryTrustList.TrustedCertificates.Add(cert.RawData);
197+
198+
if (i % 1000 == 0)
199+
{
200+
TestContext.Out.WriteLine($"Generated {i} certificates...");
201+
}
202+
}
203+
204+
TestContext.Out.WriteLine("Writing trust list just under limit...");
205+
206+
// This should succeed
207+
bool requireReboot = await m_pushClient.PushClient.UpdateTrustListAsync(boundaryTrustList).ConfigureAwait(false);
208+
Assert.False(requireReboot);
209+
210+
// Read it back
211+
TrustListDataType readTrustList = await m_pushClient.PushClient.ReadTrustListAsync().ConfigureAwait(false);
212+
Assert.IsNotNull(readTrustList);
213+
Assert.AreEqual(boundaryTrustList.TrustedCertificates.Count, readTrustList.TrustedCertificates.Count);
214+
}
215+
}
216+
}

0 commit comments

Comments
 (0)