1 /*
2   KeePass Password Safe - The Open-Source Password Manager
3   Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 using System;
21 using System.Collections.Generic;
22 using System.Diagnostics;
23 using System.IO;
24 using System.Text;
25 
26 #if !KeePassUAP
27 using System.Security.Cryptography;
28 #endif
29 
30 using KeePassLib.Utility;
31 
32 namespace KeePassLib.Cryptography
33 {
34 	public sealed class HashingStreamEx : Stream
35 	{
36 		private readonly Stream m_sBaseStream;
37 		private readonly bool m_bWriting;
38 		private HashAlgorithm m_hash;
39 
40 		private byte[] m_pbFinalHash = null;
41 
42 		public byte[] Hash
43 		{
44 			get { return m_pbFinalHash; }
45 		}
46 
47 		public override bool CanRead
48 		{
49 			get { return !m_bWriting; }
50 		}
51 
52 		public override bool CanSeek
53 		{
54 			get { return false; }
55 		}
56 
57 		public override bool CanWrite
58 		{
59 			get { return m_bWriting; }
60 		}
61 
62 		public override long Length
63 		{
64 			get { return m_sBaseStream.Length; }
65 		}
66 
67 		public override long Position
68 		{
69 			get { return m_sBaseStream.Position; }
70 			set { Debug.Assert(false); throw new NotSupportedException(); }
71 		}
72 
HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm)73 		public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm)
74 		{
75 			if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
76 
77 			m_sBaseStream = sBaseStream;
78 			m_bWriting = bWriting;
79 
80 #if !KeePassLibSD
81 			m_hash = (hashAlgorithm ?? new SHA256Managed());
82 #else // KeePassLibSD
83 			m_hash = null;
84 
85 			try { m_hash = HashAlgorithm.Create("SHA256"); }
86 			catch(Exception) { }
87 			try { if(m_hash == null) m_hash = HashAlgorithm.Create(); }
88 			catch(Exception) { }
89 #endif
90 			if(m_hash == null) { Debug.Assert(false); return; }
91 
92 			// Validate hash algorithm
93 			if(!m_hash.CanTransformMultipleBlocks)
94 			{
95 				Debug.Assert(false);
96 				m_hash = null;
97 			}
98 		}
99 
Dispose(bool disposing)100 		protected override void Dispose(bool disposing)
101 		{
102 			if(disposing)
103 			{
104 				if(m_hash != null)
105 				{
106 					try
107 					{
108 						m_hash.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
109 						m_pbFinalHash = m_hash.Hash;
110 						m_hash.Clear();
111 					}
112 					catch(Exception) { Debug.Assert(false); }
113 
114 					m_hash = null;
115 				}
116 
117 				m_sBaseStream.Close();
118 			}
119 
120 			base.Dispose(disposing);
121 		}
122 
Flush()123 		public override void Flush()
124 		{
125 			m_sBaseStream.Flush();
126 		}
127 
Seek(long lOffset, SeekOrigin soOrigin)128 		public override long Seek(long lOffset, SeekOrigin soOrigin)
129 		{
130 			Debug.Assert(false);
131 			throw new NotSupportedException();
132 		}
133 
SetLength(long lValue)134 		public override void SetLength(long lValue)
135 		{
136 			Debug.Assert(false);
137 			throw new NotSupportedException();
138 		}
139 
Read(byte[] pbBuffer, int nOffset, int nCount)140 		public override int Read(byte[] pbBuffer, int nOffset, int nCount)
141 		{
142 			if(m_bWriting) { Debug.Assert(false); throw new InvalidOperationException(); }
143 
144 			int nRead = m_sBaseStream.Read(pbBuffer, nOffset, nCount);
145 			int nPartialRead = nRead;
146 			while((nRead < nCount) && (nPartialRead != 0))
147 			{
148 				nPartialRead = m_sBaseStream.Read(pbBuffer, nOffset + nRead,
149 					nCount - nRead);
150 				nRead += nPartialRead;
151 			}
152 
153 #if DEBUG
154 			byte[] pbOrg = new byte[pbBuffer.Length];
155 			Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
156 #endif
157 
158 			if((m_hash != null) && (nRead > 0))
159 				m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
160 
161 #if DEBUG
162 			Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
163 #endif
164 
165 			return nRead;
166 		}
167 
Write(byte[] pbBuffer, int nOffset, int nCount)168 		public override void Write(byte[] pbBuffer, int nOffset, int nCount)
169 		{
170 			if(!m_bWriting) { Debug.Assert(false); throw new InvalidOperationException(); }
171 
172 #if DEBUG
173 			byte[] pbOrg = new byte[pbBuffer.Length];
174 			Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
175 #endif
176 
177 			if((m_hash != null) && (nCount > 0))
178 				m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
179 
180 #if DEBUG
181 			Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
182 #endif
183 
184 			m_sBaseStream.Write(pbBuffer, nOffset, nCount);
185 		}
186 	}
187 }
188