001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package sop.cli.picocli.commands;
006
007import java.io.File;
008import java.io.FileInputStream;
009import java.io.FileNotFoundException;
010import java.io.IOException;
011import java.util.ArrayList;
012import java.util.List;
013
014import picocli.CommandLine;
015import sop.Ready;
016import sop.cli.picocli.SopCLI;
017import sop.enums.EncryptAs;
018import sop.exception.SOPGPException;
019import sop.operation.Encrypt;
020
021@CommandLine.Command(name = "encrypt",
022        description = "Encrypt a message from standard input",
023        exitCodeOnInvalidInput = 37)
024public class EncryptCmd implements Runnable {
025
026    @CommandLine.Option(names = "--no-armor",
027            description = "ASCII armor the output",
028            negatable = true)
029    boolean armor = true;
030
031    @CommandLine.Option(names = {"--as"},
032            description = "Type of the input data. Defaults to 'binary'",
033            paramLabel = "{binary|text|mime}")
034    EncryptAs type;
035
036    @CommandLine.Option(names = "--with-password",
037            description = "Encrypt the message with a password",
038            paramLabel = "PASSWORD")
039    List<String> withPassword = new ArrayList<>();
040
041    @CommandLine.Option(names = "--sign-with",
042            description = "Sign the output with a private key",
043            paramLabel = "KEY")
044    List<File> signWith = new ArrayList<>();
045
046    @CommandLine.Parameters(description = "Certificates the message gets encrypted to",
047            index = "0..*",
048            paramLabel = "CERTS")
049    List<File> certs = new ArrayList<>();
050
051    @Override
052    public void run() {
053        Encrypt encrypt = SopCLI.getSop().encrypt();
054        if (type != null) {
055            try {
056                encrypt.mode(type);
057            } catch (SOPGPException.UnsupportedOption unsupportedOption) {
058                throw new SOPGPException.UnsupportedOption("Unsupported option '--as'.", unsupportedOption);
059            }
060        }
061
062        if (withPassword.isEmpty() && certs.isEmpty()) {
063            throw new SOPGPException.MissingArg("At least one password or cert file required for encryption.");
064        }
065
066        for (String password : withPassword) {
067            try {
068                encrypt.withPassword(password);
069            } catch (SOPGPException.UnsupportedOption unsupportedOption) {
070                throw new SOPGPException.UnsupportedOption("Unsupported option '--with-password'.", unsupportedOption);
071            }
072        }
073
074        for (File keyFile : signWith) {
075            try (FileInputStream keyIn = new FileInputStream(keyFile)) {
076                encrypt.signWith(keyIn);
077            } catch (FileNotFoundException e) {
078                throw new SOPGPException.MissingInput("Key file " + keyFile.getAbsolutePath() + " not found.", e);
079            } catch (IOException e) {
080                throw new RuntimeException(e);
081            } catch (SOPGPException.KeyIsProtected keyIsProtected) {
082                throw new SOPGPException.KeyIsProtected("Key from " + keyFile.getAbsolutePath() + " is password protected.", keyIsProtected);
083            } catch (SOPGPException.UnsupportedAsymmetricAlgo unsupportedAsymmetricAlgo) {
084                throw new SOPGPException.UnsupportedAsymmetricAlgo("Key from " + keyFile.getAbsolutePath() + " has unsupported asymmetric algorithm.", unsupportedAsymmetricAlgo);
085            } catch (SOPGPException.CertCannotSign certCannotSign) {
086                throw new RuntimeException("Key from " + keyFile.getAbsolutePath() + " cannot sign.", certCannotSign);
087            } catch (SOPGPException.BadData badData) {
088                throw new SOPGPException.BadData("Key file " + keyFile.getAbsolutePath() + " does not contain a valid OpenPGP private key.", badData);
089            }
090        }
091
092        for (File certFile : certs) {
093            try (FileInputStream certIn = new FileInputStream(certFile)) {
094                encrypt.withCert(certIn);
095            } catch (FileNotFoundException e) {
096                throw new SOPGPException.MissingInput("Certificate file " + certFile.getAbsolutePath() + " not found.", e);
097            } catch (IOException e) {
098                throw new RuntimeException(e);
099            } catch (SOPGPException.UnsupportedAsymmetricAlgo unsupportedAsymmetricAlgo) {
100                throw new SOPGPException.UnsupportedAsymmetricAlgo("Certificate from " + certFile.getAbsolutePath() + " has unsupported asymmetric algorithm.", unsupportedAsymmetricAlgo);
101            } catch (SOPGPException.CertCannotEncrypt certCannotEncrypt) {
102                throw new SOPGPException.CertCannotEncrypt("Certificate from " + certFile.getAbsolutePath() + " is not capable of encryption.", certCannotEncrypt);
103            } catch (SOPGPException.BadData badData) {
104                throw new SOPGPException.BadData("Certificate file " + certFile.getAbsolutePath() + " does not contain a valid OpenPGP certificate.", badData);
105            }
106        }
107
108        if (!armor) {
109            encrypt.noArmor();
110        }
111
112        try {
113            Ready ready = encrypt.plaintext(System.in);
114            ready.writeTo(System.out);
115        } catch (IOException e) {
116            throw new RuntimeException(e);
117        }
118    }
119}