1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 10:19:39 +02:00

Port SigningTest

This commit is contained in:
Paul Schaub 2025-03-24 13:21:23 +01:00
parent bab448eb6d
commit 9a7aeae9fa
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
3 changed files with 64 additions and 62 deletions

View file

@ -312,6 +312,7 @@ class SigningOptions(private val api: PGPainless) {
signatureType: DocumentSignatureType signatureType: DocumentSignatureType
) = apply { addDetachedSignature(signingKeyProtector, signingKey, null, signatureType) } ) = apply { addDetachedSignature(signingKeyProtector, signingKey, null, signatureType) }
@JvmOverloads
fun addDetachedSignature( fun addDetachedSignature(
signingKeyProtector: SecretKeyRingProtector, signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPKey, signingKey: OpenPGPKey,

View file

@ -13,6 +13,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.openpgp.api.KeyPassphraseProvider import org.bouncycastle.openpgp.api.KeyPassphraseProvider
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider
@ -153,6 +154,13 @@ interface SecretKeyRingProtector : KeyPassphraseProvider {
fun unlockSingleKeyWith(passphrase: Passphrase, key: PGPSecretKey): SecretKeyRingProtector = fun unlockSingleKeyWith(passphrase: Passphrase, key: PGPSecretKey): SecretKeyRingProtector =
PasswordBasedSecretKeyRingProtector.forKey(key, passphrase) 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 * 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 * will only return a non-null encryptor/decryptor based on the provided passphrase if

View file

@ -14,15 +14,10 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.bouncycastle.openpgp.PGPException; 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.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKey; import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.util.io.Streams; import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.TestTemplate; 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.generation.type.eddsa_legacy.EdDSALegacyCurve;
import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.util.MultiMap; import org.pgpainless.util.MultiMap;
import org.pgpainless.util.Passphrase; import org.pgpainless.util.Passphrase;
import org.pgpainless.util.TestAllImplementations; import org.pgpainless.util.TestAllImplementations;
@ -53,26 +47,27 @@ public class SigningTest {
@ExtendWith(TestAllImplementations.class) @ExtendWith(TestAllImplementations.class)
public void testEncryptionAndSignatureVerification() public void testEncryptionAndSignatureVerification()
throws IOException, PGPException { throws IOException, PGPException {
PGPainless api = PGPainless.getInstance();
PGPPublicKeyRing julietKeys = TestKeys.getJulietPublicKeyRing(); OpenPGPCertificate julietCert = TestKeys.getJulietCertificate();
PGPPublicKeyRing romeoKeys = TestKeys.getRomeoPublicKeyRing(); OpenPGPCertificate romeoCert = TestKeys.getRomeoCertificate();
PGPSecretKeyRing cryptieKeys = TestKeys.getCryptieSecretKeyRing(); OpenPGPKey cryptieKey = TestKeys.getCryptieKey();
KeyRingInfo cryptieInfo = new KeyRingInfo(cryptieKeys); KeyRingInfo cryptieInfo = api.inspect(cryptieKey);
PGPSecretKey cryptieSigningKey = cryptieKeys.getSecretKey(cryptieInfo.getSigningSubkeys().get(0).getKeyIdentifier()); OpenPGPKey.OpenPGPSecretKey cryptieSigningKey = cryptieKey.getSecretKey(cryptieInfo.getSigningSubkeys().get(0).getKeyIdentifier());
PGPPublicKeyRingCollection keys = new PGPPublicKeyRingCollection(Arrays.asList(julietKeys, romeoKeys));
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign() EncryptionStream encryptionStream = api.generateMessage()
.onOutputStream(out) .onOutputStream(out)
.withOptions(ProducerOptions.signAndEncrypt( .withOptions(ProducerOptions.signAndEncrypt(
EncryptionOptions.encryptDataAtRest() EncryptionOptions.encryptDataAtRest(api)
.addRecipients(keys) .addRecipient(romeoCert)
.addRecipient(KeyRingUtils.publicKeyRingFrom(cryptieKeys)), .addRecipient(julietCert)
SigningOptions.get().addInlineSignature( .addRecipient(cryptieKey.toCertificate()),
SigningOptions.get(api).addInlineSignature(
SecretKeyRingProtector.unlockSingleKeyWith(TestKeys.CRYPTIE_PASSPHRASE, cryptieSigningKey), 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)); ).setAsciiArmor(true));
byte[] messageBytes = "This message is signed and encrypted to Romeo and Juliet." byte[] messageBytes = "This message is signed and encrypted to Romeo and Juliet."
@ -85,19 +80,16 @@ public class SigningTest {
byte[] encrypted = out.toByteArray(); byte[] encrypted = out.toByteArray();
ByteArrayInputStream cryptIn = new ByteArrayInputStream(encrypted); ByteArrayInputStream cryptIn = new ByteArrayInputStream(encrypted);
PGPSecretKeyRing romeoSecret = TestKeys.getRomeoSecretKeyRing(); OpenPGPKey romeoKey = TestKeys.getRomeoKey();
PGPSecretKeyRing julietSecret = TestKeys.getJulietSecretKeyRing(); OpenPGPKey julietKey = TestKeys.getJulietKey();
PGPSecretKeyRingCollection secretKeys = new PGPSecretKeyRingCollection(
Arrays.asList(romeoSecret, julietSecret));
PGPPublicKeyRingCollection verificationKeys = new PGPPublicKeyRingCollection(
Arrays.asList(KeyRingUtils.publicKeyRingFrom(cryptieKeys), romeoKeys));
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(cryptIn) .onInputStream(cryptIn)
.withOptions(ConsumerOptions.get() .withOptions(ConsumerOptions.get(api)
.addDecryptionKeys(secretKeys, SecretKeyRingProtector.unprotectedKeys()) .addDecryptionKey(romeoKey, SecretKeyRingProtector.unprotectedKeys())
.addVerificationCerts(verificationKeys) .addDecryptionKey(julietKey, SecretKeyRingProtector.unprotectedKeys())
.addVerificationCert(cryptieKey.toCertificate())
.addVerificationCert(romeoCert)
); );
ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream(); ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream();
@ -108,19 +100,19 @@ public class SigningTest {
MessageMetadata metadata = decryptionStream.getMetadata(); MessageMetadata metadata = decryptionStream.getMetadata();
assertTrue(metadata.isEncrypted()); assertTrue(metadata.isEncrypted());
assertTrue(metadata.isVerifiedSigned()); assertTrue(metadata.isVerifiedSigned());
assertTrue(metadata.isVerifiedSignedBy(KeyRingUtils.publicKeyRingFrom(cryptieKeys))); assertTrue(metadata.isVerifiedSignedBy(cryptieKey));
assertFalse(metadata.isVerifiedSignedBy(julietKeys)); assertFalse(metadata.isVerifiedSignedBy(julietCert));
} }
@TestTemplate @TestTemplate
@ExtendWith(TestAllImplementations.class) @ExtendWith(TestAllImplementations.class)
public void testSignWithInvalidUserIdFails() { public void testSignWithInvalidUserIdFails() {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() PGPainless api = PGPainless.getInstance();
.modernKeyRing("alice", "password123") OpenPGPKey secretKeys = api.generateKey()
.getPGPSecretKeyRing(); .modernKeyRing("alice", "password123");
SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword("password123")); SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword("password123"));
SigningOptions opts = SigningOptions.get(); SigningOptions opts = SigningOptions.get(api);
// "bob" is not a valid user-id // "bob" is not a valid user-id
assertThrows(KeyException.UnboundUserIdException.class, assertThrows(KeyException.UnboundUserIdException.class,
() -> opts.addInlineSignature(protector, secretKeys, "bob", () -> opts.addInlineSignature(protector, secretKeys, "bob",
@ -152,10 +144,11 @@ public class SigningTest {
@TestTemplate @TestTemplate
@ExtendWith(TestAllImplementations.class) @ExtendWith(TestAllImplementations.class)
public void signWithHashAlgorithmOverride() throws PGPException, IOException { public void signWithHashAlgorithmOverride() throws PGPException, IOException {
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = TestKeys.getEmilKey();
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
SigningOptions options = SigningOptions.get(); SigningOptions options = SigningOptions.get(api);
assertNull(options.getHashAlgorithmOverride()); assertNull(options.getHashAlgorithmOverride());
options.overrideHashAlgorithm(HashAlgorithm.SHA224); options.overrideHashAlgorithm(HashAlgorithm.SHA224);
@ -164,9 +157,9 @@ public class SigningTest {
options.addDetachedSignature(protector, secretKeys, DocumentSignatureType.BINARY_DOCUMENT); options.addDetachedSignature(protector, secretKeys, DocumentSignatureType.BINARY_DOCUMENT);
String data = "Hello, World!\n"; String data = "Hello, World!\n";
EncryptionStream signer = PGPainless.encryptAndOrSign() EncryptionStream signer = api.generateMessage()
.onOutputStream(new ByteArrayOutputStream()) .onOutputStream(new ByteArrayOutputStream())
.withOptions(ProducerOptions.sign(options)); .withOptions(ProducerOptions.sign(options, api));
Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer); Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer);
signer.close(); signer.close();
@ -185,21 +178,21 @@ public class SigningTest {
@ExtendWith(TestAllImplementations.class) @ExtendWith(TestAllImplementations.class)
public void negotiateHashAlgorithmChoseFallbackIfEmptyPreferences() public void negotiateHashAlgorithmChoseFallbackIfEmptyPreferences()
throws PGPException, IOException { throws PGPException, IOException {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing() PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.buildKey()
.setPrimaryKey(KeySpec.getBuilder( .setPrimaryKey(KeySpec.getBuilder(
KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
.overridePreferredHashAlgorithms()) .overridePreferredHashAlgorithms())
.addUserId("Alice") .addUserId("Alice")
.build() .build();
.getPGPSecretKeyRing();
SigningOptions options = SigningOptions.get() SigningOptions options = SigningOptions.get(api)
.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, .addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys,
DocumentSignatureType.BINARY_DOCUMENT); DocumentSignatureType.BINARY_DOCUMENT);
String data = "Hello, World!\n"; String data = "Hello, World!\n";
EncryptionStream signer = PGPainless.encryptAndOrSign() EncryptionStream signer = api.generateMessage()
.onOutputStream(new ByteArrayOutputStream()) .onOutputStream(new ByteArrayOutputStream())
.withOptions(ProducerOptions.sign(options)); .withOptions(ProducerOptions.sign(options, api));
Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer); Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer);
signer.close(); signer.close();
@ -208,7 +201,7 @@ public class SigningTest {
SubkeyIdentifier signingKey = sigs.keySet().iterator().next(); SubkeyIdentifier signingKey = sigs.keySet().iterator().next();
PGPSignature signature = sigs.get(signingKey).iterator().next(); PGPSignature signature = sigs.get(signingKey).iterator().next();
assertEquals(PGPainless.getPolicy().getDataSignatureHashAlgorithmPolicy().defaultHashAlgorithm().getAlgorithmId(), assertEquals(api.getAlgorithmPolicy().getDataSignatureHashAlgorithmPolicy().defaultHashAlgorithm().getAlgorithmId(),
signature.getHashAlgorithm()); signature.getHashAlgorithm());
} }
@ -216,21 +209,21 @@ public class SigningTest {
@ExtendWith(TestAllImplementations.class) @ExtendWith(TestAllImplementations.class)
public void negotiateHashAlgorithmChoseFallbackIfUnacceptablePreferences() public void negotiateHashAlgorithmChoseFallbackIfUnacceptablePreferences()
throws PGPException, IOException { throws PGPException, IOException {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing() PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.buildKey()
.setPrimaryKey( .setPrimaryKey(
KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
.overridePreferredHashAlgorithms(HashAlgorithm.MD5)) .overridePreferredHashAlgorithms(HashAlgorithm.MD5))
.addUserId("Alice") .addUserId("Alice")
.build() .build();
.getPGPSecretKeyRing();
SigningOptions options = SigningOptions.get() SigningOptions options = SigningOptions.get(api)
.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, .addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys,
DocumentSignatureType.BINARY_DOCUMENT); DocumentSignatureType.BINARY_DOCUMENT);
String data = "Hello, World!\n"; String data = "Hello, World!\n";
EncryptionStream signer = PGPainless.encryptAndOrSign() EncryptionStream signer = api.generateMessage()
.onOutputStream(new ByteArrayOutputStream()) .onOutputStream(new ByteArrayOutputStream())
.withOptions(ProducerOptions.sign(options)); .withOptions(ProducerOptions.sign(options, api));
Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer); Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), signer);
signer.close(); signer.close();
@ -239,20 +232,20 @@ public class SigningTest {
SubkeyIdentifier signingKey = sigs.keySet().iterator().next(); SubkeyIdentifier signingKey = sigs.keySet().iterator().next();
PGPSignature signature = sigs.get(signingKey).iterator().next(); PGPSignature signature = sigs.get(signingKey).iterator().next();
assertEquals(PGPainless.getPolicy().getDataSignatureHashAlgorithmPolicy().defaultHashAlgorithm().getAlgorithmId(), assertEquals(api.getAlgorithmPolicy().getDataSignatureHashAlgorithmPolicy().defaultHashAlgorithm().getAlgorithmId(),
signature.getHashAlgorithm()); signature.getHashAlgorithm());
} }
@TestTemplate @TestTemplate
@ExtendWith(TestAllImplementations.class) @ExtendWith(TestAllImplementations.class)
public void signingWithNonCapableKeyThrowsKeyCannotSignException() { 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)) .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER))
.addUserId("Alice") .addUserId("Alice")
.build() .build();
.getPGPSecretKeyRing();
SigningOptions options = SigningOptions.get(); SigningOptions options = SigningOptions.get(api);
assertThrows(KeyException.UnacceptableSigningKeyException.class, () -> options.addDetachedSignature( assertThrows(KeyException.UnacceptableSigningKeyException.class, () -> options.addDetachedSignature(
SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT)); SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT));
assertThrows(KeyException.UnacceptableSigningKeyException.class, () -> options.addInlineSignature( assertThrows(KeyException.UnacceptableSigningKeyException.class, () -> options.addInlineSignature(
@ -262,14 +255,14 @@ public class SigningTest {
@TestTemplate @TestTemplate
@ExtendWith(TestAllImplementations.class) @ExtendWith(TestAllImplementations.class)
public void signWithInvalidUserIdThrowsKeyValidationError() { public void signWithInvalidUserIdThrowsKeyValidationError() {
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing() PGPainless api = PGPainless.getInstance();
OpenPGPKey secretKeys = api.buildKey()
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519),
KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
.addUserId("Alice") .addUserId("Alice")
.build() .build();
.getPGPSecretKeyRing();
SigningOptions options = SigningOptions.get(); SigningOptions options = SigningOptions.get(api);
assertThrows(KeyException.UnboundUserIdException.class, () -> assertThrows(KeyException.UnboundUserIdException.class, () ->
options.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, "Bob", options.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, "Bob",
DocumentSignatureType.BINARY_DOCUMENT)); DocumentSignatureType.BINARY_DOCUMENT));