/* KeePass Password Safe - The Open-Source Password Manager Copyright (C) 2003-2012 Dominik Reichl 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.Text; using System.Security.Cryptography; using System.Diagnostics; using System.IO; #if !KeePassLibSD using System.IO.Compression; #else using KeePassLibSD; #endif namespace KeePassLib.Utility { /// /// Contains static buffer manipulation and string conversion routines. /// public static class MemUtil { /// /// Convert a hexadecimal string to a byte array. The input string must be /// even (i.e. its length is a multiple of 2). /// /// String containing hexadecimal characters. /// Returns a byte array. Returns null if the string parameter /// was null or is an uneven string (i.e. if its length isn't a /// multiple of 2). /// Thrown if /// is null. public static byte[] HexStringToByteArray(string strHex) { if(strHex == null) { Debug.Assert(false); throw new ArgumentNullException("strHex"); } int nStrLen = strHex.Length; if((nStrLen & 1) != 0) { Debug.Assert(false); return null; } byte[] pb = new byte[nStrLen / 2]; byte bt; char ch; for(int i = 0; i < nStrLen; i += 2) { ch = strHex[i]; if((ch >= '0') && (ch <= '9')) bt = (byte)(ch - '0'); else if((ch >= 'a') && (ch <= 'f')) bt = (byte)(ch - 'a' + 10); else if((ch >= 'A') && (ch <= 'F')) bt = (byte)(ch - 'A' + 10); else { Debug.Assert(false); bt = 0; } bt <<= 4; ch = strHex[i + 1]; if((ch >= '0') && (ch <= '9')) bt += (byte)(ch - '0'); else if((ch >= 'a') && (ch <= 'f')) bt += (byte)(ch - 'a' + 10); else if((ch >= 'A') && (ch <= 'F')) bt += (byte)(ch - 'A' + 10); else { Debug.Assert(false); } pb[i >> 1] = bt; } return pb; } /// /// Convert a byte array to a hexadecimal string. /// /// Input byte array. /// Returns the hexadecimal string representing the byte /// array. Returns null, if the input byte array was null. Returns /// an empty string, if the input byte array has length 0. public static string ByteArrayToHexString(byte[] pbArray) { if(pbArray == null) return null; int nLen = pbArray.Length; if(nLen == 0) return string.Empty; StringBuilder sb = new StringBuilder(); byte bt, btHigh, btLow; for(int i = 0; i < nLen; ++i) { bt = pbArray[i]; btHigh = bt; btHigh >>= 4; btLow = (byte)(bt & 0x0F); if(btHigh >= 10) sb.Append((char)('A' + btHigh - 10)); else sb.Append((char)('0' + btHigh)); if(btLow >= 10) sb.Append((char)('A' + btLow - 10)); else sb.Append((char)('0' + btLow)); } return sb.ToString(); } /// /// Set all bytes in a byte array to zero. /// /// Input array. All bytes of this array will be set /// to zero. public static void ZeroByteArray(byte[] pbArray) { Debug.Assert(pbArray != null); if(pbArray == null) throw new ArgumentNullException("pbArray"); // for(int i = 0; i < pbArray.Length; ++i) // pbArray[i] = 0; Array.Clear(pbArray, 0, pbArray.Length); } /// /// Convert 2 bytes to a 16-bit unsigned integer using Little-Endian /// encoding. /// /// Input bytes. Array must contain at least 2 bytes. /// 16-bit unsigned integer. public static ushort BytesToUInt16(byte[] pb) { Debug.Assert((pb != null) && (pb.Length == 2)); if(pb == null) throw new ArgumentNullException("pb"); if(pb.Length != 2) throw new ArgumentException(); return (ushort)((ushort)pb[0] | ((ushort)pb[1] << 8)); } /// /// Convert 4 bytes to a 32-bit unsigned integer using Little-Endian /// encoding. /// /// Input bytes. /// 32-bit unsigned integer. public static uint BytesToUInt32(byte[] pb) { Debug.Assert((pb != null) && (pb.Length == 4)); if(pb == null) throw new ArgumentNullException("pb"); if(pb.Length != 4) throw new ArgumentException("Input array must contain 4 bytes!"); return (uint)pb[0] | ((uint)pb[1] << 8) | ((uint)pb[2] << 16) | ((uint)pb[3] << 24); } /// /// Convert 8 bytes to a 64-bit unsigned integer using Little-Endian /// encoding. /// /// Input bytes. /// 64-bit unsigned integer. public static ulong BytesToUInt64(byte[] pb) { Debug.Assert((pb != null) && (pb.Length == 8)); if(pb == null) throw new ArgumentNullException("pb"); if(pb.Length != 8) throw new ArgumentException(); return (ulong)pb[0] | ((ulong)pb[1] << 8) | ((ulong)pb[2] << 16) | ((ulong)pb[3] << 24) | ((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) | ((ulong)pb[6] << 48) | ((ulong)pb[7] << 56); } /// /// Convert a 16-bit unsigned integer to 2 bytes using Little-Endian /// encoding. /// /// 16-bit input word. /// Two bytes representing the 16-bit value. public static byte[] UInt16ToBytes(ushort uValue) { byte[] pb = new byte[2]; unchecked { pb[0] = (byte)uValue; pb[1] = (byte)(uValue >> 8); } return pb; } /// /// Convert a 32-bit unsigned integer to 4 bytes using Little-Endian /// encoding. /// /// 32-bit input word. /// Four bytes representing the 32-bit value. public static byte[] UInt32ToBytes(uint uValue) { byte[] pb = new byte[4]; unchecked { pb[0] = (byte)uValue; pb[1] = (byte)(uValue >> 8); pb[2] = (byte)(uValue >> 16); pb[3] = (byte)(uValue >> 24); } return pb; } /// /// Convert a 64-bit unsigned integer to 8 bytes using Little-Endian /// encoding. /// /// 64-bit input word. /// Eight bytes representing the 64-bit value. public static byte[] UInt64ToBytes(ulong uValue) { byte[] pb = new byte[8]; unchecked { pb[0] = (byte)uValue; pb[1] = (byte)(uValue >> 8); pb[2] = (byte)(uValue >> 16); pb[3] = (byte)(uValue >> 24); pb[4] = (byte)(uValue >> 32); pb[5] = (byte)(uValue >> 40); pb[6] = (byte)(uValue >> 48); pb[7] = (byte)(uValue >> 56); } return pb; } public static bool ArraysEqual(byte[] x, byte[] y) { // Return false if one of them is null (not comparable)! if((x == null) || (y == null)) { Debug.Assert(false); return false; } if(x.Length != y.Length) return false; for(int i = 0; i < x.Length; ++i) { if(x[i] != y[i]) return false; } return true; } public static void XorArray(byte[] pbSource, int nSourceOffset, byte[] pbBuffer, int nBufferOffset, int nLength) { if(pbSource == null) throw new ArgumentNullException("pbSource"); if(nSourceOffset < 0) throw new ArgumentException(); if(pbBuffer == null) throw new ArgumentNullException("pbBuffer"); if(nBufferOffset < 0) throw new ArgumentException(); if(nLength < 0) throw new ArgumentException(); if((nSourceOffset + nLength) > pbSource.Length) throw new ArgumentException(); if((nBufferOffset + nLength) > pbBuffer.Length) throw new ArgumentException(); for(int i = 0; i < nLength; ++i) pbBuffer[nBufferOffset + i] ^= pbSource[nSourceOffset + i]; } public static void CopyStream(Stream sSource, Stream sTarget) { Debug.Assert((sSource != null) && (sTarget != null)); if(sSource == null) throw new ArgumentNullException("sSource"); if(sTarget == null) throw new ArgumentNullException("sTarget"); const int nBufSize = 4096; byte[] pbBuf = new byte[nBufSize]; while(true) { int nRead = sSource.Read(pbBuf, 0, nBufSize); if(nRead == 0) break; sTarget.Write(pbBuf, 0, nRead); } // Do not close any of the streams } public static byte[] Read(Stream s, int nCount) { if(s == null) throw new ArgumentNullException("s"); if(nCount < 0) throw new ArgumentOutOfRangeException("nCount"); byte[] pb = new byte[nCount]; int iOffset = 0; while(nCount > 0) { int iRead = s.Read(pb, iOffset, nCount); if(iRead == 0) break; iOffset += iRead; nCount -= iRead; } if(iOffset != pb.Length) { byte[] pbPart = new byte[iOffset]; Array.Copy(pb, pbPart, iOffset); return pbPart; } return pb; } public static void Write(Stream s, byte[] pbData) { if(s == null) { Debug.Assert(false); return; } if(pbData == null) { Debug.Assert(false); return; } s.Write(pbData, 0, pbData.Length); } public static byte[] Compress(byte[] pbData) { if(pbData == null) throw new ArgumentNullException("pbData"); if(pbData.Length == 0) return pbData; MemoryStream msCompressed = new MemoryStream(); GZipStream gz = new GZipStream(msCompressed, CompressionMode.Compress); MemoryStream msSource = new MemoryStream(pbData, false); MemUtil.CopyStream(msSource, gz); gz.Close(); msSource.Close(); byte[] pbCompressed = msCompressed.ToArray(); msCompressed.Close(); return pbCompressed; } public static byte[] Decompress(byte[] pbCompressed) { if(pbCompressed == null) throw new ArgumentNullException("pbCompressed"); if(pbCompressed.Length == 0) return pbCompressed; MemoryStream msCompressed = new MemoryStream(pbCompressed, false); GZipStream gz = new GZipStream(msCompressed, CompressionMode.Decompress); MemoryStream msData = new MemoryStream(); MemUtil.CopyStream(gz, msData); gz.Close(); msCompressed.Close(); byte[] pbData = msData.ToArray(); msData.Close(); return pbData; } public static int IndexOf(T[] vHaystack, T[] vNeedle) where T : IEquatable { if(vHaystack == null) throw new ArgumentNullException("vHaystack"); if(vNeedle == null) throw new ArgumentNullException("vNeedle"); if(vNeedle.Length == 0) return 0; for(int i = 0; i <= (vHaystack.Length - vNeedle.Length); ++i) { bool bFound = true; for(int m = 0; m < vNeedle.Length; ++m) { if(!vHaystack[i + m].Equals(vNeedle[m])) { bFound = false; break; } } if(bFound) return i; } return -1; } public static T[] Mid(T[] v, int iOffset, int iLength) { if(v == null) throw new ArgumentNullException("v"); if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset"); if(iLength < 0) throw new ArgumentOutOfRangeException("iLength"); if(iOffset + iLength > v.Length) throw new ArgumentException(); T[] r = new T[iLength]; Array.Copy(v, iOffset, r, 0, iLength); return r; } } }