diff --git a/rinseoff/RinseOff.cs b/rinseoff/RinseOff.cs index 4b4d1bf..48b6a55 100644 --- a/rinseoff/RinseOff.cs +++ b/rinseoff/RinseOff.cs @@ -20,6 +20,15 @@ namespace rinseoff return combined.ToArray(); } - + public static byte[] decrypt_secret_bytes(byte[] ciphertext, byte[] key){ + var ciphertextList = new List(); + ciphertextList.AddRange(ciphertext); + return Sodium.SecretBox.Open( + ciphertextList.GetRange(24, ciphertextList.Count - 24).ToArray(), + ciphertextList.GetRange(0, 24).ToArray(), + key); + } + + } } diff --git a/rinseoffcli/RinseOffCLI.cs b/rinseoffcli/RinseOffCLI.cs index b1841e1..8c13967 100644 --- a/rinseoffcli/RinseOffCLI.cs +++ b/rinseoffcli/RinseOffCLI.cs @@ -1,21 +1,119 @@ using System; using System.IO; +using System.Text; +using System.Collections.Generic; +using rinseoff; namespace rinseoffcli { class Program { static void showHelp(int exitCode = 1){ - Console.WriteLine("Must specify store or load, then a file name followed by a key file.\nFormat: "); - System.Environment.Exit(exitCode); + Console.WriteLine("Must specify keygen or store/load, then a file name followed by a key file.\nFormat: "); + Environment.Exit(exitCode); } - static void storeData(string filepath, string keypath){ - Stream inputStream = Console.OpenStandardInput(); - if (! File.Exists(keypath)){ - Console.WriteLine("Key file " + keypath + " does not exist"); - System.Environment.Exit(3); + static void validateArgCount(string[] args, int count){ + if (args.Length != count){ + stderrWrite("Invalid number of arguments"); + showHelp(2); } + } + static void stderrWrite(string msg){ + var stderrStream = Console.Error; + stderrStream.WriteLine(msg); + stderrStream.Flush(); + } + static void loadData(string filepath, string keypath){ + byte[] plaintext = {}; + byte[] readBytes(string file){ + byte[] bytesToRead = {}; + try{ + bytesToRead = File.ReadAllBytes(file); + } + catch(FileNotFoundException){ + stderrWrite(file + " is not found"); + Environment.Exit(9); + } + catch(UnauthorizedAccessException){ + stderrWrite("No permssion to read " + file); + Environment.Exit(10); + } + catch(IOException){ + stderrWrite("Failed to read " + file); + Environment.Exit(11); + } + return bytesToRead; + } + var stdout = Console.OpenStandardOutput(); + try{ + plaintext = RinseOff.decrypt_secret_bytes( + readBytes(filepath), + readBytes(keypath) + ); + } + catch(Sodium.Exceptions.KeyOutOfRangeException){ + stderrWrite("Keyfile is not appropriate size for key"); + Environment.Exit(4); + } + catch(System.Security.Cryptography.CryptographicException){ + stderrWrite("Could not decrypt " + filepath + " with " + keypath); + Environment.Exit(12); + } + foreach(byte b in plaintext){ + stdout.WriteByte(b); + } + stdout.Flush(); + } + static void storeData(string filepath, string keypath){ + byte[] encryptedInput = new byte[0]; + + byte[] readUntilClose(){ + // Read binary from STDIN until close + var readData = new List(); + Stream inputStream = Console.OpenStandardInput(); + int inp; + while(true){ + inp = inputStream.ReadByte(); + if (inp != -1){ + readData.Add((byte) inp); + } + else{ + return readData.ToArray(); + } + } + } + // Encrypt stdin with keyfile data then write out to output file + try{ + encryptedInput = RinseOff.encrypt_secret_bytes(readUntilClose(), File.ReadAllBytes(keypath)); + } + catch(FileNotFoundException){ + stderrWrite("Key file " + keypath + " does not exist"); + Environment.Exit(3); + } + catch(Sodium.Exceptions.KeyOutOfRangeException){ + stderrWrite("Keyfile is not appropriate size for key"); + Environment.Exit(4); + } + catch(IOException){ + stderrWrite("Failed to read key file " + keypath); + Environment.Exit(5); + } + try{ + File.WriteAllBytes(filepath, encryptedInput); + } + catch(ArgumentNullException) + { + Environment.Exit(0); + } + catch(DirectoryNotFoundException){ + stderrWrite("Output path " + filepath + " not found"); + Environment.Exit(7); + } + catch(IOException){ + stderrWrite("Could not write to " + filepath); + Environment.Exit(8); + } } static void Main(string[] args) { @@ -24,15 +122,34 @@ namespace rinseoffcli } var cmd = args[0].ToLower(); switch(cmd){ - case "store": - if (args.Length != 3){ - Console.WriteLine("Invalid number of arguments"); - showHelp(2); + case "keygen": + validateArgCount(args, 2); + try{ + RinseOff.generateKeyFile(args[1]); } + catch(UnauthorizedAccessException){ + stderrWrite("Cannot write to key file due to lack of perms " + args[1]); + Environment.Exit(6); + } + catch(DirectoryNotFoundException){ + stderrWrite("Path not found " + args[1]); + Environment.Exit(6); + } + catch(IOException){ + stderrWrite("Path not found " + args[1]); + Environment.Exit(6); + } + break; + case "store": + validateArgCount(args, 3); storeData(args[1], args[2]); break; + case "load": + validateArgCount(args, 3); + loadData(args[1], args[2]); + break; default: - Console.WriteLine("Invalid command"); + stderrWrite("Invalid command"); showHelp(); break; case "help": diff --git a/rinseoffcli/rinseoffcli.csproj b/rinseoffcli/rinseoffcli.csproj index c73e0d1..473c02e 100644 --- a/rinseoffcli/rinseoffcli.csproj +++ b/rinseoffcli/rinseoffcli.csproj @@ -1,5 +1,9 @@ + + + + Exe netcoreapp3.1