From a4e244111c26e7fc8a3ca5b170665c3cfd442098 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 12 Nov 2021 15:35:13 +0100 Subject: [PATCH] When adding userId: Inherits common signature subpackets from primary userId --- .../algorithm/CompressionAlgorithm.java | 12 +++++++ .../pgpainless/algorithm/HashAlgorithm.java | 12 +++++++ .../algorithm/SymmetricKeyAlgorithm.java | 12 +++++++ .../secretkeyring/SecretKeyRingEditor.java | 34 +++++++++++++++++-- .../key/modification/AddUserIdTest.java | 14 ++++++-- 5 files changed, 79 insertions(+), 5 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/CompressionAlgorithm.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/CompressionAlgorithm.java index b1f11185..b189dfad 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/algorithm/CompressionAlgorithm.java +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/CompressionAlgorithm.java @@ -4,6 +4,8 @@ package org.pgpainless.algorithm; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -54,4 +56,14 @@ public enum CompressionAlgorithm { public int getAlgorithmId() { return algorithmId; } + + public static int[] toAlgorithmIds(Collection algorithms) { + int[] ids = new int[algorithms.size()]; + int i = 0; + for (Iterator iterator = algorithms.iterator(); iterator.hasNext(); ) { + int id = iterator.next().getAlgorithmId(); + ids[i++] = id; + } + return ids; + } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/HashAlgorithm.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/HashAlgorithm.java index b8c97d7e..c7d53b41 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/algorithm/HashAlgorithm.java +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/HashAlgorithm.java @@ -4,7 +4,9 @@ package org.pgpainless.algorithm; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import org.bouncycastle.bcpg.HashAlgorithmTags; @@ -85,4 +87,14 @@ public enum HashAlgorithm { public String getAlgorithmName() { return name; } + + public static int[] toAlgorithmIds(Collection algorithms) { + int[] ids = new int[algorithms.size()]; + int i = 0; + for (Iterator iterator = algorithms.iterator(); iterator.hasNext(); ) { + int id = iterator.next().getAlgorithmId(); + ids[i++] = id; + } + return ids; + } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/algorithm/SymmetricKeyAlgorithm.java b/pgpainless-core/src/main/java/org/pgpainless/algorithm/SymmetricKeyAlgorithm.java index 43c327cc..6d87c731 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/algorithm/SymmetricKeyAlgorithm.java +++ b/pgpainless-core/src/main/java/org/pgpainless/algorithm/SymmetricKeyAlgorithm.java @@ -4,6 +4,8 @@ package org.pgpainless.algorithm; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -124,4 +126,14 @@ public enum SymmetricKeyAlgorithm { public int getAlgorithmId() { return algorithmId; } + + public static int[] toAlgorithmIds(Collection algorithms) { + int[] ids = new int[algorithms.size()]; + int i = 0; + for (Iterator iterator = algorithms.iterator(); iterator.hasNext(); ) { + int id = iterator.next().getAlgorithmId(); + ids[i++] = id; + } + return ids; + } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java index 9c991c08..b2eaf21a 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java @@ -34,6 +34,10 @@ import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.PGPDigestCalculator; +import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.AlgorithmSuite; +import org.pgpainless.algorithm.CompressionAlgorithm; +import org.pgpainless.algorithm.Feature; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SignatureType; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; @@ -41,6 +45,7 @@ import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.key.generation.KeyRingBuilder; import org.pgpainless.key.generation.KeySpec; +import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.CachingSecretKeyRingProtector; import org.pgpainless.key.protection.KeyRingProtectionSettings; import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector; @@ -82,7 +87,25 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { PGPSecretKey primaryKey = secretKeyIterator.next(); PGPPublicKey publicKey = primaryKey.getPublicKey(); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(primaryKey, secretKeyRingProtector); - publicKey = addUserIdToPubKey(userId, privateKey, publicKey); + + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + PGPSignature primaryUserIdSignature = info.getLatestUserIdCertification(info.getPrimaryUserId()); + PGPSignatureSubpacketVector primaryHashedSubpackets = primaryUserIdSignature.getHashedSubPackets(); + + AlgorithmSuite algorithmSuite = AlgorithmSuite.getDefaultAlgorithmSuite(); + PGPSignatureSubpacketGenerator hashedSubpackets = new PGPSignatureSubpacketGenerator(); + hashedSubpackets.setIssuerKeyID(false, primaryKey.getKeyID()); + hashedSubpackets.setIssuerFingerprint(false, primaryKey); + hashedSubpackets.setKeyFlags(true, primaryHashedSubpackets.getKeyFlags()); + hashedSubpackets.setPreferredCompressionAlgorithms(false, + CompressionAlgorithm.toAlgorithmIds(algorithmSuite.getCompressionAlgorithms())); + hashedSubpackets.setPreferredHashAlgorithms(false, + HashAlgorithm.toAlgorithmIds(algorithmSuite.getHashAlgorithms())); + hashedSubpackets.setPreferredSymmetricAlgorithms(false, + SymmetricKeyAlgorithm.toAlgorithmIds(algorithmSuite.getSymmetricKeyAlgorithms())); + hashedSubpackets.setFeature(false, Feature.MODIFICATION_DETECTION.getFeatureId()); + + publicKey = addUserIdToPubKey(userId, privateKey, publicKey, hashedSubpackets.generate(), null); primaryKey = PGPSecretKey.replacePublicKey(primaryKey, publicKey); secretKeyList.add(primaryKey); @@ -96,12 +119,19 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { return this; } - private static PGPPublicKey addUserIdToPubKey(String userId, PGPPrivateKey privateKey, PGPPublicKey publicKey) throws PGPException { + private static PGPPublicKey addUserIdToPubKey(String userId, + PGPPrivateKey privateKey, + PGPPublicKey publicKey, + @Nullable PGPSignatureSubpacketVector hashedSubpackets, + @Nullable PGPSignatureSubpacketVector unhashedSubpackets) + throws PGPException { if (privateKey.getKeyID() != publicKey.getKeyID()) { throw new IllegalArgumentException("Key-ID mismatch!"); } // Create signature with new user-id and add it to the public key PGPSignatureGenerator signatureGenerator = SignatureUtils.getSignatureGeneratorFor(publicKey); + signatureGenerator.setHashedSubpackets(hashedSubpackets); + signatureGenerator.setUnhashedSubpackets(unhashedSubpackets); signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey); PGPSignature userIdSignature = signatureGenerator.generateCertification(userId, publicKey); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddUserIdTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddUserIdTest.java index f8998c2b..be3e8e17 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddUserIdTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddUserIdTest.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.util.Iterator; +import java.util.List; import java.util.NoSuchElementException; import org.bouncycastle.openpgp.PGPException; @@ -19,6 +20,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.TestKeys; import org.pgpainless.key.info.KeyRingInfo; @@ -31,16 +33,20 @@ public class AddUserIdTest { @ParameterizedTest @MethodSource("org.pgpainless.util.TestImplementationFactoryProvider#provideImplementationFactories") - public void addUserIdToExistingKeyRing(ImplementationFactory implementationFactory) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, InterruptedException { + + public void addUserIdToExistingKeyRing(ImplementationFactory implementationFactory) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { ImplementationFactory.setFactoryImplementation(implementationFactory); - PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("alice@wonderland.lit", "rabb1th0le"); + PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() + .simpleEcKeyRing("alice@wonderland.lit", "rabb1th0le"); KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys); Iterator userIds = info.getValidUserIds().iterator(); assertEquals("alice@wonderland.lit", userIds.next()); assertFalse(userIds.hasNext()); + List primaryUserIdKeyFlags = info.getKeyFlagsOf(info.getPrimaryUserId()); - SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector.forKey(secretKeys, Passphrase.fromPassword("rabb1th0le")); + SecretKeyRingProtector protector = PasswordBasedSecretKeyRingProtector + .forKey(secretKeys, Passphrase.fromPassword("rabb1th0le")); secretKeys = PGPainless.modifyKeyRing(secretKeys) .addUserId("cheshirecat@wonderland.lit", protector) .done(); @@ -50,6 +56,8 @@ public class AddUserIdTest { assertEquals("alice@wonderland.lit", userIds.next()); assertEquals("cheshirecat@wonderland.lit", userIds.next()); assertFalse(userIds.hasNext()); + info = PGPainless.inspectKeyRing(secretKeys); + assertEquals(primaryUserIdKeyFlags, info.getKeyFlagsOf("cheshirecat@wonderland.lit")); secretKeys = PGPainless.modifyKeyRing(secretKeys) .revokeUserId("cheshirecat@wonderland.lit", protector)