// SPDX-FileCopyrightText: 2021 Paul Schaub // // SPDX-License-Identifier: Apache-2.0 package org.pgpainless.signature; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.bouncycastle.bcpg.sig.Exportable; import org.bouncycastle.bcpg.sig.IntendedRecipientFingerprint; import org.bouncycastle.bcpg.sig.NotationData; import org.bouncycastle.bcpg.sig.RevocationKey; import org.bouncycastle.bcpg.sig.TrustSignature; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.Feature; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SignatureType; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.TestKeys; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.UnlockSecretKey; import org.pgpainless.policy.Policy; import org.pgpainless.signature.consumer.SignaturePicker; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; public class SignatureSubpacketsUtilTest { @Test public void testGetKeyExpirationTimeAsDate() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() .modernKeyRing("Expire"); Date expiration = Date.from(new Date().toInstant().plus(365, ChronoUnit.DAYS)); secretKeys = PGPainless.modifyKeyRing(secretKeys) .setExpirationDate(expiration, SecretKeyRingProtector.unprotectedKeys()) .done(); PGPSignature expirationSig = SignaturePicker.pickCurrentUserIdCertificationSignature( secretKeys, "Expire", Policy.getInstance(), new Date()); PGPPublicKey notTheRightKey = PGPainless.inspectKeyRing(secretKeys).getSigningSubkeys().get(0); assertThrows(IllegalArgumentException.class, () -> SignatureSubpacketsUtil.getKeyExpirationTimeAsDate(expirationSig, notTheRightKey)); } @Test public void testGetRevocable() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignature withoutRevocable = generator.generateCertification(secretKeys.getPublicKey()); assertNull(SignatureSubpacketsUtil.getRevocable(withoutRevocable)); generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignatureSubpacketGenerator hashed = new PGPSignatureSubpacketGenerator(); hashed.setRevocable(true, true); generator.setHashedSubpackets(hashed.generate()); PGPSignature withRevocable = generator.generateCertification(secretKeys.getPublicKey()); assertNotNull(SignatureSubpacketsUtil.getRevocable(withRevocable)); } @Test public void testParsePreferredCompressionAlgorithms() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignatureSubpacketGenerator hashed = new PGPSignatureSubpacketGenerator(); Set compressionAlgorithmSet = new LinkedHashSet<>(Arrays.asList(CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZIP)); int[] ids = new int[compressionAlgorithmSet.size()]; Iterator it = compressionAlgorithmSet.iterator(); for (int i = 0; i < ids.length; i++) { ids[i] = it.next().getAlgorithmId(); } hashed.setPreferredCompressionAlgorithms(true, ids); generator.setHashedSubpackets(hashed.generate()); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); Set parsed = SignatureSubpacketsUtil.parsePreferredCompressionAlgorithms(signature); assertEquals(compressionAlgorithmSet, parsed); } @Test public void testParseKeyFlagsOfNullIsNull() { assertNull(SignatureSubpacketsUtil.parseKeyFlags(null)); } @Test public void testParseKeyFlagsOfNullSubpacketIsNull() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignature withoutKeyFlags = generator.generateCertification(secretKeys.getPublicKey()); assertNull(SignatureSubpacketsUtil.parseKeyFlags(withoutKeyFlags)); } @Test public void testParseFeaturesIsNullForNullSubpacket() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignature withoutKeyFlags = generator.generateCertification(secretKeys.getPublicKey()); assertNull(SignatureSubpacketsUtil.parseFeatures(withoutKeyFlags)); } @Test public void testParseFeatures() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignatureSubpacketGenerator hashed = new PGPSignatureSubpacketGenerator(); hashed.setFeature(true, Feature.toBitmask(Feature.MODIFICATION_DETECTION, Feature.AEAD_ENCRYPTED_DATA)); generator.setHashedSubpackets(hashed.generate()); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); Set featureSet = SignatureSubpacketsUtil.parseFeatures(signature); assertEquals(2, featureSet.size()); assertTrue(featureSet.contains(Feature.MODIFICATION_DETECTION)); assertTrue(featureSet.contains(Feature.AEAD_ENCRYPTED_DATA)); assertFalse(featureSet.contains(Feature.VERSION_5_PUBLIC_KEY)); } @Test public void getSignatureTargetIsNull() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignature withoutSignatureTarget = generator.generateCertification(secretKeys.getPublicKey()); assertNull(SignatureSubpacketsUtil.getSignatureTarget(withoutSignatureTarget)); } @Test public void testGetUnhashedNotationData() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignatureSubpacketGenerator unhashed = new PGPSignatureSubpacketGenerator(); unhashed.addNotationData(true, true, "test@notation.data", "notation-value"); unhashed.addNotationData(true, true, "test@notation.data", "another-value"); unhashed.addNotationData(true, true, "another@notation.data", "Hello-World!"); generator.setUnhashedSubpackets(unhashed.generate()); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); List notations = SignatureSubpacketsUtil.getUnhashedNotationData(signature); assertEquals(3, notations.size()); assertEquals("test@notation.data", notations.get(0).getNotationName()); assertEquals("test@notation.data", notations.get(1).getNotationName()); assertEquals("another@notation.data", notations.get(2).getNotationName()); assertEquals("notation-value", notations.get(0).getNotationValue()); assertEquals("another-value", notations.get(1).getNotationValue()); assertEquals("Hello-World!", notations.get(2).getNotationValue()); notations = SignatureSubpacketsUtil.getUnhashedNotationData(signature, "test@notation.data"); assertEquals(2, notations.size()); assertEquals("notation-value", notations.get(0).getNotationValue()); assertEquals("another-value", notations.get(1).getNotationValue()); notations = SignatureSubpacketsUtil.getUnhashedNotationData(signature, "invalid"); assertEquals(0, notations.size()); } @Test public void testGetRevocationKeyIsNull() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); assertNull(SignatureSubpacketsUtil.getRevocationKey(signature)); } @Test public void testGetRevocationKey() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignatureSubpacketGenerator hashed = new PGPSignatureSubpacketGenerator(); hashed.addRevocationKey(true, secretKeys.getPublicKey().getAlgorithm(), secretKeys.getPublicKey().getFingerprint()); generator.setHashedSubpackets(hashed.generate()); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); RevocationKey revocationKey = SignatureSubpacketsUtil.getRevocationKey(signature); assertArrayEquals(secretKeys.getPublicKey().getFingerprint(), revocationKey.getFingerprint()); assertEquals(secretKeys.getPublicKey().getAlgorithm(), revocationKey.getAlgorithm()); } @Test public void testGetIntendedRecipientFingerprintsEmpty() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); assertEquals(0, SignatureSubpacketsUtil.getIntendedRecipientFingerprints(signature).size()); } @Test public void testGetIntendedRecipientFingerprints() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignatureSubpacketGenerator hashed = new PGPSignatureSubpacketGenerator(); hashed.addIntendedRecipientFingerprint(true, secretKeys.getPublicKey()); hashed.addIntendedRecipientFingerprint(true, TestKeys.getCryptiePublicKeyRing().getPublicKey()); generator.setHashedSubpackets(hashed.generate()); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); List intendedRecipientFingerprints = SignatureSubpacketsUtil.getIntendedRecipientFingerprints(signature); assertEquals(2, intendedRecipientFingerprints.size()); assertArrayEquals(secretKeys.getPublicKey().getFingerprint(), intendedRecipientFingerprints.get(0).getFingerprint()); assertArrayEquals(TestKeys.getCryptiePublicKeyRing().getPublicKey().getFingerprint(), intendedRecipientFingerprints.get(1).getFingerprint()); } @Test public void testGetExportableCertification() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignatureSubpacketGenerator hashed = new PGPSignatureSubpacketGenerator(); hashed.setExportable(true, true); generator.setHashedSubpackets(hashed.generate()); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); Exportable exportable = SignatureSubpacketsUtil.getExportableCertification(signature); assertNotNull(exportable); assertTrue(exportable.isExportable()); } @Test public void testGetTrustSignature() throws PGPException, IOException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing(); PGPPrivateKey certKey = UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), SecretKeyRingProtector.unprotectedKeys()); PGPSignatureGenerator generator = getSignatureGenerator(certKey, SignatureType.CASUAL_CERTIFICATION); PGPSignatureSubpacketGenerator hashed = new PGPSignatureSubpacketGenerator(); hashed.setTrust(true, 10, 3); generator.setHashedSubpackets(hashed.generate()); PGPSignature signature = generator.generateCertification(secretKeys.getPublicKey()); TrustSignature trustSignature = SignatureSubpacketsUtil.getTrustSignature(signature); assertEquals(10, trustSignature.getDepth()); assertEquals(3, trustSignature.getTrustAmount()); } private PGPSignatureGenerator getSignatureGenerator(PGPPrivateKey signingKey, SignatureType signatureType) throws PGPException { PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( ImplementationFactory.getInstance().getPGPContentSignerBuilder( signingKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithm.SHA512.getAlgorithmId())); signatureGenerator.init(signatureType.getCode(), signingKey); return signatureGenerator; } }