diff --git a/.gitignore b/.gitignore index ff104459c0..3667e71d57 100644 --- a/.gitignore +++ b/.gitignore @@ -266,4 +266,8 @@ OPC\ Foundation/ # UA Fuzzing findings folders /Fuzzing/**/findings/ /Fuzzing/**/Testcases/ -!/Fuzzing/**/Testcases/*.bin \ No newline at end of file +!/Fuzzing/**/Testcases/*.bin +/Fuzzing/Encoders/Fuzz.Tests/Assets/* +/Fuzzing/**/crash-* +/Fuzzing/**/slow-* +/Fuzzing/**/timeout-* diff --git a/Fuzzing/Encoders/Fuzz.Tests/Opc.Ua.Encoders.Fuzz.Tests.csproj b/Fuzzing/Encoders/Fuzz.Tests/Opc.Ua.Encoders.Fuzz.Tests.csproj index 26130413da..8843d1d6f3 100644 --- a/Fuzzing/Encoders/Fuzz.Tests/Opc.Ua.Encoders.Fuzz.Tests.csproj +++ b/Fuzzing/Encoders/Fuzz.Tests/Opc.Ua.Encoders.Fuzz.Tests.csproj @@ -13,6 +13,8 @@ + + @@ -53,6 +55,12 @@ Testcases\%(RecursiveDir)%(FileName)%(Extension) + + Testcases\%(RecursiveDir)%(FileName)%(Extension) + + + Testcases\%(RecursiveDir)%(FileName)%(Extension) + @@ -68,6 +76,14 @@ Testcases\%(RecursiveDir)%(FileName)%(Extension) PreserveNewest + + Testcases\%(RecursiveDir)%(FileName)%(Extension) + PreserveNewest + + + Testcases\%(RecursiveDir)%(FileName)%(Extension) + PreserveNewest + PreserveNewest diff --git a/Fuzzing/Encoders/Fuzz.Tools/Encoders.Fuzz.Tools.csproj b/Fuzzing/Encoders/Fuzz.Tools/Encoders.Fuzz.Tools.csproj index 1c16b233a4..1c4af62da8 100644 --- a/Fuzzing/Encoders/Fuzz.Tools/Encoders.Fuzz.Tools.csproj +++ b/Fuzzing/Encoders/Fuzz.Tools/Encoders.Fuzz.Tools.csproj @@ -12,6 +12,8 @@ + + @@ -20,7 +22,7 @@ - + diff --git a/Fuzzing/Encoders/Fuzz.Tools/Encoders.Testcases.cs b/Fuzzing/Encoders/Fuzz.Tools/Encoders.Testcases.cs index f12fcd4f48..dad461ca6e 100644 --- a/Fuzzing/Encoders/Fuzz.Tools/Encoders.Testcases.cs +++ b/Fuzzing/Encoders/Fuzz.Tools/Encoders.Testcases.cs @@ -27,9 +27,13 @@ * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ +using System; using System.IO; +using System.Linq; +using System.Security.Cryptography.X509Certificates; using System.Text; using Opc.Ua; +using Opc.Ua.Security.Certificates; public static partial class Testcases { @@ -38,10 +42,12 @@ public enum TestCaseEncoders : int { Binary = 0, Json = 1, - Xml = 2 + Xml = 2, + Certificates = 3, + CRLs = 4 }; - public static string[] TestcaseEncoderSuffixes = new string[] { ".Binary", ".Json", ".Xml" }; + public static string[] TestcaseEncoderSuffixes = new string[] { ".Binary", ".Json", ".Xml", ".Certificates", ".CRLs" }; public static void Run(string workPath) { @@ -57,17 +63,10 @@ public static void Run(string workPath) message = encoder.CloseAndReturnBuffer(); } - // Test the fuzz targets with the message. - FuzzableCode.LibfuzzBinaryDecoder(message); - FuzzableCode.LibfuzzBinaryEncoder(message); - using (var stream = new MemoryStream(message)) - { - FuzzableCode.AflfuzzBinaryDecoder(stream); - } - using (var stream = new MemoryStream(message)) - { - FuzzableCode.AflfuzzBinaryEncoder(stream); - } + // ensure the test case does not throw an exception + TestNewRawDataMessage(message); + + // ensure it does not throw an exception using (var stream = new MemoryStream(message)) { FuzzableCode.FuzzBinaryDecoderCore(stream, true); @@ -91,13 +90,11 @@ public static void Run(string workPath) message = memoryStream.ToArray(); } - // Test the fuzz targets with the message. - FuzzableCode.LibfuzzJsonDecoder(message); - FuzzableCode.LibfuzzJsonEncoder(message); + TestNewUtf8Message(message); + + // ensure the test case does not throw an exception string json = Encoding.UTF8.GetString(message); - FuzzableCode.AflfuzzJsonDecoder(json); - FuzzableCode.AflfuzzJsonEncoder(json); FuzzableCode.FuzzJsonDecoderCore(json, true); string fileName = Path.Combine(pathTarget, $"{messageEncoder.Method.Name}.json".ToLowerInvariant()); @@ -120,22 +117,167 @@ public static void Run(string workPath) // Test the fuzz targets with the message. byte[] message = Encoding.UTF8.GetBytes(xml); using (var stream = new MemoryStream(message)) - { - FuzzableCode.AflfuzzXmlDecoder(stream); - } - using (var stream = new MemoryStream(message)) - { - FuzzableCode.AflfuzzXmlEncoder(stream); - } - using (var stream = new MemoryStream(message)) { FuzzableCode.FuzzXmlDecoderCore(stream, true); } - FuzzableCode.LibfuzzXmlDecoder(message); - FuzzableCode.LibfuzzXmlEncoder(message); + + TestNewUtf8Message(message); string fileName = Path.Combine(pathTarget, $"{messageEncoder.Method.Name}.xml".ToLowerInvariant()); File.WriteAllBytes(fileName, Encoding.UTF8.GetBytes(xml)); } + + // Create the Testcases for the certificate decoder. + pathSuffix = TestcaseEncoderSuffixes[(int)TestCaseEncoders.Certificates]; + pathTarget = workPath + pathSuffix + Path.DirectorySeparatorChar; + X509Certificate2 rootCert = null; + X509Certificate2 certificate = null; + { + var crlName = "root.crl"; + var crlServerUrl = "http://127.0.0.1:1234/"; + var crlExtension = X509Extensions.BuildX509CRLDistributionPoints(crlServerUrl + crlName); + + // create the root cert + rootCert = CertificateFactory.CreateCertificate("CN=Root") + .AddExtension(crlExtension) + .SetLifeTime(25 * 12) + .SetCAConstraint() + .SetRSAKeySize(4096) + .CreateForRSA(); + + certificate = CertificateFactory.CreateCertificate( + "urn:op:ua:application:test", + "TestApp", + "CN=TestApp", + new string[] { "test.com", "192.168.1.111", "c034:777f:9abc::1234:5678" }) + .SetLifeTime(12) + .SetIssuer(rootCert) + .SetRSAKeySize(2048) + .CreateForRSA(); + + // Test the fuzz targets with the message. + byte[] rawData = certificate.RawData; + TestNewRawDataMessage(rawData); + + // should not throw an exception + _ = X509CertificateLoader.LoadCertificate(rawData); + FuzzableCode.FuzzCertificateChainDecoderCore(rawData,false); + FuzzableCode.FuzzCertificateChainDecoderCore(rawData, true); + + string fileName = Path.Combine(pathTarget, "certificate.bin"); + File.WriteAllBytes(fileName, rawData); + + // Test the fuzz targets with the message. + rawData = rootCert.RawData; + TestNewRawDataMessage(rawData); + + // should not throw an exception + _ = X509CertificateLoader.LoadCertificate(rawData); + FuzzableCode.FuzzCertificateChainDecoderCore(rawData, false); + FuzzableCode.FuzzCertificateChainDecoderCore(rawData, true); + + fileName = Path.Combine(pathTarget, "rootcertificate.bin"); + File.WriteAllBytes(fileName, rawData); + + // Create a binary blob chain. + rawData = new byte[certificate.RawData.Length + rootCert.RawData.Length]; + certificate.RawData.CopyTo(rawData, 0); + rootCert.RawData.CopyTo(rawData, certificate.RawData.Length); + TestNewRawDataMessage(rawData); + FuzzableCode.FuzzCertificateChainDecoderCore(rawData, false); + FuzzableCode.FuzzCertificateChainDecoderCore(rawData, true); + + // should not throw an exception + _ = X509CertificateLoader.LoadCertificate(rawData); + _ = Utils.ParseCertificateBlob(rawData, false); + _ = Utils.ParseCertificateBlob(rawData, true); + _ = Utils.ParseCertificateChainBlob(rawData, false); + _ = Utils.ParseCertificateChainBlob(rawData, true); + + fileName = Path.Combine(pathTarget, "certificatechain.bin"); + File.WriteAllBytes(fileName, rawData); + } + + // Create the Testcases for the CRL decoder. + pathSuffix = TestcaseEncoderSuffixes[(int)TestCaseEncoders.CRLs]; + pathTarget = workPath + pathSuffix + Path.DirectorySeparatorChar; + { + // create an empty CRL + X509CRL rootCrl = CertificateFactory.RevokeCertificate(rootCert, null, null); + + // Test the fuzz targets with the message. + byte[] rawData = rootCrl.RawData; + TestNewRawDataMessage(rawData); + + string fileName = Path.Combine(pathTarget, "emptyroot.crl"); + File.WriteAllBytes(fileName, rawData); + + // create a CRL with a revoked certificate + X509CRL revokedRootCrl = CertificateFactory.RevokeCertificate(rootCert, null, new X509Certificate2Collection(certificate)); + + // Test the fuzz targets with the message. + rawData = revokedRootCrl.RawData; + TestNewRawDataMessage(rawData); + + fileName = Path.Combine(pathTarget, "root.crl"); + File.WriteAllBytes(fileName, rawData); + + // create a CRL which revokes the leaf certificate + CrlBuilder crlBuilder = CrlBuilder.Create(certificate.IssuerName); + IX509CRL x509CRL = crlBuilder + .AddRevokedCertificate(certificate, CRLReason.PrivilegeWithdrawn) + .CreateForRSA(certificate); + + // Test the fuzz targets with the message. + rawData = x509CRL.RawData; + TestNewRawDataMessage(rawData); + + fileName = Path.Combine(pathTarget, "leaf.crl"); + File.WriteAllBytes(fileName, rawData); + } + } + + private static void TestNewRawDataMessage(byte[] message) + { + FuzzableCode.LibfuzzBinaryDecoder(message); + FuzzableCode.LibfuzzBinaryEncoder(message); + using (var stream = new MemoryStream(message)) + { + FuzzableCode.AflfuzzBinaryDecoder(stream); + } + using (var stream = new MemoryStream(message)) + { + FuzzableCode.AflfuzzBinaryEncoder(stream); + } + using (var stream = new MemoryStream(message)) + { + FuzzableCode.FuzzBinaryDecoderCore(stream); + } + FuzzableCode.LibfuzzXmlDecoder(message); + FuzzableCode.LibfuzzXmlEncoder(message); + FuzzableCode.LibfuzzCertificateDecoder(message); + FuzzableCode.LibfuzzCertificateChainDecoder(message); + FuzzableCode.LibfuzzCertificateChainDecoderCustom(message); + FuzzableCode.LibfuzzCRLDecoder(message); + FuzzableCode.LibfuzzCRLEncoder(message); + } + + private static void TestNewUtf8Message(byte[] message) + { + FuzzableCode.LibfuzzJsonDecoder(message); + FuzzableCode.LibfuzzJsonEncoder(message); + FuzzableCode.LibfuzzXmlDecoder(message); + FuzzableCode.LibfuzzXmlEncoder(message); + FuzzableCode.LibfuzzCertificateDecoder(message); + FuzzableCode.LibfuzzCertificateChainDecoder(message); + FuzzableCode.LibfuzzCertificateChainDecoderCustom(message); + FuzzableCode.LibfuzzCRLDecoder(message); + FuzzableCode.LibfuzzCRLEncoder(message); + + // string targets + string json = Encoding.UTF8.GetString(message); + FuzzableCode.AflfuzzJsonDecoder(json); + FuzzableCode.AflfuzzJsonEncoder(json); + FuzzableCode.FuzzJsonDecoderCore(json); } } diff --git a/Fuzzing/Encoders/Fuzz/Encoders.Fuzz.csproj b/Fuzzing/Encoders/Fuzz/Encoders.Fuzz.csproj index cfd8744a7b..bade0c4d7b 100644 --- a/Fuzzing/Encoders/Fuzz/Encoders.Fuzz.csproj +++ b/Fuzzing/Encoders/Fuzz/Encoders.Fuzz.csproj @@ -17,7 +17,7 @@ - + @@ -25,4 +25,9 @@ + + + + + diff --git a/Fuzzing/Encoders/Fuzz/FuzzableCode.CRLs.cs b/Fuzzing/Encoders/Fuzz/FuzzableCode.CRLs.cs new file mode 100644 index 0000000000..2ca7344ae7 --- /dev/null +++ b/Fuzzing/Encoders/Fuzz/FuzzableCode.CRLs.cs @@ -0,0 +1,208 @@ +/* ======================================================================== + * Copyright (c) 2005-2024 The OPC Foundation, Inc. All rights reserved. + * + * OPC Foundation MIT License 1.00 + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * The complete license agreement can be found here: + * http://opcfoundation.org/License/MIT/1.00/ + * ======================================================================*/ + +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Opc.Ua.Security.Certificates; +using Opc.Ua; + +/// +/// Fuzzing code for the CRL decoder and encoder. +/// +public static partial class FuzzableCode +{ + /// + /// The CRL decoder fuzz target for afl-fuzz. + /// + /// The stdin stream from the afl-fuzz process. + public static void AflfuzzCRLDecoder(Stream stream) + { + using (var memoryStream = PrepareArraySegmentStream(stream)) + { + FuzzCRLDecoderCore(memoryStream.ToArray()); + } + } + + /// + /// The CRL encoder fuzz target for afl-fuzz. + /// + /// The stdin stream from the afl-fuzz process. + public static void AflfuzzCRLEncoder(Stream stream) + { + X509CRL crl = null; + using (var memoryStream = PrepareArraySegmentStream(stream)) + { + try + { + crl = FuzzCRLDecoderCore(memoryStream.ToArray()); + } + catch + { + return; + } + } + + // encode the fuzzed object and see if it crashes + if (crl != null) + { + _ = CrlBuilder.Create(crl).Encode(); + } + } + + /// + /// The CRL encoder indempotent fuzz target for afl-fuzz. + /// + /// The stdin stream from the afl-fuzz process. + public static void AflfuzzCRLEncoderIndempotent(Stream stream) + { + X509CRL crl = null; + byte[] serialized = null; + using (var memoryStream = PrepareArraySegmentStream(stream)) + { + try + { + crl = FuzzCRLDecoderCore(memoryStream.ToArray(), true); + serialized = CrlBuilder.Create(crl).Encode(); + } + catch + { + return; + } + } + + // reencode the fuzzed input and see if they are indempotent + FuzzCRLEncoderIndempotentCore(serialized, crl); + } + + /// + /// The CRL decoder fuzz target for libfuzzer. + /// + public static void LibfuzzCRLDecoder(ReadOnlySpan input) + { + _ = FuzzCRLDecoderCore(input); + } + + /// + /// The CRL encoder fuzz target for libfuzzer. + /// + public static void LibfuzzCRLEncoder(ReadOnlySpan input) + { + X509CRL crl = null; + try + { + crl = FuzzCRLDecoderCore(input, true); + } + catch + { + return; + } + + // encode the fuzzed object and see if it crashes + if (crl != null) + { + _ = CrlBuilder.Create(crl).Encode(); + } + } + + /// + /// The CRL encoder indempotent fuzz target for libfuzzer. + /// + public static void LibfuzzCRLEncoderIndempotent(ReadOnlySpan input) + { + X509CRL crl = null; + byte[] serialized = null; + try + { + crl = FuzzCRLDecoderCore(input, true); + serialized = CrlBuilder.Create(crl).Encode(); + } + catch + { + return; + } + + // reencode the fuzzed input and see if they are indempotent + FuzzCRLEncoderIndempotentCore(serialized, crl); + } + + /// + /// The fuzz target for the CRL decoder. + /// + /// A byte array with fuzz content. + internal static X509CRL FuzzCRLDecoderCore(ReadOnlySpan serialized, bool throwAll = false) + { + try + { + var result = new X509CRL(serialized.ToArray()); + _ = result.Issuer; + return result; + } + catch (CryptographicException) + { + if (!throwAll) + { + return null; + } + throw; + } + } + + /// + /// The indempotent fuzz target core for the CRL encoder. + /// + /// The indempotent ASN.1 encoded data. + /// + internal static void FuzzCRLEncoderIndempotentCore(byte[] serialized, X509CRL encodeable) + { + if (serialized == null || encodeable == null) return; + + X509CRL crl2 = FuzzCRLDecoderCore(serialized, true); + byte[] serialized2 = crl2.RawData; + + using (var memoryStream2 = new MemoryStream(serialized2)) + { + X509CRL crl3 = FuzzCRLDecoderCore(serialized2, true); + + string encodeableTypeName = crl3?.GetType().Name ?? "unknown type"; + if (serialized2 == null || !serialized.SequenceEqual(serialized2)) + { + throw new Exception(Utils.Format("Indempotent encoding failed. Type={0}.", encodeableTypeName)); + } + + if (!Utils.IsEqual(crl2, crl3)) + { + throw new Exception(Utils.Format("Indempotent 3rd gen decoding failed. Type={0}.", encodeableTypeName)); + } + } + } +} + diff --git a/Fuzzing/Encoders/Fuzz/FuzzableCode.Certificates.cs b/Fuzzing/Encoders/Fuzz/FuzzableCode.Certificates.cs new file mode 100644 index 0000000000..2f56bf90d3 --- /dev/null +++ b/Fuzzing/Encoders/Fuzz/FuzzableCode.Certificates.cs @@ -0,0 +1,150 @@ +/* ======================================================================== + * Copyright (c) 2005-2024 The OPC Foundation, Inc. All rights reserved. + * + * OPC Foundation MIT License 1.00 + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * The complete license agreement can be found here: + * http://opcfoundation.org/License/MIT/1.00/ + * ======================================================================*/ + +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Opc.Ua; + +/// +/// Fuzzing code for the certificate decoder. +/// +public static partial class FuzzableCode +{ + /// + /// The certificate decoder fuzz target for afl-fuzz. + /// + /// The stdin stream from the afl-fuzz process. + public static void AflfuzzCertificateDecoder(Stream stream) + { + using (var memoryStream = PrepareArraySegmentStream(stream)) + { + FuzzCertificateDecoderCore(memoryStream.ToArray()); + } + } + + /// + /// The certificate chain decoder fuzz target for afl-fuzz. + /// + /// The stdin stream from the afl-fuzz process. + public static void AflfuzzCertificateChainDecoder(Stream stream) + { + using (var memoryStream = PrepareArraySegmentStream(stream)) + { + FuzzCertificateChainDecoderCore(memoryStream.ToArray()); + } + } + + /// + /// The certificate chain decoder with custom blob fuzz target for afl-fuzz. + /// + /// The stdin stream from the afl-fuzz process. + public static void AflfuzzCertificateChainDecoderCustom(Stream stream) + { + using (var memoryStream = PrepareArraySegmentStream(stream)) + { + FuzzCertificateChainDecoderCore(memoryStream.ToArray(), true); + } + } + + /// + /// The certificate decoder fuzz target for libfuzzer. + /// + public static void LibfuzzCertificateDecoder(ReadOnlySpan input) + { + _ = FuzzCertificateDecoderCore(input); + } + + /// + /// The certificate encoder fuzz target for libfuzzer. + /// + public static void LibfuzzCertificateChainDecoder(ReadOnlySpan input) + { + _ = FuzzCertificateChainDecoderCore(input); + } + + /// + /// The certificate encoder fuzz target for libfuzzer. + /// + public static void LibfuzzCertificateChainDecoderCustom(ReadOnlySpan input) + { + _ = FuzzCertificateChainDecoderCore(input, true); + } + + /// + /// The fuzz target for the Certificate decoder. + /// + /// A byte array with fuzz content. + internal static X509Certificate2 FuzzCertificateDecoderCore(ReadOnlySpan serialized, bool throwAll = false) + { + try + { + return X509CertificateLoader.LoadCertificate(serialized.ToArray()); + } + catch (CryptographicException) + { + if (!throwAll) + { + return null; + } + throw; + } + } + + /// + /// The fuzz target for the Certificate chain decoder. + /// + /// A byte array with fuzz content. + internal static X509Certificate2Collection FuzzCertificateChainDecoderCore(ReadOnlySpan serialized, bool useAsn1Parser = false, bool throwAll = false) + { + try + { + return Utils.ParseCertificateChainBlob(serialized.ToArray(), useAsn1Parser); + } + catch (ServiceResultException sre) + { + switch (sre.StatusCode) + { + case StatusCodes.BadCertificateInvalid: + if (!throwAll) + { + return null; + } + break; + + default: + break; + } + throw; + } + } +} + diff --git a/Fuzzing/Encoders/Fuzz/FuzzableCode.cs b/Fuzzing/Encoders/Fuzz/FuzzableCode.cs index 01d9536519..43528b1788 100644 --- a/Fuzzing/Encoders/Fuzz/FuzzableCode.cs +++ b/Fuzzing/Encoders/Fuzz/FuzzableCode.cs @@ -35,6 +35,8 @@ public static partial class FuzzableCode { private static ServiceMessageContext messageContext = ServiceMessageContext.GlobalContext; + private static BufferManager bufferManager = new BufferManager(nameof(FuzzableCode), 65535); + private static ChannelQuotas channelQuotas = new ChannelQuotas(); /// /// Print information about the fuzzer target. @@ -42,7 +44,7 @@ public static partial class FuzzableCode public static void FuzzInfo() { Console.WriteLine("OPC UA Core Encoder Fuzzer for afl-fuzz and libfuzzer."); - Console.WriteLine("Fuzzing targets for various aspects of the Binary, Json and Xml encoders."); + Console.WriteLine("Fuzzing targets for various aspects of the OPC UA Binary, Json and Xml encoders."); } /// diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Binary/readrequest.bin b/Fuzzing/Encoders/Fuzz/Testcases.Binary/readrequest.bin index 15f160ef31..ac36487658 100644 Binary files a/Fuzzing/Encoders/Fuzz/Testcases.Binary/readrequest.bin and b/Fuzzing/Encoders/Fuzz/Testcases.Binary/readrequest.bin differ diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Binary/readresponse.bin b/Fuzzing/Encoders/Fuzz/Testcases.Binary/readresponse.bin index 25561bc108..05aff1743d 100644 Binary files a/Fuzzing/Encoders/Fuzz/Testcases.Binary/readresponse.bin and b/Fuzzing/Encoders/Fuzz/Testcases.Binary/readresponse.bin differ diff --git a/Fuzzing/Encoders/Fuzz/Testcases.CRLs/emptyroot.crl b/Fuzzing/Encoders/Fuzz/Testcases.CRLs/emptyroot.crl new file mode 100644 index 0000000000..d562150772 Binary files /dev/null and b/Fuzzing/Encoders/Fuzz/Testcases.CRLs/emptyroot.crl differ diff --git a/Fuzzing/Encoders/Fuzz/Testcases.CRLs/leaf.crl b/Fuzzing/Encoders/Fuzz/Testcases.CRLs/leaf.crl new file mode 100644 index 0000000000..318d16eeeb Binary files /dev/null and b/Fuzzing/Encoders/Fuzz/Testcases.CRLs/leaf.crl differ diff --git a/Fuzzing/Encoders/Fuzz/Testcases.CRLs/root.crl b/Fuzzing/Encoders/Fuzz/Testcases.CRLs/root.crl new file mode 100644 index 0000000000..352a32b1f1 Binary files /dev/null and b/Fuzzing/Encoders/Fuzz/Testcases.CRLs/root.crl differ diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Certificates/certificate.bin b/Fuzzing/Encoders/Fuzz/Testcases.Certificates/certificate.bin new file mode 100644 index 0000000000..923dc3899f Binary files /dev/null and b/Fuzzing/Encoders/Fuzz/Testcases.Certificates/certificate.bin differ diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Certificates/certificatechain.bin b/Fuzzing/Encoders/Fuzz/Testcases.Certificates/certificatechain.bin new file mode 100644 index 0000000000..7f296b6e72 Binary files /dev/null and b/Fuzzing/Encoders/Fuzz/Testcases.Certificates/certificatechain.bin differ diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Certificates/rootcertificate.bin b/Fuzzing/Encoders/Fuzz/Testcases.Certificates/rootcertificate.bin new file mode 100644 index 0000000000..c484c05a6b Binary files /dev/null and b/Fuzzing/Encoders/Fuzz/Testcases.Certificates/rootcertificate.bin differ diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Json/readrequest.json b/Fuzzing/Encoders/Fuzz/Testcases.Json/readrequest.json index ba55d02acc..e0161ef433 100644 --- a/Fuzzing/Encoders/Fuzz/Testcases.Json/readrequest.json +++ b/Fuzzing/Encoders/Fuzz/Testcases.Json/readrequest.json @@ -1 +1 @@ -{"TypeId":{"Id":629},"Body":{"RequestHeader":{"Timestamp":"2024-04-21T04:30:30.6104202Z","RequestHandle":42,"ReturnDiagnostics":0,"TimeoutHint":0},"MaxAge":0,"TimestampsToReturn":0,"NodesToRead":[{"NodeId":{"Id":1000},"AttributeId":5},{"NodeId":{"Id":1000},"AttributeId":13},{"NodeId":{"Id":1000},"AttributeId":4},{"NodeId":{"Id":1000},"AttributeId":17},{"NodeId":{"Id":1000},"AttributeId":24}]}} \ No newline at end of file +{"TypeId":{"Id":629},"Body":{"RequestHeader":{"Timestamp":"2025-02-06T05:50:41.6658997Z","RequestHandle":422,"ReturnDiagnostics":1023,"TimeoutHint":10000},"MaxAge":1000,"TimestampsToReturn":0,"NodesToRead":[{"NodeId":{"Id":123},"AttributeId":25},{"NodeId":{"Id":4444,"Namespace":2},"AttributeId":5},{"NodeId":{"IdType":1,"Id":"RevisionCounter","Namespace":3},"AttributeId":13,"IndexRange":"1:2"},{"NodeId":{"IdType":2,"Id":"6fe986d2-d4e4-4d9e-89e2-12cf71047951"},"AttributeId":4},{"NodeId":{"Id":4444,"Namespace":2},"AttributeId":17},{"NodeId":{"IdType":3,"Id":"QhY3LAs="},"AttributeId":24}]}} \ No newline at end of file diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Json/readresponse.json b/Fuzzing/Encoders/Fuzz/Testcases.Json/readresponse.json index 80262fb5ba..4588ab0aaa 100644 --- a/Fuzzing/Encoders/Fuzz/Testcases.Json/readresponse.json +++ b/Fuzzing/Encoders/Fuzz/Testcases.Json/readresponse.json @@ -1 +1 @@ -{"TypeId":{"Id":632},"Body":{"ResponseHeader":{"Timestamp":"2024-04-21T04:30:30.6969268Z","RequestHandle":42,"ServiceDiagnostics":{"AdditionalInfo":"NodeId not found","InnerStatusCode":2153644032,"InnerDiagnosticInfo":{"AdditionalInfo":"Hello World","InnerStatusCode":2150891520,"InnerDiagnosticInfo":{"AdditionalInfo":"Hello World","InnerStatusCode":2150891520,"InnerDiagnosticInfo":{"AdditionalInfo":"Hello World","InnerStatusCode":2150891520}}}}},"Results":[{"Value":{"Type":12,"Body":"Hello World"},"SourceTimestamp":"2024-04-21T04:31:30.6908516Z","SourcePicoseconds":10,"ServerTimestamp":"2024-04-21T04:30:30.6908516Z","ServerPicoseconds":100},{"Value":{"Type":7,"Body":12345678},"StatusCode":2157772800,"SourceTimestamp":"2024-04-21T04:31:30.6908516Z","ServerTimestamp":"2024-04-21T04:30:30.6908516Z"},{"Value":{"Type":15,"Body":"AAECAwQFBg=="},"SourceTimestamp":"2024-04-21T04:31:30.6908516Z","ServerTimestamp":"2024-04-21T04:30:30.6908516Z"},{"Value":{"Type":3,"Body":42}}],"DiagnosticInfos":[{"AdditionalInfo":"Hello World","InnerStatusCode":2148925440,"InnerDiagnosticInfo":{"AdditionalInfo":"Hello World","InnerStatusCode":2150891520}}]}} \ No newline at end of file +{"TypeId":{"Id":632},"Body":{"ResponseHeader":{"Timestamp":"2025-02-06T05:50:42.1388195Z","RequestHandle":42,"ServiceDiagnostics":{"AdditionalInfo":"NodeId not found","InnerStatusCode":2161770496,"InnerDiagnosticInfo":{"AdditionalInfo":"Hello World","InnerStatusCode":2151022592,"InnerDiagnosticInfo":{"AdditionalInfo":"Hello World","InnerStatusCode":2149711872,"InnerDiagnosticInfo":{"AdditionalInfo":"Hello World","InnerStatusCode":2165637120}}}}},"Results":[{"Value":{"Type":12,"Body":"Hello World"},"SourceTimestamp":"2025-02-06T05:51:42.1387675Z","SourcePicoseconds":10,"ServerTimestamp":"2025-02-06T05:50:42.1387675Z","ServerPicoseconds":100},{"Value":{"Type":7,"Body":12345678},"StatusCode":2157772800,"SourceTimestamp":"2025-02-06T05:51:42.1387675Z","ServerTimestamp":"2025-02-06T05:50:42.1387675Z"},{"Value":{"Type":15,"Body":"AAECAwQFBg=="},"SourceTimestamp":"2025-02-06T05:51:42.1387675Z","ServerTimestamp":"2025-02-06T05:50:42.1387675Z"},{"Value":{"Type":3,"Body":42},"SourceTimestamp":"2025-02-06T05:50:42.1387675Z"},{"Value":{"Type":3,"Body":[1,2,3,4,11,22,33,44],"Dimensions":[2,2,2]},"ServerTimestamp":"2025-02-06T05:50:42.1387675Z"}],"DiagnosticInfos":[{"AdditionalInfo":"Hello World","InnerStatusCode":2148925440,"InnerDiagnosticInfo":{"AdditionalInfo":"Hello World","InnerStatusCode":2150891520}}]}} \ No newline at end of file diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Xml/readrequest.xml b/Fuzzing/Encoders/Fuzz/Testcases.Xml/readrequest.xml index 34b7bc8cca..915568f009 100644 --- a/Fuzzing/Encoders/Fuzz/Testcases.Xml/readrequest.xml +++ b/Fuzzing/Encoders/Fuzz/Testcases.Xml/readrequest.xml @@ -1,41 +1,48 @@ - 2024-05-03T12:59:58.4456622Z - 42 - 0 - 0 + 2025-02-06T05:50:42.9259436Z + 422 + 1023 + 10000 - 0 + 1000 Source_0 - i=1000 + i=123 + + 25 + + + + ns=2;i=4444 5 - i=1000 + ns=3;s=RevisionCounter 13 + 1:2 - i=1000 + g=ef982ce2-00b2-4306-8338-beb422db6f3c 4 - i=1000 + ns=2;i=4444 17 - i=1000 + b=QhY3LAs= 24 diff --git a/Fuzzing/Encoders/Fuzz/Testcases.Xml/readresponse.xml b/Fuzzing/Encoders/Fuzz/Testcases.Xml/readresponse.xml index 5d550a5183..8bbf0f8244 100644 --- a/Fuzzing/Encoders/Fuzz/Testcases.Xml/readresponse.xml +++ b/Fuzzing/Encoders/Fuzz/Testcases.Xml/readresponse.xml @@ -1,6 +1,6 @@ - 2024-05-03T13:00:39.3472074Z + 2025-02-06T05:50:43.7080229Z 42 0 @@ -12,7 +12,7 @@ -1 NodeId not found - 2153644032 + 2161770496 -1 @@ -21,7 +21,7 @@ -1 Hello World - 2150891520 + 2151022592 -1 @@ -30,7 +30,7 @@ -1 Hello World - 2150891520 + 2149711872 -1 @@ -39,7 +39,7 @@ -1 Hello World - 2150891520 + 2165637120 @@ -57,9 +57,9 @@ 0 - 2024-05-03T13:01:39.3471774Z + 2025-02-06T05:51:43.7079679Z 10 - 2024-05-03T13:00:39.3471774Z + 2025-02-06T05:50:43.7079679Z 100 @@ -71,9 +71,9 @@ 2157772800 - 2024-05-03T13:01:39.3471774Z + 2025-02-06T05:51:43.7079679Z 0 - 2024-05-03T13:00:39.3471774Z + 2025-02-06T05:50:43.7079679Z 0 @@ -85,9 +85,9 @@ 0 - 2024-05-03T13:01:39.3471774Z + 2025-02-06T05:51:43.7079679Z 0 - 2024-05-03T13:00:39.3471774Z + 2025-02-06T05:50:43.7079679Z 0 @@ -99,11 +99,43 @@ 0 - 2024-05-03T13:00:39.3471774Z + 2025-02-06T05:50:43.7079679Z 0 0001-01-01T00:00:00 0 + + + + + + + 1 + 2 + 3 + 4 + 11 + 22 + 33 + 44 + + + + 2 + 2 + 2 + + + + + + 0 + + 0001-01-01T00:00:00 + 0 + 2025-02-06T05:50:43.7079679Z + 0 + diff --git a/Fuzzing/Encoders/aflfuzz.sh b/Fuzzing/Encoders/aflfuzz.sh index ebf40ee227..621a2ba773 100644 --- a/Fuzzing/Encoders/aflfuzz.sh +++ b/Fuzzing/Encoders/aflfuzz.sh @@ -10,7 +10,12 @@ display_menu() { echo "5. Opc.Ua.JsonEncoder" echo "6. Opc.Ua.XmlDecoder" echo "7. Opc.Ua.XmlEncoder" - echo "8. Exit" + echo "8. ASN.1 Certificate decoder" + echo "9. ASN.1 Certificate chain decoder" + echo "10. ASN.1 Certificate chain decoder with custom blob parser (macOS)" + echo "11. ASN.1 CRL decoder" + echo "12. ASN.1 CRL encoder" + echo "13. Exit" } # Function to execute fuzz-afl PowerShell script based on user choice @@ -44,6 +49,26 @@ execute_powershell_script() { echo "Running afl-fuzz with Opc.Ua.XmlEncoder" pwsh ../scripts/fuzz-afl.ps1 ./Fuzz/Encoders.Fuzz.csproj -i ./Fuzz/Testcases.Xml -x ../dictionaries/xml.dict -fuzztarget AflfuzzXmlEncoder ;; + 8) + echo "Running afl-fuzz with Certificate decoder" + pwsh ../scripts/fuzz-afl.ps1 ./Fuzz/Encoders.Fuzz.csproj -i ./Fuzz/Testcases.Certificates -fuzztarget AflfuzzCertificateDecoder + ;; + 9) + echo "Running afl-fuzz with Certificate chain decoder" + pwsh ../scripts/fuzz-afl.ps1 ./Fuzz/Encoders.Fuzz.csproj -i ./Fuzz/Testcases.Certificates -fuzztarget AflfuzzCertificateChainDecoder + ;; + 10) + echo "Running afl-fuzz with Certificate chain decoder and custom blob parser" + pwsh ../scripts/fuzz-afl.ps1 ./Fuzz/Encoders.Fuzz.csproj -i ./Fuzz/Testcases.Certificates -fuzztarget AflfuzzCertificateChainDecoderCustom + ;; + 11) + echo "Running afl-fuzz with CRL Decoder" + pwsh ../scripts/fuzz-afl.ps1 ./Fuzz/Encoders.Fuzz.csproj -i ./Fuzz/Testcases.CRLs -fuzztarget AflfuzzCRLDecoder + ;; + 12) + echo "Running afl-fuzz with CRL Encoder" + pwsh ../scripts/fuzz-afl.ps1 ./Fuzz/Encoders.Fuzz.csproj -i ./Fuzz/Testcases.CRLs -fuzztarget AflfuzzCRLEncoder + ;; *) echo "Invalid option. Exiting." ;; @@ -53,18 +78,18 @@ execute_powershell_script() { # Main display_menu -read -p "Enter your choice (1-8): " choice +read -p "Enter your choice (1-12): " choice case $choice in - 1|2|3|4|5|6|7) + 1|2|3|4|5|6|7|8|9|10|11|12) execute_powershell_script $choice ;; - 8) + 13) echo "Exiting." break ;; *) - echo "Invalid input. Please enter a number between 1 and 8." + echo "Invalid input. Please enter a number between 1 and 12." ;; esac diff --git a/Fuzzing/Encoders/libfuzz.bat b/Fuzzing/Encoders/libfuzz.bat index 52407f2095..dd82b3d76f 100644 --- a/Fuzzing/Encoders/libfuzz.bat +++ b/Fuzzing/Encoders/libfuzz.bat @@ -4,13 +4,21 @@ cls echo "Select a OPA UA Encoder fuzzing function:" echo "1. Opc.Ua.BinaryDecoder" echo "2. Opc.Ua.BinaryEncoder" -echo "3. Opc.Ua.JsonDecoder" -echo "4. Opc.Ua.JsonEncoder" -echo "5. Opc.Ua.XmlDecoder" -echo "6. Opc.Ua.XmlEncoder" -echo "7. Exit" +echo "3. Opc.Ua.BinaryDecoder Indempotent" +echo "4. Opc.Ua.JsonDecoder" +echo "5. Opc.Ua.JsonEncoder" +echo "6. Opc.Ua.XmlDecoder" +echo "7. Opc.Ua.XmlEncoder" +echo "8. ASN.1 Certificate decoder" +echo "9. ASN.1 Certificate chain decoder" +echo "10. ASN.1 Certificate chain decoder with custom blob parser (macOS)" +echo "11. ASN.1 CRL decoder" +echo "12. ASN.1 CRL encoder" +echo "13. Exit" -set /p choice="Enter your choice (1-7): " +set /p choice="Enter your choice (1-13): " + +echo Choice %choice% if "%choice%"=="1" ( echo "Running libfuzzer with Opc.Ua.BinaryDecoder" @@ -19,22 +27,40 @@ if "%choice%"=="1" ( echo "Running libfuzzer with Opc.Ua.BinaryEncoder" powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzBinaryEncoder -corpus ./Fuzz/Testcases.Binary/ ) else if "%choice%"=="3" ( + echo "Running libfuzzer with Opc.Ua.BinaryEncoder Indempotent" + powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzBinaryEncoderIndempotent -corpus ./Fuzz/Testcases.Binary/ +) else if "%choice%"=="4" ( echo "Running libfuzzer with Opc.Ua.JsonDecoder" powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzJsonDecoder -dict ../dictionaries/json.dict -corpus ./Fuzz/Testcases.Json/ -) else if "%choice%"=="4" ( +) else if "%choice%"=="5" ( echo "Running libfuzzer with Opc.Ua.JsonEncoder" powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzJsonEncoder -dict ../dictionaries/json.dict -corpus ./Fuzz/Testcases.Json/ -) else if "%choice%"=="5" ( +) else if "%choice%"=="6" ( echo "Running libfuzzer with Opc.Ua.XmlDecoder" powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzXmlDecoder -dict ../dictionaries/xml.dict -corpus ./Fuzz/Testcases.Xml/ -) else if "%choice%"=="6" ( +) else if "%choice%"=="7" ( echo "Running libfuzzer with Opc.Ua.XmlEncoder" powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzXmlEncoder -dict ../dictionaries/xml.dict -corpus ./Fuzz/Testcases.Xml/ -) else if "%choice%"=="7" ( +) else if "%choice%"=="8" ( + echo "Running libfuzzer with ASN.1 Certificate decoder" + powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCertificateDecoder -corpus ./Fuzz/Testcases.Certificates/ +) else if "%choice%"=="9" ( + echo "Running libfuzzer with ASN.1 Certificate chain decoder" + powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCertificateChainDecoder -corpus ./Fuzz/Testcases.Certificates/ +) else if "%choice%"=="10" ( + echo "Running libfuzzer with ASN.1 Certificate chain decoder with custom blob parser" + powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCertificateChainDecoderCustom -corpus ./Fuzz/Testcases.Certificates/ +) else if "%choice%"=="11" ( + echo "Running libfuzzer with ASN.1 CRL decoder" + powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCRLDecoder -corpus ./Fuzz/Testcases.CRLs/ +) else if "%choice%"=="12" ( + echo "Running libfuzzer with ASN.1 CRL encoder" + powershell.exe -File ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-windows.exe" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCRLEncoder -corpus ./Fuzz/Testcases.CRLs/ +) else if "%choice%"=="13" ( echo Exiting. exit /b ) else ( - echo Invalid input. Please enter a number between 1 and 7. + echo Invalid input. Please enter a number between 1 and 13. ) echo Done \ No newline at end of file diff --git a/Fuzzing/Encoders/libfuzz.sh b/Fuzzing/Encoders/libfuzz.sh index f4e2a9f47e..1f89a4b3b9 100644 --- a/Fuzzing/Encoders/libfuzz.sh +++ b/Fuzzing/Encoders/libfuzz.sh @@ -5,11 +5,17 @@ display_menu() { echo "Select a OPA UA Encoder fuzzing function:" echo "1. Opc.Ua.BinaryDecoder" echo "2. Opc.Ua.BinaryEncoder" + echo "3. Opc.Ua.BinaryEncoder Indempotent" echo "3. Opc.Ua.JsonDecoder" echo "4. Opc.Ua.JsonEncoder" echo "5. Opc.Ua.XmlDecoder" echo "6. Opc.Ua.XmlEncoder" - echo "7. Exit" + echo "8. ASN.1 Certificate decoder" + echo "9. ASN.1 Certificate chain decoder" + echo "10. ASN.1 Certificate chain decoder with custom blob parser (macOS)" + echo "11. ASN.1 CRL decoder" + echo "12. ASN.1 CRL encoder" + echo "13. Exit" } # Function to execute fuzz-afl PowerShell script based on user choice @@ -24,21 +30,45 @@ execute_powershell_script() { pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzBinaryEncoder -corpus ./Fuzz/Testcases.Binary/ ;; 3) + echo "Running libfuzzer with Opc.Ua.BinaryEncoder" + pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzBinaryEncoderIndempotent -corpus ./Fuzz/Testcases.Binary/ + ;; + 4) echo "Running libfuzzer with Opc.Ua.JsonDecoder" pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzJsonDecoder -dict ../dictionaries/json.dict -corpus ./Fuzz/Testcases.Json/ ;; - 4) + 5) echo "Running libfuzzer with Opc.Ua.JsonEncoder" pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzJsonEncoder -dict ../dictionaries/json.dict -corpus ./Fuzz/Testcases.Json/ ;; - 5) + 6) echo "Running libfuzzer with Opc.Ua.XmlDecoder" pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzXmlDecoder -dict ../dictionaries/xml.dict -corpus ./Fuzz/Testcases.Xml/ ;; - 6) + 7) echo "Running libfuzzer with Opc.Ua.XmlEncoder" pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzXmlEncoder -dict ../dictionaries/xml.dict -corpus ./Fuzz/Testcases.Xml/ ;; + 8) + echo "Running libfuzzer with ASN.1 Certificate decoder" + pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCertificateDecoder -corpus ./Fuzz/Testcases.Certificates/ + ;; + 9) + echo "Running libfuzzer with ASN.1 Certificate chain decoder" + pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCertificateChainDecoder -corpus ./Fuzz/Testcases.Certificates/ + ;; + 10) + echo "Running libfuzzer with ASN.1 Certificate chain decoder with custom blob parser (macOS)" + pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCertificateChainDecoderCustom -corpus ./Fuzz/Testcases.Certificates/ + ;; + 11) + echo "Running libfuzzer with ASN.1 CRL decoder" + pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCRLDecoder -corpus ./Fuzz/Testcases.CRLs/ + ;; + 12) + echo "Running libfuzzer with ASN.1 CRL encoder" + pwsh ../scripts/fuzz-libfuzzer.ps1 -libFuzzer "./libfuzzer-dotnet-ubuntu" -project ./Fuzz/Encoders.Fuzz.csproj -fuzztarget LibfuzzCRLEncoder -corpus ./Fuzz/Testcases.CRLs/ + ;; *) echo "Invalid option. Exiting." ;; @@ -48,18 +78,18 @@ execute_powershell_script() { # Main display_menu -read -p "Enter your choice (1-5): " choice +read -p "Enter your choice (1-12): " choice case $choice in - 1|2|3|4|5|6) + 1|2|3|4|5|6|7|8|9|10|11|12) execute_powershell_script $choice ;; - 7) + 13) echo "Exiting." break ;; *) - echo "Invalid input. Please enter a number between 1 and 5." + echo "Invalid input. Please enter a number between 1 and 13." ;; esac diff --git a/Fuzzing/common/Fuzz.Tools/Testcases.cs b/Fuzzing/common/Fuzz.Tools/Testcases.cs index 137b2ec2a9..4065eb7f7c 100644 --- a/Fuzzing/common/Fuzz.Tools/Testcases.cs +++ b/Fuzzing/common/Fuzz.Tools/Testcases.cs @@ -43,35 +43,48 @@ public partial class Testcases public static void ReadRequest(IEncoder encoder) { - var nodeId = new NodeId(1000); + var twoByteNodeIdNumeric = new NodeId(123); + var nodeIdNumeric = new NodeId(4444, 2); + var nodeIdString = new NodeId("ns=3;s=RevisionCounter"); + var nodeIdGuid = new NodeId(Guid.NewGuid()); + var nodeIdOpaque = new NodeId(new byte[] { 66, 22, 55, 44, 11 }); var readRequest = new ReadRequest { RequestHeader = new RequestHeader { Timestamp = DateTime.UtcNow, - RequestHandle = 42, + TimeoutHint = 10000, + RequestHandle = 422, AdditionalHeader = new ExtensionObject(), + ReturnDiagnostics = (uint)DiagnosticsMasks.All, }, NodesToRead = new ReadValueIdCollection { new ReadValueId { - NodeId = nodeId, + NodeId = twoByteNodeIdNumeric, + AttributeId = Attributes.UserRolePermissions, + }, + new ReadValueId { + NodeId = nodeIdNumeric, AttributeId = Attributes.Description, }, new ReadValueId { - NodeId = nodeId, + NodeId = nodeIdString, AttributeId = Attributes.Value, + IndexRange = "1:2", }, new ReadValueId { - NodeId = nodeId, + NodeId = nodeIdGuid, AttributeId = Attributes.DisplayName, }, new ReadValueId { - NodeId = nodeId, + NodeId = nodeIdNumeric, AttributeId = Attributes.AccessLevel, }, new ReadValueId { - NodeId = nodeId, + NodeId = nodeIdOpaque, AttributeId = Attributes.RolePermissions, }, }, + MaxAge = 1000, + TimestampsToReturn = TimestampsToReturn.Source, }; encoder.EncodeMessage(readRequest); } @@ -80,6 +93,7 @@ public static void ReadResponse(IEncoder encoder) { var now = DateTime.UtcNow; var nodeId = new NodeId(1000); + var matrix = new byte[2, 2, 2] { { { 1, 2 }, { 3, 4 } }, { { 11, 22 }, { 33, 44 } } }; var readRequest = new ReadResponse { Results = new DataValueCollection { new DataValue { @@ -106,6 +120,11 @@ public static void ReadResponse(IEncoder encoder) Value = new Variant((byte)42), SourceTimestamp = now, }, + new DataValue { + Value = new Variant(new Matrix(matrix, BuiltInType.Byte)), + ServerTimestamp = now, + }, + }, DiagnosticInfos = new DiagnosticInfoCollection { new DiagnosticInfo { @@ -123,16 +142,16 @@ public static void ReadResponse(IEncoder encoder) ServiceResult = StatusCodes.Good, ServiceDiagnostics = new DiagnosticInfo { AdditionalInfo = "NodeId not found", - InnerStatusCode = StatusCodes.BadNodeIdExists, + InnerStatusCode = StatusCodes.BadAggregateConfigurationRejected, InnerDiagnosticInfo = new DiagnosticInfo { AdditionalInfo = "Hello World", - InnerStatusCode = StatusCodes.BadNodeIdUnknown, + InnerStatusCode = StatusCodes.BadIndexRangeInvalid, InnerDiagnosticInfo = new DiagnosticInfo { AdditionalInfo = "Hello World", - InnerStatusCode = StatusCodes.BadNodeIdUnknown, + InnerStatusCode = StatusCodes.BadSecureChannelIdInvalid, InnerDiagnosticInfo = new DiagnosticInfo { AdditionalInfo = "Hello World", - InnerStatusCode = StatusCodes.BadNodeIdUnknown, + InnerStatusCode = StatusCodes.BadAlreadyExists, }, }, }, diff --git a/Libraries/Opc.Ua.Security.Certificates/Properties/AssemblyInfo.cs b/Libraries/Opc.Ua.Security.Certificates/Properties/AssemblyInfo.cs index 30b5ad22b5..fc2c918461 100644 --- a/Libraries/Opc.Ua.Security.Certificates/Properties/AssemblyInfo.cs +++ b/Libraries/Opc.Ua.Security.Certificates/Properties/AssemblyInfo.cs @@ -46,7 +46,31 @@ "f78357b8745cb6e1334655afce1a9527ac92fc829ff585ea79f007e52ba0f83ead627e3edda40b" + "ec5ae574128fc9342cb57cb8285aa4e5b589c0ebef3be571b5c8f2ab1067f7c880e8f8882a73c8" + "0a12a1ef")] +[assembly: InternalsVisibleTo("Encoders.Fuzz, PublicKey = " + + // OPC Foundation Strong Name Public Key + "0024000004800000940000000602000000240000525341310004000001000100d987b12f068b35" + + "80429f3dde01397508880fc7e62621397618456ca1549aeacfbdb90c62adfe918f05ce3677b390" + + "f78357b8745cb6e1334655afce1a9527ac92fc829ff585ea79f007e52ba0f83ead627e3edda40b" + + "ec5ae574128fc9342cb57cb8285aa4e5b589c0ebef3be571b5c8f2ab1067f7c880e8f8882a73c8" + + "0a12a1ef")] +[assembly: InternalsVisibleTo("Encoders.Fuzz.Tools, PublicKey = " + + // OPC Foundation Strong Name Public Key + "0024000004800000940000000602000000240000525341310004000001000100d987b12f068b35" + + "80429f3dde01397508880fc7e62621397618456ca1549aeacfbdb90c62adfe918f05ce3677b390" + + "f78357b8745cb6e1334655afce1a9527ac92fc829ff585ea79f007e52ba0f83ead627e3edda40b" + + "ec5ae574128fc9342cb57cb8285aa4e5b589c0ebef3be571b5c8f2ab1067f7c880e8f8882a73c8" + + "0a12a1ef")] +[assembly: InternalsVisibleTo("Opc.Ua.Encoders.Fuzz.Tests, PublicKey = " + + // OPC Foundation Strong Name Public Key + "0024000004800000940000000602000000240000525341310004000001000100d987b12f068b35" + + "80429f3dde01397508880fc7e62621397618456ca1549aeacfbdb90c62adfe918f05ce3677b390" + + "f78357b8745cb6e1334655afce1a9527ac92fc829ff585ea79f007e52ba0f83ead627e3edda40b" + + "ec5ae574128fc9342cb57cb8285aa4e5b589c0ebef3be571b5c8f2ab1067f7c880e8f8882a73c8" + + "0a12a1ef")] #else [assembly: InternalsVisibleTo("Opc.Ua.Security.Certificates.Tests")] [assembly: InternalsVisibleTo("Opc.Ua.Core.Tests")] +[assembly: InternalsVisibleTo("Encoders.Fuzz")] +[assembly: InternalsVisibleTo("Encoders.Fuzz.Tools")] +[assembly: InternalsVisibleTo("Opc.Ua.Encoders.Fuzz.Tests")] #endif diff --git a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs index a41431b66f..7283148d48 100644 --- a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs +++ b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs @@ -695,7 +695,7 @@ private ServiceResult GetCertificates( { certificateTypeIds = new NodeId[1] { certificateTypeId }; certificates = new byte[1][]; - certificates[0] = certificateGroup.ApplicationCertificate.Certificate.GetRawCertData(); + certificates[0] = certificateGroup.ApplicationCertificate.Certificate.RawData; } else { diff --git a/Stack/Opc.Ua.Core/Stack/Buffers/ArrayPoolBufferWriter{T}.cs b/Stack/Opc.Ua.Core/Stack/Buffers/ArrayPoolBufferWriter{T}.cs index 7394f4f7bc..59cae11dc8 100644 --- a/Stack/Opc.Ua.Core/Stack/Buffers/ArrayPoolBufferWriter{T}.cs +++ b/Stack/Opc.Ua.Core/Stack/Buffers/ArrayPoolBufferWriter{T}.cs @@ -139,6 +139,7 @@ public Span GetSpan(int sizeHint = 0) throw new ArgumentOutOfRangeException(nameof(sizeHint), $"{nameof(sizeHint)} must be non-negative."); } + int remainingSpace = CheckAndAllocateBuffer(sizeHint); return _currentBuffer.AsSpan(_offset, remainingSpace); } diff --git a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs index 1b49fd94be..0c8ed69ead 100644 --- a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs +++ b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs @@ -2824,12 +2824,12 @@ public static byte[] Append(params byte[][] arrays) /// /// Creates a X509 certificate object from the DER encoded bytes. /// - public static X509Certificate2 ParseCertificateBlob(ReadOnlyMemory certificateData) + public static X509Certificate2 ParseCertificateBlob(ReadOnlyMemory certificateData, bool useAsnParser = false) { // macOS X509Certificate2 constructor throws exception if a certchain is encoded // use AsnParser on macOS to parse for byteblobs, #if !NETFRAMEWORK - bool useAsnParser = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + useAsnParser = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); #endif try { @@ -2858,15 +2858,16 @@ public static X509Certificate2 ParseCertificateBlob(ReadOnlyMemory certifi /// Creates a X509 certificate collection object from the DER encoded bytes. /// /// The certificate data. + /// Whether the ASN.1 library should be used to decode certificate blobs. /// - public static X509Certificate2Collection ParseCertificateChainBlob(ReadOnlyMemory certificateData) + public static X509Certificate2Collection ParseCertificateChainBlob(ReadOnlyMemory certificateData, bool useAsnParser = false) { X509Certificate2Collection certificateChain = new X509Certificate2Collection(); // macOS X509Certificate2 constructor throws exception if a certchain is encoded // use AsnParser on macOS to parse for byteblobs, #if !NETFRAMEWORK - bool useAsnParser = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + useAsnParser = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); #endif int offset = 0; int length = certificateData.Length;