public bool VerifyHmac()
        {
            using (V1AxCryptDataStream encryptedDataStream = CreateEncryptedDataStream(_reader.InputStream, DocumentHeaders.CipherTextLength))
            {
                encryptedDataStream.CopyTo(Stream.Null);
            }

            return(Hmac == DocumentHeaders.Headers.Hmac);
        }
        private V1AxCryptDataStream CreateEncryptedDataStream(Stream inputStream, long cipherTextLength)
        {
            if (_reader.CurrentItemType != AxCryptItemType.Data)
            {
                throw new InvalidOperationException("An attempt to create an encrypted data stream was made when the reader is not positioned at the data.");
            }

            _reader.SetEndOfStream();

            _expectedTotalHmacLength = _hmacStream.Position + cipherTextLength;

            V1AxCryptDataStream encryptedDataStream = new V1AxCryptDataStream(inputStream, _hmacStream, cipherTextLength);

            return(encryptedDataStream);
        }
        /// <summary>
        /// Decrypts the encrypted data to the given stream
        /// </summary>
        /// <param name="outputPlaintextStream">The resulting plain text stream.</param>
        public void DecryptTo(Stream outputPlaintextStream)
        {
            if (!PassphraseIsValid)
            {
                throw new InternalErrorException("Passphrase is not valid!");
            }

            using (ICryptoTransform decryptor = DataCrypto.DecryptingTransform())
            {
                using (V1AxCryptDataStream encryptedDataStream = CreateEncryptedDataStream(_reader.InputStream, DocumentHeaders.CipherTextLength))
                {
                    encryptedDataStream.DecryptTo(outputPlaintextStream, decryptor, DocumentHeaders.IsCompressed);
                }
            }

            if (Hmac != DocumentHeaders.Headers.Hmac)
            {
                throw new Axantum.AxCrypt.Core.Runtime.IncorrectDataException("HMAC validation error.", ErrorStatus.HmacValidationError);
            }
        }
        /// <summary>
        /// Write a copy of the current encrypted stream. Used to change meta-data
        /// and encryption key(s) etc.
        /// </summary>
        /// <param name="outputStream"></param>
        public void CopyEncryptedTo(V1DocumentHeaders outputDocumentHeaders, Stream cipherStream)
        {
            if (outputDocumentHeaders == null)
            {
                throw new ArgumentNullException("outputDocumentHeaders");
            }
            if (cipherStream == null)
            {
                throw new ArgumentNullException("cipherStream");
            }
            if (!cipherStream.CanSeek)
            {
                throw new ArgumentException("The output stream must support seek in order to back-track and write the HMAC.");
            }
            if (!PassphraseIsValid)
            {
                throw new InternalErrorException("Passphrase is not valid.");
            }

            using (V1HmacStream hmacStreamOutput = new V1HmacStream(outputDocumentHeaders.HmacSubkey.Key, cipherStream))
            {
                outputDocumentHeaders.WriteWithHmac(hmacStreamOutput);
                using (V1AxCryptDataStream encryptedDataStream = CreateEncryptedDataStream(_reader.InputStream, DocumentHeaders.CipherTextLength))
                {
                    encryptedDataStream.CopyTo(hmacStreamOutput);

                    if (Hmac != DocumentHeaders.Headers.Hmac)
                    {
                        throw new Axantum.AxCrypt.Core.Runtime.IncorrectDataException("HMAC validation error in the input stream.", ErrorStatus.HmacValidationError);
                    }
                }

                outputDocumentHeaders.Headers.Hmac = hmacStreamOutput.HmacResult;

                // Rewind and rewrite the headers, now with the updated HMAC
                outputDocumentHeaders.WriteWithoutHmac(cipherStream);
                cipherStream.Position = cipherStream.Length;
            }
        }
Example #5
0
        public static void TestAxCryptDataStream()
        {
            string streamData = "This is some data in the streamEXTRA";

            using (Stream inputStream = new MemoryStream())
            {
                byte[] streamBytes = Encoding.UTF8.GetBytes(streamData);
                inputStream.Write(streamBytes, 0, streamBytes.Length);

                using (Stream hmacStream = new MemoryStream())
                {
                    Assert.Throws <ArgumentNullException>(() =>
                    {
                        using (V1AxCryptDataStream axCryptDataStream = new V1AxCryptDataStream(null, hmacStream, inputStream.Length)) { }
                    }, "An input stream must be given, it cannot be null.");
                    Assert.Throws <ArgumentNullException>(() =>
                    {
                        using (V1AxCryptDataStream axCryptDataStream = new V1AxCryptDataStream(inputStream, null, inputStream.Length)) { }
                    }, "An HmacStream must be given, it cannot be null.");
                    Assert.Throws <ArgumentOutOfRangeException>(() =>
                    {
                        using (V1AxCryptDataStream axCryptDataStream = new V1AxCryptDataStream(inputStream, hmacStream, -inputStream.Length)) { }
                    }, "Negative length is not allowed.");

                    inputStream.Position = 0;
                    using (V1AxCryptDataStream axCryptDataStream = new V1AxCryptDataStream(inputStream, hmacStream, inputStream.Length - 5))
                    {
                        Assert.Throws <NotSupportedException>(() =>
                        {
                            axCryptDataStream.Seek(0, SeekOrigin.Begin);
                        }, "Seek is not supported.");

                        Assert.Throws <NotSupportedException>(() =>
                        {
                            axCryptDataStream.SetLength(0);
                        }, "SetLength is not supported.");

                        Assert.Throws <NotSupportedException>(() =>
                        {
                            axCryptDataStream.Write(new byte[1], 0, 1);
                        }, "Write is not supported.");

                        Assert.Throws <NotSupportedException>(() =>
                        {
                            axCryptDataStream.Position = 0;
                        }, "Setting the position is not supported.");

                        Assert.Throws <NotSupportedException>(() =>
                        {
                            axCryptDataStream.Flush();
                        }, "Flush is not supported, and not meaningful on a read-only stream.");

                        Assert.That(axCryptDataStream.CanRead, Is.True, "AxCryptDataStream can be read.");
                        Assert.That(axCryptDataStream.CanSeek, Is.True, "AxCryptDataStream is a forward only reader stream, but it does support Length and Position therefore it reports that it can Seek.");
                        Assert.That(axCryptDataStream.CanWrite, Is.False, "AxCryptDataStream is a forward only reader stream, it does not support writing.");
                        Assert.That(axCryptDataStream.Length, Is.EqualTo(inputStream.Length - 5), "The stream should report the length provided in the constructor.");
                        inputStream.Position = 5;
                        Assert.That(axCryptDataStream.Position, Is.EqualTo(0), "The original position should be zero, regardless of the actual position of the input stream.");
                        inputStream.Position = 0;

                        byte[] buffer = new byte[3];
                        int    count;
                        int    total = 0;
                        while ((count = axCryptDataStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            total += count;
                        }
                        Assert.That(total, Is.EqualTo(inputStream.Length - 5), "The AxCryptDataStream should be limited to the length provided, not the backing stream.");
                        Assert.That(hmacStream.Length, Is.EqualTo(total), "The hmac stream should have all data read written to it.");
                    }
                }
            }
        }