// SPDX-FileCopyrightText: 2023 Paul Schaub // // SPDX-License-Identifier: Apache-2.0 package org.pgpainless.key.generation; 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 java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import org.bouncycastle.openpgp.PGPException; 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.DocumentSignatureType; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.decryption_verification.ConsumerOptions; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.MessageMetadata; import org.pgpainless.encryption_signing.EncryptionOptions; import org.pgpainless.encryption_signing.EncryptionResult; import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.ProducerOptions; import org.pgpainless.encryption_signing.SigningOptions; import org.pgpainless.exception.KeyException; import org.pgpainless.key.TestKeys; 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.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; public class GenerateKeyWithoutPrimaryKeyFlagsTest { @Test public void generateKeyWithoutCertifyKeyFlag_cannotCertifyThirdParties() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { PGPSecretKeyRing secretKeys = 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)) .addUserId("Alice") .build(); PGPPublicKeyRing cert = PGPainless.extractCertificate(secretKeys); KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); assertTrue(info.getValidUserIds().contains("Alice")); long primaryKeyId = info.getKeyId(); assertTrue(info.getKeyFlagsOf("Alice").isEmpty()); assertTrue(info.getKeyFlagsOf(primaryKeyId).isEmpty()); assertFalse(info.isUsableForThirdPartyCertification()); // Key without CERTIFY_OTHER flag cannot be used to certify other keys PGPPublicKeyRing thirdPartyCert = TestKeys.getCryptiePublicKeyRing(); assertThrows(KeyException.UnacceptableThirdPartyCertificationKeyException.class, () -> PGPainless.certify().certificate(thirdPartyCert) .withKey(secretKeys, SecretKeyRingProtector.unprotectedKeys())); // Key without CERTIFY_OTHER flags is usable for encryption and signing ByteArrayOutputStream ciphertext = new ByteArrayOutputStream(); EncryptionStream encryptionStream = PGPainless.encryptAndOrSign() .onOutputStream(ciphertext) .withOptions(ProducerOptions.signAndEncrypt( EncryptionOptions.get().addRecipient(cert), SigningOptions.get().addInlineSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT) )); encryptionStream.write("Hello, World!\n".getBytes(StandardCharsets.UTF_8)); encryptionStream.close(); EncryptionResult result = encryptionStream.getResult(); assertTrue(result.isEncryptedFor(cert)); DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify() .onInputStream(new ByteArrayInputStream(ciphertext.toByteArray())) .withOptions(ConsumerOptions.get().addDecryptionKey(secretKeys) .addVerificationCert(cert)); ByteArrayOutputStream plaintext = new ByteArrayOutputStream(); Streams.pipeAll(decryptionStream, plaintext); decryptionStream.close(); MessageMetadata metadata = decryptionStream.getMetadata(); assertTrue(metadata.isEncryptedFor(cert)); assertTrue(metadata.isVerifiedSignedBy(cert)); } }