예제 #1
0
        public void TamperDetect()
        {
            // Verify that we can detect that the data has been tampered with.

            var vault = new NeonVault(GetPassword);

            using (var tempFolder = new TempFolder())
            {
                var sourcePath = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath = Path.Combine(tempFolder.Path, "target.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Encrypt(sourcePath, targetPath, "password-1");

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }

                // Modify the last HEX digit in the target file and verify
                // that decryption fails.

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.ReadWrite))
                {
                    var encrypted = new byte[(int)target.Length];

                    target.Read(encrypted, 0, encrypted.Length);

                    var lastHexDigit = (char)encrypted[encrypted.Length - 1];

                    if (lastHexDigit == '0')
                    {
                        lastHexDigit = '1';
                    }
                    else
                    {
                        lastHexDigit = '0';
                    }

                    encrypted[encrypted.Length - 1] = (byte)lastHexDigit;

                    target.Position = 0;
                    target.Write(encrypted);
                }

                Assert.Throws <CryptographicException>(
                    () =>
                {
                    using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                    {
                        var decrypted = vault.Decrypt(target);

                        Assert.Equal(unencryptedBytes, decrypted);
                    }
                });
            }
        }
예제 #2
0
        /// <inheritdoc/>
        public override void Run(CommandLine commandLine)
        {
            if (commandLine.HasHelpOption)
            {
                Console.WriteLine(usage);
                Program.Exit(0);
            }

            var sourcePath = commandLine.Arguments.ElementAtOrDefault(0);
            var targetPath = commandLine.Arguments.ElementAtOrDefault(1);

            if (string.IsNullOrEmpty(sourcePath))
            {
                Console.Error.WriteLine("*** ERROR: The SOURCE argument is required.");
                Program.Exit(1);
            }

            if (string.IsNullOrEmpty(targetPath))
            {
                Console.Error.WriteLine("*** ERROR: The TARGET argument is required.");
                Program.Exit(1);
            }

            if (!NeonVault.IsEncrypted(sourcePath))
            {
                Console.Error.WriteLine($"*** ERROR: The [{sourcePath}] file is not encrypted.");
                Program.Exit(1);
            }

            var vault = new NeonVault(Program.LookupPassword);

            vault.Decrypt(sourcePath, targetPath);

            Program.Exit(0);
        }
예제 #3
0
        public void WrongPassword()
        {
            // Verify that we can detect that the wrong password was used.

            using (var tempFolder = new TempFolder())
            {
                var vault      = new NeonVault(passwordName => password1);
                var sourcePath = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath = Path.Combine(tempFolder.Path, "target.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Encrypt(sourcePath, targetPath, "password-1");

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }

                vault = new NeonVault(passwordName => password2);   // This uses the wrong password.

                Assert.Throws <CryptographicException>(
                    () =>
                {
                    using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                    {
                        vault.Decrypt(target);
                    }
                });
            }
        }
예제 #4
0
        public void NoPassword()
        {
            // Verify the proper exception when a named password cannot be found.

            var vault = new NeonVault(passwordName => throw new KeyNotFoundException());

            using (var source = new MemoryStream(unencryptedBytes))
            {
                Assert.Throws <CryptographicException>(() => vault.Encrypt(source, "password-1"));
            }

            // Verify the exception when an encrypted file references a password
            // that doesn't exist.

            using (var tempFolder = new TempFolder())
            {
                vault = new NeonVault(passwordName => password1);

                var sourcePath = Path.Combine(tempFolder.Path, "source.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                var encrypted = vault.Encrypt(sourcePath, "password-1");

                vault = new NeonVault(passwordName => throw new KeyNotFoundException());

                using (var source = new MemoryStream(encrypted))
                {
                    Assert.Throws <CryptographicException>(() => vault.Decrypt(source));
                }
            }
        }
예제 #5
0
        public void StreamToStream()
        {
            var vault = new NeonVault(GetPassword);

            using (var tempFolder = new TempFolder())
            {
                var sourcePath = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath = Path.Combine(tempFolder.Path, "target.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                using (var source = new FileStream(sourcePath, FileMode.Open, FileAccess.Read))
                {
                    using (var target = new FileStream(targetPath, FileMode.Create, FileAccess.ReadWrite))
                    {
                        vault.Encrypt(source, target, "password-1");
                    }
                }

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }
            }
        }
예제 #6
0
        /// <summary>
        /// Loads a file into the <see cref="Files"/> dictionary, using the file name
        /// (without the directory path) as the key.  The file will be decrypted via
        /// <see cref="NeonVault"/> as necessary.
        /// </summary>
        /// <param name="path">The file path.</param>
        /// <param name="passwordProvider">
        /// Optionally specifies the password provider function to be used to locate the
        /// password required to decrypt the source file when necessary.  The password will
        /// use the <see cref="KubeHelper.LookupPassword(string)"/> method when
        /// <paramref name="passwordProvider"/> is <c>null</c>.
        /// </param>
        /// <exception cref="FileNotFoundException">Thrown if the file doesn't exist.</exception>
        /// <exception cref="FormatException">Thrown for file formatting problems.</exception>
        public void LoadFile(string path, Func <string, string> passwordProvider = null)
        {
            passwordProvider = passwordProvider ?? KubeHelper.LookupPassword;

            if (!File.Exists(path))
            {
                throw new FileNotFoundException($"File not found: {path}");
            }

            var vault = new NeonVault(passwordProvider);
            var bytes = vault.Decrypt(path);

            Files[Path.GetFileName(path)] = bytes;
        }
예제 #7
0
        public void LowercaseHEX()
        {
            // Verify that we can process lower-case HEX digits.

            var vault = new NeonVault(GetPassword);

            using (var tempFolder = new TempFolder())
            {
                var sourcePath  = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath  = Path.Combine(tempFolder.Path, "target.txt");
                var target2Path = Path.Combine(tempFolder.Path, "target2.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Encrypt(sourcePath, targetPath, "password-1");

                using (var targetReader = new StreamReader(targetPath))
                {
                    using (var target2Writer = new StreamWriter(target2Path))
                    {
                        // Copy the first line as-is and then write the remaining lines
                        // as lowercase.

                        var first = true;

                        foreach (var line in targetReader.Lines())
                        {
                            if (first)
                            {
                                target2Writer.WriteLine(line);
                                first = false;
                            }
                            else
                            {
                                target2Writer.WriteLine(line.ToUpperInvariant());
                            }
                        }
                    }
                }

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }
            }
        }
예제 #8
0
        /// <summary>
        /// <para>
        /// Loads environment variables formatted as <c>NAME=VALUE</c> from a text file into environment
        /// variables.  The file will be decrypted using <see cref="NeonVault"/> if necessary.
        /// </para>
        /// <note>
        /// Blank lines and lines beginning with '#' will be ignored.
        /// </note>
        /// </summary>
        /// <param name="path">The input file path.</param>
        /// <param name="passwordProvider">
        /// Optionally specifies the password provider function to be used to locate the
        /// password required to decrypt the source file when necessary.  The password will
        /// use the <see cref="KubeHelper.LookupPassword(string)"/> method when
        /// <paramref name="passwordProvider"/> is <c>null</c>.
        /// </param>
        /// <exception cref="FileNotFoundException">Thrown if the file doesn't exist.</exception>
        /// <exception cref="FormatException">Thrown for file formatting problems.</exception>
        public void LoadEnvironment(string path, Func <string, string> passwordProvider = null)
        {
            passwordProvider = passwordProvider ?? KubeHelper.LookupPassword;

            if (!File.Exists(path))
            {
                throw new FileNotFoundException($"File not found: {path}");
            }

            var vault = new NeonVault(passwordProvider);
            var bytes = vault.Decrypt(path);

            using (var ms = new MemoryStream(bytes))
            {
                using (var reader = new StreamReader(ms))
                {
                    var lineNumber = 1;

                    foreach (var rawLine in reader.Lines())
                    {
                        var line = rawLine.Trim();

                        if (line.Length == 0 || line.StartsWith("#"))
                        {
                            continue;
                        }

                        var fields = line.Split(new char[] { '=' }, 2);

                        if (fields.Length != 2)
                        {
                            throw new FormatException($"[{path}:{lineNumber}]: Invalid input: {line}");
                        }

                        var name  = fields[0].Trim();
                        var value = fields[1].Trim();

                        if (name.Length == 0)
                        {
                            throw new FormatException($"[{path}:{lineNumber}]: Setting name cannot be blank.");
                        }

                        Environment.SetEnvironmentVariable(name, value);
                    }
                }
            }
        }
예제 #9
0
        public void StreamToBytes()
        {
            var vault     = new NeonVault(GetPassword);
            var encrypted = (byte[])null;

            using (var source = new MemoryStream(unencryptedBytes))
            {
                encrypted = vault.Encrypt(source, "password-1");
            }

            using (var source = new MemoryStream(encrypted))
            {
                var decrypted = vault.Decrypt(source);

                Assert.Equal(unencryptedBytes, decrypted);
            }
        }
예제 #10
0
        /// <summary>
        /// Maps a logical configuration file path to an actual file on the
        /// local machine.  This is used for unit testing to map a file on
        /// the local workstation to the path where the service expects the
        /// find to be.
        /// </summary>
        /// <param name="logicalPath">The logical file path (typically expressed as a Linux path).</param>
        /// <param name="physicalPath">The physical path to the file on the local workstation.</param>
        /// <param name="passwordProvider">
        /// Optionally specifies the password provider function to be used to locate the
        /// password required to decrypt the source file when necessary.  The password will
        /// use the <see cref="KubeHelper.LookupPassword(string)"/> method when
        /// <paramref name="passwordProvider"/> is <c>null</c>.
        /// </param>
        /// <exception cref="FileNotFoundException">Thrown if there's no file at <paramref name="physicalPath"/>.</exception>
        public void SetConfigFilePath(string logicalPath, string physicalPath, Func <string, string> passwordProvider = null)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(logicalPath), nameof(logicalPath));
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(physicalPath), nameof(physicalPath));

            if (!File.Exists(physicalPath))
            {
                throw new FileNotFoundException($"Physical configuration file [{physicalPath}] does not exist.");
            }

            passwordProvider = passwordProvider ?? KubeHelper.LookupPassword;

            var vault = new NeonVault(passwordProvider);
            var bytes = vault.Decrypt(physicalPath);

            SetConfigFile(logicalPath, bytes);
        }
예제 #11
0
        public void Unencrypted()
        {
            // Verify that "decrypting" an unencrypted file simply copies
            // the data to the output.

            var vault = new NeonVault(GetPassword);

            using (var tempFolder = new TempFolder())
            {
                var sourcePath = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath = Path.Combine(tempFolder.Path, "target.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Decrypt(sourcePath, targetPath);

                Assert.Equal(unencryptedText, File.ReadAllText(targetPath));
                Assert.False(NeonVault.IsEncrypted(sourcePath));
                Assert.False(NeonVault.IsEncrypted(targetPath));
            }
        }
예제 #12
0
        public void FileToBytes()
        {
            var vault     = new NeonVault(GetPassword);
            var encrypted = (byte[])null;

            using (var tempFolder = new TempFolder())
            {
                var sourcePath = Path.Combine(tempFolder.Path, "source.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                encrypted = vault.Encrypt(sourcePath, "password-1");

                using (var source = new MemoryStream(encrypted))
                {
                    var decrypted = vault.Decrypt(source);

                    Assert.Equal(unencryptedBytes, decrypted);
                }
            }
        }
예제 #13
0
        /// <inheritdoc/>
        public override async Task RunAsync(CommandLine commandLine)
        {
            if (commandLine.HasHelpOption)
            {
                Console.WriteLine(usage);
                Program.Exit(0);
            }

            var path = commandLine.Arguments.ElementAtOrDefault(0);

            if (string.IsNullOrEmpty(path))
            {
                Console.Error.WriteLine("*** ERROR: The PATH argument is required.");
                Program.Exit(1);
            }

            if (!NeonVault.IsEncrypted(path, out var passwordName))
            {
                Console.Error.WriteLine($"*** ERROR: The [{path}] file is not encrypted.");
                Program.Exit(1);
            }

            var vault    = new NeonVault(Program.LookupPassword);
            var fileName = Path.GetFileName(path);

            using (var tempFolder = new TempFolder())
            {
                var tempPath = Path.Combine(tempFolder.Path, fileName);

                // Decrypt the file to a secure temporary folder, launch the
                // editor and re-encrypt the file after the editor returns.

                vault.Decrypt(path, tempPath);
                NeonHelper.OpenEditor(tempPath);
                vault.Encrypt(tempPath, path, passwordName);
            }

            Program.Exit(0);
            await Task.CompletedTask;
        }
예제 #14
0
        public void BOM()
        {
            // Verify that [NeonVault] can ignore UTF-8 BOM markers.

            var vault = new NeonVault(GetPassword);

            using (var tempFolder = new TempFolder())
            {
                var sourcePath  = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath  = Path.Combine(tempFolder.Path, "target.txt");
                var target2Path = Path.Combine(tempFolder.Path, "target2.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Encrypt(sourcePath, targetPath, "password-1");

                using (var targetReader = new StreamReader(targetPath))
                {
                    using (var target2 = new FileStream(target2Path, FileMode.Create, FileAccess.ReadWrite))
                    {
                        // Write the BOM followed by the unmodified encrypted file lines.

                        target2.Write(new byte[] { 0xEF, 0xBB, 0xBF });  // The BOM

                        foreach (var line in targetReader.Lines())
                        {
                            target2.Write(Encoding.ASCII.GetBytes(line + "\r\n"));
                        }
                    }
                }

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }
            }
        }
예제 #15
0
        public void FileToFile()
        {
            var vault = new NeonVault(GetPassword);

            using (var tempFolder = new TempFolder())
            {
                var sourcePath = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath = Path.Combine(tempFolder.Path, "target.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Encrypt(sourcePath, targetPath, "password-1");

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }

                Assert.False(NeonVault.IsEncrypted(sourcePath));
                Assert.True(NeonVault.IsEncrypted(targetPath));
            }
        }
예제 #16
0
        public void VaultDecrypt()
        {
            using (var manager = new KubeTestManager())
            {
                using (var tempFolder = new TempFolder())
                {
                    var orgDir = Environment.CurrentDirectory;

                    Environment.CurrentDirectory = tempFolder.Path;
                    NeonHelper.OpenEditorHandler = path => File.WriteAllText(path, plainText);

                    try
                    {
                        using (var passwordFile = new TempFile(folder: KubeHelper.PasswordsFolder))
                        {
                            File.WriteAllText(passwordFile.Path, testPassword);

                            var vault = new NeonVault(passwordName => testPassword);

                            using (var runner = new ProgramRunner())
                            {
                                // Verify that the SOURCE argument is required.

                                var result = runner.Execute(Program.Main, "vault", "decrypt");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: The SOURCE argument is required.", result.ErrorText);

                                // Verify that the TARGET argument is required.

                                result = runner.Execute(Program.Main, "vault", "decrypt");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: The SOURCE argument is required.", result.ErrorText);

                                // Verify that the SOURCE-PATH argument is required.

                                result = runner.Execute(Program.Main, "vault", "decrypt", "test.txt");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: The TARGET argument is required.", result.ErrorText);

                                // Verify that we can create an encrypted file with an explicitly
                                // named password.

                                result = runner.Execute(Program.Main, "vault", "create", "test1.txt", passwordFile.Name);

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test1.txt", out var passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test1.txt")));

                                // Verify that we can decrypt the file.

                                result = runner.Execute(Program.Main, "vault", "decrypt", "test1.txt", "decrypted.txt");

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test1.txt", out passwordName));
                                Assert.True(!NeonVault.IsEncrypted("decrypted.txt", out passwordName));
                                Assert.Equal(plainText, File.ReadAllText("decrypted.txt"));
                            }
                        }
                    }
                    finally
                    {
                        Environment.CurrentDirectory = orgDir;
                        NeonHelper.OpenEditorHandler = null;
                    }
                }
            }
        }
예제 #17
0
        /// <inheritdoc/>
        public override async Task RunAsync(CommandLine commandLine)
        {
            if (commandLine.HasHelpOption)
            {
                Console.WriteLine(usage);
                Program.Exit(0);
            }

            var splitCommandLine = commandLine.Split("--");
            var leftCommandLine  = splitCommandLine.Left;
            var rightCommandLine = splitCommandLine.Right;

            if (rightCommandLine == null || rightCommandLine.Arguments.Length == 0)
            {
                Console.Error.WriteLine("*** ERROR: Expected a command after a [--] argument.");
                Program.Exit(1);
            }

            // All arguments on the left command line should be VARIABLES files.
            // We're going to open each of these and set any enviroment variables
            // like [NAME=VALUE] we find.
            //
            // Note that these files may be encrypted.  If any are, we'll decrypt
            // to a temporary file before we read them.

            foreach (var path in leftCommandLine.Arguments)
            {
                if (!File.Exists(path))
                {
                    Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist.");
                    Program.Exit(1);
                }

                DecryptWithAction(path,
                                  decryptedPath =>
                {
                    var lineNumber = 1;

                    foreach (var line in File.ReadAllLines(decryptedPath))
                    {
                        var trimmed = line.Trim();

                        if (line == string.Empty || line.StartsWith("#"))
                        {
                            continue;
                        }

                        var fields = line.Split('=', 2);

                        if (fields.Length != 2 || fields[0] == string.Empty)
                        {
                            Console.Error.WriteLine($"*** ERROR: [{path}:{lineNumber}] is not formatted like: NAME=VALUE");
                            Program.Exit(1);
                        }

                        var name  = fields[0].Trim();
                        var value = fields[1].Trim();

                        Environment.SetEnvironmentVariable(name, value);

                        lineNumber++;
                    }
                });
            }

            // Any left command line options with a "--" prefix also specify environment variables.

            foreach (var option in leftCommandLine.Options.Where(o => o.Key.StartsWith("--")))
            {
                Environment.SetEnvironmentVariable(option.Key.Substring(2), option.Value);
            }

            // We've read all of the variable files and left command line options
            // and initialized all environment variables.  Now we need to process
            // and then execute the right command line.

            var tempFiles = new List <TempFile>();

            try
            {
                var subcommand = rightCommandLine.Items;

                // Note that the first element of the subcommand specifies the
                // executable so we don't need to process that.

                for (int i = 1; i < subcommand.Length; i++)
                {
                    var arg = subcommand[i];

                    if (arg.StartsWith("_..."))
                    {
                        // Argument is a reference to a potentially encrypted
                        // file that needs to be passed decrypted.

                        var path = arg.Substring(4);

                        if (!File.Exists(path))
                        {
                            Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist.");
                            Program.Exit(1);
                        }

                        if (NeonVault.IsEncrypted(path))
                        {
                            var tempFile = new TempFile();

                            tempFiles.Add(tempFile);
                            vault.Decrypt(path, tempFile.Path);

                            path = tempFile.Path;
                        }

                        subcommand[i] = path;
                    }
                    else if (arg.StartsWith("_.."))
                    {
                        // Argument is a reference to a potentially encrypted text file
                        // with environment variable references we'll need to update.

                        var path = arg.Substring(3);

                        if (!File.Exists(path))
                        {
                            Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist.");
                            Program.Exit(1);
                        }

                        if (NeonVault.IsEncrypted(path))
                        {
                            var tempFile = new TempFile();

                            tempFiles.Add(tempFile);
                            vault.Decrypt(path, tempFile.Path);

                            path = tempFile.Path;
                        }

                        subcommand[i] = path;

                        // Perform the subsitutions.

                        var unprocessed      = File.ReadAllText(path);
                        var processed        = string.Empty;
                        var linuxLineEndings = !unprocessed.Contains("\r\n");

                        using (var reader = new StreamReader(path))
                        {
                            using (var preprocessor = new PreprocessReader(reader))
                            {
                                preprocessor.ExpandVariables        = true;
                                preprocessor.LineEnding             = linuxLineEndings ? LineEnding.LF : LineEnding.CRLF;
                                preprocessor.ProcessStatements      = false;
                                preprocessor.StripComments          = false;
                                preprocessor.VariableExpansionRegex = PreprocessReader.AngleVariableExpansionRegex;

                                processed = preprocessor.ReadToEnd();
                            }
                        }

                        File.WriteAllText(path, processed);
                    }
                    else if (arg.StartsWith("_."))
                    {
                        // Argument is a reference to an environment variable.

                        var name = arg.Substring(2);

                        if (name == string.Empty)
                        {
                            Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] is not valid.");
                            Program.Exit(1);
                        }

                        var value = Environment.GetEnvironmentVariable(name);

                        if (value == null)
                        {
                            Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] references an undefined environment variable.");
                            Program.Exit(2);
                        }

                        subcommand[i] = value;
                    }
                    else if (arg.StartsWith("-"))
                    {
                        // Argument is a command line option.  We'll check to see if
                        // it contains a reference to an environment variable.

                        var valuePos = arg.IndexOf("=_.");

                        if (valuePos != -1)
                        {
                            var optionPart = arg.Substring(0, valuePos);
                            var name       = arg.Substring(valuePos + 3);

                            if (name == string.Empty)
                            {
                                Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] is not valid.");
                                Program.Exit(1);
                            }

                            var value = Environment.GetEnvironmentVariable(name);

                            if (value == null)
                            {
                                Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] references an undefined environment variable.");
                                Program.Exit(1);
                            }

                            subcommand[i] = $"{optionPart}={value}";
                        }
                    }
                    else
                    {
                        // Otherwise, expand any envrionment variable references.

                        subcommand[i] = Environment.ExpandEnvironmentVariables(subcommand[i]);
                    }
                }

                // Execute the subcommand.

                var subcommandArgs = new List <object>();

                foreach (var subcommandArg in subcommand)
                {
                    subcommandArgs.Add(subcommandArg);
                }

                var exitCode = NeonHelper.Execute(subcommand[0], subcommandArgs.Skip(1).ToArray());

                Program.Exit(exitCode);
            }
            finally
            {
                foreach (var tempFile in tempFiles)
                {
                    tempFile.Dispose();
                }
            }

            Program.Exit(0);
            await Task.CompletedTask;
        }
예제 #18
0
        public void SwitchLineEndings()
        {
            // Verify that we can still decrypt en encrypted file after
            // changing the line endings from CRLF --> LF:

            var vault = new NeonVault(GetPassword, lineEnding: "\r\n");

            using (var tempFolder = new TempFolder())
            {
                var sourcePath  = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath  = Path.Combine(tempFolder.Path, "target.txt");
                var target2Path = Path.Combine(tempFolder.Path, "target2.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Encrypt(sourcePath, targetPath, "password-1");

                using (var targetReader = new StreamReader(targetPath))
                {
                    using (var target2Writer = new StreamWriter(target2Path))
                    {
                        foreach (var line in targetReader.Lines())
                        {
                            target2Writer.WriteLine(line + "\n");
                        }
                    }
                }

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }
            }

            // Verify that we can still decrypt en encrypted file after
            // changing the line endings from LF --> CRLF:

            vault = new NeonVault(GetPassword, lineEnding: "\n");

            using (var tempFolder = new TempFolder())
            {
                var sourcePath  = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath  = Path.Combine(tempFolder.Path, "target.txt");
                var target2Path = Path.Combine(tempFolder.Path, "target2.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Encrypt(sourcePath, targetPath, "password-1");

                using (var targetReader = new StreamReader(targetPath))
                {
                    using (var target2Writer = new StreamWriter(target2Path))
                    {
                        foreach (var line in targetReader.Lines())
                        {
                            target2Writer.WriteLine(line + "\r\n");
                        }
                    }
                }

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }
            }
        }
예제 #19
0
        public void BadHEX()
        {
            var vault = new NeonVault(GetPassword);

            using (var tempFolder = new TempFolder())
            {
                // Verify that an invalid character in the HEX part is detected.

                var sourcePath = Path.Combine(tempFolder.Path, "source.txt");
                var targetPath = Path.Combine(tempFolder.Path, "target.txt");

                File.WriteAllText(sourcePath, unencryptedText);

                vault.Encrypt(sourcePath, targetPath, "password-1");

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                {
                    var decrypted = vault.Decrypt(target);

                    Assert.Equal(unencryptedBytes, decrypted);
                }

                // Modify the last HEX digit to be the invalid HEX digit 'Z'.

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.ReadWrite))
                {
                    var encrypted = new byte[(int)target.Length];

                    target.Read(encrypted, 0, encrypted.Length);

                    encrypted[encrypted.Length - 1] = (byte)'Z';

                    target.Position = 0;
                    target.Write(encrypted);
                }

                Assert.Throws <CryptographicException>(
                    () =>
                {
                    using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                    {
                        var decrypted = vault.Decrypt(target);

                        Assert.Equal(unencryptedBytes, decrypted);
                    }
                });

                // Verify that an odd number of HEX digits is detected by removing
                // the last character of the file.

                using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.ReadWrite))
                {
                    var encrypted = new byte[(int)target.Length];

                    target.Read(encrypted, 0, encrypted.Length);

                    target.Position = 0;
                    target.Write(encrypted, 0, encrypted.Length - 1);
                }

                Assert.Throws <CryptographicException>(
                    () =>
                {
                    using (var target = new FileStream(targetPath, FileMode.Open, FileAccess.Read))
                    {
                        var decrypted = vault.Decrypt(target);

                        Assert.Equal(unencryptedBytes, decrypted);
                    }
                });
            }
        }
예제 #20
0
        public async Task VaultEncrypt()
        {
            using (var testManager = new KubeTestManager())
            {
                using (var tempFolder = new TempFolder())
                {
                    var orgDir = Environment.CurrentDirectory;

                    Environment.CurrentDirectory = tempFolder.Path;
                    NeonHelper.OpenEditorHandler = path => File.WriteAllText(path, plainText);

                    try
                    {
                        using (var passwordFile = new TempFile(folder: KubeHelper.PasswordsFolder))
                        {
                            File.WriteAllText(passwordFile.Path, testPassword);

                            var vault = new NeonVault(passwordName => testPassword);

                            using (var runner = new ProgramRunner())
                            {
                                // Verify that the PATH argument is required.

                                var result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: The PATH argument is required.", result.ErrorText);

                                // Verify that the TARGET argument is required when [--password-name]
                                // or [--p] is not present.

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "decrypt", "source.txt");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: The TARGET argument is required.", result.ErrorText);

                                // Verify that we can encrypt a file in-place, specifying an
                                // explicit password name (using --password-name=NAME).

                                File.WriteAllText("test1.txt", plainText);

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test1.txt", $"--password-name={passwordFile.Name}");

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test1.txt", out var passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test1.txt")));

                                // Verify that we can encrypt a file in-place, specifying an
                                // explicit password name (using --p=NAME).

                                File.WriteAllText("test2.txt", plainText);

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test2.txt", $"-p={passwordFile.Name}");

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test2.txt", out passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test2.txt")));

                                // Verify that we get an error trying to encrypt in-place without a
                                // password name being explicitly specified and also without a
                                // [.password-name] file present.

                                File.WriteAllText("test3.txt", plainText);

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test3.txt");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: A PASSWORD-NAME argument or [.password-name] file is required.", result.ErrorText);

                                // Verify that we get an error trying to encrypt (not in-place) without a
                                // password name being explicitly specified and also without a
                                // [.password-name] file present.

                                File.WriteAllText("test4.txt", plainText);

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test4.txt", "test4.encypted.txt");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: A PASSWORD-NAME argument or [.password-name] file is required.", result.ErrorText);

                                // Verify that we can encrypt a file to another with
                                // and explicit password argument.

                                File.WriteAllText("test5.txt", plainText);

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test5.txt", "test5.encypted.txt", passwordName);

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test5.encypted.txt", out passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test5.encypted.txt")));

                                // Verify that we can encrypt a file to another with
                                // and explicit [--password-name] option.

                                File.WriteAllText("test6.txt", plainText);

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test6.txt", "test6.encypted.txt", $"--password-name={passwordName}");

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test6.encypted.txt", out passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test6.encypted.txt")));

                                // Verify that we can encrypt a file in-place using a [.password-name] file.

                                File.WriteAllText("test7.txt", plainText);
                                File.WriteAllText(".password-name", passwordName);

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", "test7.txt");

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test7.txt", out passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test7.txt")));

                                // Verify that we can encrypt a file (not in-place) to another where
                                // the source file is located in a different directory from the target
                                // to ensure that we look for the [.password-name] file starting at
                                // the target directory.

                                using (var tempFile = new TempFile())
                                {
                                    File.WriteAllText(tempFile.Path, plainText);

                                    result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "encrypt", tempFile.Path, "test8.encrypted.txt");

                                    Assert.Equal(0, result.ExitCode);
                                    Assert.True(NeonVault.IsEncrypted("test8.encrypted.txt", out passwordName));
                                    Assert.Equal(passwordFile.Name, passwordName);
                                    Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test8.encrypted.txt")));
                                }
                            }
                        }
                    }
                    finally
                    {
                        Environment.CurrentDirectory = orgDir;
                        NeonHelper.OpenEditorHandler = null;
                    }
                }
            }
        }
예제 #21
0
        public async Task VaultPasswordName()
        {
            using (var testManager = new KubeTestManager())
            {
                using (var tempFolder = new TempFolder())
                {
                    var orgDir = Environment.CurrentDirectory;

                    Environment.CurrentDirectory = tempFolder.Path;
                    NeonHelper.OpenEditorHandler = path => File.WriteAllText(path, plainText);

                    try
                    {
                        using (var passwordFile = new TempFile(folder: KubeHelper.PasswordsFolder))
                        {
                            File.WriteAllText(passwordFile.Path, testPassword);

                            var vault = new NeonVault(passwordName => testPassword);

                            using (var runner = new ProgramRunner())
                            {
                                // Verify that the PATH argument is required.

                                var result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "password-name");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: The PATH argument is required.", result.ErrorText);

                                // Verify that we can create an encrypted file with an explicitly
                                // named password.

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test1.txt", passwordFile.Name);

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test1.txt", out var passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test1.txt")));

                                // Verify that we can get the password with a line ending.

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "password-name", "test1.txt", passwordFile.Name);

                                Assert.Equal(0, result.ExitCode);
                                Assert.Contains(passwordFile.Name, result.OutputText);
                                Assert.Contains('\n', result.OutputText);

                                // Verify that we can get the password without a line ending.

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "password-name", "-n", "test1.txt", passwordFile.Name);

                                Assert.Equal(0, result.ExitCode);
                                Assert.Equal(passwordFile.Name, result.OutputText);
                            }
                        }
                    }
                    finally
                    {
                        Environment.CurrentDirectory = orgDir;
                        NeonHelper.OpenEditorHandler = null;
                    }
                }
            }
        }
예제 #22
0
        public async Task VaultCreate()
        {
            using (var testManager = new KubeTestManager())
            {
                using (var tempFolder = new TempFolder())
                {
                    var orgDir = Environment.CurrentDirectory;

                    Environment.CurrentDirectory = tempFolder.Path;
                    NeonHelper.OpenEditorHandler = path => File.WriteAllText(path, plainText);

                    try
                    {
                        using (var passwordFile = new TempFile(folder: KubeHelper.PasswordsFolder))
                        {
                            File.WriteAllText(passwordFile.Path, testPassword);

                            var vault = new NeonVault(passwordName => testPassword);

                            using (var runner = new ProgramRunner())
                            {
                                // Verify that the PATH argument is required.

                                var result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: The PATH argument is required.", result.ErrorText);

                                // Verify that the PASSWORD-NAME argument is required when there's
                                // no default [.password-name] file.

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test1.txt");

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains("*** ERROR: A PASSWORD-NAME argument or [.password-name] file is required.", result.ErrorText);

                                // Verify that we can create an encrypted file with an explicitly
                                // named password.

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test2.txt", passwordFile.Name);

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test2.txt", out var passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test2.txt")));

                                // Verify that we see an error for a missing password.

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test3.txt", missingPasswordName);

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains($"*** ERROR: [System.Security.Cryptography.CryptographicException]: Password named [{missingPasswordName}] not found or is blank or whitespace.", result.ErrorText);

                                // Verify that we see an error for an invalid password.

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test4.txt", badPasswordName);

                                Assert.NotEqual(0, result.ExitCode);
                                Assert.Contains($"*** ERROR: [System.Security.Cryptography.CryptographicException]: Password name [bad/password] contains invalid characters.  Only ASCII letters, digits, underscores, dashs and dots are allowed.", result.ErrorText);

                                // Verify that a local [.password-name] file is used successfully when we don't
                                // explicitly pass a password name.

                                File.WriteAllText(".password-name", passwordFile.Name);

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test5.txt");

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test5.txt", out passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test5.txt")));

                                // Verify that a [.password-name] file in the parent directory is used successfully
                                // when we don't explicitly pass a password name.

                                Directory.CreateDirectory("subfolder");
                                Environment.CurrentDirectory = Path.Combine(Environment.CurrentDirectory, "subfolder");

                                result = await runner.ExecuteAsync(Program.Main, "tool", "vault", "create", "test6.txt");

                                Assert.Equal(0, result.ExitCode);
                                Assert.True(NeonVault.IsEncrypted("test6.txt", out passwordName));
                                Assert.Equal(passwordFile.Name, passwordName);
                                Assert.Equal(plainText, Encoding.UTF8.GetString(vault.Decrypt("test6.txt")));
                            }
                        }
                    }
                    finally
                    {
                        Environment.CurrentDirectory = orgDir;
                        NeonHelper.OpenEditorHandler = null;
                    }
                }
            }
        }