From 9a7aeae9fadedc1c10b0a65aa5cd6af3c511fe96 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 24 Mar 2025 13:21:23 +0100 Subject: [PATCH] Port SigningTest --- .../encryption_signing/SigningOptions.kt | 1 + .../key/protection/SecretKeyRingProtector.kt | 8 ++ .../encryption_signing/SigningTest.java | 117 ++++++++---------- 3 files changed, 64 insertions(+), 62 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt index 0d518c96..f34e6dd1 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt @@ -312,6 +312,7 @@ class SigningOptions(private val api: PGPainless) { signatureType: DocumentSignatureType ) = apply { addDetachedSignature(signingKeyProtector, signingKey, null, signatureType) } + @JvmOverloads fun addDetachedSignature( signingKeyProtector: SecretKeyRingProtector, signingKey: OpenPGPKey, diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/SecretKeyRingProtector.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/SecretKeyRingProtector.kt index 23f455e7..5de7de48 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/SecretKeyRingProtector.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/SecretKeyRingProtector.kt @@ -13,6 +13,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.openpgp.api.KeyPassphraseProvider import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey import org.bouncycastle.openpgp.api.OpenPGPKey +import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider @@ -153,6 +154,13 @@ interface SecretKeyRingProtector : KeyPassphraseProvider { fun unlockSingleKeyWith(passphrase: Passphrase, key: PGPSecretKey): SecretKeyRingProtector = PasswordBasedSecretKeyRingProtector.forKey(key, passphrase) + @JvmStatic + fun unlockSingleKeyWith( + passphrase: Passphrase, + key: OpenPGPSecretKey + ): SecretKeyRingProtector = + PasswordBasedSecretKeyRingProtector.forKey(key.pgpSecretKey, passphrase) + /** * Use the provided passphrase to lock/unlock only the provided (sub-)key. This protector * will only return a non-null encryptor/decryptor based on the provided passphrase if diff --git a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java index 6fb0192b..0f10f71d 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/encryption_signing/SigningTest.java @@ -14,15 +14,10 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; import org.bouncycastle.openpgp.api.OpenPGPKey; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.TestTemplate; @@ -42,7 +37,6 @@ import org.pgpainless.key.generation.type.KeyType; import org.pgpainless.key.generation.type.eddsa_legacy.EdDSALegacyCurve; import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.util.MultiMap; import org.pgpainless.util.Passphrase; import org.pgpainless.util.TestAllImplementations; @@ -53,26 +47,27 @@ public class SigningTest { @ExtendWith(TestAllImplementations.class) public void testEncryptionAndSignatureVerification() throws IOException, PGPException { + PGPainless api = PGPainless.getInstance(); - PGPPublicKeyRing julietKeys = TestKeys.getJulietPublicKeyRing(); - PGPPublicKeyRing romeoKeys = TestKeys.getRomeoPublicKeyRing(); + OpenPGPCertificate julietCert = TestKeys.getJulietCertificate(); + OpenPGPCertificate romeoCert = TestKeys.getRomeoCertificate(); - PGPSecretKeyRing cryptieKeys = TestKeys.getCryptieSecretKeyRing(); - KeyRingInfo cryptieInfo = new KeyRingInfo(cryptieKeys); - PGPSecretKey cryptieSigningKey = cryptieKeys.getSecretKey(cryptieInfo.getSigningSubkeys().get(0).getKeyIdentifier()); - - PGPPublicKeyRingCollection keys = new PGPPublicKeyRingCollection(Arrays.asList(julietKeys, romeoKeys)); + OpenPGPKey cryptieKey = TestKeys.getCryptieKey(); + KeyRingInfo cryptieInfo = api.inspect(cryptieKey); + OpenPGPKey.OpenPGPSecretKey cryptieSigningKey = cryptieKey.getSecretKey(cryptieInfo.getSigningSubkeys().get(0).getKeyIdentifier()); ByteArrayOutputStream out = new ByteArrayOutputStream(); - EncryptionStream encryptionStream = PGPainless.encryptAndOrSign() + EncryptionStream encryptionStream = api.generateMessage() .onOutputStream(out) .withOptions(ProducerOptions.signAndEncrypt( - EncryptionOptions.encryptDataAtRest() - .addRecipients(keys) - .addRecipient(KeyRingUtils.publicKeyRingFrom(cryptieKeys)), - SigningOptions.get().addInlineSignature( + EncryptionOptions.encryptDataAtRest(api) + .addRecipient(romeoCert) + .addRecipient(julietCert) + .addRecipient(cryptieKey.toCertificate()), + SigningOptions.get(api).addInlineSignature( SecretKeyRingProtector.unlockSingleKeyWith(TestKeys.CRYPTIE_PASSPHRASE, cryptieSigningKey), - cryptieKeys, TestKeys.CRYPTIE_UID, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT) + cryptieKey, TestKeys.CRYPTIE_UID, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT), + api ).setAsciiArmor(true)); byte[] messageBytes = "This message is signed and encrypted to Romeo and Juliet." @@ -85,19 +80,16 @@ public class SigningTest { byte[] encrypted = out.toByteArray(); ByteArrayInputStream cryptIn = new ByteArrayInputStream(encrypted); - PGPSecretKeyRing romeoSecret = TestKeys.getRomeoSecretKeyRing(); - PGPSecretKeyRing julietSecret = TestKeys.getJulietSecretKeyRing(); - - PGPSecretKeyRingCollection secretKeys = new PGPSecretKeyRingCollection( - Arrays.asList(romeoSecret, julietSecret)); - PGPPublicKeyRingCollection verificationKeys = new PGPPublicKeyRingCollection( - Arrays.asList(KeyRingUtils.publicKeyRingFrom(cryptieKeys), romeoKeys)); + OpenPGPKey romeoKey = TestKeys.getRomeoKey(); + OpenPGPKey julietKey = TestKeys.getJulietKey(); DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(cryptIn) - .withOptions(ConsumerOptions.get() - .addDecryptionKeys(secretKeys, SecretKeyRingProtector.unprotectedKeys()) - .addVerificationCerts(verificationKeys) + .withOptions(ConsumerOptions.get(api) + .addDecryptionKey(romeoKey, SecretKeyRingProtector.unprotectedKeys()) + .addDecryptionKey(julietKey, SecretKeyRingProtector.unprotectedKeys()) + .addVerificationCert(cryptieKey.toCertificate()) + .addVerificationCert(romeoCert) ); ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream(); @@ -108,19 +100,19 @@ public class SigningTest { MessageMetadata metadata = decryptionStream.getMetadata(); assertTrue(metadata.isEncrypted()); assertTrue(metadata.isVerifiedSigned()); - assertTrue(metadata.isVerifiedSignedBy(KeyRingUtils.publicKeyRingFrom(cryptieKeys))); - assertFalse(metadata.isVerifiedSignedBy(julietKeys)); + assertTrue(metadata.isVerifiedSignedBy(cryptieKey)); + assertFalse(metadata.isVerifiedSignedBy(julietCert)); } @TestTemplate @ExtendWith(TestAllImplementations.class) public void testSignWithInvalidUserIdFails() { - PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() - .modernKeyRing("alice", "password123") - .getPGPSecretKeyRing(); + PGPainless api = PGPainless.getInstance(); + OpenPGPKey secretKeys = api.generateKey() + .modernKeyRing("alice", "password123"); SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword("password123")); - SigningOptions opts = SigningOptions.get(); + SigningOptions opts = SigningOptions.get(api); // "bob" is not a valid user-id assertThrows(KeyException.UnboundUserIdException.class, () -> opts.addInlineSignature(protector, secretKeys, "bob", @@ -152,10 +144,11 @@ public class SigningTest { @TestTemplate @ExtendWith(TestAllImplementations.class) public void signWithHashAlgorithmOverride() throws PGPException, IOException { - PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); + PGPainless api = PGPainless.getInstance(); + OpenPGPKey secretKeys = TestKeys.getEmilKey(); SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); - SigningOptions options = SigningOptions.get(); + SigningOptions options = SigningOptions.get(api); assertNull(options.getHashAlgorithmOverride()); options.overrideHashAlgorithm(HashAlgorithm.SHA224); @@ -164,9 +157,9 @@ public class SigningTest { options.addDetachedSignature(protector, secretKeys, DocumentSignatureType.BINARY_DOCUMENT); String data = "Hello, World!\n"; - EncryptionStream signer = PGPainless.encryptAndOrSign() + EncryptionStream signer = api.generateMessage() .onOutputStream(new ByteArrayOutputStream()) - .withOptions(ProducerOptions.sign(options)); + .withOptions(ProducerOptions.sign(options, api)); Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer); signer.close(); @@ -185,21 +178,21 @@ public class SigningTest { @ExtendWith(TestAllImplementations.class) public void negotiateHashAlgorithmChoseFallbackIfEmptyPreferences() throws PGPException, IOException { - PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing() + PGPainless api = PGPainless.getInstance(); + OpenPGPKey secretKeys = api.buildKey() .setPrimaryKey(KeySpec.getBuilder( KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) .overridePreferredHashAlgorithms()) .addUserId("Alice") - .build() - .getPGPSecretKeyRing(); + .build(); - SigningOptions options = SigningOptions.get() + SigningOptions options = SigningOptions.get(api) .addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT); String data = "Hello, World!\n"; - EncryptionStream signer = PGPainless.encryptAndOrSign() + EncryptionStream signer = api.generateMessage() .onOutputStream(new ByteArrayOutputStream()) - .withOptions(ProducerOptions.sign(options)); + .withOptions(ProducerOptions.sign(options, api)); Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer); signer.close(); @@ -208,7 +201,7 @@ public class SigningTest { SubkeyIdentifier signingKey = sigs.keySet().iterator().next(); PGPSignature signature = sigs.get(signingKey).iterator().next(); - assertEquals(PGPainless.getPolicy().getDataSignatureHashAlgorithmPolicy().defaultHashAlgorithm().getAlgorithmId(), + assertEquals(api.getAlgorithmPolicy().getDataSignatureHashAlgorithmPolicy().defaultHashAlgorithm().getAlgorithmId(), signature.getHashAlgorithm()); } @@ -216,21 +209,21 @@ public class SigningTest { @ExtendWith(TestAllImplementations.class) public void negotiateHashAlgorithmChoseFallbackIfUnacceptablePreferences() throws PGPException, IOException { - PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing() + PGPainless api = PGPainless.getInstance(); + OpenPGPKey secretKeys = api.buildKey() .setPrimaryKey( KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) .overridePreferredHashAlgorithms(HashAlgorithm.MD5)) .addUserId("Alice") - .build() - .getPGPSecretKeyRing(); + .build(); - SigningOptions options = SigningOptions.get() + SigningOptions options = SigningOptions.get(api) .addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT); String data = "Hello, World!\n"; - EncryptionStream signer = PGPainless.encryptAndOrSign() + EncryptionStream signer = api.generateMessage() .onOutputStream(new ByteArrayOutputStream()) - .withOptions(ProducerOptions.sign(options)); + .withOptions(ProducerOptions.sign(options, api)); Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer); signer.close(); @@ -239,20 +232,20 @@ public class SigningTest { SubkeyIdentifier signingKey = sigs.keySet().iterator().next(); PGPSignature signature = sigs.get(signingKey).iterator().next(); - assertEquals(PGPainless.getPolicy().getDataSignatureHashAlgorithmPolicy().defaultHashAlgorithm().getAlgorithmId(), + assertEquals(api.getAlgorithmPolicy().getDataSignatureHashAlgorithmPolicy().defaultHashAlgorithm().getAlgorithmId(), signature.getHashAlgorithm()); } @TestTemplate @ExtendWith(TestAllImplementations.class) public void signingWithNonCapableKeyThrowsKeyCannotSignException() { - PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing() + PGPainless api = PGPainless.getInstance(); + OpenPGPKey secretKeys = api.buildKey() .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER)) .addUserId("Alice") - .build() - .getPGPSecretKeyRing(); + .build(); - SigningOptions options = SigningOptions.get(); + SigningOptions options = SigningOptions.get(api); assertThrows(KeyException.UnacceptableSigningKeyException.class, () -> options.addDetachedSignature( SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT)); assertThrows(KeyException.UnacceptableSigningKeyException.class, () -> options.addInlineSignature( @@ -262,14 +255,14 @@ public class SigningTest { @TestTemplate @ExtendWith(TestAllImplementations.class) public void signWithInvalidUserIdThrowsKeyValidationError() { - PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing() + PGPainless api = PGPainless.getInstance(); + OpenPGPKey secretKeys = api.buildKey() .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) .addUserId("Alice") - .build() - .getPGPSecretKeyRing(); + .build(); - SigningOptions options = SigningOptions.get(); + SigningOptions options = SigningOptions.get(api); assertThrows(KeyException.UnboundUserIdException.class, () -> options.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, "Bob", DocumentSignatureType.BINARY_DOCUMENT));