Initial commit

This commit is contained in:
Paul Schaub 2024-11-14 01:55:13 +01:00
commit 1d697eb682
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
18 changed files with 742 additions and 0 deletions

35
bcsop/pom.xml Normal file
View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.pgpainless</groupId>
<artifactId>bc-sop</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bcsop</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.pgpainless</groupId>
<artifactId>sop-java</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk18on</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -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;
}
}

View file

@ -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.");
}
}

View file

@ -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();
}
};
}
}

View file

@ -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<PGPSessionKey> sessionKeys = new ArrayList<>();
private final List<String> messagePassphrases = new ArrayList<>();
private final List<PGPPublicKeyRing> verificationCerts = new ArrayList<>();
private final List<PGPSecretKeyRing> encryptionKeys = new ArrayList<>();
private final List<String> encryptionKeyPassphrases = new ArrayList<>();
@NotNull
@Override
public ReadyWithResult<DecryptionResult> ciphertext(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt, SOPGPException.KeyIsProtected, IOException {
return new ReadyWithResult<DecryptionResult>() {
@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;
}
}

View file

@ -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<PGPPublicKey> list = new ArrayList<>();
Iterator<PGPPublicKey> 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;
}
}

View file

@ -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<Profile> subcommand(@NotNull String s) {
return List.of();
}
}

View file

@ -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";
}
}