From 9c216e1ff437f07bb42dd12dfaef65b3a3ebaa2c Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 12 Jul 2023 01:07:29 +0200 Subject: [PATCH] Implement '--signing-only' option for 'generate-key' subcommand --- .../org/pgpainless/sop/GenerateKeyImpl.java | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java index f86d2893..b6d5fe73 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java @@ -9,7 +9,6 @@ import java.io.OutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -18,9 +17,13 @@ import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.KeyFlag; +import org.pgpainless.key.generation.KeyRingBuilder; +import org.pgpainless.key.generation.KeySpec; +import org.pgpainless.key.generation.type.KeyType; +import org.pgpainless.key.generation.type.eddsa.EdDSACurve; import org.pgpainless.key.generation.type.rsa.RsaLength; -import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface; -import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.key.generation.type.xdh.XDHSpec; import org.pgpainless.util.ArmorUtils; import org.pgpainless.util.Passphrase; import sop.Profile; @@ -39,6 +42,7 @@ public class GenerateKeyImpl implements GenerateKey { public static final List SUPPORTED_PROFILES = Arrays.asList(CURVE25519_PROFILE, RSA4096_PROFILE); private boolean armor = true; + private boolean signingOnly = false; private final Set userIds = new LinkedHashSet<>(); private Passphrase passphrase = Passphrase.emptyPassphrase(); private String profile = CURVE25519_PROFILE.getName(); @@ -76,35 +80,25 @@ public class GenerateKeyImpl implements GenerateKey { throw new SOPGPException.UnsupportedProfile("generate-key", profileName); } + @Override + public GenerateKey signingOnly() { + signingOnly = true; + return this; + } + @Override public Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo { - Iterator userIdIterator = userIds.iterator(); - Passphrase passphraseCopy = new Passphrase(passphrase.getChars()); // generateKeyRing clears the original passphrase - PGPSecretKeyRing key; try { - String primaryUserId = userIdIterator.hasNext() ? userIdIterator.next() : null; - key = generateKeyWithProfile(profile, primaryUserId, passphrase); - - if (userIdIterator.hasNext()) { - SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(key); - - while (userIdIterator.hasNext()) { - editor.addUserId(userIdIterator.next(), SecretKeyRingProtector.unlockAnyKeyWith(passphraseCopy)); - } - - key = editor.done(); - } - - PGPSecretKeyRing finalKey = key; + final PGPSecretKeyRing key = generateKeyWithProfile(profile, userIds, passphrase, signingOnly); return new Ready() { @Override public void writeTo(OutputStream outputStream) throws IOException { if (armor) { - ArmoredOutputStream armoredOutputStream = ArmorUtils.toAsciiArmoredStream(finalKey, outputStream); - finalKey.encode(armoredOutputStream); + ArmoredOutputStream armoredOutputStream = ArmorUtils.toAsciiArmoredStream(key, outputStream); + key.encode(armoredOutputStream); armoredOutputStream.close(); } else { - finalKey.encode(outputStream); + key.encode(outputStream); } } }; @@ -115,23 +109,38 @@ public class GenerateKeyImpl implements GenerateKey { } } - private PGPSecretKeyRing generateKeyWithProfile(String profile, String primaryUserId, Passphrase passphrase) + private PGPSecretKeyRing generateKeyWithProfile(String profile, Set userIds, Passphrase passphrase, boolean signingOnly) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { - PGPSecretKeyRing key; + KeyRingBuilder keyBuilder; // XDH + EdDSA if (profile.equals(CURVE25519_PROFILE.getName())) { - key = PGPainless.generateKeyRing() - .modernKeyRing(primaryUserId, passphrase); + keyBuilder = PGPainless.buildKeyRing() + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA)); + if (!signingOnly) { + keyBuilder.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)); + } } // RSA 4096 else if (profile.equals(RSA4096_PROFILE.getName())) { - key = PGPainless.generateKeyRing() - .rsaKeyRing(primaryUserId, RsaLength._4096, passphrase); + keyBuilder = PGPainless.buildKeyRing() + .setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._4096), KeyFlag.CERTIFY_OTHER)) + .addSubkey(KeySpec.getBuilder(KeyType.RSA(RsaLength._4096), KeyFlag.SIGN_DATA)); + if (!signingOnly) { + keyBuilder.addSubkey(KeySpec.getBuilder(KeyType.RSA(RsaLength._4096), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)); + } } else { // Missing else-if branch for profile. Oops. throw new SOPGPException.UnsupportedProfile("generate-key", profile); } - return key; + + for (String userId : userIds) { + keyBuilder.addUserId(userId); + } + if (!passphrase.isEmpty()) { + keyBuilder.setPassphrase(passphrase); + } + return keyBuilder.build(); } }