From b92db9fe543c347aa7c78aaa0f8345aef3f54bfe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:50:47 +0000 Subject: [PATCH 1/5] Initial plan From 5fa413070425cbfb9dddad1f19d52c8fb2733289 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:58:18 +0000 Subject: [PATCH 2/5] Fix UTF-8 encoding in ClearSign methods and add tests Co-authored-by: mattosaurus <22458485+mattosaurus@users.noreply.github.com> --- PgpCore.Tests/PgpCore.Tests.csproj | 2 +- .../UnitTests/Sign/SignAsync.String.cs | 34 +++++++++++++++++++ .../UnitTests/Sign/SignSync.String.cs | 34 +++++++++++++++++++ PgpCore.Tests/UnitTests/TestBase.cs | 2 +- PgpCore/PGP.cs | 8 ++--- 5 files changed, 74 insertions(+), 6 deletions(-) diff --git a/PgpCore.Tests/PgpCore.Tests.csproj b/PgpCore.Tests/PgpCore.Tests.csproj index 802c034..b8c77b2 100644 --- a/PgpCore.Tests/PgpCore.Tests.csproj +++ b/PgpCore.Tests/PgpCore.Tests.csproj @@ -1,7 +1,7 @@  - net472;netcoreapp6.0 + net472;net8.0 false diff --git a/PgpCore.Tests/UnitTests/Sign/SignAsync.String.cs b/PgpCore.Tests/UnitTests/Sign/SignAsync.String.cs index 00daa16..37ca11d 100644 --- a/PgpCore.Tests/UnitTests/Sign/SignAsync.String.cs +++ b/PgpCore.Tests/UnitTests/Sign/SignAsync.String.cs @@ -236,5 +236,39 @@ public async Task ClearSignAsync_SignMessageWithHeaders_ShouldSignMessage(KeyTyp // Teardown testFactory.Teardown(); } + + [Theory] + [InlineData(KeyType.Generated)] + [InlineData(KeyType.Known)] + [InlineData(KeyType.KnownGpg)] + public async Task ClearSignAsync_SignMessageWithUtf8Characters_ShouldPreserveUtf8(KeyType keyType) + { + // Arrange + TestFactory testFactory = new TestFactory(); + await testFactory.ArrangeAsync(keyType, FileType.Known); + EncryptionKeys signingKeys = new EncryptionKeys(testFactory.PrivateKey, testFactory.Password); + EncryptionKeys verificationKeys = new EncryptionKeys(testFactory.PublicKey); + PGP pgpSign = new PGP(signingKeys); + PGP pgpVerify = new PGP(verificationKeys); + string utf8Content = "Test with UTF-8: š ž č ć đ ñ ü ö ä € ₹ 中文 日本語 한글"; + + // Act + string signedContent = await pgpSign.ClearSignAsync(utf8Content); + bool verified = await pgpVerify.VerifyClearAsync(signedContent); + + // Assert + using (new AssertionScope()) + { + verified.Should().BeTrue(); + signedContent.Should().Contain(utf8Content); + // Verify that specific UTF-8 characters are preserved + signedContent.Should().Contain("š"); + signedContent.Should().Contain("€"); + signedContent.Should().Contain("中文"); + } + + // Teardown + testFactory.Teardown(); + } } } diff --git a/PgpCore.Tests/UnitTests/Sign/SignSync.String.cs b/PgpCore.Tests/UnitTests/Sign/SignSync.String.cs index 88824ad..48d1f81 100644 --- a/PgpCore.Tests/UnitTests/Sign/SignSync.String.cs +++ b/PgpCore.Tests/UnitTests/Sign/SignSync.String.cs @@ -236,5 +236,39 @@ public void ClearSign_SignMessageWithHeaders_ShouldSignMessage(KeyType keyType) // Teardown testFactory.Teardown(); } + + [Theory] + [InlineData(KeyType.Generated)] + [InlineData(KeyType.Known)] + [InlineData(KeyType.KnownGpg)] + public void ClearSign_SignMessageWithUtf8Characters_ShouldPreserveUtf8(KeyType keyType) + { + // Arrange + TestFactory testFactory = new TestFactory(); + testFactory.Arrange(keyType, FileType.Known); + EncryptionKeys signingKeys = new EncryptionKeys(testFactory.PrivateKey, testFactory.Password); + EncryptionKeys verificationKeys = new EncryptionKeys(testFactory.PublicKey); + PGP pgpSign = new PGP(signingKeys); + PGP pgpVerify = new PGP(verificationKeys); + string utf8Content = "Test with UTF-8: š ž č ć đ ñ ü ö ä € ₹ 中文 日本語 한글"; + + // Act + string signedContent = pgpSign.ClearSign(utf8Content); + bool verified = pgpVerify.VerifyClear(signedContent); + + // Assert + using (new AssertionScope()) + { + verified.Should().BeTrue(); + signedContent.Should().Contain(utf8Content); + // Verify that specific UTF-8 characters are preserved + signedContent.Should().Contain("š"); + signedContent.Should().Contain("€"); + signedContent.Should().Contain("中文"); + } + + // Teardown + testFactory.Teardown(); + } } } diff --git a/PgpCore.Tests/UnitTests/TestBase.cs b/PgpCore.Tests/UnitTests/TestBase.cs index 8b28783..a0f2325 100644 --- a/PgpCore.Tests/UnitTests/TestBase.cs +++ b/PgpCore.Tests/UnitTests/TestBase.cs @@ -14,7 +14,7 @@ public abstract class TestBase #if NETFRAMEWORK public const string VERSION = "BouncyCastle.NET Cryptography (net461) v2.4.0+83ebf4a805"; #else - public const string VERSION = "BouncyCastle.NET Cryptography (net6.0) v2.4.0+83ebf4a805"; + public const string VERSION = "BouncyCastle.NET Cryptography (net8.0) v2.4.0+83ebf4a805"; #endif public const string DEFAULTNAME = "name"; public const string TESTNAME = "Test Name"; diff --git a/PgpCore/PGP.cs b/PgpCore/PGP.cs index 1b3cf1a..a0f58a7 100644 --- a/PgpCore/PGP.cs +++ b/PgpCore/PGP.cs @@ -222,11 +222,11 @@ private async Task OutputClearSignedAsync(Stream inputStream, Stream outputStrea while (streamReader.Peek() >= 0) { string line = await streamReader.ReadLineAsync(); - byte[] lineByteArray = Encoding.ASCII.GetBytes(line); + byte[] lineByteArray = Encoding.UTF8.GetBytes(line); // Does the line end with whitespace? // Trailing white space needs to be removed from the end of the document for a valid signature RFC 4880 Section 7.1 string cleanLine = line.TrimEnd(); - byte[] cleanLineByteArray = Encoding.ASCII.GetBytes(cleanLine); + byte[] cleanLineByteArray = Encoding.UTF8.GetBytes(cleanLine); pgpSignatureGenerator.Update(cleanLineByteArray, 0, cleanLineByteArray.Length); await armoredOutputStream.WriteAsync(lineByteArray, 0, lineByteArray.Length); @@ -273,11 +273,11 @@ private void OutputClearSigned(Stream inputStream, Stream outputStream, IDiction { string line = streamReader.ReadLine(); if (line == null) continue; - byte[] lineByteArray = Encoding.ASCII.GetBytes(line); + byte[] lineByteArray = Encoding.UTF8.GetBytes(line); // Does the line end with whitespace? // Trailing white space needs to be removed from the end of the document for a valid signature RFC 4880 Section 7.1 string cleanLine = line.TrimEnd(); - byte[] cleanLineByteArray = Encoding.ASCII.GetBytes(cleanLine); + byte[] cleanLineByteArray = Encoding.UTF8.GetBytes(cleanLine); pgpSignatureGenerator.Update(cleanLineByteArray, 0, cleanLineByteArray.Length); armoredOutputStream.Write(lineByteArray, 0, lineByteArray.Length); From ac8584c4fcd00c5afff928a824e0d46f06b6cb96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Dec 2025 17:05:41 +0000 Subject: [PATCH 3/5] Fix test version string for compatibility with BouncyCastle Co-authored-by: mattosaurus <22458485+mattosaurus@users.noreply.github.com> --- PgpCore.Tests/UnitTests/TestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PgpCore.Tests/UnitTests/TestBase.cs b/PgpCore.Tests/UnitTests/TestBase.cs index a0f2325..8b28783 100644 --- a/PgpCore.Tests/UnitTests/TestBase.cs +++ b/PgpCore.Tests/UnitTests/TestBase.cs @@ -14,7 +14,7 @@ public abstract class TestBase #if NETFRAMEWORK public const string VERSION = "BouncyCastle.NET Cryptography (net461) v2.4.0+83ebf4a805"; #else - public const string VERSION = "BouncyCastle.NET Cryptography (net8.0) v2.4.0+83ebf4a805"; + public const string VERSION = "BouncyCastle.NET Cryptography (net6.0) v2.4.0+83ebf4a805"; #endif public const string DEFAULTNAME = "name"; public const string TESTNAME = "Test Name"; From 5e10e01ee0eae71472849d06e7a0d1f69ef22c96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 4 Jan 2026 06:38:10 +0000 Subject: [PATCH 4/5] Add UTF-8 encoding tests for file and stream ClearSign methods Co-authored-by: mattosaurus <22458485+mattosaurus@users.noreply.github.com> --- .../UnitTests/Sign/SignAsync.File.cs | 37 ++++++++++++++++++ .../UnitTests/Sign/SignAsync.Stream.cs | 39 +++++++++++++++++++ PgpCore.Tests/UnitTests/Sign/SignSync.File.cs | 37 ++++++++++++++++++ .../UnitTests/Sign/SignSync.Stream.cs | 39 +++++++++++++++++++ 4 files changed, 152 insertions(+) diff --git a/PgpCore.Tests/UnitTests/Sign/SignAsync.File.cs b/PgpCore.Tests/UnitTests/Sign/SignAsync.File.cs index dd76286..a73c8f1 100644 --- a/PgpCore.Tests/UnitTests/Sign/SignAsync.File.cs +++ b/PgpCore.Tests/UnitTests/Sign/SignAsync.File.cs @@ -275,5 +275,42 @@ public async Task ClearSignAsync_SignMessageWithHeaders_ShouldSignMessage(KeyTyp // Teardown testFactory.Teardown(); } + + [Theory] + [InlineData(KeyType.Generated)] + [InlineData(KeyType.Known)] + [InlineData(KeyType.KnownGpg)] + public async Task ClearSignAsync_SignMessageWithUtf8Characters_ShouldPreserveUtf8(KeyType keyType) + { + // Arrange + TestFactory testFactory = new TestFactory(); + await testFactory.ArrangeAsync(keyType); + string utf8Content = "Test with UTF-8: š ž č ć đ ñ ü ö ä € ₹ 中文 日本語 한글"; + File.WriteAllText(testFactory.ContentFileInfo.FullName, utf8Content, Encoding.UTF8); + + EncryptionKeys signingKeys = new EncryptionKeys(testFactory.PrivateKeyFileInfo, testFactory.Password); + EncryptionKeys verificationKeys = new EncryptionKeys(testFactory.PublicKeyFileInfo); + PGP pgpSign = new PGP(signingKeys); + PGP pgpVerify = new PGP(verificationKeys); + + // Act + await pgpSign.ClearSignAsync(testFactory.ContentFileInfo, testFactory.EncryptedContentFileInfo); + bool verified = await pgpVerify.VerifyClearAsync(testFactory.EncryptedContentFileInfo); + string signedContent = File.ReadAllText(testFactory.EncryptedContentFileInfo.FullName, Encoding.UTF8); + + // Assert + using (new AssertionScope()) + { + verified.Should().BeTrue(); + signedContent.Should().Contain(utf8Content); + // Verify that specific UTF-8 characters are preserved + signedContent.Should().Contain("š"); + signedContent.Should().Contain("€"); + signedContent.Should().Contain("中文"); + } + + // Teardown + testFactory.Teardown(); + } } } diff --git a/PgpCore.Tests/UnitTests/Sign/SignAsync.Stream.cs b/PgpCore.Tests/UnitTests/Sign/SignAsync.Stream.cs index b09ea5b..265ee48 100644 --- a/PgpCore.Tests/UnitTests/Sign/SignAsync.Stream.cs +++ b/PgpCore.Tests/UnitTests/Sign/SignAsync.Stream.cs @@ -289,5 +289,44 @@ public async Task ClearSignAsync_SignMessageWithHeaders_ShouldSignMessage(KeyTyp // Teardown testFactory.Teardown(); } + + [Theory] + [InlineData(KeyType.Generated)] + [InlineData(KeyType.Known)] + [InlineData(KeyType.KnownGpg)] + public async Task ClearSignAsync_SignMessageWithUtf8Characters_ShouldPreserveUtf8(KeyType keyType) + { + // Arrange + TestFactory testFactory = new TestFactory(); + await testFactory.ArrangeAsync(keyType); + string utf8Content = "Test with UTF-8: š ž č ć đ ñ ü ö ä € ₹ 中文 日本語 한글"; + File.WriteAllText(testFactory.ContentFileInfo.FullName, utf8Content, Encoding.UTF8); + + EncryptionKeys signingKeys = new EncryptionKeys(testFactory.PrivateKeyStream, testFactory.Password); + EncryptionKeys verificationKeys = new EncryptionKeys(testFactory.PublicKeyStream); + PGP pgpSign = new PGP(signingKeys); + PGP pgpVerify = new PGP(verificationKeys); + + // Act + using (Stream outputFileStream = testFactory.EncryptedContentFileInfo.Create()) + await pgpSign.ClearSignAsync(testFactory.ContentStream, outputFileStream); + + bool verified = await pgpVerify.VerifyClearAsync(testFactory.EncryptedContentStream); + string signedContent = File.ReadAllText(testFactory.EncryptedContentFileInfo.FullName, Encoding.UTF8); + + // Assert + using (new AssertionScope()) + { + verified.Should().BeTrue(); + signedContent.Should().Contain(utf8Content); + // Verify that specific UTF-8 characters are preserved + signedContent.Should().Contain("š"); + signedContent.Should().Contain("€"); + signedContent.Should().Contain("中文"); + } + + // Teardown + testFactory.Teardown(); + } } } diff --git a/PgpCore.Tests/UnitTests/Sign/SignSync.File.cs b/PgpCore.Tests/UnitTests/Sign/SignSync.File.cs index 419e237..384aebb 100644 --- a/PgpCore.Tests/UnitTests/Sign/SignSync.File.cs +++ b/PgpCore.Tests/UnitTests/Sign/SignSync.File.cs @@ -275,5 +275,42 @@ public void ClearSign_SignMessageWithHeaders_ShouldSignMessage(KeyType keyType) // Teardown testFactory.Teardown(); } + + [Theory] + [InlineData(KeyType.Generated)] + [InlineData(KeyType.Known)] + [InlineData(KeyType.KnownGpg)] + public void ClearSign_SignMessageWithUtf8Characters_ShouldPreserveUtf8(KeyType keyType) + { + // Arrange + TestFactory testFactory = new TestFactory(); + testFactory.Arrange(keyType); + string utf8Content = "Test with UTF-8: š ž č ć đ ñ ü ö ä € ₹ 中文 日本語 한글"; + File.WriteAllText(testFactory.ContentFileInfo.FullName, utf8Content, Encoding.UTF8); + + EncryptionKeys signingKeys = new EncryptionKeys(testFactory.PrivateKeyFileInfo, testFactory.Password); + EncryptionKeys verificationKeys = new EncryptionKeys(testFactory.PublicKeyFileInfo); + PGP pgpSign = new PGP(signingKeys); + PGP pgpVerify = new PGP(verificationKeys); + + // Act + pgpSign.ClearSign(testFactory.ContentFileInfo, testFactory.EncryptedContentFileInfo); + bool verified = pgpVerify.VerifyClear(testFactory.EncryptedContentFileInfo); + string signedContent = File.ReadAllText(testFactory.EncryptedContentFileInfo.FullName, Encoding.UTF8); + + // Assert + using (new AssertionScope()) + { + verified.Should().BeTrue(); + signedContent.Should().Contain(utf8Content); + // Verify that specific UTF-8 characters are preserved + signedContent.Should().Contain("š"); + signedContent.Should().Contain("€"); + signedContent.Should().Contain("中文"); + } + + // Teardown + testFactory.Teardown(); + } } } diff --git a/PgpCore.Tests/UnitTests/Sign/SignSync.Stream.cs b/PgpCore.Tests/UnitTests/Sign/SignSync.Stream.cs index fb3be23..b859502 100644 --- a/PgpCore.Tests/UnitTests/Sign/SignSync.Stream.cs +++ b/PgpCore.Tests/UnitTests/Sign/SignSync.Stream.cs @@ -289,5 +289,44 @@ public void ClearSign_SignMessageWithHeaders_ShouldSignMessage(KeyType keyType) // Teardown testFactory.Teardown(); } + + [Theory] + [InlineData(KeyType.Generated)] + [InlineData(KeyType.Known)] + [InlineData(KeyType.KnownGpg)] + public void ClearSign_SignMessageWithUtf8Characters_ShouldPreserveUtf8(KeyType keyType) + { + // Arrange + TestFactory testFactory = new TestFactory(); + testFactory.Arrange(keyType); + string utf8Content = "Test with UTF-8: š ž č ć đ ñ ü ö ä € ₹ 中文 日本語 한글"; + File.WriteAllText(testFactory.ContentFileInfo.FullName, utf8Content, Encoding.UTF8); + + EncryptionKeys signingKeys = new EncryptionKeys(testFactory.PrivateKeyStream, testFactory.Password); + EncryptionKeys verificationKeys = new EncryptionKeys(testFactory.PublicKeyStream); + PGP pgpSign = new PGP(signingKeys); + PGP pgpVerify = new PGP(verificationKeys); + + // Act + using (Stream outputFileStream = testFactory.EncryptedContentFileInfo.Create()) + pgpSign.ClearSign(testFactory.ContentStream, outputFileStream); + + bool verified = pgpVerify.VerifyClear(testFactory.EncryptedContentStream); + string signedContent = File.ReadAllText(testFactory.EncryptedContentFileInfo.FullName, Encoding.UTF8); + + // Assert + using (new AssertionScope()) + { + verified.Should().BeTrue(); + signedContent.Should().Contain(utf8Content); + // Verify that specific UTF-8 characters are preserved + signedContent.Should().Contain("š"); + signedContent.Should().Contain("€"); + signedContent.Should().Contain("中文"); + } + + // Teardown + testFactory.Teardown(); + } } } From 0b7923b9eaa5431e5485ce484e35228846073a22 Mon Sep 17 00:00:00 2001 From: mattosaurus Date: Tue, 6 Jan 2026 07:10:44 +0000 Subject: [PATCH 5/5] Update version --- PgpCore/PgpCore.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PgpCore/PgpCore.csproj b/PgpCore/PgpCore.csproj index f86f58d..ac054b6 100644 --- a/PgpCore/PgpCore.csproj +++ b/PgpCore/PgpCore.csproj @@ -10,10 +10,10 @@ https://github.com/mattosaurus/PgpCore https://github.com/mattosaurus/PgpCore PGP .NET Core - 6.5.3.0 + 6.5.4.0 6.0.0.0 - 6.5.3 - v6.5.3 - Fix missing bytes + 6.5.4 + v6.5.4 - Fix clearsign encoding MIT true true