From fb71ef2193c7859f2c2dba8e0cc41c0e346b5493 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Oct 2024 14:54:26 +0200 Subject: [PATCH 01/73] PGPainless 1.7.1-SNAPSHOT --- CHANGELOG.md | 4 ++-- version.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5d0a314..0f1af4ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog -## 1.7.0-SNAPSHOT +## 1.7.0 - Bump `bcpg-jdk8on` to `1.78.1` - Bump `bcprov-jdk18on` to `1.78.1` - Bump `logback-core` and `logback-classic` to `1.4.14` @@ -27,7 +27,7 @@ SPDX-License-Identifier: CC0-1.0 - Do not choke on LibrePGP OED packets - Supersede `addPassphrase()`/`addDecryptionPassphrase()` methods with more clear `addMessagePassphrase()` - `pgpainless-sop`, `pgpainless-cli` - - Bump `sop-java` to `10.0.0`, implementing [SOP Spec Revision 10](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-10.html) + - Bump `sop-java` to `10.0.1`, implementing [SOP Spec Revision 10](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-10.html) - Change API of `sop.encrypt` to return a `ReadyWithResult` to expose the session key - `decrypt --verify-with`: Fix to not throw `NoSignature` exception (exit code 3) if `VERIFICATIONS` is empty - Separate signature verification operations into `SOPV` interface diff --git a/version.gradle b/version.gradle index 33a389db..33805855 100644 --- a/version.gradle +++ b/version.gradle @@ -4,8 +4,8 @@ allprojects { ext { - shortVersion = '1.7.0' - isSnapshot = false + shortVersion = '1.7.1' + isSnapshot = true pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.78.1' From 52b6d5c3f7d34631fb6433dd78152df1b87870db Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Oct 2024 15:18:49 +0200 Subject: [PATCH 02/73] Fix some minor code issues --- .../src/main/kotlin/org/pgpainless/sop/DecryptImpl.kt | 6 +++--- .../src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt | 2 +- .../src/main/kotlin/org/pgpainless/sop/InlineDetachImpl.kt | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/DecryptImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/DecryptImpl.kt index f6a9fb1d..de2b2b3c 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/DecryptImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/DecryptImpl.kt @@ -92,7 +92,7 @@ class DecryptImpl : Decrypt { } override fun verifyWithCert(cert: InputStream): Decrypt = apply { - KeyReader.readPublicKeys(cert, true)?.let { consumerOptions.addVerificationCerts(it) } + KeyReader.readPublicKeys(cert, true).let { consumerOptions.addVerificationCerts(it) } } override fun withKey(key: InputStream): Decrypt = apply { @@ -107,10 +107,10 @@ class DecryptImpl : Decrypt { } override fun withPassword(password: String): Decrypt = apply { - consumerOptions.addDecryptionPassphrase(Passphrase.fromPassword(password)) + consumerOptions.addMessagePassphrase(Passphrase.fromPassword(password)) password.trimEnd().let { if (it != password) { - consumerOptions.addDecryptionPassphrase(Passphrase.fromPassword(it)) + consumerOptions.addMessagePassphrase(Passphrase.fromPassword(it)) } } } diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt index 83d60aa5..b227561e 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt @@ -136,7 +136,7 @@ class EncryptImpl : Encrypt { } override fun withPassword(password: String): Encrypt = apply { - encryptionOptions.addPassphrase(Passphrase.fromPassword(password)) + encryptionOptions.addMessagePassphrase(Passphrase.fromPassword(password)) } private fun modeToStreamEncoding(mode: EncryptAs): StreamEncoding { diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineDetachImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineDetachImpl.kt index 82414a96..88ca8c54 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineDetachImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineDetachImpl.kt @@ -34,7 +34,7 @@ class InlineDetachImpl : InlineDetach { private val sigOut = ByteArrayOutputStream() - override fun writeTo(messageOutputStream: OutputStream): Signatures { + override fun writeTo(outputStream: OutputStream): Signatures { var pgpIn = OpenPgpInputStream(messageInputStream) if (pgpIn.isNonOpenPgp) { throw SOPGPException.BadData("Data appears to be non-OpenPGP.") @@ -50,7 +50,7 @@ class InlineDetachImpl : InlineDetach { try { signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage( - armorIn, messageOutputStream) + armorIn, outputStream) if (signatures.isEmpty) { throw SOPGPException.BadData( "Data did not contain OpenPGP signatures.") @@ -86,7 +86,7 @@ class InlineDetachImpl : InlineDetach { if (next is PGPLiteralData) { // Write out contents of Literal Data packet val literalIn = (next as PGPLiteralData).dataStream - Streams.pipeAll(literalIn, messageOutputStream) + Streams.pipeAll(literalIn, outputStream) literalIn.close() continue } From e5a0617621daf77d3b0c8a9b002757d3794969a6 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Oct 2024 15:47:00 +0200 Subject: [PATCH 03/73] Fix CLI being spammed by logback by downgrading to logback-core 1.2.13 --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index 33805855..9b763f35 100644 --- a/version.gradle +++ b/version.gradle @@ -11,7 +11,7 @@ allprojects { bouncyCastleVersion = '1.78.1' bouncyPgVersion = bouncyCastleVersion junitVersion = '5.8.2' - logbackVersion = '1.4.14' + logbackVersion = '1.2.13' mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' sopJavaVersion = '10.0.1' From ab6cde3ec6edfab8dbc67a9d9b1fc33fa1597c23 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Oct 2024 16:28:50 +0200 Subject: [PATCH 04/73] Bump sop-java to 10.0.2 --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index 9b763f35..d32d055f 100644 --- a/version.gradle +++ b/version.gradle @@ -14,6 +14,6 @@ allprojects { logbackVersion = '1.2.13' mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' - sopJavaVersion = '10.0.1' + sopJavaVersion = '10.0.2' } } From 60c963fca587616fbd2c0159181f61dc2770de73 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Oct 2024 16:33:47 +0200 Subject: [PATCH 05/73] Document logback spam --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index d32d055f..36bc35bc 100644 --- a/version.gradle +++ b/version.gradle @@ -11,7 +11,7 @@ allprojects { bouncyCastleVersion = '1.78.1' bouncyPgVersion = bouncyCastleVersion junitVersion = '5.8.2' - logbackVersion = '1.2.13' + logbackVersion = '1.2.13' // 1.4+ cause CLI spam :/ mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' sopJavaVersion = '10.0.2' From fac23745b4f659cb8f337be3914fd10cc200c44d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Oct 2024 16:35:52 +0200 Subject: [PATCH 06/73] PGPainless 1.7.1 --- README.md | 2 +- pgpainless-sop/README.md | 4 ++-- version.gradle | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5a9c1896..3ba3b028 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ repositories { } dependencies { - implementation 'org.pgpainless:pgpainless-core:1.7.0' + implementation 'org.pgpainless:pgpainless-core:1.7.1' } ``` diff --git a/pgpainless-sop/README.md b/pgpainless-sop/README.md index 0673c406..0b79941d 100644 --- a/pgpainless-sop/README.md +++ b/pgpainless-sop/README.md @@ -23,7 +23,7 @@ To start using pgpainless-sop in your code, include the following lines in your ... dependencies { ... - implementation "org.pgpainless:pgpainless-sop:1.7.0" + implementation "org.pgpainless:pgpainless-sop:1.7.1" ... } @@ -34,7 +34,7 @@ dependencies { org.pgpainless pgpainless-sop - 1.7.0 + 1.7.1 ... diff --git a/version.gradle b/version.gradle index 36bc35bc..69a8b0e1 100644 --- a/version.gradle +++ b/version.gradle @@ -5,7 +5,7 @@ allprojects { ext { shortVersion = '1.7.1' - isSnapshot = true + isSnapshot = false pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.78.1' From dabafe538f88229e2b12f19751bcbda1a42bbfef Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Oct 2024 16:37:56 +0200 Subject: [PATCH 07/73] PGPainless 1.7.2-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index 69a8b0e1..2fd08f58 100644 --- a/version.gradle +++ b/version.gradle @@ -4,8 +4,8 @@ allprojects { ext { - shortVersion = '1.7.1' - isSnapshot = false + shortVersion = '1.7.2' + isSnapshot = true pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.78.1' From 54569b3b02d60aa0493c70b1cb5cdb56fd754bb8 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Oct 2024 16:46:22 +0200 Subject: [PATCH 08/73] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1af4ca..0b36c66b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog +## 1.7.1 +- Bump `sop-java` to `10.0.2` +- Downgrade `logback-core` and `logback-classic` to `1.2.13` (fix CLI spam) + ## 1.7.0 - Bump `bcpg-jdk8on` to `1.78.1` From d966349032cdee9d46fc511dd62b70d8eec25a69 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 15 Oct 2024 14:45:28 +0200 Subject: [PATCH 09/73] Add test to verify proper functionality of hash algorithm policy overrides for SOP --- .../sop/VerifyLegacySignatureTest.java | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 pgpainless-sop/src/test/java/org/pgpainless/sop/VerifyLegacySignatureTest.java diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/VerifyLegacySignatureTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/VerifyLegacySignatureTest.java new file mode 100644 index 00000000..23fd9840 --- /dev/null +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/VerifyLegacySignatureTest.java @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: 2024 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.sop; + +import org.junit.jupiter.api.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.policy.Policy; +import sop.ByteArrayAndResult; +import sop.Verification; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class VerifyLegacySignatureTest { + + @Test + public void verifyLegacySignature() throws IOException { + // Key generated in 2012 using SHA1 for self sigs + String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "Version: PGPainless\n" + + "Comment: 21FA 6E60 D6C9 B7D1 0EAC 56A2 984B 91CF D303 214C\n" + + "Comment: Legacy \n" + + "\n" + + "lQVYBFDUVsABDADg6AuFsM0JckT7spS/1KNdobaZ1vrOFhGdyXbJ1jUkbMwi+f5o\n" + + "UtQsfeFQRBHeQFfmtt4mo6lE6cAsQFJPFat/ReNxCwCqHi5QbennpbueHJ5N2KVj\n" + + "YrIz6eeTsVKs16gS17zLOMkeBt0TK8+Vu7HHfqLqQ1jNNGujwPydUbO8M431XKeW\n" + + "WhM9ziV9m/20nHYJGIM+aN9AicxtR+khFsNjpRlCMg+8kKUelP2FDWv/5QZwnSXc\n" + + "nMFaCJiH1hx56027AB8PZrUW+ShRhqb0P3EhOt+Gs3IW39rGjc9iQVEWl7745BTZ\n" + + "xEZ4FO84DQQtdKBp510VN8LfiZkO7K9JKOo+vqL4IvSBCJNRVvxDxInShHfVyht7\n" + + "jJJvEC0mxv1Oi8rZ/g9iNd6/Ijthi3svNd3DwNFyMzhrbggynEyWr8nu17Zz0c6C\n" + + "KT7XtFZWOUmio8G14KH6dFRCt7TGRw7mz059ViICMN56Ka5LJaQgGRbT+omY2CQJ\n" + + "q5eSkZMXLndmjtUAEQEAAQAL+QFsyzhLl/oLPs0+63JrTaXPY9s5EpNEkHYTgN29\n" + + "HTUELKdWFBaa7M9sBbCJmiODdEB0mfT+yGW9R6wPCXiaEj8ysMt0QvVzG03Qr6pX\n" + + "kWCmHSuW5ZQHytJjDJMA0T+3K0fQWWFPC/bmX12+1Flw4qI9g6oigub1aF4eJFdV\n" + + "XVq7vhadY9aSIiGtJnX+PqiRIIwPeRDfMjsvwA6H/1dwftltRnbLVr0vnUutRnPv\n" + + "ZGbiOim35bWubLW55Ehycb4T4KyW70Xq0Lljr04/33d7S/SUNHXM/ci2kFDEkJb8\n" + + "N+rssxaVjgPsn9+5wQFDEcrewdMLgaRHSrEf46GvcYMbM8lfnzrDyhYc5+vc24b5\n" + + "85WCVYaYKFrJGEa1vHAMmDwXqDNETtDtaYXZpNsUqvjlG+lU4/p0zeqGfyIDLnzK\n" + + "R5zAmWQkd4aSrgN4F6/7xQ1npnvBq/eZiHJx4sBsPMS10TFPPi3A9jEAiu0eljTq\n" + + "E7eXqDObHD8xSjQ3gm9fBclTUQYA4vVChPT9SgJo2aY5ug+2nLWfv4hgK2LTRNkt\n" + + "exEelaU3vrl83a/HEljg3DZ63P6odRIv0HGRl4YSOEC5CANDcDqjz34p7T6suRU5\n" + + "GzrZHey33joLj9oAGF+2HefmHpvWc8ZzFaS14XiO4m9TMMLZwSokNyhccHl7FSYZ\n" + + "XqxzXD2JnaM+m3XMGRVnASQ2gtmsv8dpXuto+gF/9W1b8kyPp1sjtgup2O4PjiQg\n" + + "1uQMpx6H3OSC8tCH3f9/MvlVTpgtBgD9r5PnN5h6fQd6MQl7UEdTU3jIOcXrr1xh\n" + + "0rQkTQx1WJ29f/tr/sGPb3HgPcpk+x6xctRVpW6yf6OLBP02CnJllBYE73tqIxxa\n" + + "qK+3kDAqIa9n/Ds8SZTH/45JXDFLay5g7kFMpv6dxUUMtdJ8INmcChVPxKeUB5DZ\n" + + "iGMzmCTsR0RxvEIc3ofht7mrMhH361xUZGbIMP6ykZZNlE4FmOW4zBATa8o4V3gl\n" + + "mdbIYopEGPwAuj1gIy0G7fLL0cayEkkF/RI7uep4d2QY87mC+fswbiPWM3mp6/7i\n" + + "e2JLmA2bdDju7SL6X4DMgV8RQakOlQf17JEGA4HrKi3odugiBjdXWv6ZmfcIIPgq\n" + + "ns2Us6wCcr4uqCxEvYj2fUd/q03ui5aglLTqSSuNtnB9yww0EYrj9qjHFIi/ByrF\n" + + "L6DVBrMDJ0BwHY5LkY1OWot4GyjLE43Uqu0ObZhFSMttGQkRxdae0R9+4NPR7Dlw\n" + + "B8+zwytxGRs1NgTy7O+KRl9e3K05bgUXVNsJtBtMZWdhY3kgPGxlZ2FjeUBleGFt\n" + + "cGxlLmNvbT6JAdAEEwECAEQJEJhLkc/TAyFMFiEEIfpuYNbJt9EOrFaimEuRz9MD\n" + + "IUwFglDUVsACngECmwMFFgIDAQAECwkIBwIVAgWJH6AIMQKZAQAAWU0L/jUvlxt0\n" + + "TLLFTcT1tQWvy1MBLJcdiXuoN0/w1Rcz54iSCgWeuNZ5BD6qwCMORmVG1fMuvtCt\n" + + "Lq4NZizE63QfeFE8q22vrNDoZ5pAnjC7KlMMjq1ykQHN7cqH1FgxrS3PrBo1k8/s\n" + + "0P6863Vlso02YYbWluJt4HbnX0vEap4/z05RLBCQyZyiaon5zad5rNd0z1nXfMC8\n" + + "EPRK9MsjBX5/5zhx6RPwCrAlrk5dKZ3Nks6bquTCme8sayBgBHX0Tjeum+3sfwiE\n" + + "Jn2xTYJU6cB7fWYREi9E9z7YrmpVCjDkh8U7p0MLC3dmIYUT3EDL5F0jxTReoX+B\n" + + "7f8HrKUIOyvLlAJs4oxYG/g9QHzVFSAbekwf3Jnwm4Czd6qPx62gI6na11ku64Ua\n" + + "RezZ3NkTInSXi1+Bi7mT4qVcV6Z6vl5YXe8T/Zihcv5/Wp4bNEJ2dHJlhwVAn8Ax\n" + + "Ykl8S2ZVfQ5hN8gWLRW40wnCrbuNUdWI/el9D1arc8AQclXfF8/4kULTq50FWARQ\n" + + "1FbAAQwAv/eK+LYwdkUoGfATB6wcmqaJFrjFIaKYbM1VEWckb4FYc0T1yc9MEq65\n" + + "gz1/PUPt+XwQCa/gP5iCcVuze91ksJVkoeOjy/CQgMD1D1s0IVikVMvOKqdnVa4k\n" + + "SxkLkOvVdzZ5QebDbE5QqfTupyr/SgWarm7TYb4HVFNG5xXVh8+uFMpLe897E+/K\n" + + "mSQMZZ8vdKVvnEm+EOlm0ZzRml4kM8k1LyVxJdoLUJ0t5Ac7B1k/Xq0Fz1Pl3Yjr\n" + + "xahxvz68gTph+uL0IlnxKIt+lI2YKTaZ/QZ6POzif0UHLH4akEoTLjzlzkgNYdiI\n" + + "O3ZekqHViYtlX0brc7TYo3iip1LIvv3NMI7QskA2v9V1NWcf/cPBt0uwJ2wMDDDy\n" + + "bckrrwwsfNn6qFxY3xFo1aexzgpG2C9ZVpIDLMd3F6SUoqrrmAHJLoP0dSYBVujO\n" + + "EAJdPqvLC45KJFgXu6IrBqFrx+WTACJCvgoF8XLLhEba99CwmS8Rc2luS+G3iB8l\n" + + "YQlj5QWXABEBAAEAC/wMe00lhe/f7ZGbIVYun4ahZfnWTyxyI9JPvYh62ZjJSNqD\n" + + "B2IIo/PitLDXObGcpPgQl3wR3sYKT7sOuwZ2ihsFgd38yk8lVktVZwM7SZQGi9VT\n" + + "gu59+eVPV6oaDLmimJ+7YQCNXZj2ewXmDXwe+Aq7ucjCIrtklY7m14Tt4MH1H9z5\n" + + "X3xJw2A4GAiCRvfClV3oJbTJSRPH1Ouch9r3c7uPqm6zPBBmHg4Yr1k2hGNwKa6X\n" + + "IOtJyb8ebzKogJ7n7zo4Cpst01PkdLPnXK3fTEBYjuBQa5F2sSvT89uK3seN3J7W\n" + + "OP05lCcg1k9e4bnD9uGlba0fhsgUhqTEg3za6MNcVezPqRXGXlkWH5gjxbVQHu8B\n" + + "Y8Ix9YvWhCwIA25bSE51bTq2vQuCTaRG5fXVWD8qZ043APcB99c9zW9OvmiJzH47\n" + + "zYk+rB+lByK8/KiaXUqcKjyUniXc9LKda71xb4MwoBuBF9RdCsQvHwFRibdpMd0t\n" + + "a9O7RoTFKPxhUewySoEGANTBWhstEUlsytFMSeNmCmpNR2/mKbuE+n78+zaPCmLF\n" + + "TsLWxil+y3FrJCvffn9k5shtxLADtEvKJKWl/vjXxh9DXzFvMgRPsrETzAkg0zwr\n" + + "+5P8d26x4xcnQaE59RQIhyiJPsT4fXqld+kaKDng0vYkVRGHSIC//NPMPA2KaTdC\n" + + "4EQvEx702dF3/+tIDwXO/kjk6taEEOv0W5nj0aHm+JtEw+X0ja1VvUcDx50Ttwpc\n" + + "LzojtWjFpBNFHLGZyWac4QYA5vx6WsovX9j3YXkYDHbN+r8rCfL/16+z+qEJ/pbw\n" + + "2eevICtB4KLcqXlep4rSLhDJlYphxZfHhsVahwX1ga+fGDB/AuDozJhtfQp7evwN\n" + + "NH5IIAT3o56iBUIO2CywWcdkY+HMo787MbITfvVOdOrGE8hRcCFdkZepaSwfbTRz\n" + + "LZH+jKAU6xOgInuoPVLOOIIlLaTVb6TTRV9BXyRUdele0DqbZIMCwE8P53kFuGuM\n" + + "sRZQ1RNha8H7WU2T2m7QxDl3Bf4+KYQ5AfFPkGZKMQcIJy4CR7hSP9gk3/4B06RM\n" + + "DH3c4rmd50CPpQ5TTA0cGCthOnYVewUgJaxQjKAToX8xCQYFRO59YOc8PMVZ/xgf\n" + + "kGrEkX4tlwECbjoWx2kWT4uZvYmnUzfDdXXr8E+9h2ziEKobF0/b9HQB5BKKLycr\n" + + "KzoTKbV4En1602VltRInAfnjpmQ7VSYV/JyoHJ824d/7O+fLLZkmyibLiSMWPwYu\n" + + "z9rt26lC3cT/HSMrG3L0jjdWH7bYaIkBsAQYAQIAGgWCUNRWwAKeAQKbDAUWAgMB\n" + + "AAQLCQgHAhUCAAoJEJhLkc/TAyFMEn4MAKI6RC+VUJr+p2bMf5Pbfml/iy5QsRBG\n" + + "J1iTyPzu8yJUzHs60y6YckGrIKSFE5x6a6utz/CdtpIlb9e/FJvl82zjxJkFjhre\n" + + "fhHjcu6iIvLCCer6v1XtL4frx6Qoi6TGmlKXWvaLTuRINQFomLwScoHRW1QSQHTE\n" + + "BNUmIo89nRU5PQ8LJBGZWzdkVqVmdbK8ek5ycuolwLUQizbeGIhJo/9IIC2i2RCJ\n" + + "hMVsmbjHB1zdVbwPZuwtCH7ROr4xTLp9Gwq1XcIRYY5am/SyBLgkwKSyrXQs6Zsr\n" + + "2qRd2+ccBF0UYFxvH9JOKmBS6QGwtnAYRqbeeCj8Lx3mgAIv15kGeKd72ezFi0ZT\n" + + "smO3dpb6pSD44BSsdvjZdHENCxYIbBsroDZrZGShygluOCrFjG//PSSbrNE+Bz70\n" + + "imnM2QH/XaS6rpbNPGfrn0Vw5M/ZFT/9PWrEg4ZdCI32ei5uyjYwL7aPAPS3MqkB\n" + + "SV9g8CiU0cX7hiBYYpktcDVU3uRCR4Fkvw==\n" + + "=n8qw\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; + // Sig generated in 2012 using SHA1 + String oldSig = "-----BEGIN PGP MESSAGE-----\n" + + "Version: PGPainless\n" + + "\n" + + "owEB2gEl/pANAwACAZhLkc/TAyFMAcsTYgAAAAAASGVsbG8sIFdvcmxkIYkBswQA\n" + + "AQIAJwkQmEuRz9MDIUwWIQQh+m5g1sm30Q6sVqKYS5HP0wMhTAWCUNRWwAAALxEL\n" + + "/2uhYsTLM8nUnYm2GJB6pkapX1kbQrqfAhK46IjxcPpRdl6CW4cFrG6iFegx4YLE\n" + + "fu44VKG+XGy/RTZXIEJubi9zVyOGGJM9Bwwdcp/eekO16/kJ7BsbkaO+5AG/fNeg\n" + + "bL5C8D2m6jV1seAt/+tRyM9jLkRi9odq8BsGA6ZcthAxh3MUoo1yw3QwwEcFFHg/\n" + + "gBw4ZtL8KIQN1PKDz3sSV4GXPQAiz+/uADZ2lL6mbDEK/gXAK1KevIO3U8ZU9B6l\n" + + "cOF9fJww31SCqFGDq50Lzwz7eySJB1TZ0IoehGDXoQ8JF88uTVfACkBATE0Zx7zg\n" + + "TAYIgPSjWY4TEDZ9YjdxJ0hKTMncxVfZPB+J/mYCpVADYSEhLbUJ1ntjc0s35xJD\n" + + "udLSwUWuboedVdEcaqnfgHoaaV+nKk+6F9y8NO56RK3Bfx5FmKmNZHbhfXO/qRt9\n" + + "H43UktMUD6xWxxJv7mutThOp2aizBeboa5YSJ1mxtkPW0/lyK1jr438ETHUnCeu6\n" + + "Vw==\n" + + "=TtKx\n" + + "-----END PGP MESSAGE-----"; + + SOPImpl sop = new SOPImpl(); + byte[] cert = sop.extractCert().key(KEY.getBytes(StandardCharsets.UTF_8)) + .getBytes(); + ByteArrayAndResult> result = sop.inlineVerify() + .cert(cert) + .data(oldSig.getBytes(StandardCharsets.UTF_8)) + .toByteArrayAndResult(); + + assertFalse(result.getResult().isEmpty()); + + // Adjust data signature hash policy to accept new SHA-1 sigs + PGPainless.getPolicy().setDataSignatureHashAlgorithmPolicy( + Policy.HashAlgorithmPolicy.static2022RevocationSignatureHashAlgorithmPolicy()); + + // Sig generated in 2024 using SHA1 + String newSig = "-----BEGIN PGP MESSAGE-----\n" + + "Version: PGPainless\n" + + "\n" + + "owEB2gEl/pANAwACAZhLkc/TAyFMAcsTYgAAAAAASGVsbG8sIFdvcmxkIYkBswQA\n" + + "AQIAJwWCZw5i2AkQmEuRz9MDIUwWIQQh+m5g1sm30Q6sVqKYS5HP0wMhTAAAhVML\n" + + "+QGH+O2fEJoAY8ZxKz/mosg4it9IeSzMhBvDgZJE8Jc+VGk7EuXL0M8pfHL+Jgmv\n" + + "FMzF3chzzLS7QA4K6hbxO31/M8TNSU12geuzQiBV7Kb1hjpvIObBgEqYsX50ZV8r\n" + + "5DHcr7huABUOH6tCKmCA2OxOvr1QV8X39h856bz3WqqP9HW8kZ6H1Z6d7XWlRMtW\n" + + "mAnSevvOJbb0Z3D97obYqytSLzi2Jyv+w2R9kYzMQff2Rl6Cv4F7zsRrF9JRC0m6\n" + + "X/s+VSNuT2yG0/4F5y8vrxvNkfd8YfM8DM6irJV4yJyVuwIoZnM913XCA4F7Uo4t\n" + + "Z8ER17SY4WOYvdja/7qPcOUjX5n1dDU0W7q2muqnZXREw2JXTULiDl0MET3K4kFu\n" + + "a6FyyMGGQwFpAnZ4gDZKzw06abd95AgHx4QlkD89J7MnUBBV+AGHNAQlCPPEVPQq\n" + + "dWTInYndt4GKCUxVkJeHD6ZPLdxEEvICmEap4FQzhqM8U7weoEsSinoVoc4JmSY9\n" + + "dQ==\n" + + "=XrzP\n" + + "-----END PGP MESSAGE-----"; + result = sop.inlineVerify() + .cert(cert) + .data(newSig.getBytes(StandardCharsets.UTF_8)) + .toByteArrayAndResult(); + + assertFalse(result.getResult().isEmpty()); + } +} From f1610f64256828d7ba9190d73b20bd18d07b1ba1 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 24 Oct 2024 17:41:18 +0200 Subject: [PATCH 10/73] Fix returning proper value for KeyRingInfo.lastModified While porting to kotlin the code was accidentally changed to return the key ring creation time instead of the latest self-sig creation time --- .../src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt index f4305225..484ae18f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt @@ -405,7 +405,7 @@ class KeyRingInfo( .plus(signatures.userIdRevocations.values) .plus(signatures.subkeyBindings.values) .plus(signatures.subkeyRevocations.values) - .maxByOrNull { creationDate } + .maxByOrNull { it.creationTime } /** * Return the creation time of the latest added subkey. * From de4a11352853b6e18146b10a87a928b2984a0fab Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 24 Oct 2024 19:06:56 +0200 Subject: [PATCH 11/73] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b36c66b..2bb0d18f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog +## 1.7.2-SNAPSHOT +- Fix bug in `KeyRingInfo.lastModified` (thanks to @Jerbell, @sosnovsky for reporting) + ## 1.7.1 - Bump `sop-java` to `10.0.2` - Downgrade `logback-core` and `logback-classic` to `1.2.13` (fix CLI spam) From 34e9748d0fbc93770aa55a17fc8e7cfb35031c12 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 31 Oct 2024 14:39:56 +0100 Subject: [PATCH 12/73] Bump sop-java to 10.0.3 --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index 2fd08f58..41052ed8 100644 --- a/version.gradle +++ b/version.gradle @@ -14,6 +14,6 @@ allprojects { logbackVersion = '1.2.13' // 1.4+ cause CLI spam :/ mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' - sopJavaVersion = '10.0.2' + sopJavaVersion = '10.0.3' } } From f6c4ddd288a4d623e66805ce4bd2ba195492281b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 31 Oct 2024 14:44:46 +0100 Subject: [PATCH 13/73] Update changelog --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb0d18f..2816afb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,14 @@ SPDX-License-Identifier: CC0-1.0 ## 1.7.2-SNAPSHOT - Fix bug in `KeyRingInfo.lastModified` (thanks to @Jerbell, @sosnovsky for reporting) +- Bump `sop-java` to `10.0.3` + - allow multiple arguments `--with-key-password` in `revoke-key` command + - Properly pass `--old-key-password` and `--new-key-password` options as indirect arguments in `change-key-password` command ## 1.7.1 - Bump `sop-java` to `10.0.2` - Downgrade `logback-core` and `logback-classic` to `1.2.13` (fix CLI spam) - ## 1.7.0 - Bump `bcpg-jdk8on` to `1.78.1` - Bump `bcprov-jdk18on` to `1.78.1` @@ -42,6 +44,11 @@ SPDX-License-Identifier: CC0-1.0 - Throw `BadData` error when passing KEYS where CERTS are expected. - `armor`: Remove `--label` option +## 1.6.8 +- Bump `sop-java` to `7.0.2` +- SOP `change-key-password`: Fix reading password from indirect parameter instead of erroneously passing filename (fixes #453) +- SOP `revoke-key`: Allow for multiple `--with-key-password` options + ## 1.6.7 - SOP: Fix OOM error when detached-signing large amounts of data (fix #432) - Move `CachingBcPublicKeyDataDecryptorFactory` from `org.bouncycastle` packet to `org.pgpainless.decryption_verification` to avoid package split (partially addresses #428) From df5297a66104a642684406e44e1ec2dfa161b106 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 31 Oct 2024 14:52:45 +0100 Subject: [PATCH 14/73] PGPainless 1.7.2 --- CHANGELOG.md | 2 +- README.md | 2 +- pgpainless-sop/README.md | 4 ++-- version.gradle | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2816afb6..b410a6fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog -## 1.7.2-SNAPSHOT +## 1.7.2 - Fix bug in `KeyRingInfo.lastModified` (thanks to @Jerbell, @sosnovsky for reporting) - Bump `sop-java` to `10.0.3` - allow multiple arguments `--with-key-password` in `revoke-key` command diff --git a/README.md b/README.md index 3ba3b028..71a05ba2 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ repositories { } dependencies { - implementation 'org.pgpainless:pgpainless-core:1.7.1' + implementation 'org.pgpainless:pgpainless-core:1.7.2' } ``` diff --git a/pgpainless-sop/README.md b/pgpainless-sop/README.md index 0b79941d..8696d287 100644 --- a/pgpainless-sop/README.md +++ b/pgpainless-sop/README.md @@ -23,7 +23,7 @@ To start using pgpainless-sop in your code, include the following lines in your ... dependencies { ... - implementation "org.pgpainless:pgpainless-sop:1.7.1" + implementation "org.pgpainless:pgpainless-sop:1.7.2" ... } @@ -34,7 +34,7 @@ dependencies { org.pgpainless pgpainless-sop - 1.7.1 + 1.7.2 ... diff --git a/version.gradle b/version.gradle index 41052ed8..d13aac0a 100644 --- a/version.gradle +++ b/version.gradle @@ -5,7 +5,7 @@ allprojects { ext { shortVersion = '1.7.2' - isSnapshot = true + isSnapshot = false pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.78.1' From e46cbae6b8ef41de15e8df986b0955608a0c2ca7 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 31 Oct 2024 14:54:34 +0100 Subject: [PATCH 15/73] PGPainless 1.7.3-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index d13aac0a..eb32a54b 100644 --- a/version.gradle +++ b/version.gradle @@ -4,8 +4,8 @@ allprojects { ext { - shortVersion = '1.7.2' - isSnapshot = false + shortVersion = '1.7.3' + isSnapshot = true pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.78.1' From 89790a0a94391dec3bf392c8d3124f4903106ac8 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 11 Nov 2024 11:52:56 +0100 Subject: [PATCH 16/73] Fix decryption example in README Fixes #456 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71a05ba2..41d3af6b 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ This behaviour can be modified though using the `Policy` class. DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(encryptedInputStream) .withOptions(new ConsumerOptions() - .addMessagePassphrase(bobSecKeys, secretKeyProtector) + .addDecryptionKey(bobSecKeys, secretKeyProtector) .addVerificationCert(alicePubKeys) ); From b99822f405eaaf9283ea5d5a959fa08d6d318306 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 19 Nov 2024 13:58:54 +0100 Subject: [PATCH 17/73] Ignore certificate signatures of unknown type --- .../decryption_verification/OpenPgpInputStream.java | 9 +++++---- .../exception/SignatureValidationException.java | 9 ++++++++- .../pgpainless/key/util/OpenPgpKeyAttributeUtil.java | 10 +++++++--- .../kotlin/org/pgpainless/algorithm/SignatureType.kt | 3 ++- .../bouncycastle/extensions/PGPSignatureExtensions.kt | 5 +++-- .../signature/consumer/SignatureValidator.kt | 6 ++++-- .../pgpainless/signature/consumer/SignatureVerifier.kt | 5 +++-- .../org/pgpainless/algorithm/SignatureTypeTest.java | 3 +-- 8 files changed, 33 insertions(+), 17 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpInputStream.java index ff020a7b..3522f509 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpInputStream.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpInputStream.java @@ -32,6 +32,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import java.util.NoSuchElementException; import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.openpgp.PGPCompressedData; @@ -208,8 +209,8 @@ public class OpenPgpInputStream extends BufferedInputStream { } try { - SignatureType.valueOf(sigType); - } catch (IllegalArgumentException e) { + SignatureType.requireFromCode(sigType); + } catch (NoSuchElementException e) { return; } @@ -236,8 +237,8 @@ public class OpenPgpInputStream extends BufferedInputStream { if (opsVersion == 3) { int opsSigType = bcpgIn.read(); try { - SignatureType.valueOf(opsSigType); - } catch (IllegalArgumentException e) { + SignatureType.requireFromCode(opsSigType); + } catch (NoSuchElementException e) { return; } int opsHashAlg = bcpgIn.read(); diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/SignatureValidationException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/SignatureValidationException.java index b5b8941d..2141ec5c 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/SignatureValidationException.java +++ b/pgpainless-core/src/main/java/org/pgpainless/exception/SignatureValidationException.java @@ -28,7 +28,14 @@ public class SignatureValidationException extends PGPException { StringBuilder sb = new StringBuilder(); sb.append(rejections.size()).append(" rejected signatures:\n"); for (PGPSignature signature : rejections.keySet()) { - sb.append(SignatureType.valueOf(signature.getSignatureType())).append(' ') + String typeString; + SignatureType type = SignatureType.fromCode(signature.getSignatureType()); + if (type == null) { + typeString = "0x" + Long.toHexString(signature.getSignatureType()); + } else { + typeString = type.toString(); + } + sb.append(typeString).append(' ') .append(signature.getCreationTime()).append(": ") .append(rejections.get(signature).getMessage()).append('\n'); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/OpenPgpKeyAttributeUtil.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/OpenPgpKeyAttributeUtil.java index e97a2d7a..f7a78404 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/util/OpenPgpKeyAttributeUtil.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/OpenPgpKeyAttributeUtil.java @@ -34,7 +34,11 @@ public final class OpenPgpKeyAttributeUtil { continue; } - SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType()); + SignatureType signatureType = SignatureType.fromCode(signature.getSignatureType()); + if (signatureType == null) { + // unknown signature type + continue; + } if (signatureType == SignatureType.POSITIVE_CERTIFICATION || signatureType == SignatureType.GENERIC_CERTIFICATION) { int[] hashAlgos = signature.getHashedSubPackets().getPreferredHashAlgorithms(); @@ -71,8 +75,8 @@ public final class OpenPgpKeyAttributeUtil { continue; } - SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType()); - if (signatureType != SignatureType.POSITIVE_CERTIFICATION + SignatureType signatureType = SignatureType.fromCode(signature.getSignatureType()); + if (signatureType == null || signatureType != SignatureType.POSITIVE_CERTIFICATION && signatureType != SignatureType.GENERIC_CERTIFICATION) { continue; } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt index e6b2299f..be6917df 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt @@ -170,7 +170,8 @@ enum class SignatureType(val code: Int) { @JvmStatic fun isRevocationSignature(signatureType: Int): Boolean { - return isRevocationSignature(valueOf(signatureType)) + val sigType = fromCode(signatureType) + return sigType?.let { isRevocationSignature(it) } ?: false } @JvmStatic diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/extensions/PGPSignatureExtensions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/extensions/PGPSignatureExtensions.kt index df40c461..1393883c 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/extensions/PGPSignatureExtensions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/bouncycastle/extensions/PGPSignatureExtensions.kt @@ -77,7 +77,8 @@ fun PGPSignature.wasIssuedBy(key: PGPPublicKey): Boolean = wasIssuedBy(OpenPgpFi /** Return true, if this signature is a hard revocation. */ val PGPSignature.isHardRevocation get() = - when (SignatureType.requireFromCode(signatureType)) { + when (SignatureType.fromCode(signatureType)) { + null -> false SignatureType.KEY_REVOCATION, SignatureType.SUBKEY_REVOCATION, SignatureType.CERTIFICATION_REVOCATION -> { @@ -104,4 +105,4 @@ val PGPSignature.signatureHashAlgorithm: HashAlgorithm get() = HashAlgorithm.requireFromId(hashAlgorithm) fun PGPSignature.isOfType(type: SignatureType): Boolean = - SignatureType.requireFromCode(signatureType) == type + SignatureType.fromCode(signatureType) == type diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidator.kt index e16ef158..951beb0f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidator.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidator.kt @@ -235,7 +235,8 @@ abstract class SignatureValidator { signature: PGPSignature, policy: Policy ): Policy.HashAlgorithmPolicy { - return when (SignatureType.requireFromCode(signature.signatureType)) { + return when (SignatureType.fromCode(signature.signatureType)) { + null -> policy.certificationSignatureHashAlgorithmPolicy SignatureType.CERTIFICATION_REVOCATION, SignatureType.KEY_REVOCATION, SignatureType.SUBKEY_REVOCATION -> policy.revocationSignatureHashAlgorithmPolicy @@ -598,7 +599,8 @@ abstract class SignatureValidator { if (signatureType.none { signature.isOfType(it) }) { throw SignatureValidationException( "Signature is of type" + - " ${SignatureType.requireFromCode(signature.signatureType)}, " + + " ${SignatureType.fromCode(signature.signatureType) ?: + ("0x" + signature.signatureType.toString(16))}, " + "while only ${signatureType.contentToString()} are allowed here.") } } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureVerifier.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureVerifier.kt index 77793c90..d51b2379 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureVerifier.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureVerifier.kt @@ -59,12 +59,13 @@ class SignatureVerifier { policy: Policy, referenceTime: Date ): Boolean { - val type = SignatureType.requireFromCode(signature.signatureType) + val type = SignatureType.fromCode(signature.signatureType) return when (type) { SignatureType.GENERIC_CERTIFICATION, SignatureType.NO_CERTIFICATION, SignatureType.CASUAL_CERTIFICATION, - SignatureType.POSITIVE_CERTIFICATION -> + SignatureType.POSITIVE_CERTIFICATION, + null -> verifyUserIdCertification( userId, signature, signingKey, keyWithUserId, policy, referenceTime) SignatureType.CERTIFICATION_REVOCATION -> diff --git a/pgpainless-core/src/test/java/org/pgpainless/algorithm/SignatureTypeTest.java b/pgpainless-core/src/test/java/org/pgpainless/algorithm/SignatureTypeTest.java index 1bf78776..7f2bc2a6 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/algorithm/SignatureTypeTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/algorithm/SignatureTypeTest.java @@ -5,7 +5,6 @@ package org.pgpainless.algorithm; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; @@ -31,6 +30,6 @@ public class SignatureTypeTest { assertFalse(SignatureType.isRevocationSignature(SignatureType.STANDALONE.getCode())); assertFalse(SignatureType.isRevocationSignature(SignatureType.TIMESTAMP.getCode())); - assertThrows(IllegalArgumentException.class, () -> SignatureType.isRevocationSignature(-3)); + assertFalse(SignatureType.isRevocationSignature(-3)); } } From fdf49cfafbea21f1d6ae30c2de5f4b152e213e1e Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 19 Nov 2024 13:59:41 +0100 Subject: [PATCH 18/73] Improve error message when no acceptable certificate signature is found Relates to #457 --- .../src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt index 11fe1643..f65bb7bc 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt @@ -71,7 +71,9 @@ abstract class KeyAccessor(protected val info: KeyRingInfo, protected val key: S return info.latestDirectKeySelfSignature } - return info.getCurrentSubkeyBindingSignature(key.subkeyId)!! + return info.getCurrentSubkeyBindingSignature(key.subkeyId) + ?: throw NoSuchElementException( + "Key does not carry acceptable self-signature signature.") } } From 391549a7d6776a1f10b01b69112e5b1dbb410103 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 21 Dec 2024 10:27:06 +0100 Subject: [PATCH 19/73] Revert "Ensure proper compatibility with keys with missing direct-key or certification self-sigs" This reverts commit 620c1fc96a55c04d9607632b6967f66105681e55. --- .../org/pgpainless/key/info/KeyAccessor.kt | 4 - .../org/pgpainless/key/info/KeyRingInfo.kt | 3 - .../pgpainless/key/KeyWithoutSelfSigsTest.kt | 111 ------------------ 3 files changed, 118 deletions(-) delete mode 100644 pgpainless-core/src/test/kotlin/org/pgpainless/key/KeyWithoutSelfSigsTest.kt diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt index f65bb7bc..935c4f48 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt @@ -67,10 +67,6 @@ abstract class KeyAccessor(protected val info: KeyRingInfo, protected val key: S info.getLatestUserIdCertification(userId).let { if (it != null) return it } } - if (info.latestDirectKeySelfSignature != null) { - return info.latestDirectKeySelfSignature - } - return info.getCurrentSubkeyBindingSignature(key.subkeyId) ?: throw NoSuchElementException( "Key does not carry acceptable self-signature signature.") diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt index 484ae18f..ce4fbe56 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt @@ -172,11 +172,8 @@ class KeyRingInfo( primaryUserIdCertification?.let { getKeyExpirationTimeAsDate(it, publicKey) } if (latestDirectKeySelfSignature == null && primaryUserIdCertification == null) { - /* throw NoSuchElementException( "No direct-key signature and no user-id signature found.") - */ - return null } if (directKeyExpirationDate != null && userIdExpirationDate == null) { return directKeyExpirationDate diff --git a/pgpainless-core/src/test/kotlin/org/pgpainless/key/KeyWithoutSelfSigsTest.kt b/pgpainless-core/src/test/kotlin/org/pgpainless/key/KeyWithoutSelfSigsTest.kt deleted file mode 100644 index c7879483..00000000 --- a/pgpainless-core/src/test/kotlin/org/pgpainless/key/KeyWithoutSelfSigsTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.key - -import java.io.ByteArrayOutputStream -import org.bouncycastle.openpgp.PGPPublicKey -import org.bouncycastle.openpgp.PGPPublicKeyRing -import org.bouncycastle.openpgp.PGPSecretKeyRing -import org.bouncycastle.util.io.Streams -import org.junit.jupiter.api.Test -import org.pgpainless.PGPainless -import org.pgpainless.algorithm.KeyFlag -import org.pgpainless.decryption_verification.ConsumerOptions -import org.pgpainless.encryption_signing.EncryptionOptions -import org.pgpainless.encryption_signing.ProducerOptions -import org.pgpainless.encryption_signing.SigningOptions -import org.pgpainless.key.generation.KeySpec -import org.pgpainless.key.generation.type.KeyType -import org.pgpainless.key.generation.type.eddsa_legacy.EdDSALegacyCurve -import org.pgpainless.key.generation.type.xdh_legacy.XDHLegacySpec -import org.pgpainless.key.protection.SecretKeyRingProtector - -class KeyWithoutSelfSigsTest { - - @Test - fun signAndVerify() { - val key = PGPainless.readKeyRing().secretKeyRing(KEY) - val cert = PGPainless.extractCertificate(key!!) - - val ciphertextOut = ByteArrayOutputStream() - val encryptionStream = - PGPainless.encryptAndOrSign() - .onOutputStream(ciphertextOut) - .withOptions( - ProducerOptions.signAndEncrypt( - EncryptionOptions.encryptCommunications().addRecipient(cert), - SigningOptions.get() - .addSignature(SecretKeyRingProtector.unprotectedKeys(), key))) - encryptionStream.write("Hello, World!\n".toByteArray()) - encryptionStream.close() - - val plaintextOut = ByteArrayOutputStream() - val decryptionStream = - PGPainless.decryptAndOrVerify() - .onInputStream(ciphertextOut.toByteArray().inputStream()) - .withOptions( - ConsumerOptions.get() - .addVerificationCert(cert) - .addDecryptionKey(key, SecretKeyRingProtector.unprotectedKeys())) - Streams.pipeAll(decryptionStream, plaintextOut) - decryptionStream.close() - } - - fun generateKey() { - val key = - PGPainless.buildKeyRing() - .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519))) - .addSubkey( - KeySpec.getBuilder( - KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.SIGN_DATA)) - .addSubkey( - KeySpec.getBuilder( - KeyType.XDH_LEGACY(XDHLegacySpec._X25519), - KeyFlag.ENCRYPT_STORAGE, - KeyFlag.ENCRYPT_COMMS)) - .build() - .let { - var cert = PGPainless.extractCertificate(it) - cert = - PGPPublicKeyRing( - buildList { - val iterator = cert.publicKeys - val primaryKey = iterator.next() - add( - PGPPublicKey.removeCertification( - primaryKey, primaryKey.signatures.next())) - while (iterator.hasNext()) { - add(iterator.next()) - } - }) - PGPSecretKeyRing.replacePublicKeys(it, cert) - } - println(PGPainless.asciiArmor(key)) - } - - companion object { - - const val KEY = - "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + - "Version: PGPainless\n" + - "Comment: DA3E CC77 1CD6 46F0 C6C4 4FDA 86A3 7B22 7802 2FC7\n" + - "\n" + - "lFgEZUuWuhYJKwYBBAHaRw8BAQdAuXfarON/+UG1qwhVy4/VCYuEb9iLFLb8KGQt\n" + - "KfX4Se0AAQDgqGHsb2M43F+6wK5Hla+oZzFkTUsBx8HMpRx2yeQT6hFAnFgEZUuW\n" + - "uhYJKwYBBAHaRw8BAQdAx0OHISLtekltdUVGGrG/Gs3asc/jG/nqCkBEZ5uyELwA\n" + - "AP0faf8bprP3fj248/NacfynKEVnjzc1gocfhGiWrnVgAxC1iNUEGBYKAH0FAmVL\n" + - "lroCngECmwIFFgIDAQAECwkIBwUVCgkIC18gBBkWCgAGBQJlS5a6AAoJED9gFx9r\n" + - "B25syqoA/0JR3Zcs6fHQ0jW7+u6330SD5h8WvG78IKsE6AfChBLXAP4hlXGidztq\n" + - "5sOHEQvXD2KPCHEJ6MuQ+rbNSSf0fQhgDwAKCRCGo3sieAIvxzmIAP9+9vRoevUM\n" + - "luQhZzQ7DgYqTCyNkeq2cpVgOfa0lyVDgwEApwrd5DlU3GorGHAQHFS6jhw1IOoG\n" + - "FGQ3zpWaOXd7XwKcXQRlS5a6EgorBgEEAZdVAQUBAQdAZIY7ISyNzp0oMoK0dgb8\n" + - "dX6t/i4Uh+l0jnxM0Z1dEB8DAQgHAAD/fhL5dzdJQ7hFhr78AmDEZKFE4txZFPvd\n" + - "ZVFvIWTthFgQ5Ih1BBgWCgAdBQJlS5a6Ap4BApsMBRYCAwEABAsJCAcFFQoJCAsA\n" + - "CgkQhqN7IngCL8cIGgEAzydjTfKvdrTvzXXu97j8TAoOxk89QnLqsM6BU0VsVmkA\n" + - "/1IzH+PXgPPW9ff+elxTi2NWmK+P033P6i5b5Jdf41YD\n" + - "=GBVS\n" + - "-----END PGP PRIVATE KEY BLOCK-----" - } -} From 3e96af54508863e2fb9d3095fa6e5febce9b9fe9 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 21 Dec 2024 11:14:37 +0100 Subject: [PATCH 20/73] Remove dependency on deprecated com.ginsberg:junit5-system-exit library --- pgpainless-cli/build.gradle | 2 - .../java/org/pgpainless/cli/ExitCodeTest.java | 33 +++--- .../RoundTripEncryptDecryptCmdTest.java | 2 - .../RoundTripInlineSignVerifyCmdTest.java | 101 ++++-------------- .../misc/SignUsingPublicKeyBehaviorTest.java | 73 +++---------- 5 files changed, 56 insertions(+), 155 deletions(-) diff --git a/pgpainless-cli/build.gradle b/pgpainless-cli/build.gradle index 3d9a6a09..006fae7c 100644 --- a/pgpainless-cli/build.gradle +++ b/pgpainless-cli/build.gradle @@ -32,8 +32,6 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" - // https://todd.ginsberg.com/post/testing-system-exit/ - testImplementation 'com.ginsberg:junit5-system-exit:1.1.2' // implementation "ch.qos.logback:logback-core:1.2.6" // We want logback logging in tests and in the app diff --git a/pgpainless-cli/src/test/java/org/pgpainless/cli/ExitCodeTest.java b/pgpainless-cli/src/test/java/org/pgpainless/cli/ExitCodeTest.java index 07c9bf68..b1ca143a 100644 --- a/pgpainless-cli/src/test/java/org/pgpainless/cli/ExitCodeTest.java +++ b/pgpainless-cli/src/test/java/org/pgpainless/cli/ExitCodeTest.java @@ -4,28 +4,35 @@ package org.pgpainless.cli; -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import com.ginsberg.junit.exit.FailOnSystemExit; import org.junit.jupiter.api.Test; +import org.pgpainless.cli.commands.CLITest; +import org.slf4j.LoggerFactory; import sop.exception.SOPGPException; -public class ExitCodeTest { +import java.io.IOException; - @Test - @ExpectSystemExitWithStatus(SOPGPException.UnsupportedSubcommand.EXIT_CODE) - public void testUnknownCommand_69() { - PGPainlessCLI.main(new String[] {"generate-kex"}); +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ExitCodeTest extends CLITest { + + public ExitCodeTest() { + super(LoggerFactory.getLogger(ExitCodeTest.class)); } @Test - @ExpectSystemExitWithStatus(SOPGPException.UnsupportedOption.EXIT_CODE) - public void testCommandWithUnknownOption_37() { - PGPainlessCLI.main(new String[] {"generate-key", "-k", "\"k is unknown\""}); + public void testUnknownCommand_69() throws IOException { + assertEquals(SOPGPException.UnsupportedSubcommand.EXIT_CODE, + executeCommand("unsupported-subcommand")); } @Test - @FailOnSystemExit - public void successfulExecutionDoesNotTerminateJVM() { - PGPainlessCLI.main(new String[] {"version"}); + public void testCommandWithUnknownOption_37() throws IOException { + assertEquals(SOPGPException.UnsupportedOption.EXIT_CODE, + executeCommand("generate-key", "-k", "\"k is unknown\"")); + } + + @Test + public void successfulExecutionDoesNotTerminateJVM() throws IOException { + assertSuccess(executeCommand("version")); } } diff --git a/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripEncryptDecryptCmdTest.java b/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripEncryptDecryptCmdTest.java index 9969298a..f8d56bc3 100644 --- a/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripEncryptDecryptCmdTest.java +++ b/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripEncryptDecryptCmdTest.java @@ -14,7 +14,6 @@ import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; -import com.ginsberg.junit.exit.FailOnSystemExit; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; @@ -83,7 +82,6 @@ public class RoundTripEncryptDecryptCmdTest extends CLITest { "-----END PGP PUBLIC KEY BLOCK-----"; @Test - @FailOnSystemExit public void encryptAndDecryptAMessage() throws IOException { // Juliets key and cert File julietKeyFile = pipeStdoutToFile("juliet.key"); diff --git a/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripInlineSignVerifyCmdTest.java b/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripInlineSignVerifyCmdTest.java index 82fda430..cf350e66 100644 --- a/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripInlineSignVerifyCmdTest.java +++ b/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripInlineSignVerifyCmdTest.java @@ -4,104 +4,49 @@ package org.pgpainless.cli.commands; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; import java.nio.charset.StandardCharsets; -import com.ginsberg.junit.exit.FailOnSystemExit; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.pgpainless.cli.PGPainlessCLI; -import org.pgpainless.cli.TestUtils; +import org.slf4j.LoggerFactory; -public class RoundTripInlineSignVerifyCmdTest { - private static File tempDir; - private static PrintStream originalSout; +public class RoundTripInlineSignVerifyCmdTest extends CLITest { - @BeforeAll - public static void prepare() throws IOException { - tempDir = TestUtils.createTempDirectory(); + public RoundTripInlineSignVerifyCmdTest() { + super(LoggerFactory.getLogger(RoundTripInlineSignVerifyCmdTest.class)); } @Test - @FailOnSystemExit public void encryptAndDecryptAMessage() throws IOException { - originalSout = System.out; - File sigmundKeyFile = new File(tempDir, "sigmund.key"); - assertTrue(sigmundKeyFile.createNewFile()); - - File sigmundCertFile = new File(tempDir, "sigmund.cert"); - assertTrue(sigmundCertFile.createNewFile()); - - File msgFile = new File(tempDir, "signed.asc"); - assertTrue(msgFile.createNewFile()); - - File passwordFile = new File(tempDir, "password"); - assertTrue(passwordFile.createNewFile()); - // write password file - FileOutputStream passwordOut = new FileOutputStream(passwordFile); - passwordOut.write("sw0rdf1sh".getBytes(StandardCharsets.UTF_8)); - passwordOut.close(); + File password = writeFile("password", "sw0rdf1sh"); // generate key - OutputStream sigmundKeyOut = new FileOutputStream(sigmundKeyFile); - System.setOut(new PrintStream(sigmundKeyOut)); - PGPainlessCLI.execute("generate-key", - "--with-key-password=" + passwordFile.getAbsolutePath(), - "Sigmund Freud "); - sigmundKeyOut.close(); + File sigmundKey = pipeStdoutToFile("sigmund.key"); + assertSuccess(executeCommand("generate-key", "--with-key-password=" + password.getAbsolutePath(), + "Sigmund Freud ")); // extract cert - FileInputStream sigmundKeyIn = new FileInputStream(sigmundKeyFile); - System.setIn(sigmundKeyIn); - OutputStream sigmundCertOut = new FileOutputStream(sigmundCertFile); - System.setOut(new PrintStream(sigmundCertOut)); - PGPainlessCLI.execute("extract-cert"); - sigmundKeyIn.close(); - sigmundCertOut.close(); + File sigmundCert = pipeStdoutToFile("sigmund.cert"); + pipeFileToStdin(sigmundKey); + assertSuccess(executeCommand("extract-cert")); // sign message - String msg = "Hello World!\n"; - ByteArrayInputStream msgIn = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8)); - System.setIn(msgIn); - OutputStream msgAscOut = new FileOutputStream(msgFile); - System.setOut(new PrintStream(msgAscOut)); - PGPainlessCLI.execute("inline-sign", - "--with-key-password=" + passwordFile.getAbsolutePath(), - sigmundKeyFile.getAbsolutePath()); - msgAscOut.close(); + pipeBytesToStdin("Hello, World!\n".getBytes(StandardCharsets.UTF_8)); + File signedMsg = pipeStdoutToFile("signed.asc"); + assertSuccess(executeCommand("inline-sign", "--with-key-password=" + password.getAbsolutePath(), + sigmundKey.getAbsolutePath())); - File verifyFile = new File(tempDir, "verify.txt"); + // verify message + File verifyFile = nonExistentFile("verify.txt"); + pipeFileToStdin(signedMsg); + assertSuccess(executeCommand("inline-verify", "--verifications-out", verifyFile.getAbsolutePath(), + sigmundCert.getAbsolutePath())); - FileInputStream msgAscIn = new FileInputStream(msgFile); - System.setIn(msgAscIn); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - PrintStream pOut = new PrintStream(out); - System.setOut(pOut); - PGPainlessCLI.execute("inline-verify", - "--verifications-out", verifyFile.getAbsolutePath(), - sigmundCertFile.getAbsolutePath()); - msgAscIn.close(); - - assertEquals(msg, out.toString()); - } - - @AfterAll - public static void after() { - System.setOut(originalSout); - // CHECKSTYLE:OFF - System.out.println(tempDir.getAbsolutePath()); - // CHECKSTYLE:ON + String verifications = readStringFromFile(verifyFile); + assertFalse(verifications.trim().isEmpty()); } } diff --git a/pgpainless-cli/src/test/java/org/pgpainless/cli/misc/SignUsingPublicKeyBehaviorTest.java b/pgpainless-cli/src/test/java/org/pgpainless/cli/misc/SignUsingPublicKeyBehaviorTest.java index affe621e..d6065b82 100644 --- a/pgpainless-cli/src/test/java/org/pgpainless/cli/misc/SignUsingPublicKeyBehaviorTest.java +++ b/pgpainless-cli/src/test/java/org/pgpainless/cli/misc/SignUsingPublicKeyBehaviorTest.java @@ -4,28 +4,18 @@ package org.pgpainless.cli.misc; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.nio.charset.StandardCharsets; -import com.ginsberg.junit.exit.ExpectSystemExitWithStatus; -import org.bouncycastle.util.io.Streams; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.pgpainless.cli.PGPainlessCLI; -import org.pgpainless.cli.TestUtils; +import org.pgpainless.cli.commands.CLITest; +import org.slf4j.LoggerFactory; import sop.exception.SOPGPException; -public class SignUsingPublicKeyBehaviorTest { +public class SignUsingPublicKeyBehaviorTest extends CLITest { public static final String KEY_THAT_IS_A_CERT = "" + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + @@ -89,61 +79,24 @@ public class SignUsingPublicKeyBehaviorTest { "=oJQ2\n" + "-----END PGP PUBLIC KEY BLOCK-----"; - - private static File tempDir; - private static PrintStream originalSout; - - @BeforeAll - public static void prepare() throws IOException { - tempDir = TestUtils.createTempDirectory(); + public SignUsingPublicKeyBehaviorTest() { + super(LoggerFactory.getLogger(SignUsingPublicKeyBehaviorTest.class)); } @Test - @ExpectSystemExitWithStatus(SOPGPException.KeyCannotSign.EXIT_CODE) public void testSignatureCreationAndVerification() throws IOException { - originalSout = System.out; - InputStream originalIn = System.in; - // Write alice key to disc - File aliceKeyFile = new File(tempDir, "alice.key"); - assertTrue(aliceKeyFile.createNewFile()); - OutputStream aliceKeyOut = new FileOutputStream(aliceKeyFile); - Streams.pipeAll(new ByteArrayInputStream(KEY_THAT_IS_A_CERT.getBytes(StandardCharsets.UTF_8)), aliceKeyOut); - aliceKeyOut.close(); - - // Write alice pub key to disc - File aliceCertFile = new File(tempDir, "alice.pub"); - assertTrue(aliceCertFile.createNewFile()); - OutputStream aliceCertOut = new FileOutputStream(aliceCertFile); - Streams.pipeAll(new ByteArrayInputStream(KEY_THAT_IS_A_CERT.getBytes(StandardCharsets.UTF_8)), aliceCertOut); - aliceCertOut.close(); + File aliceKeyFile = writeFile("alice.key", KEY_THAT_IS_A_CERT); // Write test data to disc - String data = "If privacy is outlawed, only outlaws will have privacy.\n"; - - File dataFile = new File(tempDir, "data"); - assertTrue(dataFile.createNewFile()); - FileOutputStream dataOut = new FileOutputStream(dataFile); - Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), dataOut); - dataOut.close(); + File dataFile = writeFile("data", "If privacy is outlawed, only outlaws will have privacy.\n"); // Sign test data - FileInputStream dataIn = new FileInputStream(dataFile); - System.setIn(dataIn); - File sigFile = new File(tempDir, "sig.asc"); - assertTrue(sigFile.createNewFile()); - FileOutputStream sigOut = new FileOutputStream(sigFile); - System.setOut(new PrintStream(sigOut)); - PGPainlessCLI.main(new String[] {"sign", "--armor", aliceKeyFile.getAbsolutePath()}); + File sigFile = pipeStdoutToFile("sig.asc"); + pipeFileToStdin(dataFile); + assertEquals(SOPGPException.KeyCannotSign.EXIT_CODE, + executeCommand("sign", "--armor", aliceKeyFile.getAbsolutePath())); - System.setIn(originalIn); - } - - @AfterAll - public static void after() { - System.setOut(originalSout); - // CHECKSTYLE:OFF - System.out.println(tempDir.getAbsolutePath()); - // CHECKSTYLE:ON + assertTrue(readStringFromFile(sigFile).trim().isEmpty()); } } From a43ae437221ef6dd81aeb9dc058a4918019c169d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 21 Dec 2024 12:43:05 +0100 Subject: [PATCH 21/73] Bump logback to 1.5.13 I hope I mitigated logback spam by modifying the logback.xml file and by setting 'slf4j.internal.verbosity' to 'WARN'. See https://github.com/pgpainless/pgpainless/issues/426 for reference --- .../org/pgpainless/cli/PGPainlessCLI.java | 4 ++++ pgpainless-cli/src/main/resources/logback.xml | 19 +------------------ version.gradle | 2 +- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/pgpainless-cli/src/main/java/org/pgpainless/cli/PGPainlessCLI.java b/pgpainless-cli/src/main/java/org/pgpainless/cli/PGPainlessCLI.java index 938bf1aa..7625dd17 100644 --- a/pgpainless-cli/src/main/java/org/pgpainless/cli/PGPainlessCLI.java +++ b/pgpainless-cli/src/main/java/org/pgpainless/cli/PGPainlessCLI.java @@ -14,6 +14,10 @@ import sop.cli.picocli.SopCLI; public class PGPainlessCLI { static { + // Prevent slf4j initialization logging + // https://github.com/qos-ch/slf4j/issues/422#issuecomment-2277280185 + System.setProperty("slf4j.internal.verbosity", "WARN"); + SopCLI.EXECUTABLE_NAME = "pgpainless-cli"; SopCLI.setSopInstance(new SOPImpl()); } diff --git a/pgpainless-cli/src/main/resources/logback.xml b/pgpainless-cli/src/main/resources/logback.xml index 559589ef..8451d6a4 100644 --- a/pgpainless-cli/src/main/resources/logback.xml +++ b/pgpainless-cli/src/main/resources/logback.xml @@ -5,22 +5,5 @@ SPDX-License-Identifier: Apache-2.0 --> - - System.err - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - System.out - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - + \ No newline at end of file diff --git a/version.gradle b/version.gradle index eb32a54b..07c66ec5 100644 --- a/version.gradle +++ b/version.gradle @@ -11,7 +11,7 @@ allprojects { bouncyCastleVersion = '1.78.1' bouncyPgVersion = bouncyCastleVersion junitVersion = '5.8.2' - logbackVersion = '1.2.13' // 1.4+ cause CLI spam :/ + logbackVersion = '1.5.13' mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' sopJavaVersion = '10.0.3' From 501838631815cbeea7a668529d6dee52cb038af8 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 21 Dec 2024 13:24:05 +0100 Subject: [PATCH 22/73] Extract pgpainless-sop-version property via resource filtering --- pgpainless-cli/build.gradle | 19 ------------------- pgpainless-sop/build.gradle | 8 +++++++- .../kotlin/org/pgpainless/sop/VersionImpl.kt | 4 ++-- .../main/resources/pgpainless-sop.properties | 1 + 4 files changed, 10 insertions(+), 22 deletions(-) create mode 100644 pgpainless-sop/src/main/resources/pgpainless-sop.properties diff --git a/pgpainless-cli/build.gradle b/pgpainless-cli/build.gradle index 006fae7c..d6550139 100644 --- a/pgpainless-cli/build.gradle +++ b/pgpainless-cli/build.gradle @@ -6,25 +6,6 @@ plugins { id 'application' id "com.github.johnrengelman.shadow" version "6.1.0" } -def generatedVersionDir = "${buildDir}/generated-version" - -sourceSets { - main { - output.dir(generatedVersionDir, builtBy: 'generateVersionProperties') - } -} - -task generateVersionProperties { - doLast { - def propertiesFile = file "$generatedVersionDir/version.properties" - propertiesFile.parentFile.mkdirs() - propertiesFile.createNewFile() - // Instead of using a Properties object here, we directly write to the file - // since Properties adds a timestamp, ruining reproducibility - propertiesFile.write("version="+rootProject.version.toString()) - } -} -processResources.dependsOn generateVersionProperties dependencies { diff --git a/pgpainless-sop/build.gradle b/pgpainless-sop/build.gradle index 26beec67..aa0f5993 100644 --- a/pgpainless-sop/build.gradle +++ b/pgpainless-sop/build.gradle @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2021 Paul Schaub // // SPDX-License-Identifier: Apache-2.0 - +import org.apache.tools.ant.filters.* plugins { id 'java-library' } @@ -30,6 +30,12 @@ dependencies { implementation "com.google.code.findbugs:jsr305:3.0.2" } +processResources { + filter ReplaceTokens, tokens: [ + "project.version": project.version.toString() + ] +} + test { useJUnitPlatform() environment("test.implementation", "sop.testsuite.pgpainless.PGPainlessSopInstanceFactory") diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt index 7ebdade9..6b7f5968 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt @@ -49,11 +49,11 @@ https://www.bouncycastle.org/java.html""" // See https://stackoverflow.com/a/50119235 return try { val resourceIn: InputStream = - javaClass.getResourceAsStream("/version.properties") + javaClass.getResourceAsStream("/pgpainless-sop.properties") ?: throw IOException("File version.properties not found.") val properties = Properties().apply { load(resourceIn) } - properties.getProperty("version") + properties.getProperty("pgpainless-sop-version") } catch (e: IOException) { "DEVELOPMENT" } diff --git a/pgpainless-sop/src/main/resources/pgpainless-sop.properties b/pgpainless-sop/src/main/resources/pgpainless-sop.properties new file mode 100644 index 00000000..d71e996b --- /dev/null +++ b/pgpainless-sop/src/main/resources/pgpainless-sop.properties @@ -0,0 +1 @@ +pgpainless-sop-version=@project.version@ \ No newline at end of file From 4dbc633d7d1c7ab7d5b4f375b191a63af0f7a124 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 21 Dec 2024 13:41:37 +0100 Subject: [PATCH 23/73] version --extended: Include sop-java version Fixes #454 --- .../kotlin/org/pgpainless/sop/VersionImpl.kt | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt index 6b7f5968..c6761a39 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt @@ -8,6 +8,7 @@ import java.io.IOException import java.io.InputStream import java.util.* import org.bouncycastle.jce.provider.BouncyCastleProvider +import sop.SOP import sop.operation.Version /** Implementation of the `version` operation using PGPainless. */ @@ -32,7 +33,7 @@ https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-stateless-cli-$specVersi Based on pgpainless-core ${getVersion()} https://pgpainless.org - +${formatSopJavaVersion()} Using $bcVersion https://www.bouncycastle.org/java.html""" } @@ -49,8 +50,8 @@ https://www.bouncycastle.org/java.html""" // See https://stackoverflow.com/a/50119235 return try { val resourceIn: InputStream = - javaClass.getResourceAsStream("/pgpainless-sop.properties") - ?: throw IOException("File version.properties not found.") + SOP::class.java.getResourceAsStream("/pgpainless-sop.properties") + ?: throw IOException("File pgpainless-sop.properties not found.") val properties = Properties().apply { load(resourceIn) } properties.getProperty("pgpainless-sop-version") @@ -59,5 +60,29 @@ https://www.bouncycastle.org/java.html""" } } + private fun formatSopJavaVersion(): String { + return getSopJavaVersion()?.let { + """ + + sop-java $it + + """ + .trimIndent() + } + ?: "" + } + + private fun getSopJavaVersion(): String? { + return try { + val resourceIn: InputStream = + javaClass.getResourceAsStream("/sop-java-version.properties") + ?: throw IOException("File sop-java-version.properties not found.") + val properties = Properties().apply { load(resourceIn) } + properties.getProperty("sop-java-version") + } catch (e: IOException) { + null + } + } + override fun isSopSpecImplementationIncomplete(): Boolean = false } From 02330a5aa1aa0353b8a199006c0026fd79dabff5 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 21 Dec 2024 14:32:42 +0100 Subject: [PATCH 24/73] Reproducible file order --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 04b8a0af..29e054c2 100644 --- a/build.gradle +++ b/build.gradle @@ -45,6 +45,7 @@ allprojects { // without this we would generate an empty pgpainless.jar for the project root // https://stackoverflow.com/a/25445035 jar { + reproducibleFileOrder = true onlyIf { !sourceSets.main.allSource.files.isEmpty() } } From 1db59acf0ded75dbf36d520527f0cc33ac2d58b2 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 10 Jan 2025 10:40:56 +0100 Subject: [PATCH 25/73] Add reuse license header to properties file --- pgpainless-sop/src/main/resources/pgpainless-sop.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pgpainless-sop/src/main/resources/pgpainless-sop.properties b/pgpainless-sop/src/main/resources/pgpainless-sop.properties index d71e996b..d0f47796 100644 --- a/pgpainless-sop/src/main/resources/pgpainless-sop.properties +++ b/pgpainless-sop/src/main/resources/pgpainless-sop.properties @@ -1 +1,4 @@ +# SPDX-FileCopyrightText: 2025 Paul Schaub +# +# SPDX-License-Identifier: Apache-2.0 pgpainless-sop-version=@project.version@ \ No newline at end of file From 3af506fedb3117f06da4f1d155dacdd7cdf2cb30 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 10 Jan 2025 10:41:11 +0100 Subject: [PATCH 26/73] Migrate reuse from dep5 to REUSE.toml --- .reuse/dep5 | 86 ------------------------------------------ REUSE.toml | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 86 deletions(-) delete mode 100644 .reuse/dep5 create mode 100644 REUSE.toml diff --git a/.reuse/dep5 b/.reuse/dep5 deleted file mode 100644 index 7703aa47..00000000 --- a/.reuse/dep5 +++ /dev/null @@ -1,86 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: PGPainless -Upstream-Contact: Paul Schaub -Source: https://pgpainless.org - -# Sample paragraph, commented out: -# -# Files: src/* -# Copyright: $YEAR $NAME <$CONTACT> -# License: ... - -# GitBlameIgnore -Files: .git-blame-ignore-revs -Copyright: 2023 Paul Schaub -License: CC0-1.0 - -# Documentation -Files: docs/* -Copyright: 2022 Paul Schaub -License: CC-BY-3.0 - -Files: .readthedocs.yaml -Copyright: 2022 Paul Schaub -License: CC0-1.0 - -# Gradle build tool -Files: gradle* -Copyright: 2015 the original author or authors. -License: Apache-2.0 - -# Editorconfig -Files: .editorconfig -Copyright: Facebook -License: Apache-2.0 - -# PGPainless Logo -Files: assets/repository-open-graph.png -Copyright: 2021 Paul Schaub -License: CC-BY-3.0 - -Files: assets/pgpainless.svg -Copyright: 2021 Paul Schaub -License: CC-BY-3.0 - -Files: assets/logo.png -Copyright: 2022 Paul Schaub -License: CC-BY-3.0 - -Files: assets/test_vectors/* -Copyright: 2018 Paul Schaub -License: CC0-1.0 - -Files: pgpainless-core/src/test/resources/* -Copyright: 2020 Paul Schaub -License: CC0-1.0 - -Files: audit/* -Copyright: 2021 Paul Schaub -License: CC0-1.0 - -# GH Pages -Files: CNAME -Copyright: 2022 Paul Schaub -License: CC0-1.0 - -Files: _config.yml -Copyright: 2022 Paul Schaub -License: CC0-1.0 - -Files: _layouts/* -Copyright: 2022 Paul Schaub , 2017 Steve Smith -License: CC-BY-SA-3.0 - -# Man Pages -Files: pgpainless-cli/rewriteManPages.sh -Copyright: 2022 Paul Schaub -License: Apache-2.0 - -Files: pgpainless-cli/packaging/man/* -Copyright: 2022 Paul Schaub -License: Apache-2.0 - -# Github Issue Templates -Files: .github/ISSUE_TEMPLATE/* -Copyright: 2024 Paul Schaub -License: CC0-1.0 diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 00000000..991b2099 --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,106 @@ +version = 1 +SPDX-PackageName = "PGPainless" +SPDX-PackageSupplier = "Paul Schaub " +SPDX-PackageDownloadLocation = "https://pgpainless.org" + +[[annotations]] +path = ".git-blame-ignore-revs" +precedence = "aggregate" +SPDX-FileCopyrightText = "2023 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" + +[[annotations]] +path = "docs/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2022 Paul Schaub " +SPDX-License-Identifier = "CC-BY-3.0" + +[[annotations]] +path = ".readthedocs.yaml" +precedence = "aggregate" +SPDX-FileCopyrightText = "2022 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" + +[[annotations]] +path = "gradle**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2015 the original author or authors." +SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = ".editorconfig" +precedence = "aggregate" +SPDX-FileCopyrightText = "Facebook" +SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = "assets/repository-open-graph.png" +precedence = "aggregate" +SPDX-FileCopyrightText = "2021 Paul Schaub " +SPDX-License-Identifier = "CC-BY-3.0" + +[[annotations]] +path = "assets/pgpainless.svg" +precedence = "aggregate" +SPDX-FileCopyrightText = "2021 Paul Schaub " +SPDX-License-Identifier = "CC-BY-3.0" + +[[annotations]] +path = "assets/logo.png" +precedence = "aggregate" +SPDX-FileCopyrightText = "2022 Paul Schaub " +SPDX-License-Identifier = "CC-BY-3.0" + +[[annotations]] +path = "assets/test_vectors/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2018 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" + +[[annotations]] +path = "pgpainless-core/src/test/resources/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2020 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" + +[[annotations]] +path = "audit/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2021 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" + +[[annotations]] +path = "CNAME" +precedence = "aggregate" +SPDX-FileCopyrightText = "2022 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" + +[[annotations]] +path = "_config.yml" +precedence = "aggregate" +SPDX-FileCopyrightText = "2022 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" + +[[annotations]] +path = "_layouts/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2022 Paul Schaub , 2017 Steve Smith" +SPDX-License-Identifier = "CC-BY-SA-3.0" + +[[annotations]] +path = "pgpainless-cli/rewriteManPages.sh" +precedence = "aggregate" +SPDX-FileCopyrightText = "2022 Paul Schaub " +SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = "pgpainless-cli/packaging/man/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2022 Paul Schaub " +SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = ".github/ISSUE_TEMPLATE/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" From 588b9d7469a0908d54ac69772540370e9f07038e Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 28 Jan 2025 12:21:46 +0100 Subject: [PATCH 27/73] Add REUSE.toml file to REUSE.toml file --- REUSE.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 991b2099..d250e0be 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -3,6 +3,12 @@ SPDX-PackageName = "PGPainless" SPDX-PackageSupplier = "Paul Schaub " SPDX-PackageDownloadLocation = "https://pgpainless.org" +[[annotations]] +path = "REUSE.toml" +precedence = "aggregate" +SPDX-FileCopyrightText = "2025 Paul Schaub " +SPDX-License-Identifier = "CC0-1.0" + [[annotations]] path = ".git-blame-ignore-revs" precedence = "aggregate" From c5bae9d50df08dd21b5572d600f36ee2152f6c6b Mon Sep 17 00:00:00 2001 From: Eric Duffy Date: Fri, 14 Feb 2025 10:35:15 -0600 Subject: [PATCH 28/73] Fix a typo on signature creation bounds check message --- .../org/pgpainless/signature/consumer/SignatureValidator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidator.kt index 951beb0f..7cc384e1 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidator.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidator.kt @@ -690,7 +690,7 @@ abstract class SignatureValidator { } if (notAfter != null && timestamp > notAfter) { throw SignatureValidationException( - "Signature was made before the latest allowed signature creation time." + + "Signature was made after the latest allowed signature creation time." + " Created: ${timestamp.formatUTC()}," + " latest allowed: ${notAfter.formatUTC()}") } From 8d03810bf360800c85c6a95c8a4de13c5552b160 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 18 Feb 2025 14:25:59 +0100 Subject: [PATCH 29/73] Fix typo in test --- .../pgpainless/decryption_verification/MessageMetadataTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/MessageMetadataTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/MessageMetadataTest.java index 2c29d62a..7c443829 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/MessageMetadataTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/MessageMetadataTest.java @@ -24,7 +24,7 @@ public class MessageMetadataTest { @Test public void processTestMessage_COMP_ENC_ENC_LIT() { // Note: COMP of ENC does not make sense, since ENC is indistinguishable from randomness - // and randomness cannot be encrypted. + // and randomness cannot be compressed. // For the sake of testing though, this is okay. MessageMetadata.Message message = new MessageMetadata.Message(); From 25c720b033059e30c974c0fb9862ab6489707f10 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 22 Feb 2025 13:37:51 +0100 Subject: [PATCH 30/73] SOP inline-sign: Wrap data in LiteralData(text) packet if signing mode is text Fixes #465 --- .../org/pgpainless/sop/InlineSignImpl.kt | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt index 6fdf59a1..ed514a5f 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt @@ -12,6 +12,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.util.io.Streams import org.pgpainless.PGPainless import org.pgpainless.algorithm.DocumentSignatureType +import org.pgpainless.algorithm.StreamEncoding import org.pgpainless.bouncycastle.extensions.openPgpFingerprint import org.pgpainless.encryption_signing.ProducerOptions import org.pgpainless.encryption_signing.SigningOptions @@ -56,11 +57,20 @@ class InlineSignImpl : InlineSign { val producerOptions = ProducerOptions.sign(signingOptions).apply { - if (mode == InlineSignAs.clearsigned) { - setCleartextSigned() - setAsciiArmor(true) // CSF is always armored - } else { - setAsciiArmor(armor) + when (mode) { + InlineSignAs.clearsigned -> { + setCleartextSigned() + setAsciiArmor(true) // CSF is always armored + setEncoding(StreamEncoding.TEXT) + applyCRLFEncoding() + } + InlineSignAs.text -> { + setEncoding(StreamEncoding.TEXT) + applyCRLFEncoding() + } + else -> { + setAsciiArmor(armor) + } } } From cb6dde4e39573afdff28abca917acea98e18abb9 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 22 Feb 2025 15:46:36 +0100 Subject: [PATCH 31/73] SOP: encrypt: Apply CRLF encoding if text encoding is used Fixes #466 --- .../src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt index b227561e..daf23b55 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt @@ -65,6 +65,10 @@ class EncryptImpl : Encrypt { .setAsciiArmor(armor) .setEncoding(modeToStreamEncoding(mode)) + if (modeToStreamEncoding(mode) != StreamEncoding.BINARY) { + options.applyCRLFEncoding() + } + signingKeys.forEach { try { signingOptions!!.addInlineSignature(protector, it, modeToSignatureType(mode)) From bfdbac0f2d71b9a974abbe6a5d41b73f00852cc4 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 22 Feb 2025 17:26:21 +0100 Subject: [PATCH 32/73] Fix test by comparing to CRLF'd plaintext --- .../commands/RoundTripInlineSignInlineVerifyCmdTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripInlineSignInlineVerifyCmdTest.java b/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripInlineSignInlineVerifyCmdTest.java index 0676f213..057cec98 100644 --- a/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripInlineSignInlineVerifyCmdTest.java +++ b/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripInlineSignInlineVerifyCmdTest.java @@ -138,6 +138,10 @@ public class RoundTripInlineSignInlineVerifyCmdTest extends CLITest { "\n" + "There is only one Lord of the Keys, only one who can bend them to his will. And he does not share power."; + private static final String MESSAGE_CRLF = "One does not simply use OpenPGP!\r\n" + + "\r\n" + + "There is only one Lord of the Keys, only one who can bend them to his will. And he does not share power."; + @Test public void createCleartextSignedMessage() throws IOException { File key = writeFile("key.asc", KEY_1); @@ -153,7 +157,7 @@ public class RoundTripInlineSignInlineVerifyCmdTest extends CLITest { String cleartextSigned = ciphertextOut.toString(); assertTrue(cleartextSigned.startsWith("-----BEGIN PGP SIGNED MESSAGE-----\n" + "Hash: ")); - assertTrue(cleartextSigned.contains(MESSAGE)); + assertTrue(cleartextSigned.contains(MESSAGE_CRLF)); assertTrue(cleartextSigned.contains("\n-----BEGIN PGP SIGNATURE-----\n")); assertTrue(cleartextSigned.endsWith("-----END PGP SIGNATURE-----\n")); } @@ -203,7 +207,7 @@ public class RoundTripInlineSignInlineVerifyCmdTest extends CLITest { "--verifications-out", verifications.getAbsolutePath(), cert.getAbsolutePath())); - assertEquals(MESSAGE, plaintextOut.toString()); + assertEquals(MESSAGE_CRLF, plaintextOut.toString()); String verificationString = readStringFromFile(verifications); assertTrue(verificationString.contains(CERT_1_SIGNING_KEY)); } From 74b28afd4a7f44e4213a2530f2801fafe2be7edc Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 5 Mar 2025 14:30:13 +0100 Subject: [PATCH 33/73] SOP: inline-sign, detached-sign: Do not apply compression --- .../src/main/kotlin/org/pgpainless/sop/DetachedSignImpl.kt | 6 +++++- .../src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/DetachedSignImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/DetachedSignImpl.kt index a84ed6ef..19bc782b 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/DetachedSignImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/DetachedSignImpl.kt @@ -11,6 +11,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.util.io.Streams import org.pgpainless.PGPainless +import org.pgpainless.algorithm.CompressionAlgorithm import org.pgpainless.algorithm.DocumentSignatureType import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.bouncycastle.extensions.openPgpFingerprint @@ -57,7 +58,10 @@ class DetachedSignImpl : DetachedSign { val signingStream = PGPainless.encryptAndOrSign() .discardOutput() - .withOptions(ProducerOptions.sign(signingOptions).setAsciiArmor(armor)) + .withOptions( + ProducerOptions.sign(signingOptions) + .setAsciiArmor(armor) + .overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED)) return object : ReadyWithResult() { override fun writeTo(outputStream: OutputStream): SigningResult { diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt index ed514a5f..bd77b553 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/InlineSignImpl.kt @@ -11,6 +11,7 @@ import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.util.io.Streams import org.pgpainless.PGPainless +import org.pgpainless.algorithm.CompressionAlgorithm import org.pgpainless.algorithm.DocumentSignatureType import org.pgpainless.algorithm.StreamEncoding import org.pgpainless.bouncycastle.extensions.openPgpFingerprint @@ -72,6 +73,7 @@ class InlineSignImpl : InlineSign { setAsciiArmor(armor) } } + overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED) } return object : Ready() { From 4185bf0326165cbf52c261851d77cc91232bcc81 Mon Sep 17 00:00:00 2001 From: Bastien JANSEN Date: Tue, 11 Mar 2025 17:57:55 +0100 Subject: [PATCH 34/73] Fix #469 --- .../encryption_signing/CRLFGeneratorStream.kt | 2 +- .../CanonicalizedDataEncryptionTest.java | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/CRLFGeneratorStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/CRLFGeneratorStream.kt index badc8b48..76d747e0 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/CRLFGeneratorStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/CRLFGeneratorStream.kt @@ -37,7 +37,7 @@ class CRLFGeneratorStream(private val crlfOut: OutputStream, encoding: StreamEnc } override fun close() { - if (!isBinary && lastB == 'r'.code) { + if (!isBinary && lastB == '\r'.code) { crlfOut.write('\n'.code) } crlfOut.close() diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CanonicalizedDataEncryptionTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CanonicalizedDataEncryptionTest.java index 36e473ac..f9936db0 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CanonicalizedDataEncryptionTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CanonicalizedDataEncryptionTest.java @@ -13,6 +13,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.stream.Stream; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.CompressionAlgorithmTags; @@ -30,6 +31,9 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.DocumentSignatureType; import org.pgpainless.algorithm.HashAlgorithm; @@ -288,11 +292,9 @@ public class CanonicalizedDataEncryptionTest { } } - @Test - public void resultOfDecryptionIsCRLFEncoded() throws PGPException, IOException { - String before = "Foo\nBar!\n"; - String after = "Foo\r\nBar!\r\n"; - + @ParameterizedTest + @MethodSource("resultOfDecryptionIsCRLFEncodedArguments") + public void resultOfDecryptionIsCRLFEncoded(String before, String after) throws PGPException, IOException { String encrypted = encryptAndSign(before, DocumentSignatureType.BINARY_DOCUMENT, StreamEncoding.TEXT, true); ByteArrayInputStream in = new ByteArrayInputStream(encrypted.getBytes(StandardCharsets.UTF_8)); @@ -309,6 +311,16 @@ public class CanonicalizedDataEncryptionTest { assertArrayEquals(after.getBytes(StandardCharsets.UTF_8), decrypted.toByteArray()); } + private static Stream resultOfDecryptionIsCRLFEncodedArguments() { + return Stream.of( + Arguments.of("foo", "foo"), + Arguments.of("rrr", "rrr"), + Arguments.of("Foo\nBar!\n", "Foo\r\nBar!\r\n"), + Arguments.of("Foo\rBar!\r", "Foo\r\nBar!\r\n"), + Arguments.of("Foo\r\nBar!\r\n", "Foo\r\nBar!\r\n") + ); + } + @Test public void resultOfDecryptionIsNotCRLFEncoded() throws PGPException, IOException { String beforeAndAfter = "Foo\nBar!\n"; From 885442920597ed5e022cce6c5e1c87c4da6fcf06 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 20:53:24 +0100 Subject: [PATCH 35/73] Revert "SOP: encrypt: Apply CRLF encoding if text encoding is used" This reverts commit cb6dde4e39573afdff28abca917acea98e18abb9. --- .../src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt index daf23b55..b227561e 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/EncryptImpl.kt @@ -65,10 +65,6 @@ class EncryptImpl : Encrypt { .setAsciiArmor(armor) .setEncoding(modeToStreamEncoding(mode)) - if (modeToStreamEncoding(mode) != StreamEncoding.BINARY) { - options.applyCRLFEncoding() - } - signingKeys.forEach { try { signingOptions!!.addInlineSignature(protector, it, modeToSignatureType(mode)) From 13c3295e64655ce5528431047ba556f301d0d0f9 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 21:00:26 +0100 Subject: [PATCH 36/73] Bump sop-java to 10.1.0 --- pgpainless-sop/build.gradle | 2 +- .../src/main/kotlin/org/pgpainless/sop/ArmorImpl.kt | 6 ------ .../main/kotlin/org/pgpainless/sop/VersionImpl.kt | 12 ------------ version.gradle | 2 +- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/pgpainless-sop/build.gradle b/pgpainless-sop/build.gradle index aa0f5993..53b499c6 100644 --- a/pgpainless-sop/build.gradle +++ b/pgpainless-sop/build.gradle @@ -22,7 +22,7 @@ dependencies { testImplementation "ch.qos.logback:logback-classic:$logbackVersion" // Depend on "shared" sop-java test suite (fixtures are turned into tests by inheritance inside test sources) - testImplementation(testFixtures("org.pgpainless:sop-java:$sopJavaVersion")) + testImplementation "org.pgpainless:sop-java-testfixtures:$sopJavaVersion" implementation(project(":pgpainless-core")) api "org.pgpainless:sop-java:$sopJavaVersion" diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/ArmorImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/ArmorImpl.kt index bf7e63f1..40ac811d 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/ArmorImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/ArmorImpl.kt @@ -12,7 +12,6 @@ import org.bouncycastle.util.io.Streams import org.pgpainless.decryption_verification.OpenPgpInputStream import org.pgpainless.util.ArmoredOutputStreamFactory import sop.Ready -import sop.enums.ArmorLabel import sop.exception.SOPGPException import sop.operation.Armor @@ -46,9 +45,4 @@ class ArmorImpl : Armor { } } } - - @Deprecated("Setting custom labels is not supported.") - override fun label(label: ArmorLabel): Armor { - throw SOPGPException.UnsupportedOption("Setting custom Armor labels not supported.") - } } diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt index c6761a39..46aa4f14 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt @@ -72,17 +72,5 @@ https://www.bouncycastle.org/java.html""" ?: "" } - private fun getSopJavaVersion(): String? { - return try { - val resourceIn: InputStream = - javaClass.getResourceAsStream("/sop-java-version.properties") - ?: throw IOException("File sop-java-version.properties not found.") - val properties = Properties().apply { load(resourceIn) } - properties.getProperty("sop-java-version") - } catch (e: IOException) { - null - } - } - override fun isSopSpecImplementationIncomplete(): Boolean = false } diff --git a/version.gradle b/version.gradle index 07c66ec5..4d36ca57 100644 --- a/version.gradle +++ b/version.gradle @@ -14,6 +14,6 @@ allprojects { logbackVersion = '1.5.13' mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' - sopJavaVersion = '10.0.3' + sopJavaVersion = '10.1.0' } } From 9a1a01fe050fe14c5a0afcfed6ac54d659b408b3 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 21:00:51 +0100 Subject: [PATCH 37/73] pgpainless-cli version: Fix repository URL --- .../src/main/kotlin/org/pgpainless/sop/VersionImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt index 46aa4f14..94b9c016 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/VersionImpl.kt @@ -26,7 +26,7 @@ class VersionImpl : Version { String.format(Locale.US, "Bouncy Castle %.2f", BouncyCastleProvider().version) val specVersion = String.format("%02d", SOP_VERSION) return """${getName()} ${getVersion()} -https://codeberg.org/PGPainless/pgpainless/src/branch/master/pgpainless-sop +https://codeberg.org/PGPainless/pgpainless/src/branch/main/pgpainless-sop Implementation of the Stateless OpenPGP Protocol Version $specVersion https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-stateless-cli-$specVersion From 883eb80a63381e310bf96390ac72d856266aa8f0 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 22:04:03 +0100 Subject: [PATCH 38/73] Bump bcpg, bcprov to 1.80, add bcutil dependency Adding bcutil as a dependency is apparently required now. See https://github.com/bcgit/bc-java/issues/1977 --- pgpainless-core/build.gradle | 1 + .../CachingBcPublicKeyDataDecryptorFactory.kt | 33 ++++++++++++++++--- .../CustomPublicKeyDataDecryptorFactory.kt | 5 +-- .../HardwareSecurity.kt | 16 ++++++--- ...stomPublicKeyDataDecryptorFactoryTest.java | 4 +-- version.gradle | 2 +- 6 files changed, 47 insertions(+), 14 deletions(-) diff --git a/pgpainless-core/build.gradle b/pgpainless-core/build.gradle index bab6ecf1..64d538d5 100644 --- a/pgpainless-core/build.gradle +++ b/pgpainless-core/build.gradle @@ -22,6 +22,7 @@ dependencies { // Bouncy Castle api "org.bouncycastle:bcprov-jdk18on:$bouncyCastleVersion" api "org.bouncycastle:bcpg-jdk18on:$bouncyPgVersion" + api "org.bouncycastle:bcutil-jdk18on:$bouncyCastleVersion" // api(files("../libs/bcpg-jdk18on-1.70.jar")) // @Nullable, @Nonnull annotations diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CachingBcPublicKeyDataDecryptorFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CachingBcPublicKeyDataDecryptorFactory.kt index 3d07065e..9bafa6da 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CachingBcPublicKeyDataDecryptorFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CachingBcPublicKeyDataDecryptorFactory.kt @@ -4,7 +4,11 @@ package org.pgpainless.decryption_verification +import org.bouncycastle.bcpg.AEADEncDataPacket +import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket import org.bouncycastle.openpgp.PGPPrivateKey +import org.bouncycastle.openpgp.PGPSessionKey +import org.bouncycastle.openpgp.operator.PGPDataDecryptor import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory import org.bouncycastle.util.encoders.Base64 import org.pgpainless.key.SubkeyIdentifier @@ -21,16 +25,34 @@ import org.pgpainless.key.SubkeyIdentifier class CachingBcPublicKeyDataDecryptorFactory( privateKey: PGPPrivateKey, override val subkeyIdentifier: SubkeyIdentifier -) : BcPublicKeyDataDecryptorFactory(privateKey), CustomPublicKeyDataDecryptorFactory { +) : CustomPublicKeyDataDecryptorFactory() { + private val decryptorFactory: BcPublicKeyDataDecryptorFactory = + BcPublicKeyDataDecryptorFactory(privateKey) private val cachedSessions: MutableMap = mutableMapOf() + override fun createDataDecryptor(p0: Boolean, p1: Int, p2: ByteArray?): PGPDataDecryptor { + return decryptorFactory.createDataDecryptor(p0, p1, p2) + } + + override fun createDataDecryptor(p0: AEADEncDataPacket?, p1: PGPSessionKey?): PGPDataDecryptor { + return decryptorFactory.createDataDecryptor(p0, p1) + } + + override fun createDataDecryptor( + p0: SymmetricEncIntegrityPacket?, + p1: PGPSessionKey? + ): PGPDataDecryptor { + return decryptorFactory.createDataDecryptor(p0, p1) + } + override fun recoverSessionData( keyAlgorithm: Int, - secKeyData: Array + secKeyData: Array, + pkeskVersion: Int ): ByteArray = lookupSessionKeyData(secKeyData) - ?: costlyRecoverSessionData(keyAlgorithm, secKeyData).also { + ?: costlyRecoverSessionData(keyAlgorithm, secKeyData, pkeskVersion).also { cacheSessionKeyData(secKeyData, it) } @@ -39,8 +61,9 @@ class CachingBcPublicKeyDataDecryptorFactory( private fun costlyRecoverSessionData( keyAlgorithm: Int, - secKeyData: Array - ): ByteArray = super.recoverSessionData(keyAlgorithm, secKeyData) + secKeyData: Array, + pkeskVersion: Int + ): ByteArray = decryptorFactory.recoverSessionData(keyAlgorithm, secKeyData, pkeskVersion) private fun cacheSessionKeyData(secKeyData: Array, sessionKey: ByteArray) { cachedSessions[toKey(secKeyData)] = sessionKey.clone() diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt index 4a0dbeba..cb6254dc 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt @@ -4,6 +4,7 @@ package org.pgpainless.decryption_verification +import org.bouncycastle.openpgp.operator.AbstractPublicKeyDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.pgpainless.key.SubkeyIdentifier @@ -14,7 +15,7 @@ import org.pgpainless.key.SubkeyIdentifier * * @see [ConsumerOptions.addCustomDecryptorFactory] */ -interface CustomPublicKeyDataDecryptorFactory : PublicKeyDataDecryptorFactory { +abstract class CustomPublicKeyDataDecryptorFactory : AbstractPublicKeyDataDecryptorFactory() { /** * Identifier for the subkey for which this particular [CustomPublicKeyDataDecryptorFactory] is @@ -22,5 +23,5 @@ interface CustomPublicKeyDataDecryptorFactory : PublicKeyDataDecryptorFactory { * * @return subkey identifier */ - val subkeyIdentifier: SubkeyIdentifier + abstract val subkeyIdentifier: SubkeyIdentifier } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt index 1974e290..50ef3e02 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt @@ -29,11 +29,17 @@ class HardwareSecurity { * @param keyId id of the key * @param keyAlgorithm algorithm * @param sessionKeyData encrypted session key + * @param pkeskVersion version of the Public-Key-Encrypted-Session-Key packet (3 or 6) * @return decrypted session key * @throws HardwareSecurityException exception */ @Throws(HardwareSecurityException::class) - fun decryptSessionKey(keyId: Long, keyAlgorithm: Int, sessionKeyData: ByteArray): ByteArray + fun decryptSessionKey( + keyId: Long, + keyAlgorithm: Int, + sessionKeyData: ByteArray, + pkeskVersion: Int + ): ByteArray } /** @@ -44,7 +50,7 @@ class HardwareSecurity { class HardwareDataDecryptorFactory( override val subkeyIdentifier: SubkeyIdentifier, private val callback: DecryptionCallback, - ) : CustomPublicKeyDataDecryptorFactory { + ) : CustomPublicKeyDataDecryptorFactory() { // luckily we can instantiate the BcPublicKeyDataDecryptorFactory with null as argument. private val factory: PublicKeyDataDecryptorFactory = BcPublicKeyDataDecryptorFactory(null) @@ -73,10 +79,12 @@ class HardwareSecurity { override fun recoverSessionData( keyAlgorithm: Int, - secKeyData: Array + secKeyData: Array, + pkeskVersion: Int ): ByteArray { return try { - callback.decryptSessionKey(subkeyIdentifier.subkeyId, keyAlgorithm, secKeyData[0]) + callback.decryptSessionKey( + subkeyIdentifier.subkeyId, keyAlgorithm, secKeyData[0], pkeskVersion) } catch (e: HardwareSecurityException) { throw PGPException("Hardware-backed decryption failed.", e) } diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactoryTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactoryTest.java index 73c3bf56..71fbf9be 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactoryTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactoryTest.java @@ -55,14 +55,14 @@ public class CustomPublicKeyDataDecryptorFactoryTest { HardwareSecurity.DecryptionCallback hardwareDecryptionCallback = new HardwareSecurity.DecryptionCallback() { @Override - public byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData) + public byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData, int pkeskVersion) throws HardwareSecurity.HardwareSecurityException { // Emulate hardware decryption. try { PGPSecretKey decryptionKey = secretKey.getSecretKey(encryptionKey.getKeyID()); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKey, Passphrase.emptyPassphrase()); PublicKeyDataDecryptorFactory internal = new BcPublicKeyDataDecryptorFactory(privateKey); - return internal.recoverSessionData(keyAlgorithm, new byte[][] {sessionKeyData}); + return internal.recoverSessionData(keyAlgorithm, new byte[][] {sessionKeyData}, pkeskVersion); } catch (PGPException e) { throw new HardwareSecurity.HardwareSecurityException(); } diff --git a/version.gradle b/version.gradle index 4d36ca57..25dae7be 100644 --- a/version.gradle +++ b/version.gradle @@ -8,7 +8,7 @@ allprojects { isSnapshot = true pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 - bouncyCastleVersion = '1.78.1' + bouncyCastleVersion = '1.80' bouncyPgVersion = bouncyCastleVersion junitVersion = '5.8.2' logbackVersion = '1.5.13' From d7e08186ac150bcc1c8e4d1d07a98d783390c72a Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 22:20:00 +0100 Subject: [PATCH 39/73] Update CHANGELOG --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b410a6fb..88f39db0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog +## 1.7.3 +- Bump `bcpg-jdk8on` to `1.80` +- Bump `bcprov-jdk18on` to `1.80` +- Add dependency on `bcutil-jdk18on` as a workaround +- Ignore unknown type signatures on certificates +- Fix typo on signature creation bounds check (thanks @elduffy) +- Fix superfluous newline added in CRLF encoding (thanks @bjansen) +- Bump `sop-java` to `1.10.0` + - SOP inline-sign: Do not apply compression + ## 1.7.2 - Fix bug in `KeyRingInfo.lastModified` (thanks to @Jerbell, @sosnovsky for reporting) - Bump `sop-java` to `10.0.3` From 3b1dbf4102961b3f767d9221ffb0cc893db91611 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 22:40:44 +0100 Subject: [PATCH 40/73] PGPainless 1.7.3 --- README.md | 2 +- pgpainless-sop/README.md | 4 ++-- version.gradle | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 41d3af6b..1ae88818 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ repositories { } dependencies { - implementation 'org.pgpainless:pgpainless-core:1.7.2' + implementation 'org.pgpainless:pgpainless-core:1.7.3' } ``` diff --git a/pgpainless-sop/README.md b/pgpainless-sop/README.md index 8696d287..d041bc0e 100644 --- a/pgpainless-sop/README.md +++ b/pgpainless-sop/README.md @@ -23,7 +23,7 @@ To start using pgpainless-sop in your code, include the following lines in your ... dependencies { ... - implementation "org.pgpainless:pgpainless-sop:1.7.2" + implementation "org.pgpainless:pgpainless-sop:1.7.3" ... } @@ -34,7 +34,7 @@ dependencies { org.pgpainless pgpainless-sop - 1.7.2 + 1.7.3 ... diff --git a/version.gradle b/version.gradle index 25dae7be..1d32c6bf 100644 --- a/version.gradle +++ b/version.gradle @@ -5,7 +5,7 @@ allprojects { ext { shortVersion = '1.7.3' - isSnapshot = true + isSnapshot = false pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.80' From bb9393d948142a7f517ed62306eef4a3360b29ed Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 22:43:46 +0100 Subject: [PATCH 41/73] PGPainless 1.7.4-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index 1d32c6bf..541df81e 100644 --- a/version.gradle +++ b/version.gradle @@ -4,8 +4,8 @@ allprojects { ext { - shortVersion = '1.7.3' - isSnapshot = false + shortVersion = '1.7.4' + isSnapshot = true pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.80' From effc9e747aa806e119f974ddda96be293b99176c Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 22:55:43 +0100 Subject: [PATCH 42/73] Remove -werror flag from javadoc task --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 29e054c2..52240b95 100644 --- a/build.gradle +++ b/build.gradle @@ -279,7 +279,7 @@ if (JavaVersion.current().isJava8Compatible()) { // gradle. See https://github.com/gradle/gradle/issues/2354 // See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363) // for information about the -Xwerror option. - options.addStringOption('Xwerror', '-quiet') + // options.addStringOption('Xwerror', '-quiet') } } From 62b0d0a560e27e396e12c2e50fb10bfe0b5d6c83 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 14 Mar 2025 13:44:24 +0100 Subject: [PATCH 43/73] Remove YourKit profiler usage --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 1ae88818..b8e578f7 100644 --- a/README.md +++ b/README.md @@ -222,9 +222,6 @@ Parts of PGPainless development ([project page](https://nlnet.nl/project/PGPainl NGI Assure is made possible with financial support from the [European Commission](https://ec.europa.eu/)'s [Next Generation Internet](https://ngi.eu/) programme, under the aegis of [DG Communications Networks, Content and Technology](https://ec.europa.eu/info/departments/communications-networks-content-and-technology_en). [![NGI Assure Logo](https://blog.jabberhead.tk/wp-content/uploads/2022/05/NGIAssure_tag.svg)](https://nlnet.nl/assure/) -Thanks to [YourKit](https://www.yourkit.com/) for providing a free license of the [YourKit Java Profiler](https://www.yourkit.com/java/profiler/) to support PGPainless Development! -[![YourKit Logo](https://www.yourkit.com/images/yklogo.png)](https://www.yourkit.com/) - Big thank you also to those who decided to support the work by donating! Notably @msfjarvis From c861a5eb732c72e28e15ec89545c74d59fd7edbc Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 20 Mar 2025 17:31:30 +0100 Subject: [PATCH 44/73] Set Java release version to 8 to fix Kotlin desugaring Fixes https://github.com/pgpainless/pgpainless/issues/471 --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 52240b95..b084a89c 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,10 @@ allprojects { options.release = 8 } + tasks.withType(JavaCompile).configureEach { + options.release = 8 + } + // Only generate jar for submodules // without this we would generate an empty pgpainless.jar for the project root // https://stackoverflow.com/a/25445035 From cb51bb64f358566384b331d78bf2f4fc31deb983 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 20 Mar 2025 17:35:08 +0100 Subject: [PATCH 45/73] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88f39db0..5386a787 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog +## 1.7.4-SNAPSHOT +- Fix proper Kotlin desugaring for Java 8 + ## 1.7.3 - Bump `bcpg-jdk8on` to `1.80` - Bump `bcprov-jdk18on` to `1.80` From f22cada0cef1933f2f52bc9142bd6311a0e5a30d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 20 Mar 2025 18:02:07 +0100 Subject: [PATCH 46/73] PGPainless 1.7.4 --- CHANGELOG.md | 2 +- README.md | 2 +- pgpainless-sop/README.md | 4 ++-- version.gradle | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5386a787..c9f79e45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog -## 1.7.4-SNAPSHOT +## 1.7.4 - Fix proper Kotlin desugaring for Java 8 ## 1.7.3 diff --git a/README.md b/README.md index b8e578f7..f2810265 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ repositories { } dependencies { - implementation 'org.pgpainless:pgpainless-core:1.7.3' + implementation 'org.pgpainless:pgpainless-core:1.7.4' } ``` diff --git a/pgpainless-sop/README.md b/pgpainless-sop/README.md index d041bc0e..50551fec 100644 --- a/pgpainless-sop/README.md +++ b/pgpainless-sop/README.md @@ -23,7 +23,7 @@ To start using pgpainless-sop in your code, include the following lines in your ... dependencies { ... - implementation "org.pgpainless:pgpainless-sop:1.7.3" + implementation "org.pgpainless:pgpainless-sop:1.7.4" ... } @@ -34,7 +34,7 @@ dependencies { org.pgpainless pgpainless-sop - 1.7.3 + 1.7.4 ... diff --git a/version.gradle b/version.gradle index 541df81e..709d1863 100644 --- a/version.gradle +++ b/version.gradle @@ -5,7 +5,7 @@ allprojects { ext { shortVersion = '1.7.4' - isSnapshot = true + isSnapshot = false pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.80' From 7dc4329c52cfd8d4fa6bf9eb3a2b501f2ea4e16d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 20 Mar 2025 18:05:37 +0100 Subject: [PATCH 47/73] PGPainless 1.7.5-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index 709d1863..05fff865 100644 --- a/version.gradle +++ b/version.gradle @@ -4,8 +4,8 @@ allprojects { ext { - shortVersion = '1.7.4' - isSnapshot = false + shortVersion = '1.7.5' + isSnapshot = true pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 bouncyCastleVersion = '1.80' From eebd02e309116139af06fec41683feb5f880f152 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 20 Mar 2025 18:18:40 +0100 Subject: [PATCH 48/73] Perform coveralls task after jacocoRootReport --- .github/workflows/gradle_push.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle_push.yml b/.github/workflows/gradle_push.yml index e4624726..8c03a399 100644 --- a/.github/workflows/gradle_push.yml +++ b/.github/workflows/gradle_push.yml @@ -28,9 +28,13 @@ jobs: with: java-version: '11' distribution: 'temurin' - - name: Build, Check and Coverage + - name: Build and Check + uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 + with: + arguments: check jacocoRootReport + - name: Coveralls uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} with: - arguments: check jacocoRootReport coveralls \ No newline at end of file + arguments: coveralls From a0254f47fb89d4d0cf827619417adfd84828c138 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 25 Mar 2025 11:03:22 +0100 Subject: [PATCH 49/73] Simplify mavenCentralChecksums task --- build.gradle | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/build.gradle b/build.gradle index b084a89c..f8cfbc9d 100644 --- a/build.gradle +++ b/build.gradle @@ -296,34 +296,13 @@ task mavenCentralChecksums() { description 'Fetch and display checksums for artifacts published to Maven Central' String ver = project.hasProperty('release') ? release : shortVersion doLast { - Process p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/pgpainless-core/${ver}/pgpainless-core-${ver}.jar.sha256".execute() - if (p.waitFor() == 0) { - print p.text.trim() - println " pgpainless-core/build/libs/pgpainless-core-${ver}.jar" - } - - p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/pgpainless-sop/${ver}/pgpainless-sop-${ver}.jar.sha256".execute() - if (p.waitFor() == 0) { - print p.text.trim() - println " pgpainless-sop/build/libs/pgpainless-sop-${ver}.jar" - } - - p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/pgpainless-cli/${ver}/pgpainless-cli-${ver}-all.jar.sha256".execute() - if (p.waitFor() == 0) { - print p.text.trim() - println " pgpainless-cli/build/libs/pgpainless-cli-${ver}-all.jar" - } - - p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/pgpainless-cli/${ver}/pgpainless-cli-${ver}.jar.sha256".execute() - if (p.waitFor() == 0) { - print p.text.trim() - println " pgpainless-cli/build/libs/pgpainless-cli-${ver}.jar" - } - - p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/hsregex/${ver}/hsregex-${ver}.jar.sha256".execute() - if (p.waitFor() == 0) { - print p.text.trim() - println " hsregex/build/libs/hsregex-${ver}.jar" + for (Project p : rootProject.subprojects) { + String url = "https://repo1.maven.org/maven2/org/pgpainless/${p.name}/${ver}/${p.name}-${ver}.jar.sha256" + Process fetch = "curl -f $url".execute() + if (fetch.waitFor() == 0) { + print fetch.text.trim() + println " ${p.name}/build/libs/${p.name}-${ver}.jar" + } } } } From 65113a6d82cc0671f5065eb4b0d96bbb41d1132f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 25 Mar 2025 11:22:16 +0100 Subject: [PATCH 50/73] Rework gradle, making use of toolchain feature --- build.gradle | 44 ++++++++----------------------------- pgpainless-cli/build.gradle | 19 ---------------- version.gradle | 3 +-- 3 files changed, 10 insertions(+), 56 deletions(-) diff --git a/build.gradle b/build.gradle index f8cfbc9d..d05845b4 100644 --- a/build.gradle +++ b/build.gradle @@ -33,18 +33,6 @@ allprojects { apply plugin: 'kotlin' apply plugin: 'com.diffplug.spotless' - java { - targetCompatibility = JavaVersion.VERSION_1_8 - } - - compileJava { - options.release = 8 - } - - tasks.withType(JavaCompile).configureEach { - options.release = 8 - } - // Only generate jar for submodules // without this we would generate an empty pgpainless.jar for the project root // https://stackoverflow.com/a/25445035 @@ -68,8 +56,6 @@ allprojects { description = "Simple to use OpenPGP API for Java based on Bouncycastle" version = shortVersion - sourceCompatibility = javaSourceCompatibility - repositories { mavenCentral() mavenLocal() @@ -84,6 +70,10 @@ allprojects { fileMode = 0644 } + kotlin { + jvmToolchain(javaSourceCompatibility) + } + // Compatibility of default implementations in kotlin interfaces with Java implementations. tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions { @@ -124,7 +114,7 @@ allprojects { sourceDirectories.setFrom(project.files(sourceSets.main.allSource.srcDirs)) classDirectories.setFrom(project.files(sourceSets.main.output)) reports { - xml.enabled true + xml.required = true } } @@ -142,15 +132,15 @@ subprojects { apply plugin: 'signing' task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' + archiveClassifier = 'sources' from sourceSets.main.allSource } task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' + archiveClassifier = 'javadoc' from javadoc.destinationDir } task testsJar(type: Jar, dependsOn: testClasses) { - classifier = 'tests' + archiveClassifier = 'tests' from sourceSets.test.output } @@ -247,7 +237,7 @@ task jacocoRootReport(type: JacocoReport) { classDirectories.setFrom(files(subprojects.sourceSets.main.output)) executionData.setFrom(files(subprojects.jacocoTestReport.executionData)) reports { - xml.enabled true + xml.required = true xml.destination file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml") } // We could remove the following setOnlyIf line, but then @@ -258,10 +248,6 @@ task jacocoRootReport(type: JacocoReport) { } task javadocAll(type: Javadoc) { - def currentJavaVersion = JavaVersion.current() - if (currentJavaVersion.compareTo(JavaVersion.VERSION_1_9) >= 0) { - options.addStringOption("-release", "8"); - } source subprojects.collect {project -> project.sourceSets.main.allJava } destinationDir = new File(buildDir, 'javadoc') @@ -275,18 +261,6 @@ task javadocAll(type: Javadoc) { ] as String[] } -if (JavaVersion.current().isJava8Compatible()) { - tasks.withType(Javadoc) { - // The '-quiet' as second argument is actually a hack, - // since the one paramater addStringOption doesn't seem to - // work, we extra add '-quiet', which is added anyway by - // gradle. See https://github.com/gradle/gradle/issues/2354 - // See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363) - // for information about the -Xwerror option. - // options.addStringOption('Xwerror', '-quiet') - } -} - /** * Fetch sha256 checksums of artifacts published to maven central. * diff --git a/pgpainless-cli/build.gradle b/pgpainless-cli/build.gradle index d6550139..b3bc192c 100644 --- a/pgpainless-cli/build.gradle +++ b/pgpainless-cli/build.gradle @@ -4,7 +4,6 @@ plugins { id 'application' - id "com.github.johnrengelman.shadow" version "6.1.0" } dependencies { @@ -31,22 +30,6 @@ mainClassName = 'org.pgpainless.cli.PGPainlessCLI' application { mainClass = mainClassName } -/** -jar { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - manifest { - attributes 'Main-Class': "$mainClassName" - } - - from { - configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } - } { - exclude "META-INF/*.SF" - exclude "META-INF/*.DSA" - exclude "META-INF/*.RSA" - } -} - */ run { // https://stackoverflow.com/questions/59445306/pipe-into-gradle-run @@ -56,5 +39,3 @@ run { args Eval.me(appArgs) } } - -// tasks."jar".dependsOn(":pgpainless-core:assemble", ":pgpainless-sop:assemble") diff --git a/version.gradle b/version.gradle index 05fff865..72b388a6 100644 --- a/version.gradle +++ b/version.gradle @@ -6,8 +6,7 @@ allprojects { ext { shortVersion = '1.7.5' isSnapshot = true - pgpainlessMinAndroidSdk = 10 - javaSourceCompatibility = 1.8 + javaSourceCompatibility = 11 bouncyCastleVersion = '1.80' bouncyPgVersion = bouncyCastleVersion junitVersion = '5.8.2' From f054c30460cae0887c4c73b5124f1c7484515aeb Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 25 Mar 2025 11:58:55 +0100 Subject: [PATCH 51/73] Upgrade gradle-wrapper to 8.8 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8049c684..0d184210 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 2b9c5ea2724baac3a03b0866f776b95eac15e849 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 25 Mar 2025 12:01:29 +0100 Subject: [PATCH 52/73] Update CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9f79e45..a593249b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog +## 1.7.5-SNAPSHOT +- Actually attempt to fix Kotlin desugaring. +- Bump javaSourceCompatibility and javaTargetCompatibility to 11 +- Bump gradle-wrapper to 8.8 + ## 1.7.4 - Fix proper Kotlin desugaring for Java 8 From de5ef4de004eceaaecccab997e37b48618791238 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 25 Mar 2025 12:05:33 +0100 Subject: [PATCH 53/73] PGPainless 1.7.5 --- CHANGELOG.md | 2 +- README.md | 2 +- pgpainless-sop/README.md | 4 ++-- version.gradle | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a593249b..8edff65d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog -## 1.7.5-SNAPSHOT +## 1.7.5 - Actually attempt to fix Kotlin desugaring. - Bump javaSourceCompatibility and javaTargetCompatibility to 11 - Bump gradle-wrapper to 8.8 diff --git a/README.md b/README.md index f2810265..44eb180d 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ repositories { } dependencies { - implementation 'org.pgpainless:pgpainless-core:1.7.4' + implementation 'org.pgpainless:pgpainless-core:1.7.5' } ``` diff --git a/pgpainless-sop/README.md b/pgpainless-sop/README.md index 50551fec..e21c821f 100644 --- a/pgpainless-sop/README.md +++ b/pgpainless-sop/README.md @@ -23,7 +23,7 @@ To start using pgpainless-sop in your code, include the following lines in your ... dependencies { ... - implementation "org.pgpainless:pgpainless-sop:1.7.4" + implementation "org.pgpainless:pgpainless-sop:1.7.5" ... } @@ -34,7 +34,7 @@ dependencies { org.pgpainless pgpainless-sop - 1.7.4 + 1.7.5 ... diff --git a/version.gradle b/version.gradle index 72b388a6..54933725 100644 --- a/version.gradle +++ b/version.gradle @@ -5,7 +5,7 @@ allprojects { ext { shortVersion = '1.7.5' - isSnapshot = true + isSnapshot = false javaSourceCompatibility = 11 bouncyCastleVersion = '1.80' bouncyPgVersion = bouncyCastleVersion From a6f8058fb43b97d25e0acb25ebede521f5e8dbe6 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 25 Mar 2025 12:09:43 +0100 Subject: [PATCH 54/73] PGPainless 1.7.6-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index 54933725..73c8c686 100644 --- a/version.gradle +++ b/version.gradle @@ -4,8 +4,8 @@ allprojects { ext { - shortVersion = '1.7.5' - isSnapshot = false + shortVersion = '1.7.6' + isSnapshot = true javaSourceCompatibility = 11 bouncyCastleVersion = '1.80' bouncyPgVersion = bouncyCastleVersion From 811f72ffef7a00edcca041e51e50697a66eeddf7 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 26 Mar 2025 15:00:59 +0100 Subject: [PATCH 55/73] Fix RevocationSignatureBuilder properly calculating 3rd-party delegation revocations --- .../pgpainless/signature/builder/RevocationSignatureBuilder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/RevocationSignatureBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/RevocationSignatureBuilder.kt index d8c594fe..36589613 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/RevocationSignatureBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/RevocationSignatureBuilder.kt @@ -52,7 +52,7 @@ class RevocationSignatureBuilder : AbstractSignatureBuilder Date: Wed, 26 Mar 2025 15:04:22 +0100 Subject: [PATCH 56/73] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8edff65d..75b18280 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog +## 1.7.5-SNAPSHOT +- Fix `RevocationSignatureBuilder` properly calculating third-party signatures of type `KeyRevocation` (delegation revocations) + ## 1.7.5 - Actually attempt to fix Kotlin desugaring. - Bump javaSourceCompatibility and javaTargetCompatibility to 11 From 143c9777d644fe3ccaff8d6f93ade2c9aaa5699c Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 3 Apr 2025 14:59:08 +0200 Subject: [PATCH 57/73] Implement graal nativeimage compilation Requires sop-java 10.1.1-SNAPSHOT for now, as that version includes picocli configurations files --- pgpainless-cli/build.gradle | 8 +++++++- version.gradle | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pgpainless-cli/build.gradle b/pgpainless-cli/build.gradle index b3bc192c..6c1d283a 100644 --- a/pgpainless-cli/build.gradle +++ b/pgpainless-cli/build.gradle @@ -4,6 +4,11 @@ plugins { id 'application' + id 'org.graalvm.buildtools.native' version '0.10.6' +} + +graalvmNative { + toolchainDetection = true } dependencies { @@ -16,7 +21,8 @@ dependencies { // implementation "ch.qos.logback:logback-core:1.2.6" // We want logback logging in tests and in the app testImplementation "ch.qos.logback:logback-classic:$logbackVersion" - implementation "ch.qos.logback:logback-classic:$logbackVersion" + // implementation "ch.qos.logback:logback-classic:$logbackVersion" + implementation "org.slf4j:slf4j-nop:$slf4jVersion" implementation(project(":pgpainless-sop")) implementation "org.pgpainless:sop-java-picocli:$sopJavaVersion" diff --git a/version.gradle b/version.gradle index 73c8c686..a4ace4c2 100644 --- a/version.gradle +++ b/version.gradle @@ -13,6 +13,6 @@ allprojects { logbackVersion = '1.5.13' mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' - sopJavaVersion = '10.1.0' + sopJavaVersion = '10.1.1-SNAPSHOT' } } From 2d0608cf0f6f2da15ad6e2fcc5babaa962dbd882 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 13 Apr 2025 19:45:12 +0200 Subject: [PATCH 58/73] Re-add shadow plugin --- pgpainless-cli/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/pgpainless-cli/build.gradle b/pgpainless-cli/build.gradle index 6c1d283a..e86ddedf 100644 --- a/pgpainless-cli/build.gradle +++ b/pgpainless-cli/build.gradle @@ -5,6 +5,7 @@ plugins { id 'application' id 'org.graalvm.buildtools.native' version '0.10.6' + id 'com.github.johnrengelman.shadow' version '8.1.1' } graalvmNative { From d20a3b75569d529d05ff11716a165268dcc812f8 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Apr 2025 15:30:58 +0200 Subject: [PATCH 59/73] Add config files for nativeimage Those were generated by running the following commands in order: gradle -Pagent test gradle metadataCopy --task test --dir src/main/resources/META-INF/native-image gradle nativeCompile The resulting nativeimage can resolve method calls that use reflection. Yay --- .../META-INF/native-image/jni-config.json | 2 + .../predefined-classes-config.json | 7 + .../META-INF/native-image/proxy-config.json | 2 + .../META-INF/native-image/reflect-config.json | 991 ++++++++++++++++++ .../native-image/resource-config.json | 95 ++ .../native-image/serialization-config.json | 41 + 6 files changed, 1138 insertions(+) create mode 100644 pgpainless-cli/src/main/resources/META-INF/native-image/jni-config.json create mode 100644 pgpainless-cli/src/main/resources/META-INF/native-image/predefined-classes-config.json create mode 100644 pgpainless-cli/src/main/resources/META-INF/native-image/proxy-config.json create mode 100644 pgpainless-cli/src/main/resources/META-INF/native-image/reflect-config.json create mode 100644 pgpainless-cli/src/main/resources/META-INF/native-image/resource-config.json create mode 100644 pgpainless-cli/src/main/resources/META-INF/native-image/serialization-config.json diff --git a/pgpainless-cli/src/main/resources/META-INF/native-image/jni-config.json b/pgpainless-cli/src/main/resources/META-INF/native-image/jni-config.json new file mode 100644 index 00000000..32960f8c --- /dev/null +++ b/pgpainless-cli/src/main/resources/META-INF/native-image/jni-config.json @@ -0,0 +1,2 @@ +[ +] \ No newline at end of file diff --git a/pgpainless-cli/src/main/resources/META-INF/native-image/predefined-classes-config.json b/pgpainless-cli/src/main/resources/META-INF/native-image/predefined-classes-config.json new file mode 100644 index 00000000..84789507 --- /dev/null +++ b/pgpainless-cli/src/main/resources/META-INF/native-image/predefined-classes-config.json @@ -0,0 +1,7 @@ +[ + { + "type":"agent-extracted", + "classes":[ + ] + } +] diff --git a/pgpainless-cli/src/main/resources/META-INF/native-image/proxy-config.json b/pgpainless-cli/src/main/resources/META-INF/native-image/proxy-config.json new file mode 100644 index 00000000..32960f8c --- /dev/null +++ b/pgpainless-cli/src/main/resources/META-INF/native-image/proxy-config.json @@ -0,0 +1,2 @@ +[ +] \ No newline at end of file diff --git a/pgpainless-cli/src/main/resources/META-INF/native-image/reflect-config.json b/pgpainless-cli/src/main/resources/META-INF/native-image/reflect-config.json new file mode 100644 index 00000000..25ad3faf --- /dev/null +++ b/pgpainless-cli/src/main/resources/META-INF/native-image/reflect-config.json @@ -0,0 +1,991 @@ +[ +{ + "name":"[B" +}, +{ + "name":"[Ljava.lang.Object;" +}, +{ + "name":"[Ljava.lang.String;" +}, +{ + "name":"[Lsun.security.pkcs.SignerInfo;" +}, +{ + "name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder", + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.joran.SerializedModelConfigurator", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.util.DefaultJoranConfigurator", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.core.ConsoleAppender", + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"setTarget","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"ch.qos.logback.core.OutputStreamAppender", + "methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }] +}, +{ + "name":"ch.qos.logback.core.encoder.Encoder", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder", + "methods":[{"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }] +}, +{ + "name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase", + "methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"ch.qos.logback.core.spi.ContextAware", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"groovy.lang.Closure" +}, +{ + "name":"java.io.FilePermission" +}, +{ + "name":"java.lang.Enum" +}, +{ + "name":"java.lang.Object", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"java.lang.RuntimePermission" +}, +{ + "name":"java.lang.String" +}, +{ + "name":"java.lang.System", + "methods":[{"name":"console","parameterTypes":[] }] +}, +{ + "name":"java.lang.invoke.MethodHandle" +}, +{ + "name":"java.net.NetPermission" +}, +{ + "name":"java.net.SocketPermission" +}, +{ + "name":"java.net.URLPermission", + "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] +}, +{ + "name":"java.nio.channels.SelectionKey", + "fields":[{"name":"attachment"}] +}, +{ + "name":"java.nio.file.Path" +}, +{ + "name":"java.nio.file.Paths", + "methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }] +}, +{ + "name":"java.security.AlgorithmParametersSpi" +}, +{ + "name":"java.security.AllPermission" +}, +{ + "name":"java.security.MessageDigestSpi" +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.security.SecurityPermission" +}, +{ + "name":"java.security.cert.PKIXRevocationChecker" +}, +{ + "name":"java.security.interfaces.DSAPrivateKey" +}, +{ + "name":"java.security.interfaces.DSAPublicKey" +}, +{ + "name":"java.security.interfaces.RSAPrivateKey" +}, +{ + "name":"java.security.interfaces.RSAPublicKey" +}, +{ + "name":"java.security.spec.DSAParameterSpec" +}, +{ + "name":"java.sql.Connection" +}, +{ + "name":"java.sql.Driver" +}, +{ + "name":"java.sql.DriverManager", + "methods":[{"name":"getConnection","parameterTypes":["java.lang.String"] }, {"name":"getDriver","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.sql.Time", + "methods":[{"name":"","parameterTypes":["long"] }] +}, +{ + "name":"java.sql.Timestamp", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.time.Duration", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.Instant", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.LocalDate", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.LocalDateTime", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.LocalTime", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.MonthDay", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.OffsetDateTime", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.OffsetTime", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.Period", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.Year", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.YearMonth", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.time.ZoneId", + "methods":[{"name":"of","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.time.ZoneOffset", + "methods":[{"name":"of","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.time.ZonedDateTime", + "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] +}, +{ + "name":"java.util.Date" +}, +{ + "name":"java.util.HashSet" +}, +{ + "name":"java.util.LinkedHashSet" +}, +{ + "name":"java.util.PropertyPermission" +}, +{ + "name":"java.util.concurrent.ArrayBlockingQueue" +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.locks.AbstractOwnableSynchronizer" +}, +{ + "name":"java.util.concurrent.locks.AbstractQueuedSynchronizer" +}, +{ + "name":"java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject" +}, +{ + "name":"java.util.concurrent.locks.ReentrantLock" +}, +{ + "name":"java.util.concurrent.locks.ReentrantLock$NonfairSync" +}, +{ + "name":"java.util.concurrent.locks.ReentrantLock$Sync" +}, +{ + "name":"javax.security.auth.x500.X500Principal", + "fields":[{"name":"thisX500Name"}], + "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }] +}, +{ + "name":"javax.smartcardio.CardPermission" +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.CONTEXT$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.CompositeSignatures$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.DSTU4145$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.Dilithium$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.ECGOST$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.EdEC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.ElGamal$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.Falcon$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.GM$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.GOST$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.IES$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.LMS$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.MLDSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.MLKEM$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.SLHDSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.SPHINCSPlus$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.edec.KeyPairGeneratorSpi$EdDSA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.edec.KeyPairGeneratorSpi$XDH", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyPairGeneratorSpi", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Blake2b$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Blake2s$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Blake3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.DSTU7564$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Haraka$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Keccak$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD160$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD320$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA224$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA384$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA512$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SM3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Skein$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Tiger$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Whirlpool$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.BC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.BCFKS$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.AES$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ARIA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.CAST5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.CAST6$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Camellia$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ChaCha$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DES$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DSTU7624$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.GOST28147$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.GOST3412_2015$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Grain128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Grainv1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.HC128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Noekeon$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.OpenSSLPBKDF$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Poly1305$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC6$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Rijndael$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SCRYPT$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SEED$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SM4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Salsa20$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Serpent$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Shacal2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SipHash$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SipHash128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Skipjack$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.TEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.TLSKDF$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.VMPC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.VMPCKSA3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.XSalsa20$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.XTEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.ExitCodeTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"successfulExecutionDoesNotTerminateJVM","parameterTypes":[] }, {"name":"testCommandWithUnknownOption_37","parameterTypes":[] }, {"name":"testUnknownCommand_69","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.TestUtils", + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true +}, +{ + "name":"org.pgpainless.cli.commands.ArmorCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"armorAlreadyArmoredDataIsIdempotent","parameterTypes":[] }, {"name":"armorMessage","parameterTypes":[] }, {"name":"armorPublicKey","parameterTypes":[] }, {"name":"armorSecretKey","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.CLITest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"cleanup","parameterTypes":[] }, {"name":"setup","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.DearmorCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"dearmorBrokenArmoredKeyFails","parameterTypes":[] }, {"name":"dearmorCertificate","parameterTypes":[] }, {"name":"dearmorGarbageEmitsEmpty","parameterTypes":[] }, {"name":"dearmorMessage","parameterTypes":[] }, {"name":"dearmorSecretKey","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.ExtractCertCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"extractCertFromGarbageFails","parameterTypes":[] }, {"name":"testExtractCert","parameterTypes":[] }, {"name":"testExtractCertFromCertFails","parameterTypes":[] }, {"name":"testExtractCertUnarmored","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.GenerateKeyCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"testGenerateBinaryKey","parameterTypes":[] }, {"name":"testGenerateKey","parameterTypes":[] }, {"name":"testGenerateKeyWithMultipleUserIds","parameterTypes":[] }, {"name":"testGeneratePasswordProtectedKey_missingPasswordFile","parameterTypes":[] }, {"name":"testPasswordProtectedKey","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.InlineDetachCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"detachInbandSignatureAndMessage","parameterTypes":[] }, {"name":"detachInbandSignatureAndMessageNoArmor","parameterTypes":[] }, {"name":"detachMissingSignaturesFromCleartextSignedMessageFails","parameterTypes":[] }, {"name":"detachNonOpenPgpDataFails","parameterTypes":[] }, {"name":"existingSignatureOutCausesException","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.ListProfilesCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"listProfileOfGenerateKey","parameterTypes":[] }, {"name":"listProfilesOfEncrypt","parameterTypes":[] }, {"name":"listProfilesWithoutCommand","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.RoundTripEncryptDecryptCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"decryptGarbageFails","parameterTypes":[] }, {"name":"decryptMalformedMessageYieldsBadData","parameterTypes":[] }, {"name":"decryptMessageWithSessionKey","parameterTypes":[] }, {"name":"decryptMessageWithWrongKeyFails","parameterTypes":[] }, {"name":"decryptWithPasswordWithPendingWhitespaceWorks","parameterTypes":[] }, {"name":"decryptWithWhitespacePasswordWorks","parameterTypes":[] }, {"name":"decrypt_verifyWithGarbageCertFails","parameterTypes":[] }, {"name":"decrypt_withGarbageKeyFails","parameterTypes":[] }, {"name":"encryptAndDecryptAMessage","parameterTypes":[] }, {"name":"encryptAndDecryptMessageWithPassphrase","parameterTypes":[] }, {"name":"encryptWithGarbageCertFails","parameterTypes":[] }, {"name":"encryptWithPasswordADecryptWithPasswordBFails","parameterTypes":[] }, {"name":"encryptWithProtectedKey_wrongPassphraseFails","parameterTypes":[] }, {"name":"encryptWithTrailingWhitespaceDecryptWithoutWorks","parameterTypes":[] }, {"name":"encrypt_signWithGarbageKeyFails","parameterTypes":[] }, {"name":"testDecryptVerifyOut_withoutVerifyWithFails","parameterTypes":[] }, {"name":"testDecryptWithSessionKeyVerifyWithYieldsExpectedVerifications","parameterTypes":[] }, {"name":"testDecryptWithoutDecryptionOptionFails","parameterTypes":[] }, {"name":"testEncryptDecryptRoundTripWithPasswordProtectedKey","parameterTypes":[] }, {"name":"testEncryptDecryptWithFreshRSAKey","parameterTypes":[] }, {"name":"testEncryptWithIncapableCert","parameterTypes":[] }, {"name":"testEncrypt_SignWithCertFails","parameterTypes":[] }, {"name":"testMissingArgumentsIfNoArgsSupplied","parameterTypes":[] }, {"name":"testSessionKeyOutWritesSessionKeyOut","parameterTypes":[] }, {"name":"testSignWithIncapableKey","parameterTypes":[] }, {"name":"testVerificationsOutAlreadyExistFails","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.RoundTripInlineSignInlineVerifyCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"cannotVerifyEncryptedMessage","parameterTypes":[] }, {"name":"cannotVerifyMalformedMessage","parameterTypes":[] }, {"name":"createAndVerifyCleartextSignedMessage","parameterTypes":[] }, {"name":"createAndVerifyMultiKeyBinarySignedMessage","parameterTypes":[] }, {"name":"createAndVerifyTextSignedMessage","parameterTypes":[] }, {"name":"createCleartextSignedMessage","parameterTypes":[] }, {"name":"createMalformedMessage","parameterTypes":[] }, {"name":"createSignedMessageWithKeyAAndVerifyWithKeyBFails","parameterTypes":[] }, {"name":"createTextSignedMessageInlineDetachAndDetachedVerify","parameterTypes":[] }, {"name":"signWithProtectedKeyWithWrongPassphraseFails","parameterTypes":[] }, {"name":"testInlineSignWithMissingSecretKeysFails","parameterTypes":[] }, {"name":"testUnlockKeyWithOneOfMultiplePasswords","parameterTypes":[] }, {"name":"verifyPrependedSignedMessage","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.RoundTripInlineSignVerifyCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"encryptAndDecryptAMessage","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.RoundTripSignVerifyCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"createArmoredSignature","parameterTypes":[] }, {"name":"createUnarmoredSignature","parameterTypes":[] }, {"name":"signWithProtectedKey","parameterTypes":[] }, {"name":"signWithProtectedKey_missingPassphraseFails","parameterTypes":[] }, {"name":"signWithProtectedKey_wrongPassphraseFails","parameterTypes":[] }, {"name":"testNotAfter","parameterTypes":[] }, {"name":"testNotBefore","parameterTypes":[] }, {"name":"testSignWithIncapableKey","parameterTypes":[] }, {"name":"testSignatureCreationAndVerification","parameterTypes":[] }, {"name":"unarmorArmoredSigAndVerify","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.commands.VersionCmdTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"testExtendedVersion","parameterTypes":[] }, {"name":"testGetBackendVersion","parameterTypes":[] }, {"name":"testSopSpecVersion","parameterTypes":[] }, {"name":"testVersion","parameterTypes":[] }] +}, +{ + "name":"org.pgpainless.cli.misc.SignUsingPublicKeyBehaviorTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"testSignatureCreationAndVerification","parameterTypes":[] }] +}, +{ + "name":"picocli.AutoComplete$GenerateCompletion", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"picocli.CommandLine$AutoHelpMixin", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"picocli.CommandLine$HelpCommand", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"sop.cli.picocli.SopCLI", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.SopCLI$InitLocale", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"sop.cli.picocli.commands.AbstractSopCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"sop.cli.picocli.commands.ArmorCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.ChangeKeyPasswordCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"sop.cli.picocli.commands.DearmorCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.DecryptCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.EncryptCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.ExtractCertCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.GenerateKeyCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.InlineDetachCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.InlineSignCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.InlineVerifyCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.ListProfilesCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.RevokeKeyCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"sop.cli.picocli.commands.SignCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.VerifyCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.VersionCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sop.cli.picocli.commands.VersionCmd$Exclusive", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.DSA$SHA256withDSA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.DSAKeyFactory", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.DSAParameters", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"","parameterTypes":[] }, {"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA2$SHA256", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.X509Factory", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSASignature$SHA256withRSA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.util.ObjectIdentifier" +}, +{ + "name":"sun.security.x509.AuthorityInfoAccessExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CertificateExtensions" +}, +{ + "name":"sun.security.x509.CertificatePoliciesExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +} +] \ No newline at end of file diff --git a/pgpainless-cli/src/main/resources/META-INF/native-image/resource-config.json b/pgpainless-cli/src/main/resources/META-INF/native-image/resource-config.json new file mode 100644 index 00000000..af650dc9 --- /dev/null +++ b/pgpainless-cli/src/main/resources/META-INF/native-image/resource-config.json @@ -0,0 +1,95 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E" + }, { + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/java.util.spi.ResourceBundleControlProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.engine.TestEngine\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherSessionListener\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.PostDiscoveryFilter\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.TestExecutionListener\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qjunit-platform.properties\\E" + }, { + "pattern":"\\Qlogback-test.scmo\\E" + }, { + "pattern":"\\Qlogback-test.xml\\E" + }, { + "pattern":"\\Qlogback.scmo\\E" + }, { + "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }, { + "pattern":"\\Qpgpainless-sop.properties\\E" + }, { + "pattern":"\\Qsop-java-version.properties\\E" + }, { + "pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E" + }, { + "pattern":"java.base:\\Qsun/text/resources/nfkc.icu\\E" + }]}, + "bundles":[{ + "name":"msg_armor", + "locales":["de", "und"] + }, { + "name":"msg_change-key-password", + "locales":["de", "und"] + }, { + "name":"msg_dearmor", + "locales":["de", "und"] + }, { + "name":"msg_decrypt", + "locales":["de", "und"] + }, { + "name":"msg_detached-sign", + "locales":["de", "und"] + }, { + "name":"msg_detached-verify", + "locales":["de", "und"] + }, { + "name":"msg_encrypt", + "locales":["de", "und"] + }, { + "name":"msg_extract-cert", + "locales":["de", "und"] + }, { + "name":"msg_generate-key", + "locales":["de", "und"] + }, { + "name":"msg_inline-detach", + "locales":["de", "und"] + }, { + "name":"msg_inline-sign", + "locales":["de", "und"] + }, { + "name":"msg_inline-verify", + "locales":["de", "und"] + }, { + "name":"msg_list-profiles", + "locales":["de", "und"] + }, { + "name":"msg_revoke-key", + "locales":["de", "und"] + }, { + "name":"msg_sop", + "locales":["de", "und"] + }, { + "name":"msg_version", + "locales":["de", "und"] + }] +} \ No newline at end of file diff --git a/pgpainless-cli/src/main/resources/META-INF/native-image/serialization-config.json b/pgpainless-cli/src/main/resources/META-INF/native-image/serialization-config.json new file mode 100644 index 00000000..3f06d9a8 --- /dev/null +++ b/pgpainless-cli/src/main/resources/META-INF/native-image/serialization-config.json @@ -0,0 +1,41 @@ +{ + "types":[ + { + "name":"java.lang.Enum" + }, + { + "name":"java.lang.Object[]" + }, + { + "name":"java.util.HashSet" + }, + { + "name":"java.util.LinkedHashSet" + }, + { + "name":"java.util.concurrent.ArrayBlockingQueue" + }, + { + "name":"java.util.concurrent.locks.AbstractOwnableSynchronizer" + }, + { + "name":"java.util.concurrent.locks.AbstractQueuedSynchronizer" + }, + { + "name":"java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject" + }, + { + "name":"java.util.concurrent.locks.ReentrantLock" + }, + { + "name":"java.util.concurrent.locks.ReentrantLock$NonfairSync" + }, + { + "name":"java.util.concurrent.locks.ReentrantLock$Sync" + } + ], + "lambdaCapturingTypes":[ + ], + "proxies":[ + ] +} \ No newline at end of file From 05c84835e69d5de69c64d1f007b5b5838da77b8a Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Apr 2025 15:31:27 +0200 Subject: [PATCH 60/73] Bump SOP-Java to 10.1.1 --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index a4ace4c2..f3c4f100 100644 --- a/version.gradle +++ b/version.gradle @@ -13,6 +13,6 @@ allprojects { logbackVersion = '1.5.13' mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' - sopJavaVersion = '10.1.1-SNAPSHOT' + sopJavaVersion = '10.1.1' } } From 83613250efecc50daf6171c8f46a944597b496a9 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Apr 2025 15:55:29 +0200 Subject: [PATCH 61/73] PGPainless 1.7.6 --- CHANGELOG.md | 4 +++- README.md | 2 +- pgpainless-sop/README.md | 4 ++-- version.gradle | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75b18280..b1f63d6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,10 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog -## 1.7.5-SNAPSHOT +## 1.7.6 - Fix `RevocationSignatureBuilder` properly calculating third-party signatures of type `KeyRevocation` (delegation revocations) +- Enable support for native images +- Re-enable shadow plugin and build fat-jar ## 1.7.5 - Actually attempt to fix Kotlin desugaring. diff --git a/README.md b/README.md index 44eb180d..e305e43a 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ repositories { } dependencies { - implementation 'org.pgpainless:pgpainless-core:1.7.5' + implementation 'org.pgpainless:pgpainless-core:1.7.6' } ``` diff --git a/pgpainless-sop/README.md b/pgpainless-sop/README.md index e21c821f..da112604 100644 --- a/pgpainless-sop/README.md +++ b/pgpainless-sop/README.md @@ -23,7 +23,7 @@ To start using pgpainless-sop in your code, include the following lines in your ... dependencies { ... - implementation "org.pgpainless:pgpainless-sop:1.7.5" + implementation "org.pgpainless:pgpainless-sop:1.7.6" ... } @@ -34,7 +34,7 @@ dependencies { org.pgpainless pgpainless-sop - 1.7.5 + 1.7.6 ... diff --git a/version.gradle b/version.gradle index f3c4f100..7ed1f065 100644 --- a/version.gradle +++ b/version.gradle @@ -5,7 +5,7 @@ allprojects { ext { shortVersion = '1.7.6' - isSnapshot = true + isSnapshot = false javaSourceCompatibility = 11 bouncyCastleVersion = '1.80' bouncyPgVersion = bouncyCastleVersion From 3b92ccc59d8c6f8c9ef4ccc5f5e8317659fee729 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Apr 2025 16:05:05 +0200 Subject: [PATCH 62/73] PGPainless 1.7.7-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index 7ed1f065..003de912 100644 --- a/version.gradle +++ b/version.gradle @@ -4,8 +4,8 @@ allprojects { ext { - shortVersion = '1.7.6' - isSnapshot = false + shortVersion = '1.7.7' + isSnapshot = true javaSourceCompatibility = 11 bouncyCastleVersion = '1.80' bouncyPgVersion = bouncyCastleVersion From 5a413f53a42a26169901ce634b6dfa7cc002f22b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 21 Apr 2025 19:11:52 +0200 Subject: [PATCH 63/73] Specify license information for native-image metadata --- REUSE.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index d250e0be..66b5e867 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -93,6 +93,12 @@ precedence = "aggregate" SPDX-FileCopyrightText = "2022 Paul Schaub , 2017 Steve Smith" SPDX-License-Identifier = "CC-BY-SA-3.0" +[[annotations]] +path = "pgpainless-cli/src/main/resources/META-INF/native-image/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2025 Paul Schaub " +SPDX-License-Identifier = "Apache-2.0" + [[annotations]] path = "pgpainless-cli/rewriteManPages.sh" precedence = "aggregate" From 0649c041cdead499b391f874830a0bdc2c17e246 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 21 Apr 2025 19:12:10 +0200 Subject: [PATCH 64/73] gradle: migrate to new shadow plugin namespace --- pgpainless-cli/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-cli/build.gradle b/pgpainless-cli/build.gradle index e86ddedf..e4c3f060 100644 --- a/pgpainless-cli/build.gradle +++ b/pgpainless-cli/build.gradle @@ -5,7 +5,7 @@ plugins { id 'application' id 'org.graalvm.buildtools.native' version '0.10.6' - id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'com.gradleup.shadow' version '8.3.6' } graalvmNative { From 7953ade13673568487eb0a3b7b141f96a08af585 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 3 Jun 2025 12:37:04 +0200 Subject: [PATCH 65/73] Bump checkstyle to 10.25.0 Fixes https://github.com/pgpainless/pgpainless/security/dependabot/24 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d05845b4..93100f4d 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,7 @@ allprojects { // checkstyle checkstyle { - toolVersion = '10.12.1' + toolVersion = '10.25.0' } spotless { From 5f30df6d166962f780fbfcb870f9934d84e6e771 Mon Sep 17 00:00:00 2001 From: Felix Hagemans Date: Wed, 4 Jun 2025 16:02:23 +0200 Subject: [PATCH 66/73] Fixed typo in sop readme --- pgpainless-sop/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-sop/README.md b/pgpainless-sop/README.md index da112604..7f7267cd 100644 --- a/pgpainless-sop/README.md +++ b/pgpainless-sop/README.md @@ -67,7 +67,7 @@ byte[] encrypted = sop.encrypt() // Decrypt a message ByteArrayAndResult messageAndVerifications = sop.decrypt() - .verifyWith(cert) + .verifyWithCert(cert) .withKey(key) .ciphertext(encrypted) .toByteArrayAndResult(); From 0f54cc615c5409b5fa12d61182f63d022e78f828 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 10:30:42 +0200 Subject: [PATCH 67/73] Bump BC to 1.81, update native-image reflect-config, resource-config --- .../META-INF/native-image/reflect-config.json | 108 +----------------- .../native-image/resource-config.json | 2 - version.gradle | 2 +- 3 files changed, 5 insertions(+), 107 deletions(-) diff --git a/pgpainless-cli/src/main/resources/META-INF/native-image/reflect-config.json b/pgpainless-cli/src/main/resources/META-INF/native-image/reflect-config.json index 25ad3faf..63bdf5f3 100644 --- a/pgpainless-cli/src/main/resources/META-INF/native-image/reflect-config.json +++ b/pgpainless-cli/src/main/resources/META-INF/native-image/reflect-config.json @@ -1,16 +1,7 @@ [ -{ - "name":"[B" -}, { "name":"[Ljava.lang.Object;" }, -{ - "name":"[Ljava.lang.String;" -}, -{ - "name":"[Lsun.security.pkcs.SignerInfo;" -}, { "name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder", "queryAllPublicMethods":true, @@ -70,9 +61,6 @@ { "name":"java.lang.RuntimePermission" }, -{ - "name":"java.lang.String" -}, { "name":"java.lang.System", "methods":[{"name":"console","parameterTypes":[] }] @@ -101,9 +89,6 @@ "name":"java.nio.file.Paths", "methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }] }, -{ - "name":"java.security.AlgorithmParametersSpi" -}, { "name":"java.security.AllPermission" }, @@ -119,21 +104,6 @@ { "name":"java.security.cert.PKIXRevocationChecker" }, -{ - "name":"java.security.interfaces.DSAPrivateKey" -}, -{ - "name":"java.security.interfaces.DSAPublicKey" -}, -{ - "name":"java.security.interfaces.RSAPrivateKey" -}, -{ - "name":"java.security.interfaces.RSAPublicKey" -}, -{ - "name":"java.security.spec.DSAParameterSpec" -}, { "name":"java.sql.Connection" }, @@ -208,9 +178,6 @@ "name":"java.time.ZonedDateTime", "methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }] }, -{ - "name":"java.util.Date" -}, { "name":"java.util.HashSet" }, @@ -245,11 +212,6 @@ { "name":"java.util.concurrent.locks.ReentrantLock$Sync" }, -{ - "name":"javax.security.auth.x500.X500Principal", - "fields":[{"name":"thisX500Name"}], - "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }] -}, { "name":"javax.smartcardio.CardPermission" }, @@ -333,6 +295,10 @@ "name":"org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.NoSig$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", "methods":[{"name":"","parameterTypes":[] }] @@ -914,18 +880,6 @@ "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, -{ - "name":"sun.security.provider.DSA$SHA256withDSA", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"sun.security.provider.DSAKeyFactory", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"sun.security.provider.DSAParameters", - "methods":[{"name":"","parameterTypes":[] }] -}, { "name":"sun.security.provider.NativePRNG", "methods":[{"name":"","parameterTypes":[] }, {"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] @@ -933,59 +887,5 @@ { "name":"sun.security.provider.SHA", "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"sun.security.provider.SHA2$SHA256", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"sun.security.provider.X509Factory", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"sun.security.rsa.RSAKeyFactory$Legacy", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"sun.security.rsa.RSASignature$SHA256withRSA", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"sun.security.util.ObjectIdentifier" -}, -{ - "name":"sun.security.x509.AuthorityInfoAccessExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] -}, -{ - "name":"sun.security.x509.AuthorityKeyIdentifierExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] -}, -{ - "name":"sun.security.x509.BasicConstraintsExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] -}, -{ - "name":"sun.security.x509.CRLDistributionPointsExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] -}, -{ - "name":"sun.security.x509.CertificateExtensions" -}, -{ - "name":"sun.security.x509.CertificatePoliciesExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] -}, -{ - "name":"sun.security.x509.ExtendedKeyUsageExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] -}, -{ - "name":"sun.security.x509.KeyUsageExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] -}, -{ - "name":"sun.security.x509.SubjectKeyIdentifierExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] } ] \ No newline at end of file diff --git a/pgpainless-cli/src/main/resources/META-INF/native-image/resource-config.json b/pgpainless-cli/src/main/resources/META-INF/native-image/resource-config.json index af650dc9..3c66a520 100644 --- a/pgpainless-cli/src/main/resources/META-INF/native-image/resource-config.json +++ b/pgpainless-cli/src/main/resources/META-INF/native-image/resource-config.json @@ -40,8 +40,6 @@ "pattern":"\\Qsop-java-version.properties\\E" }, { "pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E" - }, { - "pattern":"java.base:\\Qsun/text/resources/nfkc.icu\\E" }]}, "bundles":[{ "name":"msg_armor", diff --git a/version.gradle b/version.gradle index 003de912..bc2515a4 100644 --- a/version.gradle +++ b/version.gradle @@ -7,7 +7,7 @@ allprojects { shortVersion = '1.7.7' isSnapshot = true javaSourceCompatibility = 11 - bouncyCastleVersion = '1.80' + bouncyCastleVersion = '1.81' bouncyPgVersion = bouncyCastleVersion junitVersion = '5.8.2' logbackVersion = '1.5.13' From 4cf6c6b16a2f8d352fe54d3b0276eff936caeb03 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 10:42:50 +0200 Subject: [PATCH 68/73] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1f63d6e..13c93c07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ SPDX-License-Identifier: CC0-1.0 # PGPainless Changelog +## 1.7.7-SNAPSHOT +- Bump `bcpg-jdk8on` to `1.81` +- Bump `bcprov-jdk18on` to `1.81` + ## 1.7.6 - Fix `RevocationSignatureBuilder` properly calculating third-party signatures of type `KeyRevocation` (delegation revocations) - Enable support for native images From f2cbde43bee0da523717b65f10f95defc4ad6f60 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 1 Jul 2025 10:54:06 +0200 Subject: [PATCH 69/73] Update codeql action to v3 --- .github/workflows/codeql-analysis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 031ba56d..d45b16a3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'java' ] + language: [ 'java-kotlin' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support @@ -46,7 +46,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -57,7 +57,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -71,4 +71,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 0ee31b232ac7b4beda0940bcbd52fc078209dfc4 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 23 Jul 2025 11:23:34 +0200 Subject: [PATCH 70/73] Allow UserIDs with trailing/leading whitespace and escape newlines in ASCII armor --- .../org/pgpainless/key/generation/KeyRingBuilder.kt | 2 +- .../modification/secretkeyring/SecretKeyRingEditor.kt | 7 ++++--- .../src/main/kotlin/org/pgpainless/util/ArmorUtils.kt | 3 ++- .../test/java/org/pgpainless/sop/GenerateKeyTest.java | 10 ++++++++++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt index 05adf7d9..aacfcceb 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt @@ -45,7 +45,7 @@ class KeyRingBuilder : KeyRingBuilderInterface { } override fun addUserId(userId: CharSequence): KeyRingBuilder = apply { - userIds[userId.toString().trim()] = null + userIds[userId.toString()] = null } override fun addUserId(userId: ByteArray): KeyRingBuilder = diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt index ec93c6d6..56fb7695 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt @@ -569,9 +569,10 @@ class SecretKeyRingEditor( } private fun sanitizeUserId(userId: CharSequence): CharSequence = - // TODO: Further research how to sanitize user IDs. - // e.g. what about newlines? - userId.toString().trim() + // I'm not sure, what kind of sanitization is needed. + // Newlines are allowed, they just need to be escaped when emitted in an ASCII armor header + // Trailing/Leading whitespace is also fine. + userId.toString() private fun callbackFromRevocationAttributes(attributes: RevocationAttributes?) = object : RevocationSignatureSubpackets.Callback { diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt index b5d5b839..b6e802b2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt @@ -247,7 +247,8 @@ class ArmorUtils { .add(OpenPgpFingerprint.of(publicKey).prettyPrint()) // Primary / First User ID (primary ?: first)?.let { - headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add(it) + headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() } + .add(it.replace("\n", "\\n").replace("\r", "\\r")) } // X-1 further identities when (userIds.size) { diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/GenerateKeyTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/GenerateKeyTest.java index ca6df790..521cdfe0 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/GenerateKeyTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/GenerateKeyTest.java @@ -100,4 +100,14 @@ public class GenerateKeyTest { assertThrows(SOPGPException.UnsupportedProfile.class, () -> sop.generateKey().profile("invalid")); } + + @Test + public void generateKeyWithNewlinesInUserId() throws IOException { + byte[] keyBytes = sop.generateKey() + .userId("Foo\n\nBar") + .generate() + .getBytes(); + + assertTrue(new String(keyBytes).contains("Foo\\n\\nBar")); + } } From 9b0a3cd4c7ffe8866ab9622fde866b79cd34a62b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 23 Jul 2025 11:24:10 +0200 Subject: [PATCH 71/73] Do not trim passphrases automatically --- .../main/kotlin/org/pgpainless/util/Passphrase.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/Passphrase.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/Passphrase.kt index 4d1e49d2..bd25f2b9 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/Passphrase.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/Passphrase.kt @@ -11,14 +11,9 @@ import org.bouncycastle.util.Arrays * * @param chars may be null for empty passwords. */ -class Passphrase(chars: CharArray?) { +class Passphrase(private val chars: CharArray?) { private val lock = Any() private var valid = true - private val chars: CharArray? - - init { - this.chars = trimWhitespace(chars) - } /** * Return a copy of the underlying char array. A return value of null represents an empty @@ -67,6 +62,13 @@ class Passphrase(chars: CharArray?) { override fun hashCode(): Int = getChars()?.let { String(it) }.hashCode() + /** + * Return a copy of this [Passphrase], but with whitespace characters trimmed off. + * + * @return copy with trimmed whitespace + */ + fun withTrimmedWhitespace(): Passphrase = Passphrase(trimWhitespace(chars)) + companion object { /** From 0d807cb6b8317f2e878509b5cb5f4a9a91e5287d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 23 Jul 2025 11:25:31 +0200 Subject: [PATCH 72/73] Fix typo in error message --- .../key/modification/secretkeyring/SecretKeyRingEditor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt index 56fb7695..5480442d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt @@ -478,7 +478,7 @@ class SecretKeyRingEditor( val prevBinding = inspectKeyRing(secretKeyRing).getCurrentSubkeyBindingSignature(keyId) ?: throw NoSuchElementException( - "Previous subkey binding signaure for ${keyId.openPgpKeyId()} MUST NOT be null.") + "Previous subkey binding signature for ${keyId.openPgpKeyId()} MUST NOT be null.") val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, protector, prevBinding) secretKeyRing = injectCertification(secretKeyRing, subkey, bindingSig) } From 0fe3a7abf660ee7814e87255c13b5775030e73f9 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 30 Aug 2025 10:36:40 +0200 Subject: [PATCH 73/73] Fix ArmorUtils spotless complaint --- .../src/main/kotlin/org/pgpainless/util/ArmorUtils.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt index b6e802b2..db1cb54d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt @@ -247,7 +247,8 @@ class ArmorUtils { .add(OpenPgpFingerprint.of(publicKey).prettyPrint()) // Primary / First User ID (primary ?: first)?.let { - headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() } + headerMap + .getOrPut(HEADER_COMMENT) { mutableSetOf() } .add(it.replace("\n", "\\n").replace("\r", "\\r")) } // X-1 further identities