1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-06-17 00:54:50 +02:00
pgpainless/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/WrongSignerUserIdTest.java
Paul Schaub ffdbd21491 Implement configuration option for SignerUserId subpacket verification level.
By default we ignore SignerUserId subpackets on signatures.
This behavior can be changed by calling Policy.setSignerUserIdValidationLevel().
Right now, STRICT and DISABLED are available as options, but it may make sense to implement
another option PARTIALLY, which will accept signatures made by key with user-id 'A <foo@bar>'
but where the sig contains a signer user id of value 'foo@bar' for example.
2022-03-14 11:10:12 +01:00

175 lines
8 KiB
Java

// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.decryption_verification;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.key.protection.UnlockSecretKey;
import org.pgpainless.policy.Policy;
import org.pgpainless.util.Passphrase;
public class WrongSignerUserIdTest {
private static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
" Comment: Alice's OpenPGP Transferable Secret Key\n" +
" Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html\n" +
"\n" +
" lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U\n" +
" b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj\n" +
" ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ\n" +
" CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l\n" +
" nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf\n" +
" a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB\n" +
" BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA\n" +
" /3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF\n" +
" u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM\n" +
" hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb\n" +
" Pnn+We1aTBhaGa86AQ==\n" +
" =n8OM\n" +
" -----END PGP PRIVATE KEY BLOCK-----";
private static final String USER_ID = "Alice Lovelace <alice@openpgp.example>";
@Test
public void verificationSucceedsWithDisabledCheck() throws PGPException, IOException {
executeTest(false, true);
}
@Test
public void verificationFailsWithEnabledCheck() throws PGPException, IOException {
executeTest(true, false);
}
@AfterAll
public static void resetDefault() {
PGPainless.getPolicy().setSignerUserIdValidationLevel(Policy.SignerUserIdValidationLevel.DISABLED);
}
public void executeTest(boolean enableCheck, boolean expectSucessfulVerification) throws IOException, PGPException {
PGPainless.getPolicy().setSignerUserIdValidationLevel(enableCheck ? Policy.SignerUserIdValidationLevel.STRICT : Policy.SignerUserIdValidationLevel.DISABLED);
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
assertEquals(USER_ID, secretKeys.getPublicKey().getUserIDs().next());
String messageWithWrongUserId = generateTestMessage(secretKeys);
verifyTestMessage(messageWithWrongUserId, secretKeys, expectSucessfulVerification);
}
private void verifyTestMessage(String messageWithWrongUserId, PGPSecretKeyRing secretKeys, boolean expectSuccessfulVerification) throws IOException, PGPException {
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKeys);
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(
new ByteArrayInputStream(messageWithWrongUserId.getBytes(StandardCharsets.UTF_8)))
.withOptions(new ConsumerOptions()
.addDecryptionKey(secretKeys)
.addVerificationCert(certificate));
ByteArrayOutputStream out = new ByteArrayOutputStream();
Streams.pipeAll(decryptionStream, out);
decryptionStream.close();
OpenPgpMetadata metadata = decryptionStream.getResult();
if (expectSuccessfulVerification) {
assertTrue(metadata.isVerified());
} else {
assertFalse(metadata.isVerified());
}
}
private String generateTestMessage(PGPSecretKeyRing secretKeys) throws PGPException, IOException {
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKeys);
assertEquals(USER_ID, certificate.getPublicKey().getUserIDs().next());
Iterator<PGPSecretKey> keys = secretKeys.getSecretKeys();
PGPSecretKey signingKey = keys.next();
PGPSecretKey encryptionKey = keys.next();
PGPPrivateKey signingPrivKey = UnlockSecretKey.unlockSecretKey(signingKey, Passphrase.emptyPassphrase());
// ARMOR
ByteArrayOutputStream cipherText = new ByteArrayOutputStream();
ArmoredOutputStream armorOut = new ArmoredOutputStream(cipherText);
// ENCRYPTION
PGPDataEncryptorBuilder dataEncryptorBuilder = new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256);
dataEncryptorBuilder.setWithIntegrityPacket(true);
PGPEncryptedDataGenerator encDataGenerator = new PGPEncryptedDataGenerator(dataEncryptorBuilder);
encDataGenerator.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encryptionKey.getPublicKey()));
OutputStream encStream = encDataGenerator.open(armorOut, new byte[4096]);
// COMPRESSION
PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZLIB);
BCPGOutputStream bOut = new BCPGOutputStream(compressedDataGenerator.open(encStream));
// SIGNING
PGPSignatureGenerator sigGen = new PGPSignatureGenerator(
new BcPGPContentSignerBuilder(signingKey.getPublicKey().getAlgorithm(), HashAlgorithm.SHA512.getAlgorithmId()));
sigGen.init(PGPSignature.BINARY_DOCUMENT, signingPrivKey);
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator();
subpacketGenerator.addSignerUserID(false, "Albert Lovelace <albert@openpgp.example>");
sigGen.setHashedSubpackets(subpacketGenerator.generate());
sigGen.generateOnePassVersion(false).encode(bOut);
// LITERAL DATA
PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
OutputStream lOut = literalDataGenerator.open(bOut, PGPLiteralDataGenerator.BINARY,
PGPLiteralDataGenerator.CONSOLE, new Date(), new byte[4096]);
// write msg
ByteArrayInputStream msgIn = new ByteArrayInputStream("Hello, World!\n".getBytes(StandardCharsets.UTF_8));
int ch;
while ((ch = msgIn.read()) >= 0) {
lOut.write(ch);
sigGen.update((byte) ch);
}
lOut.close();
sigGen.generate().encode(bOut);
compressedDataGenerator.close();
encStream.close();
armorOut.close();
return cipherText.toString();
}
}