From 1d697eb6828ac7a9e4f7534995b20be4af26e86c Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 14 Nov 2024 01:55:13 +0100 Subject: [PATCH] Initial commit --- .gitignore | 38 ++++++ .idea/.gitignore | 3 + .idea/encodings.xml | 11 ++ .idea/misc.xml | 14 ++ .idea/uiDesigner.xml | 124 ++++++++++++++++++ .idea/vcs.xml | 6 + bcsop-cli/pom.xml | 32 +++++ .../src/main/java/org/pgpainless/Main.java | 7 + bcsop/pom.xml | 35 +++++ .../bouncycastle/sop/BouncyCastleSOP.java | 99 ++++++++++++++ .../bouncycastle/sop/operation/BCArmor.java | 37 ++++++ .../bouncycastle/sop/operation/BCDearmor.java | 28 ++++ .../bouncycastle/sop/operation/BCDecrypt.java | 124 ++++++++++++++++++ .../sop/operation/BCExtractCert.java | 59 +++++++++ .../sop/operation/BCListProfiles.java | 15 +++ .../bouncycastle/sop/operation/BCVersion.java | 56 ++++++++ pom.xml | 47 +++++++ src/main/java/org/pgpainless/Main.java | 7 + 18 files changed, 742 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 bcsop-cli/pom.xml create mode 100644 bcsop-cli/src/main/java/org/pgpainless/Main.java create mode 100644 bcsop/pom.xml create mode 100644 bcsop/src/main/java/org/pgpainless/bouncycastle/sop/BouncyCastleSOP.java create mode 100644 bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java create mode 100644 bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDearmor.java create mode 100644 bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java create mode 100644 bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCExtractCert.java create mode 100644 bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java create mode 100644 bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCVersion.java create mode 100644 pom.xml create mode 100644 src/main/java/org/pgpainless/Main.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..ad25f04 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..fdc35ea --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/bcsop-cli/pom.xml b/bcsop-cli/pom.xml new file mode 100644 index 0000000..96d54af --- /dev/null +++ b/bcsop-cli/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + org.pgpainless + bc-sop + 1.0-SNAPSHOT + + + bcsop-cli + + + 21 + 21 + UTF-8 + + + + + org.pgpainless + bcsop + 1.0-SNAPSHOT + + + org.pgpainless + sop-java-picocli + + + + \ No newline at end of file diff --git a/bcsop-cli/src/main/java/org/pgpainless/Main.java b/bcsop-cli/src/main/java/org/pgpainless/Main.java new file mode 100644 index 0000000..e6dea03 --- /dev/null +++ b/bcsop-cli/src/main/java/org/pgpainless/Main.java @@ -0,0 +1,7 @@ +package org.pgpainless; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/bcsop/pom.xml b/bcsop/pom.xml new file mode 100644 index 0000000..ea8203c --- /dev/null +++ b/bcsop/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + org.pgpainless + bc-sop + 1.0-SNAPSHOT + + + bcsop + + + 21 + 21 + UTF-8 + + + + + org.pgpainless + sop-java + + + org.bouncycastle + bcprov-jdk18on + + + org.bouncycastle + bcpg-jdk18on + + + + \ No newline at end of file diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/BouncyCastleSOP.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/BouncyCastleSOP.java new file mode 100644 index 0000000..b50d8ba --- /dev/null +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/BouncyCastleSOP.java @@ -0,0 +1,99 @@ +package org.pgpainless.bouncycastle.sop; + +import org.jetbrains.annotations.NotNull; +import org.pgpainless.bouncycastle.sop.operation.*; +import sop.SOP; +import sop.operation.*; + +public class BouncyCastleSOP implements SOP { + + @NotNull + @Override + public Armor armor() { + return new BCArmor(); + } + + @NotNull + @Override + public GenerateKey generateKey() { + return null; + } + + @NotNull + @Override + public ExtractCert extractCert() { + return new BCExtractCert(); + } + + @NotNull + @Override + public DetachedSign detachedSign() { + return null; + } + + @NotNull + @Override + public InlineSign inlineSign() { + return null; + } + + @NotNull + @Override + public InlineDetach inlineDetach() { + return null; + } + + @NotNull + @Override + public Encrypt encrypt() { + return null; + } + + @NotNull + @Override + public Decrypt decrypt() { + return null; + } + + @NotNull + @Override + public Dearmor dearmor() { + return new BCDearmor(); + } + + @NotNull + @Override + public ListProfiles listProfiles() { + return new BCListProfiles(); + } + + @NotNull + @Override + public RevokeKey revokeKey() { + return null; + } + + @NotNull + @Override + public ChangeKeyPassword changeKeyPassword() { + return null; + } + + @NotNull + @Override + public Version version() { + return new BCVersion(); + } + + @NotNull + @Override + public DetachedVerify detachedVerify() { + return null; + } + + @NotNull + @Override + public InlineVerify inlineVerify() { + return null; + } +} diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java new file mode 100644 index 0000000..9de9904 --- /dev/null +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java @@ -0,0 +1,37 @@ +package org.pgpainless.bouncycastle.sop.operation; + +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.util.io.Streams; +import org.jetbrains.annotations.NotNull; +import sop.Ready; +import sop.enums.ArmorLabel; +import sop.exception.SOPGPException; +import sop.operation.Armor; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class BCArmor implements Armor { + + @NotNull + @Override + public Ready data(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException { + return new Ready() { + @Override + public void writeTo(@NotNull OutputStream outputStream) throws IOException { + ArmoredOutputStream aOut = ArmoredOutputStream.builder() + .clearHeaders() + .build(outputStream); + Streams.pipeAll(inputStream, aOut); + aOut.close(); + } + }; + } + + @NotNull + @Override + public Armor label(@NotNull ArmorLabel armorLabel) throws SOPGPException.UnsupportedOption { + throw new SOPGPException.UnsupportedOption("Custom labels not supported."); + } +} diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDearmor.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDearmor.java new file mode 100644 index 0000000..d1b2287 --- /dev/null +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDearmor.java @@ -0,0 +1,28 @@ +package org.pgpainless.bouncycastle.sop.operation; + +import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.util.io.Streams; +import org.jetbrains.annotations.NotNull; +import sop.Ready; +import sop.exception.SOPGPException; +import sop.operation.Dearmor; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class BCDearmor implements Dearmor { + + @NotNull + @Override + public Ready data(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException { + return new Ready() { + @Override + public void writeTo(@NotNull OutputStream outputStream) throws IOException { + ArmoredInputStream aIn = new ArmoredInputStream(inputStream); + Streams.pipeAll(aIn, outputStream); + aIn.close(); + } + }; + } +} diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java new file mode 100644 index 0000000..251c48f --- /dev/null +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java @@ -0,0 +1,124 @@ +package org.pgpainless.bouncycastle.sop.operation; + +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; +import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.bc.BcSessionKeyDataDecryptorFactory; +import org.jetbrains.annotations.NotNull; +import sop.DecryptionResult; +import sop.ReadyWithResult; +import sop.SessionKey; +import sop.exception.SOPGPException; +import sop.operation.Decrypt; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StreamCorruptedException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class BCDecrypt implements Decrypt { + + private Date notBefore = new Date(Long.MAX_VALUE); // end of time + private Date notAfter = new Date(); // now + private final List sessionKeys = new ArrayList<>(); + private final List messagePassphrases = new ArrayList<>(); + private final List verificationCerts = new ArrayList<>(); + private final List encryptionKeys = new ArrayList<>(); + private final List encryptionKeyPassphrases = new ArrayList<>(); + + + @NotNull + @Override + public ReadyWithResult ciphertext(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt, SOPGPException.KeyIsProtected, IOException { + return new ReadyWithResult() { + @Override + public DecryptionResult writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException { + InputStream in = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory objFac = new BcPGPObjectFactory(in); + Object next = objFac.nextObject(); + + if (next instanceof PGPEncryptedDataList) { + PGPEncryptedDataList encDataList = (PGPEncryptedDataList) next; + + if (!sessionKeys.isEmpty()) { + PGPSessionKeyEncryptedData sesKeyEnc = encDataList.extractSessionKeyEncryptedData(); + SessionKeyDataDecryptorFactory decFac = new BcSessionKeyDataDecryptorFactory(sessionKeys.get(0)); + try { + in = sesKeyEnc.getDataStream(decFac); + objFac = new BcPGPObjectFactory(in); + next = objFac.nextObject(); + } catch (PGPException e) { + throw new RuntimeException(e); + } + } else { + for (PGPEncryptedData encData : encDataList) { + if (encData instanceof PGPPublicKeyEncryptedData) { + + } + } + } + } + return null; + } + }; + } + + @NotNull + @Override + public Decrypt verifyNotBefore(@NotNull Date date) throws SOPGPException.UnsupportedOption { + this.notBefore = date; + return this; + } + + @NotNull + @Override + public Decrypt verifyNotAfter(@NotNull Date date) throws SOPGPException.UnsupportedOption { + this.notAfter = date; + return this; + } + + @NotNull + @Override + public Decrypt verifyWithCert(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { + InputStream decodeIn = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory objFac = new BcPGPObjectFactory(decodeIn); + PGPPublicKeyRing cert = (PGPPublicKeyRing) objFac.nextObject(); + verificationCerts.add(cert); + return this; + } + + @NotNull + @Override + public Decrypt withSessionKey(@NotNull SessionKey sessionKey) throws SOPGPException.UnsupportedOption { + this.sessionKeys.add(new PGPSessionKey(sessionKey.getAlgorithm(), sessionKey.getKey())); + return this; + } + + @NotNull + @Override + public Decrypt withPassword(@NotNull String s) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { + this.messagePassphrases.add(s); + return this; + } + + @NotNull + @Override + public Decrypt withKey(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException { + InputStream decodeIn = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory objFac = new BcPGPObjectFactory(decodeIn); + PGPSecretKeyRing key = (PGPSecretKeyRing) objFac.nextObject(); + this.encryptionKeys.add(key); + return this; + } + + @NotNull + @Override + public Decrypt withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable { + String passphrase = new String(bytes); + this.encryptionKeyPassphrases.add(passphrase); + return this; + } +} diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCExtractCert.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCExtractCert.java new file mode 100644 index 0000000..d0ad5a2 --- /dev/null +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCExtractCert.java @@ -0,0 +1,59 @@ +package org.pgpainless.bouncycastle.sop.operation; + +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; +import org.jetbrains.annotations.NotNull; +import sop.Ready; +import sop.exception.SOPGPException; +import sop.operation.ExtractCert; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class BCExtractCert implements ExtractCert { + + private boolean armor = true; + + @NotNull + @Override + public Ready key(@NotNull InputStream inputStream) throws IOException, SOPGPException.BadData { + InputStream decodeIn = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory objFac = new BcPGPObjectFactory(decodeIn); + PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) objFac.nextObject(); + + List list = new ArrayList<>(); + Iterator iterator = secretKeys.getPublicKeys(); + while (iterator.hasNext()) { + list.add(iterator.next()); + } + PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(list); + + return new Ready() { + @Override + public void writeTo(@NotNull OutputStream outputStream) throws IOException { + if (armor) { + ArmoredOutputStream aOut = ArmoredOutputStream.builder() + .clearHeaders() + .enableCRC(true) + .build(outputStream); + publicKeys.encode(aOut); + aOut.close(); + } else { + publicKeys.encode(outputStream); + } + } + }; + } + + @NotNull + @Override + public ExtractCert noArmor() { + this.armor = false; + return this; + } +} diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java new file mode 100644 index 0000000..6b31387 --- /dev/null +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java @@ -0,0 +1,15 @@ +package org.pgpainless.bouncycastle.sop.operation; + +import org.jetbrains.annotations.NotNull; +import sop.Profile; +import sop.operation.ListProfiles; + +import java.util.List; + +public class BCListProfiles implements ListProfiles { + @NotNull + @Override + public List subcommand(@NotNull String s) { + return List.of(); + } +} diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCVersion.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCVersion.java new file mode 100644 index 0000000..3acdd5a --- /dev/null +++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCVersion.java @@ -0,0 +1,56 @@ +package org.pgpainless.bouncycastle.sop.operation; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import sop.exception.SOPGPException; +import sop.operation.Version; + +public class BCVersion implements Version { + + @NotNull + @Override + public String getBackendVersion() { + return "Bouncy Castle 1.79"; // TODO: Extract from pom + } + + @NotNull + @Override + public String getName() { + return "Bouncy Castle SOP"; + } + + @NotNull + @Override + public String getVersion() { + return "1.0-SNAPSHOT"; // TODO: Extract from pom + } + + @NotNull + @Override + public String getExtendedVersion() { + return ""; + } + + @Override + public int getSopSpecRevisionNumber() { + return 10; + } + + @Override + public boolean isSopSpecImplementationIncomplete() { + return false; + } + + @Nullable + @Override + public String getSopSpecImplementationRemarks() { + return ""; + } + + @NotNull + @Override + public String getSopVVersion() throws SOPGPException.UnsupportedOption { + return "1.0"; + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..52c5d17 --- /dev/null +++ b/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + org.pgpainless + bc-sop + 1.0-SNAPSHOT + pom + + bcsop + bcsop-cli + + + + 21 + 21 + UTF-8 + + + + + + org.bouncycastle + bcprov-jdk18on + 1.79 + + + org.bouncycastle + bcpg-jdk18on + 1.79 + + + org.pgpainless + sop-java + 10.0.0 + + + org.pgpainless + sop-java-picocli + 10.0.0 + + + + + \ No newline at end of file diff --git a/src/main/java/org/pgpainless/Main.java b/src/main/java/org/pgpainless/Main.java new file mode 100644 index 0000000..e6dea03 --- /dev/null +++ b/src/main/java/org/pgpainless/Main.java @@ -0,0 +1,7 @@ +package org.pgpainless; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file