Files
modernkeepass/ModernKeePassLib/Serialization/HashedBlockStream.cs
Geoffroy Bonneville 8e690747e2 Changed test project type to WIndows 8.1
Changed test project framework from Nunit to MSTest
Changed HashAlgorithm from BouncyCastle to WinRT crypto
WIP progress bar in opendatabaseusercontrol
TextBox with button made generic
WIP implement copy on button click in Entry Page
2017-11-08 14:42:47 +01:00

313 lines
7.3 KiB
C#

/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Native;
using ModernKeePassLib.Utility;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace ModernKeePassLib.Serialization
{
public sealed class HashedBlockStream : Stream
{
private const int NbDefaultBufferSize = 1024 * 1024; // 1 MB
private Stream m_sBaseStream;
private bool m_bWriting;
private bool m_bVerify;
private bool m_bEos = false;
private BinaryReader m_brInput;
private BinaryWriter m_bwOutput;
private byte[] m_pbBuffer;
private int m_nBufferPos = 0;
private uint m_uBlockIndex = 0;
public override bool CanRead
{
get { return !m_bWriting; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return m_bWriting; }
}
public override long Length
{
get { Debug.Assert(false); throw new NotSupportedException(); }
}
public override long Position
{
get { Debug.Assert(false); throw new NotSupportedException(); }
set { Debug.Assert(false); throw new NotSupportedException(); }
}
public HashedBlockStream(Stream sBaseStream, bool bWriting)
{
Initialize(sBaseStream, bWriting, 0, true);
}
public HashedBlockStream(Stream sBaseStream, bool bWriting, int nBufferSize)
{
Initialize(sBaseStream, bWriting, nBufferSize, true);
}
public HashedBlockStream(Stream sBaseStream, bool bWriting, int nBufferSize,
bool bVerify)
{
Initialize(sBaseStream, bWriting, nBufferSize, bVerify);
}
private void Initialize(Stream sBaseStream, bool bWriting, int nBufferSize,
bool bVerify)
{
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
if(nBufferSize < 0) throw new ArgumentOutOfRangeException("nBufferSize");
if(nBufferSize == 0) nBufferSize = NbDefaultBufferSize;
m_sBaseStream = sBaseStream;
m_bWriting = bWriting;
m_bVerify = bVerify;
UTF8Encoding utf8 = StrUtil.Utf8;
if(!m_bWriting) // Reading mode
{
if(!m_sBaseStream.CanRead)
throw new InvalidOperationException();
m_brInput = new BinaryReader(sBaseStream, utf8);
m_pbBuffer = MemUtil.EmptyByteArray;
}
else // Writing mode
{
if(!m_sBaseStream.CanWrite)
throw new InvalidOperationException();
m_bwOutput = new BinaryWriter(sBaseStream, utf8);
m_pbBuffer = new byte[nBufferSize];
}
}
protected override void Dispose(bool disposing)
{
if(disposing && (m_sBaseStream != null))
{
if(!m_bWriting) // Reading mode
{
m_brInput.Dispose();
m_brInput = null;
}
else // Writing mode
{
if(m_nBufferPos == 0) // No data left in buffer
WriteHashedBlock(); // Write terminating block
else
{
WriteHashedBlock(); // Write remaining buffered data
WriteHashedBlock(); // Write terminating block
}
Flush();
m_bwOutput.Dispose();
m_bwOutput = null;
}
m_sBaseStream.Dispose();
m_sBaseStream = null;
}
base.Dispose(disposing);
}
public override void Flush()
{
if(m_bWriting) m_bwOutput.Flush();
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
throw new NotSupportedException();
}
public override void SetLength(long lValue)
{
throw new NotSupportedException();
}
public override int Read(byte[] pbBuffer, int nOffset, int nCount)
{
if(m_bWriting) throw new InvalidOperationException();
int nRemaining = nCount;
while(nRemaining > 0)
{
if(m_nBufferPos == m_pbBuffer.Length)
{
if(ReadHashedBlock() == false)
return (nCount - nRemaining); // Bytes actually read
}
int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nRemaining);
Array.Copy(m_pbBuffer, m_nBufferPos, pbBuffer, nOffset, nCopy);
nOffset += nCopy;
m_nBufferPos += nCopy;
nRemaining -= nCopy;
}
return nCount;
}
private bool ReadHashedBlock()
{
if(m_bEos) return false; // End of stream reached already
m_nBufferPos = 0;
if(m_brInput.ReadUInt32() != m_uBlockIndex)
throw new InvalidDataException();
++m_uBlockIndex;
byte[] pbStoredHash = m_brInput.ReadBytes(32);
if((pbStoredHash == null) || (pbStoredHash.Length != 32))
throw new InvalidDataException();
int nBufferSize = 0;
try { nBufferSize = m_brInput.ReadInt32(); }
catch(NullReferenceException) // Mono bug workaround (LaunchPad 783268)
{
if(!NativeLib.IsUnix()) throw;
}
if(nBufferSize < 0)
throw new InvalidDataException();
if(nBufferSize == 0)
{
for(int iHash = 0; iHash < 32; ++iHash)
{
if(pbStoredHash[iHash] != 0)
throw new InvalidDataException();
}
m_bEos = true;
m_pbBuffer = MemUtil.EmptyByteArray;
return false;
}
m_pbBuffer = m_brInput.ReadBytes(nBufferSize);
if((m_pbBuffer == null) || ((m_pbBuffer.Length != nBufferSize) && m_bVerify))
throw new InvalidDataException();
if(m_bVerify)
{
byte[] pbComputedHash = CryptoUtil.HashSha256(m_pbBuffer);
if((pbComputedHash == null) || (pbComputedHash.Length != 32))
throw new InvalidOperationException();
if(!MemUtil.ArraysEqual(pbStoredHash, pbComputedHash))
throw new InvalidDataException();
}
return true;
}
public override void Write(byte[] pbBuffer, int nOffset, int nCount)
{
if(!m_bWriting) throw new InvalidOperationException();
while(nCount > 0)
{
if(m_nBufferPos == m_pbBuffer.Length)
WriteHashedBlock();
int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nCount);
Array.Copy(pbBuffer, nOffset, m_pbBuffer, m_nBufferPos, nCopy);
nOffset += nCopy;
m_nBufferPos += nCopy;
nCount -= nCopy;
}
}
private void WriteHashedBlock()
{
m_bwOutput.Write(m_uBlockIndex);
++m_uBlockIndex;
if(m_nBufferPos > 0)
{
byte[] pbHash = CryptoUtil.HashSha256(m_pbBuffer, 0, m_nBufferPos);
// For KeePassLibSD:
// SHA256Managed sha256 = new SHA256Managed();
// byte[] pbHash;
// if(m_nBufferPos == m_pbBuffer.Length)
// pbHash = sha256.ComputeHash(m_pbBuffer);
// else
// {
// byte[] pbData = new byte[m_nBufferPos];
// Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
// pbHash = sha256.ComputeHash(pbData);
// }
m_bwOutput.Write(pbHash);
}
else
{
m_bwOutput.Write((ulong)0); // Zero hash
m_bwOutput.Write((ulong)0);
m_bwOutput.Write((ulong)0);
m_bwOutput.Write((ulong)0);
}
m_bwOutput.Write(m_nBufferPos);
if(m_nBufferPos > 0)
m_bwOutput.Write(m_pbBuffer, 0, m_nBufferPos);
m_nBufferPos = 0;
}
}
}