From c53c69f3acb503f01bcf75d2f0601ba51c4c243a Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 15 Nov 2023 17:32:43 +0100 Subject: [PATCH] Kotlin conversion: EncryptExternal --- .../external/operation/EncryptExternal.java | 160 ------------------ .../sop/external/operation/EncryptExternal.kt | 111 ++++++++++++ 2 files changed, 111 insertions(+), 160 deletions(-) delete mode 100644 external-sop/src/main/java/sop/external/operation/EncryptExternal.java create mode 100644 external-sop/src/main/kotlin/sop/external/operation/EncryptExternal.kt diff --git a/external-sop/src/main/java/sop/external/operation/EncryptExternal.java b/external-sop/src/main/java/sop/external/operation/EncryptExternal.java deleted file mode 100644 index f41a36e..0000000 --- a/external-sop/src/main/java/sop/external/operation/EncryptExternal.java +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package sop.external.operation; - -import sop.EncryptionResult; -import sop.ReadyWithResult; -import sop.SessionKey; -import sop.enums.EncryptAs; -import sop.exception.SOPGPException; -import sop.external.ExternalSOP; -import sop.operation.Encrypt; - -import javax.annotation.Nonnull; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - -/** - * Implementation of the {@link Encrypt} operation using an external SOP binary. - */ -public class EncryptExternal implements Encrypt { - - private final ExternalSOP.TempDirProvider tempDirProvider; - private final List commandList = new ArrayList<>(); - private final List envList; - private int SIGN_WITH_COUNTER = 0; - private int KEY_PASSWORD_COUNTER = 0; - private int PASSWORD_COUNTER = 0; - private int CERT_COUNTER = 0; - - public EncryptExternal(String binary, Properties environment, ExternalSOP.TempDirProvider tempDirProvider) { - this.tempDirProvider = tempDirProvider; - this.commandList.add(binary); - this.commandList.add("encrypt"); - this.envList = ExternalSOP.propertiesToEnv(environment); - } - - @Override - @Nonnull - public Encrypt noArmor() { - this.commandList.add("--no-armor"); - return this; - } - - @Override - @Nonnull - public Encrypt mode(@Nonnull EncryptAs mode) - throws SOPGPException.UnsupportedOption { - this.commandList.add("--as=" + mode); - return this; - } - - @Override - @Nonnull - public Encrypt signWith(@Nonnull InputStream key) - throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, - IOException { - String envVar = "SIGN_WITH_" + SIGN_WITH_COUNTER++; - commandList.add("--sign-with=@ENV:" + envVar); - envList.add(envVar + "=" + ExternalSOP.readString(key)); - return this; - } - - @Override - @Nonnull - public Encrypt withKeyPassword(@Nonnull byte[] password) - throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { - String envVar = "KEY_PASSWORD_" + KEY_PASSWORD_COUNTER++; - commandList.add("--with-key-password=@ENV:" + envVar); - envList.add(envVar + "=" + new String(password)); - return this; - } - - @Override - @Nonnull - public Encrypt withPassword(@Nonnull String password) - throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { - String envVar = "PASSWORD_" + PASSWORD_COUNTER++; - commandList.add("--with-password=@ENV:" + envVar); - envList.add(envVar + "=" + password); - return this; - } - - @Override - @Nonnull - public Encrypt withCert(@Nonnull InputStream cert) - throws SOPGPException.CertCannotEncrypt, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, - IOException { - String envVar = "CERT_" + CERT_COUNTER++; - commandList.add("@ENV:" + envVar); - envList.add(envVar + "=" + ExternalSOP.readString(cert)); - return this; - } - - @Override - @Nonnull - public Encrypt profile(@Nonnull String profileName) { - commandList.add("--profile=" + profileName); - return this; - } - - @Override - @Nonnull - public ReadyWithResult plaintext(@Nonnull InputStream plaintext) - throws SOPGPException.KeyIsProtected, IOException { - File tempDir = tempDirProvider.provideTempDirectory(); - - File sessionKeyOut = new File(tempDir, "session-key-out"); - sessionKeyOut.delete(); - commandList.add("--session-key-out=" + sessionKeyOut.getAbsolutePath()); - - String[] command = commandList.toArray(new String[0]); - String[] env = envList.toArray(new String[0]); - try { - Process process = Runtime.getRuntime().exec(command, env); - OutputStream processOut = process.getOutputStream(); - InputStream processIn = process.getInputStream(); - - return new ReadyWithResult() { - @Override - public EncryptionResult writeTo(@Nonnull OutputStream outputStream) throws IOException { - byte[] buf = new byte[4096]; - int r; - while ((r = plaintext.read(buf)) > 0) { - processOut.write(buf, 0, r); - } - - plaintext.close(); - processOut.close(); - - while ((r = processIn.read(buf)) > 0) { - outputStream.write(buf, 0 , r); - } - - processIn.close(); - outputStream.close(); - - ExternalSOP.finish(process); - - FileInputStream sessionKeyOutIn = new FileInputStream(sessionKeyOut); - String line = ExternalSOP.readString(sessionKeyOutIn); - SessionKey sessionKey = SessionKey.fromString(line.trim()); - sessionKeyOutIn.close(); - sessionKeyOut.delete(); - - return new EncryptionResult(sessionKey); - } - }; - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/external-sop/src/main/kotlin/sop/external/operation/EncryptExternal.kt b/external-sop/src/main/kotlin/sop/external/operation/EncryptExternal.kt new file mode 100644 index 0000000..6f1cc6c --- /dev/null +++ b/external-sop/src/main/kotlin/sop/external/operation/EncryptExternal.kt @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop.external.operation + +import java.io.File +import java.io.FileInputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.util.* +import sop.EncryptionResult +import sop.ReadyWithResult +import sop.SessionKey.Companion.fromString +import sop.enums.EncryptAs +import sop.external.ExternalSOP +import sop.external.ExternalSOP.Companion.finish +import sop.external.ExternalSOP.Companion.readString +import sop.operation.Encrypt + +/** Implementation of the [Encrypt] operation using an external SOP binary. */ +class EncryptExternal( + binary: String, + environment: Properties, + private val tempDirProvider: ExternalSOP.TempDirProvider +) : Encrypt { + + private val commandList = mutableListOf(binary, "encrypt") + private val envList = ExternalSOP.propertiesToEnv(environment).toMutableList() + + private var argCounter = 0 + + override fun noArmor(): Encrypt = apply { commandList.add("--no-armor") } + + override fun mode(mode: EncryptAs): Encrypt = apply { commandList.add("--as=$mode") } + + override fun signWith(key: InputStream): Encrypt = apply { + commandList.add("--sign-with@ENV:SIGN_WITH_$argCounter") + envList.add("SIGN_WITH_$argCounter=${ExternalSOP.readString(key)}") + argCounter += 1 + } + + override fun withKeyPassword(password: ByteArray): Encrypt = apply { + commandList.add("--with-key-password=@ENV:KEY_PASSWORD_$argCounter") + envList.add("KEY_PASSWORD_$argCounter=${String(password)}") + argCounter += 1 + } + + override fun withPassword(password: String): Encrypt = apply { + commandList.add("--with-password=@ENV:PASSWORD_$argCounter") + envList.add("PASSWORD_$argCounter=$password") + argCounter += 1 + } + + override fun withCert(cert: InputStream): Encrypt = apply { + commandList.add("@ENV:CERT_$argCounter") + envList.add("CERT_$argCounter=${readString(cert)}") + argCounter += 1 + } + + override fun profile(profileName: String): Encrypt = apply { + commandList.add("--profile=$profileName") + } + + override fun plaintext(plaintext: InputStream): ReadyWithResult { + val tempDir = tempDirProvider.provideTempDirectory() + + val sessionKeyOut = File(tempDir, "session-key-out") + sessionKeyOut.delete() + commandList.add("--session-key-out=${sessionKeyOut.absolutePath}") + try { + val process = + Runtime.getRuntime().exec(commandList.toTypedArray(), envList.toTypedArray()) + val processOut = process.outputStream + val processIn = process.inputStream + + return object : ReadyWithResult() { + override fun writeTo(outputStream: OutputStream): EncryptionResult { + val buf = ByteArray(4096) + var r: Int + while (plaintext.read(buf).also { r = it } > 0) { + processOut.write(buf, 0, r) + } + + plaintext.close() + processOut.close() + + while (processIn.read(buf).also { r = it } > 0) { + outputStream.write(buf, 0, r) + } + + processIn.close() + outputStream.close() + + finish(process) + + val sessionKeyOutIn = FileInputStream(sessionKeyOut) + val line = readString(sessionKeyOutIn) + val sessionKey = fromString(line.trim()) + sessionKeyOutIn.close() + sessionKeyOut.delete() + + return EncryptionResult(sessionKey) + } + } + } catch (e: IOException) { + throw RuntimeException(e) + } + } +}