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; } }
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."); } } } }