Beispiel #1
0
        /// <summary>
        /// Encrypts a stream to another stream.
        /// </summary>
        /// <param name="source">The source stream.</param>
        /// <param name="target">The target stream.</param>
        /// <param name="passwordName">Identifies the password.</param>
        /// <exception cref="CryptographicException">Thrown if the password was not found or for other encryption problems.</exception>
        public void Encrypt(Stream source, Stream target, string passwordName)
        {
            Covenant.Requires <ArgumentNullException>(source != null, nameof(source));
            Covenant.Requires <ArgumentException>(source.CanRead && source.CanSeek, nameof(source));
            Covenant.Requires <ArgumentNullException>(target != null, nameof(target));

            var key = GetKeyFromPassword(passwordName);

            passwordName = passwordName.ToLowerInvariant();

            using (var cipher = new AesCipher(key, maxPaddingBytes: 128))
            {
                using (var encrypted = new MemoryStream())
                {
                    cipher.EncryptStream(source, encrypted);

                    // Write the header line to the target.

                    var header = $"{MagicString}1.0;AES256;{passwordName}{lineEnding}";

                    target.Write(Encoding.ASCII.GetBytes(header));

                    // Write the encrypted data as HEX (80 characters per line).

                    var buffer     = new byte[1];
                    var lineLength = 0;

                    encrypted.Position = 0;

                    while (true)
                    {
                        if (encrypted.Read(buffer, 0, 1) == 0)
                        {
                            break;
                        }

                        target.Write(Encoding.ASCII.GetBytes(NeonHelper.ToHex(buffer[0], uppercase: true)));
                        lineLength += 2;

                        if (lineLength == 80)
                        {
                            target.Write(Encoding.ASCII.GetBytes(lineEnding));
                            lineLength = 0;
                        }
                    }
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Decrypts a stream to another stream.
        /// </summary>
        /// <param name="source">The source stream.</param>
        /// <param name="target">The target stream.</param>
        /// <exception cref="CryptographicException">Thrown if the password was not found or for other decryption problems.</exception>
        public void Decrypt(Stream source, Stream target)
        {
            // Read the first 512 bytes of the source stream to detect whether
            // this is a [NeonVault] encrypted file.  We'll simply copy the
            // source stream to the target if it isn't one of our files.

            var isVaultFile = true;
            var startPos    = source.Position;
            var buffer      = new byte[512];
            var cb          = source.Read(buffer, 0, buffer.Length);
            var afterBOMPos = 0;

            // Skip over any UTF-8 byte order marker (BOM) bytes.

            if (cb > 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF)
            {
                afterBOMPos = 3;
            }

            if (cb - afterBOMPos < MagicBytes.Length)
            {
                isVaultFile = false;
            }

            for (int i = 0; i < MagicBytes.Length; i++)
            {
                if (buffer[i + afterBOMPos] != MagicBytes[i])
                {
                    isVaultFile = false;
                    break;
                }
            }

            if (!isVaultFile)
            {
                source.Position = startPos;

                source.CopyTo(target);
                return;
            }

            // This is a [NeonVault] file.  We need to validate the remaining parameters
            // on the header line, extract the password name.

            var lfPos = -1;

            for (int i = afterBOMPos; i < buffer.Length; i++)
            {
                if (buffer[i] == (char)'\n')
                {
                    lfPos = i + 1;
                    break;
                }
            }

            if (lfPos == -1)
            {
                throw new CryptographicException($"Invalid [{nameof(NeonVault)}] file: Invalid header line.");
            }

            var headerLine = Encoding.UTF8.GetString(buffer, 0, lfPos).Trim();
            var fields     = headerLine.Split(';');

            if (fields.Length != 5)
            {
                throw new CryptographicException($"Invalid [{nameof(NeonVault)}] file: Unexpected number of header line parameters.");
            }

            if (fields[2] != "1.0")
            {
                throw new CryptographicException($"Unsupported [{nameof(NeonVault)}] file: Unexpected version number: {fields[2]}");
            }

            if (fields[3] != "AES256")
            {
                throw new CryptographicException($"Unsupported [{nameof(NeonVault)}] file: Unexpected cipher: {fields[3]}");
            }

            var passwordName = fields[4].ToLowerInvariant();
            var key          = GetKeyFromPassword(passwordName);

            // Read the HEX lines and convert them into bytes and then write then
            // to a MemoryStream to be decrypted.

            source.Position = startPos + lfPos;

            using (var encrypted = new MemoryStream())
            {
                while (true)
                {
                    if (source.Read(buffer, 0, 1) == 0)
                    {
                        break;
                    }

                    var firstHex = char.ToUpperInvariant((char)buffer[0]);

                    if (char.IsWhiteSpace(firstHex))
                    {
                        continue;
                    }

                    if (source.Read(buffer, 0, 1) == 0)
                    {
                        throw new CryptographicException($"Invalid [{nameof(NeonVault)}] file: Odd numer of HEX digits on a line.");
                    }

                    var secondHex = char.ToUpperInvariant((char)buffer[0]);

                    if (char.IsWhiteSpace(firstHex))
                    {
                        throw new CryptographicException($"Invalid [{nameof(NeonVault)}] file: Odd numer of HEX digits on a line.");
                    }

                    if (!NeonHelper.IsHex(firstHex) || !NeonHelper.IsHex(secondHex))
                    {
                        throw new CryptographicException($"Invalid [{nameof(NeonVault)}] file: Invalid HEX digit.");
                    }

                    byte value;

                    if ('0' <= firstHex && firstHex <= '9')
                    {
                        value = (byte)(firstHex - '0');
                    }
                    else
                    {
                        value = (byte)(firstHex - 'A' + 10);
                    }

                    value <<= 4;

                    if ('0' <= secondHex && secondHex <= '9')
                    {
                        value |= (byte)(secondHex - '0');
                    }
                    else
                    {
                        value |= (byte)(secondHex - 'A' + 10);
                    }

                    encrypted.WriteByte(value);
                }

                encrypted.Position = 0;

                using (var cipher = new AesCipher(key))
                {
                    cipher.DecryptStream(encrypted, target);
                }
            }
        }