From bd225825e725f1f00ab559163a75ea2059056460 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 11:15:29 +0200 Subject: [PATCH 01/10] Move validate-userid to SOPV --- external-sop/src/main/kotlin/sop/external/ExternalSOPV.kt | 4 ++++ sop-java/src/main/kotlin/sop/SOP.kt | 3 --- sop-java/src/main/kotlin/sop/SOPV.kt | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/external-sop/src/main/kotlin/sop/external/ExternalSOPV.kt b/external-sop/src/main/kotlin/sop/external/ExternalSOPV.kt index f22f947..3341055 100644 --- a/external-sop/src/main/kotlin/sop/external/ExternalSOPV.kt +++ b/external-sop/src/main/kotlin/sop/external/ExternalSOPV.kt @@ -10,9 +10,11 @@ import sop.SOPV import sop.external.ExternalSOP.TempDirProvider import sop.external.operation.DetachedVerifyExternal import sop.external.operation.InlineVerifyExternal +import sop.external.operation.ValidateUserIdExternal import sop.external.operation.VersionExternal import sop.operation.DetachedVerify import sop.operation.InlineVerify +import sop.operation.ValidateUserId import sop.operation.Version /** @@ -37,6 +39,8 @@ class ExternalSOPV( override fun inlineVerify(): InlineVerify = InlineVerifyExternal(binaryName, properties, tempDirProvider) + override fun validateUserId(): ValidateUserId = ValidateUserIdExternal(binaryName, properties) + companion object { /** diff --git a/sop-java/src/main/kotlin/sop/SOP.kt b/sop-java/src/main/kotlin/sop/SOP.kt index fbd0428..a942c56 100644 --- a/sop-java/src/main/kotlin/sop/SOP.kt +++ b/sop-java/src/main/kotlin/sop/SOP.kt @@ -68,7 +68,4 @@ interface SOP : SOPV { /** Certify OpenPGP Certificate User-IDs. */ fun certifyUserId(): CertifyUserId? - - /** Validate a UserID in an OpenPGP certificate. */ - fun validateUserId(): ValidateUserId? } diff --git a/sop-java/src/main/kotlin/sop/SOPV.kt b/sop-java/src/main/kotlin/sop/SOPV.kt index 58a7f13..27eb6e3 100644 --- a/sop-java/src/main/kotlin/sop/SOPV.kt +++ b/sop-java/src/main/kotlin/sop/SOPV.kt @@ -6,6 +6,7 @@ package sop import sop.operation.DetachedVerify import sop.operation.InlineVerify +import sop.operation.ValidateUserId import sop.operation.Version /** Subset of [SOP] implementing only OpenPGP signature verification. */ @@ -31,4 +32,7 @@ interface SOPV { * a message, use [detachedVerify] instead. */ fun inlineVerify(): InlineVerify? + + /** Validate a UserID in an OpenPGP certificate. */ + fun validateUserId(): ValidateUserId? } From e383eed435fd3d66418d6ad75c7bc0f36e9f9695 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 11:28:28 +0200 Subject: [PATCH 02/10] Remove animalsniffer --- build.gradle | 13 ------------- version.gradle | 1 - 2 files changed, 14 deletions(-) diff --git a/build.gradle b/build.gradle index 1bff704..10f2b87 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,6 @@ buildscript { } plugins { - id 'ru.vyarus.animalsniffer' version '2.0.0' id 'org.jetbrains.kotlin.jvm' version "1.9.21" id 'com.diffplug.spotless' version '6.22.0' apply false } @@ -35,18 +34,6 @@ allprojects { apply plugin: 'kotlin-kapt' apply plugin: 'com.diffplug.spotless' - // For non-cli modules enable android api compatibility check - if (it.name.equals('sop-java')) { - // animalsniffer - apply plugin: 'ru.vyarus.animalsniffer' - dependencies { - signature "net.sf.androidscents.signature:android-api-level-${minAndroidSdk}:2.3.3_r2@signature" - } - animalsniffer { - sourceSets = [sourceSets.main] - } - } - // Only generate jar for submodules // https://stackoverflow.com/a/25445035 jar { diff --git a/version.gradle b/version.gradle index c757e1e..4d90e55 100644 --- a/version.gradle +++ b/version.gradle @@ -6,7 +6,6 @@ allprojects { ext { shortVersion = '11.0.0' isSnapshot = true - minAndroidSdk = 10 javaSourceCompatibility = 11 gsonVersion = '2.10.1' jsrVersion = '3.0.2' From 9a4313c3fc98c41d11e14ccc17cba38841ff8e4d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 11:28:58 +0200 Subject: [PATCH 03/10] Clean up unused version literal --- version.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/version.gradle b/version.gradle index 4d90e55..a58af0b 100644 --- a/version.gradle +++ b/version.gradle @@ -10,7 +10,6 @@ allprojects { gsonVersion = '2.10.1' jsrVersion = '3.0.2' junitVersion = '5.8.2' - junitSysExitVersion = '1.1.2' logbackVersion = '1.2.13' // 1.4+ cause CLI spam mockitoVersion = '4.5.1' picocliVersion = '4.6.3' From fc1fb57c2e013abd2d7bfe000d3de0ed44d72658 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 11:29:25 +0200 Subject: [PATCH 04/10] Fix: Pass chars to StringBuilder.append() --- sop-java/src/test/java/sop/VerificationJSONTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sop-java/src/test/java/sop/VerificationJSONTest.java b/sop-java/src/test/java/sop/VerificationJSONTest.java index 10253d8..a80e6fc 100644 --- a/sop-java/src/test/java/sop/VerificationJSONTest.java +++ b/sop-java/src/test/java/sop/VerificationJSONTest.java @@ -81,12 +81,12 @@ public class VerificationJSONTest { sb.append("\"signers\": ["); for (Iterator iterator = json.getSigners().iterator(); iterator.hasNext(); ) { String signer = iterator.next(); - sb.append("\"").append(signer).append("\""); + sb.append('\"').append(signer).append('\"'); if (iterator.hasNext()) { sb.append(", "); } } - sb.append("]"); + sb.append(']'); } if (json.getComment() != null) { @@ -94,7 +94,7 @@ public class VerificationJSONTest { sb.append(", "); } comma = true; - sb.append("\"comment\": \"").append(json.getComment()).append("\""); + sb.append("\"comment\": \"").append(json.getComment()).append('\"'); } if (json.getExt() != null) { @@ -104,7 +104,7 @@ public class VerificationJSONTest { comma = true; sb.append("\"ext\": ").append(json.getExt().toString()); } - return sb.append("}").toString(); + return sb.append('}').toString(); } }; From 4599b9424a345343f91cdbf79e88f508ec2b879d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 11:29:53 +0200 Subject: [PATCH 05/10] Bump logback to 1.5.13 --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index a58af0b..864c585 100644 --- a/version.gradle +++ b/version.gradle @@ -10,7 +10,7 @@ allprojects { gsonVersion = '2.10.1' jsrVersion = '3.0.2' junitVersion = '5.8.2' - logbackVersion = '1.2.13' // 1.4+ cause CLI spam + logbackVersion = '1.5.13' mockitoVersion = '4.5.1' picocliVersion = '4.6.3' slf4jVersion = '1.7.36' From 48f71abaa542094db55dc3e231dc8438063d18a4 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 11:30:21 +0200 Subject: [PATCH 06/10] Bump version to 14.0.0-SNAPSHOT --- version.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle b/version.gradle index 864c585..cfb972a 100644 --- a/version.gradle +++ b/version.gradle @@ -4,7 +4,7 @@ allprojects { ext { - shortVersion = '11.0.0' + shortVersion = '14.0.0' isSnapshot = true javaSourceCompatibility = 11 gsonVersion = '2.10.1' From 23a724ee0b262f52a62d49decf9aadcc9abe7625 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 12:12:23 +0200 Subject: [PATCH 07/10] Version: Fix getSopJavaVersion() --- .../src/main/java/sop/testsuite/operation/VersionTest.java | 6 ++++++ sop-java/src/main/kotlin/sop/operation/Version.kt | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/sop-java-testfixtures/src/main/java/sop/testsuite/operation/VersionTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/VersionTest.java index f836935..47644bf 100644 --- a/sop-java-testfixtures/src/main/java/sop/testsuite/operation/VersionTest.java +++ b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/VersionTest.java @@ -86,4 +86,10 @@ public class VersionTest extends AbstractSOPTest { throw new TestAbortedException("Implementation does not provide coverage for any sopv interface version."); } } + + @ParameterizedTest + @MethodSource("provideInstances") + public void sopJavaVersionTest(SOP sop) { + assertNotNull(sop.version().getSopJavaVersion()); + } } diff --git a/sop-java/src/main/kotlin/sop/operation/Version.kt b/sop-java/src/main/kotlin/sop/operation/Version.kt index a10fe7c..6c8aa95 100644 --- a/sop-java/src/main/kotlin/sop/operation/Version.kt +++ b/sop-java/src/main/kotlin/sop/operation/Version.kt @@ -115,12 +115,12 @@ interface Version { fun getSopJavaVersion(): String? { return try { val resourceIn: InputStream = - javaClass.getResourceAsStream("/sop-java-version.properties") + Version::class.java.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 + "DEVELOPMENT" } } } From 3554e44ada59cd7de2724b03e30ed4491a152937 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 12:12:41 +0200 Subject: [PATCH 08/10] Add/fix missing localizations for new SOP commands --- sop-java-picocli/src/main/resources/msg_update-key.properties | 2 +- .../src/main/resources/msg_update-key_de.properties | 2 +- sop-java-picocli/src/main/resources/msg_version.properties | 1 + sop-java-picocli/src/main/resources/msg_version_de.properties | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sop-java-picocli/src/main/resources/msg_update-key.properties b/sop-java-picocli/src/main/resources/msg_update-key.properties index e12fbbc..0b5243e 100644 --- a/sop-java-picocli/src/main/resources/msg_update-key.properties +++ b/sop-java-picocli/src/main/resources/msg_update-key.properties @@ -4,7 +4,7 @@ usage.header=Keep a secret key up-to-date no-armor=ASCII armor the output signing-only=TODO: Document -no-new-mechanisms=Do not add feature support for new mechanisms, which the key did not previously support +no-added-capabilities=Do not add feature support for new mechanisms, which the key did not previously support with-key-password.0=Passphrase to unlock the secret key(s). with-key-password.1=Is an INDIRECT data type (e.g. file, environment variable, file descriptor...). merge-certs.0=Merge additional elements found in the corresponding CERTS objects into the updated secret keys diff --git a/sop-java-picocli/src/main/resources/msg_update-key_de.properties b/sop-java-picocli/src/main/resources/msg_update-key_de.properties index 1b8a84d..91e5532 100644 --- a/sop-java-picocli/src/main/resources/msg_update-key_de.properties +++ b/sop-java-picocli/src/main/resources/msg_update-key_de.properties @@ -4,7 +4,7 @@ usage.header=Halte einen Schlüssel auf dem neusten Stand no-armor=Schütze Ausgabe mit ASCII Armor signing-only=TODO: Dokumentieren -no-new-mechanisms=Füge keine neuen Funktionen hinzu, die der Schlüssel nicht bereits zuvor unterstützt hat +no-added-capabilities=Füge keine neuen Funktionen hinzu, die der Schlüssel nicht bereits zuvor unterstützt hat with-key-password.0=Passwort zum Entsperren der privaten Schlüssel with-key-password.1=Ist INDIREKTER Datentyp (z.B.. Datei, Umgebungsvariable, Dateideskriptor...). merge-certs.0=Führe zusätzliche Elemente aus entsprechenden CERTS Objekten mit dem privaten Schlüssel zusammen diff --git a/sop-java-picocli/src/main/resources/msg_version.properties b/sop-java-picocli/src/main/resources/msg_version.properties index c7d0168..1327a78 100644 --- a/sop-java-picocli/src/main/resources/msg_version.properties +++ b/sop-java-picocli/src/main/resources/msg_version.properties @@ -5,6 +5,7 @@ usage.header=Display version information about the tool extended=Print an extended version string backend=Print information about the cryptographic backend sop-spec=Print the latest revision of the SOP specification targeted by the implementation +sopv=Print the SOPV API version standardOutput=version information diff --git a/sop-java-picocli/src/main/resources/msg_version_de.properties b/sop-java-picocli/src/main/resources/msg_version_de.properties index c317916..c99045c 100644 --- a/sop-java-picocli/src/main/resources/msg_version_de.properties +++ b/sop-java-picocli/src/main/resources/msg_version_de.properties @@ -5,6 +5,7 @@ usage.header=Zeige Versionsinformationen extended=Gebe erweiterte Versionsinformationen aus backend=Gebe Informationen über das kryptografische Backend aus sop-spec=Gebe die neuste Revision der SOP Spezifikation aus, welche von dieser Implementierung umgesetzt wird +sopv=Gebe die SOPV API Version aus standardOutput=Versionsinformationen From e893fafb0546246db96560904569aeb9081b956c Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 12:24:15 +0200 Subject: [PATCH 09/10] Update CHANGELOG --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0500cb..9271f3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ SPDX-License-Identifier: Apache-2.0 # Changelog +## 14.0.0-SNAPSHOT +- Implement changes from SOP spec `11`, `12`, `13`, `14` + - Implement `update-key` command + - Implement `merge-certs` command + - Implement `certify-userid` command + - Implement `validate-userid` command + - Add `UnspecificFailure` exception + - Add `KeyCannotCertify` exception + - Add `NoHardwareKeyFound` exception + - Add `HardwareKeyFailure` exception + - Add `PrimaryKeyBad` exception + - Add `CertUserIdNoMatch` exception + - `Verification`: Add support for JSON description extensions +- Remove `animalsniffer` from build dependencies +- Bump `logback` to `1.5.13` + ## 10.1.1 - Prepare jar files for use in native images, e.g. using GraalVM by generating and including configuration files for reflection, resources and dynamic proxies. From e67f8d02505ea71ed64015a6276a1998aef760ba Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 17 Jun 2025 13:22:16 +0200 Subject: [PATCH 10/10] Improve CHANGELOG again --- CHANGELOG.md | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9271f3d..3f32b28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,17 +7,20 @@ SPDX-License-Identifier: Apache-2.0 # Changelog ## 14.0.0-SNAPSHOT -- Implement changes from SOP spec `11`, `12`, `13`, `14` - - Implement `update-key` command - - Implement `merge-certs` command - - Implement `certify-userid` command - - Implement `validate-userid` command - - Add `UnspecificFailure` exception - - Add `KeyCannotCertify` exception - - Add `NoHardwareKeyFound` exception - - Add `HardwareKeyFailure` exception - - Add `PrimaryKeyBad` exception - - Add `CertUserIdNoMatch` exception +- Update implementation to [SOP Specification revision 14](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-14.html), + including changes from revisions `11`, `12`, `13`, `14`. + - Implement newly introduced operations + - `update-key` 'fixes' everything wrong with a key + - `merge-certs` merges a certificate with other copies + - `certify-userid` create signatures over user-ids on certificates + - `validate-userid` validate signatures over user-ids + - Add new exceptions + - `UnspecificFailure` maps generic application errors + - `KeyCannotCertify` signals that a key cannot be used for third-party certifications + - `NoHardwareKeyFound` signals that a key backed by a hardware device cannot be found + - `HardwareKeyFailure` signals a hardware device failure + - `PrimaryKeyBad` signals an unusable or bad primary key + - `CertUserIdNoMatch` signals that a user-id cannot be found/validated on a certificate - `Verification`: Add support for JSON description extensions - Remove `animalsniffer` from build dependencies - Bump `logback` to `1.5.13`