using System.IO.Compression; namespace ReleaseBuilder.Build; public static partial class Command { /// /// Implementation of the gpg sign command /// private static class GpgSign { /// /// Performs a GPG sign operation on the files /// /// The files to sign /// The signature file to create /// The runtime configuration /// An awaitable task public static async Task SignReleaseFiles(IEnumerable files, string signaturefile, RuntimeConfig rtcfg) { var tmpfile = signaturefile + ".tmp"; if (File.Exists(tmpfile)) File.Delete(tmpfile); var (gpgid, passphrase) = GetGpgIdAndPassphrase(rtcfg); using (var zip = ZipFile.Open(tmpfile, ZipArchiveMode.Create)) { foreach (var file in files) foreach (var armored in new[] { true, false }) { var outputfile = file + (armored ? ".sig.asc" : ".sig"); var outputpath = Path.Combine(Path.GetDirectoryName(file) ?? string.Empty, outputfile); await ProcessHelper.Execute( [ Program.Configuration.Commands.Gpg!, "--pinentry-mode", "loopback", "--passphrase-fd", "0", "--batch", "--yes", armored ? "--armor" : "--no-armor", "-u", gpgid, "--output", outputfile, "--detach-sign", file ], workingDirectory: Path.GetDirectoryName(file), writeStdIn: (stdin) => stdin.WriteLineAsync(passphrase) ); zip.CreateEntryFromFile(outputpath, Path.GetFileName(outputfile)); File.Delete(outputpath); } // Add information about the signing key using (var stream = zip.CreateEntry("sign-key.txt", CompressionLevel.Optimal).Open()) stream.Write(System.Text.Encoding.UTF8.GetBytes($"{gpgid}\nhttps://keys.openpgp.org/search?q={gpgid}\nhttps://pgp.mit.edu/pks/lookup?op=get&search={gpgid}\n")); } File.Move(tmpfile, signaturefile, true); } /// /// Gets the GPG ID and passphrase from the keyfile /// /// The runtime configuration /// The GPG ID and passphrase static (string GpgId, string GpgPassphrase) GetGpgIdAndPassphrase(RuntimeConfig rtcfg) { using var ms = new MemoryStream(); using var fs = File.OpenRead(Program.Configuration.ConfigFiles.GpgKeyfile); SharpAESCrypt.SharpAESCrypt.Decrypt(rtcfg.KeyfilePassword, fs, ms); var parts = System.Text.Encoding.UTF8.GetString(ms.ToArray()).Split('\n', 2, StringSplitOptions.RemoveEmptyEntries); return (parts[0], parts[1]); } } }