1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-15 00:42:06 +01:00

When adding userId: Inherits common signature subpackets from primary userId

This commit is contained in:
Paul Schaub 2021-11-12 15:35:13 +01:00
parent 1b12cc1e09
commit a4e244111c
5 changed files with 79 additions and 5 deletions

View file

@ -4,6 +4,8 @@
package org.pgpainless.algorithm; package org.pgpainless.algorithm;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -54,4 +56,14 @@ public enum CompressionAlgorithm {
public int getAlgorithmId() { public int getAlgorithmId() {
return algorithmId; return algorithmId;
} }
public static int[] toAlgorithmIds(Collection<CompressionAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
int i = 0;
for (Iterator<CompressionAlgorithm> iterator = algorithms.iterator(); iterator.hasNext(); ) {
int id = iterator.next().getAlgorithmId();
ids[i++] = id;
}
return ids;
}
} }

View file

@ -4,7 +4,9 @@
package org.pgpainless.algorithm; package org.pgpainless.algorithm;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.bcpg.HashAlgorithmTags;
@ -85,4 +87,14 @@ public enum HashAlgorithm {
public String getAlgorithmName() { public String getAlgorithmName() {
return name; return name;
} }
public static int[] toAlgorithmIds(Collection<HashAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
int i = 0;
for (Iterator<HashAlgorithm> iterator = algorithms.iterator(); iterator.hasNext(); ) {
int id = iterator.next().getAlgorithmId();
ids[i++] = id;
}
return ids;
}
} }

View file

@ -4,6 +4,8 @@
package org.pgpainless.algorithm; package org.pgpainless.algorithm;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -124,4 +126,14 @@ public enum SymmetricKeyAlgorithm {
public int getAlgorithmId() { public int getAlgorithmId() {
return algorithmId; return algorithmId;
} }
public static int[] toAlgorithmIds(Collection<SymmetricKeyAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
int i = 0;
for (Iterator<SymmetricKeyAlgorithm> iterator = algorithms.iterator(); iterator.hasNext(); ) {
int id = iterator.next().getAlgorithmId();
ids[i++] = id;
}
return ids;
}
} }

View file

@ -34,6 +34,10 @@ import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator; 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.HashAlgorithm;
import org.pgpainless.algorithm.SignatureType; import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
@ -41,6 +45,7 @@ import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.key.OpenPgpFingerprint;
import org.pgpainless.key.generation.KeyRingBuilder; import org.pgpainless.key.generation.KeyRingBuilder;
import org.pgpainless.key.generation.KeySpec; import org.pgpainless.key.generation.KeySpec;
import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.CachingSecretKeyRingProtector; import org.pgpainless.key.protection.CachingSecretKeyRingProtector;
import org.pgpainless.key.protection.KeyRingProtectionSettings; import org.pgpainless.key.protection.KeyRingProtectionSettings;
import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector; import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector;
@ -82,7 +87,25 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
PGPSecretKey primaryKey = secretKeyIterator.next(); PGPSecretKey primaryKey = secretKeyIterator.next();
PGPPublicKey publicKey = primaryKey.getPublicKey(); PGPPublicKey publicKey = primaryKey.getPublicKey();
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(primaryKey, secretKeyRingProtector); 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); primaryKey = PGPSecretKey.replacePublicKey(primaryKey, publicKey);
secretKeyList.add(primaryKey); secretKeyList.add(primaryKey);
@ -96,12 +119,19 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
return this; 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()) { if (privateKey.getKeyID() != publicKey.getKeyID()) {
throw new IllegalArgumentException("Key-ID mismatch!"); throw new IllegalArgumentException("Key-ID mismatch!");
} }
// Create signature with new user-id and add it to the public key // Create signature with new user-id and add it to the public key
PGPSignatureGenerator signatureGenerator = SignatureUtils.getSignatureGeneratorFor(publicKey); PGPSignatureGenerator signatureGenerator = SignatureUtils.getSignatureGeneratorFor(publicKey);
signatureGenerator.setHashedSubpackets(hashedSubpackets);
signatureGenerator.setUnhashedSubpackets(unhashedSubpackets);
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey); signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
PGPSignature userIdSignature = signatureGenerator.generateCertification(userId, publicKey); PGPSignature userIdSignature = signatureGenerator.generateCertification(userId, publicKey);

View file

@ -12,6 +12,7 @@ import java.io.IOException;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import org.bouncycastle.openpgp.PGPException; 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.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.TestKeys; import org.pgpainless.key.TestKeys;
import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.info.KeyRingInfo;
@ -31,16 +33,20 @@ public class AddUserIdTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("org.pgpainless.util.TestImplementationFactoryProvider#provideImplementationFactories") @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); 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); KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
Iterator<String> userIds = info.getValidUserIds().iterator(); Iterator<String> userIds = info.getValidUserIds().iterator();
assertEquals("alice@wonderland.lit", userIds.next()); assertEquals("alice@wonderland.lit", userIds.next());
assertFalse(userIds.hasNext()); assertFalse(userIds.hasNext());
List<KeyFlag> 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) secretKeys = PGPainless.modifyKeyRing(secretKeys)
.addUserId("cheshirecat@wonderland.lit", protector) .addUserId("cheshirecat@wonderland.lit", protector)
.done(); .done();
@ -50,6 +56,8 @@ public class AddUserIdTest {
assertEquals("alice@wonderland.lit", userIds.next()); assertEquals("alice@wonderland.lit", userIds.next());
assertEquals("cheshirecat@wonderland.lit", userIds.next()); assertEquals("cheshirecat@wonderland.lit", userIds.next());
assertFalse(userIds.hasNext()); assertFalse(userIds.hasNext());
info = PGPainless.inspectKeyRing(secretKeys);
assertEquals(primaryUserIdKeyFlags, info.getKeyFlagsOf("cheshirecat@wonderland.lit"));
secretKeys = PGPainless.modifyKeyRing(secretKeys) secretKeys = PGPainless.modifyKeyRing(secretKeys)
.revokeUserId("cheshirecat@wonderland.lit", protector) .revokeUserId("cheshirecat@wonderland.lit", protector)