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 da99c854..693ca454 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/GenerateKeyImpl.java @@ -8,18 +8,22 @@ import java.io.IOException; 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; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.pgpainless.PGPainless; +import org.pgpainless.key.generation.type.rsa.RsaLength; import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.util.ArmorUtils; import org.pgpainless.util.Passphrase; +import sop.Profile; import sop.Ready; import sop.exception.SOPGPException; import sop.operation.GenerateKey; @@ -29,9 +33,16 @@ import sop.operation.GenerateKey; */ public class GenerateKeyImpl implements GenerateKey { + public static final Profile DEFAULT_PROFILE = new Profile("default", "Generate keys based on XDH and EdDSA"); + public static final Profile RSA3072_PROFILE = new Profile("rfc4880-rsa3072@pgpainless.org", "Generate 3072-bit RSA keys"); + public static final Profile RSA4096_PROFILE = new Profile("rfc4880-rsa4096@pgpainless.org", "Generate 4096-bit RSA keys"); + + public static final List SUPPORTED_PROFILES = Arrays.asList(DEFAULT_PROFILE, RSA3072_PROFILE, RSA4096_PROFILE); + private boolean armor = true; private final Set userIds = new LinkedHashSet<>(); private Passphrase passphrase = Passphrase.emptyPassphrase(); + private String profile = DEFAULT_PROFILE.getName(); @Override public GenerateKey noArmor() { @@ -51,6 +62,18 @@ public class GenerateKeyImpl implements GenerateKey { return this; } + @Override + public GenerateKey profile(String profileName) { + for (Profile profile : SUPPORTED_PROFILES) { + if (profile.getName().equals(profileName)) { + this.profile = profileName; + return this; + } + } + + throw new SOPGPException.UnsupportedProfile("generate-key", profileName); + } + @Override public Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo { Iterator userIdIterator = userIds.iterator(); @@ -58,8 +81,7 @@ public class GenerateKeyImpl implements GenerateKey { PGPSecretKeyRing key; try { String primaryUserId = userIdIterator.hasNext() ? userIdIterator.next() : null; - key = PGPainless.generateKeyRing() - .modernKeyRing(primaryUserId, passphrase); + key = generateKeyWithProfile(profile, primaryUserId, passphrase); if (userIdIterator.hasNext()) { SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(key); @@ -90,4 +112,26 @@ public class GenerateKeyImpl implements GenerateKey { throw new RuntimeException(e); } } + + private PGPSecretKeyRing generateKeyWithProfile(String profile, String primaryUserId, Passphrase passphrase) + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + PGPSecretKeyRing key; + // XDH + EdDSA + if (profile.equals(DEFAULT_PROFILE.getName())) { + key = PGPainless.generateKeyRing() + .modernKeyRing(primaryUserId, passphrase); + } + else if (profile.equals(RSA3072_PROFILE.getName())) { + key = PGPainless.generateKeyRing() + .simpleRsaKeyRing(primaryUserId, RsaLength._3072, passphrase); + } + else if (profile.equals(RSA4096_PROFILE.getName())) { + key = PGPainless.generateKeyRing() + .simpleRsaKeyRing(primaryUserId, RsaLength._4096, passphrase); + } + else { + throw new SOPGPException.UnsupportedProfile("generate-key", profile); + } + return key; + } } diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/ListProfilesImpl.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/ListProfilesImpl.java new file mode 100644 index 00000000..06519bb3 --- /dev/null +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/ListProfilesImpl.java @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.sop; + +import java.util.List; + +import sop.Profile; +import sop.exception.SOPGPException; +import sop.operation.ListProfiles; + +public class ListProfilesImpl implements ListProfiles { + + @Override + public List subcommand(String command) { + if (command == null) { + throw new SOPGPException.UnsupportedProfile("null"); + } + + switch (command) { + case "generate-key": + return GenerateKeyImpl.SUPPORTED_PROFILES; + + default: + throw new SOPGPException.UnsupportedProfile(command); + } + } +} diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/SOPImpl.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/SOPImpl.java index a49f7e34..a0e5f631 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/SOPImpl.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/SOPImpl.java @@ -17,6 +17,7 @@ import sop.operation.ExtractCert; import sop.operation.GenerateKey; import sop.operation.InlineSign; import sop.operation.InlineVerify; +import sop.operation.ListProfiles; import sop.operation.Version; /** @@ -96,6 +97,11 @@ public class SOPImpl implements SOP { return new DearmorImpl(); } + @Override + public ListProfiles listProfiles() { + return new ListProfilesImpl(); + } + @Override public InlineDetach inlineDetach() { return new InlineDetachImpl(); diff --git a/version.gradle b/version.gradle index 55e9b151..1adf151a 100644 --- a/version.gradle +++ b/version.gradle @@ -18,6 +18,6 @@ allprojects { logbackVersion = '1.2.11' mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' - sopJavaVersion = '4.1.1' + sopJavaVersion = '5.0.0-SNAPSHOT' } }