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.Verification; 016import sop.cli.picocli.DateParser; 017import sop.cli.picocli.Print; 018import sop.cli.picocli.SopCLI; 019import sop.exception.SOPGPException; 020import sop.operation.Verify; 021 022@CommandLine.Command(name = "verify", 023 description = "Verify a detached signature over the data from standard input", 024 exitCodeOnInvalidInput = 37) 025public class VerifyCmd implements Runnable { 026 027 @CommandLine.Parameters(index = "0", 028 description = "Detached signature", 029 paramLabel = "SIGNATURE") 030 File signature; 031 032 @CommandLine.Parameters(index = "1..*", 033 arity = "1..*", 034 description = "Public key certificates", 035 paramLabel = "CERT") 036 List<File> certificates = new ArrayList<>(); 037 038 @CommandLine.Option(names = {"--not-before"}, 039 description = "ISO-8601 formatted UTC date (eg. '2020-11-23T16:35Z)\n" + 040 "Reject signatures with a creation date not in range.\n" + 041 "Defaults to beginning of time (\"-\").", 042 paramLabel = "DATE") 043 String notBefore = "-"; 044 045 @CommandLine.Option(names = {"--not-after"}, 046 description = "ISO-8601 formatted UTC date (eg. '2020-11-23T16:35Z)\n" + 047 "Reject signatures with a creation date not in range.\n" + 048 "Defaults to current system time (\"now\").\n" + 049 "Accepts special value \"-\" for end of time.", 050 paramLabel = "DATE") 051 String notAfter = "now"; 052 053 @Override 054 public void run() { 055 Verify verify = SopCLI.getSop().verify(); 056 if (notAfter != null) { 057 try { 058 verify.notAfter(DateParser.parseNotAfter(notAfter)); 059 } catch (SOPGPException.UnsupportedOption unsupportedOption) { 060 Print.errln("Unsupported option '--not-after'."); 061 Print.trace(unsupportedOption); 062 System.exit(unsupportedOption.getExitCode()); 063 } 064 } 065 if (notBefore != null) { 066 try { 067 verify.notBefore(DateParser.parseNotBefore(notBefore)); 068 } catch (SOPGPException.UnsupportedOption unsupportedOption) { 069 Print.errln("Unsupported option '--not-before'."); 070 Print.trace(unsupportedOption); 071 System.exit(unsupportedOption.getExitCode()); 072 } 073 } 074 075 for (File certFile : certificates) { 076 try (FileInputStream certIn = new FileInputStream(certFile)) { 077 verify.cert(certIn); 078 } catch (FileNotFoundException fileNotFoundException) { 079 Print.errln("Certificate file " + certFile.getAbsolutePath() + " not found."); 080 081 Print.trace(fileNotFoundException); 082 System.exit(1); 083 } catch (IOException ioException) { 084 Print.errln("IO Error."); 085 Print.trace(ioException); 086 System.exit(1); 087 } catch (SOPGPException.BadData badData) { 088 Print.errln("Certificate file " + certFile.getAbsolutePath() + " appears to not contain a valid OpenPGP certificate."); 089 Print.trace(badData); 090 System.exit(badData.getExitCode()); 091 } 092 } 093 094 if (signature != null) { 095 try (FileInputStream sigIn = new FileInputStream(signature)) { 096 verify.signatures(sigIn); 097 } catch (FileNotFoundException e) { 098 Print.errln("Signature file " + signature.getAbsolutePath() + " does not exist."); 099 Print.trace(e); 100 System.exit(1); 101 } catch (IOException e) { 102 Print.errln("IO Error."); 103 Print.trace(e); 104 System.exit(1); 105 } catch (SOPGPException.BadData badData) { 106 Print.errln("File " + signature.getAbsolutePath() + " does not contain a valid OpenPGP signature."); 107 Print.trace(badData); 108 System.exit(badData.getExitCode()); 109 } 110 } 111 112 List<Verification> verifications = null; 113 try { 114 verifications = verify.data(System.in); 115 } catch (SOPGPException.NoSignature e) { 116 Print.errln("No verifiable signature found."); 117 Print.trace(e); 118 System.exit(e.getExitCode()); 119 } catch (IOException ioException) { 120 Print.errln("IO Error."); 121 Print.trace(ioException); 122 System.exit(1); 123 } catch (SOPGPException.BadData badData) { 124 Print.errln("Standard Input appears not to contain a valid OpenPGP message."); 125 Print.trace(badData); 126 System.exit(badData.getExitCode()); 127 } 128 for (Verification verification : verifications) { 129 Print.outln(verification.toString()); 130 } 131 } 132}