namespace DSInternals.Common
{
using System;
using System.IO;
using System.Security;
using System.Security.Principal;
using System.Text;
using DSInternals.Common.Properties;
public static class ByteArrayExtensions
{
private const string HexDigitsUpper = "0123456789ABCDEF";
private const string HexDigitsLower = "0123456789abcdef";
public static void ZeroFill(this byte[] array)
{
Array.Clear(array, 0, array.Length);
}
public static byte[] HexToBinary(this string hex, int startIndex, int length)
{
// Input validation
Validator.AssertNotNull(hex, nameof(hex));
if (length % 2 != 0)
{
// Each byte in a HEX string must be encoded using 2 characters.
var exception = new ArgumentException(Resources.NotHexStringMessage, nameof(hex));
exception.Data.Add("Value", hex);
throw exception;
}
if(startIndex < 0 || startIndex >= hex.Length )
{
throw new ArgumentOutOfRangeException(nameof(startIndex));
}
if (length < 0 || startIndex + length > hex.Length)
{
throw new ArgumentOutOfRangeException(nameof(length));
}
// Prepare the result
byte[] bytes = new byte[length / 2];
// Perform the conversion
for (int nibbleIndex = 0, byteIndex = 0; nibbleIndex < length; byteIndex = ++nibbleIndex / 2)
{
char nibble = hex[startIndex + nibbleIndex];
if ('0' <= nibble && nibble <= '9')
{
bytes[byteIndex] = (byte)((bytes[byteIndex] << 4) | (nibble - '0'));
}
else if ('a' <= nibble && nibble <= 'f')
{
bytes[byteIndex] = (byte)((bytes[byteIndex] << 4) | (nibble - 'a' + 0xA));
}
else if ('A' <= nibble && nibble <= 'F')
{
bytes[byteIndex] = (byte)((bytes[byteIndex] << 4) | (nibble - 'A' + 0xA));
}
else
{
// Invalid digit
var exception = new ArgumentException(Resources.NotHexStringMessage, nameof(hex));
exception.Data.Add("Value", hex);
throw exception;
}
}
return bytes;
}
public static byte[] HexToBinary(this string hex)
{
// Trivial case
if (String.IsNullOrEmpty(hex))
{
return null;
}
return hex.HexToBinary(0, hex.Length);
}
public static string ToHex(this byte[] bytes, bool caps = false)
{
if (bytes == null)
{
return null;
}
string hexDigits = caps ? HexDigitsUpper : HexDigitsLower;
StringBuilder hex = new StringBuilder(bytes.Length * 2);
foreach(byte currentByte in bytes)
{
hex.Append(hexDigits[(int)(currentByte >> 4)]);
hex.Append(hexDigits[(int)(currentByte & 0xF)]);
}
return hex.ToString();
}
public static SecureString ReadSecureWString(this byte[] buffer, int startIndex)
{
Validator.AssertNotNull(buffer, nameof(buffer));
// TODO: Assert startIndex > 0
int maxLength = buffer.Length - startIndex;
// Prepare an empty SecureString that will eventually be returned
var result = new SecureString();
for (int i = startIndex; i < buffer.Length; i += UnicodeEncoding.CharSize)
{
// Convert the next 2 bytes from the byte array into a unicode character
char c = BitConverter.ToChar(buffer, i);
if (c == Char.MinValue)
{
// End of string has been reached
return result;
}
result.AppendChar(c);
}
// If we reached this point, the \0 char has not been found, so throw an exception.
// TODO: Add a reasonable exception message
throw new ArgumentException();
}
public static void SwapBytes(this byte[] bytes, int index1, int index2)
{
byte temp = bytes[index1];
bytes[index1] = bytes[index2];
bytes[index2] = temp;
}
///
/// Encodes an integer into a 4-byte array, in big endian.
///
/// The integer to encode.
/// Array of bytes, in big endian order.
public static byte[] GetBigEndianBytes(this uint number)
{
byte[] bytes = BitConverter.GetBytes(number);
if (BitConverter.IsLittleEndian)
{
bytes.SwapBytes(0, 3);
bytes.SwapBytes(1, 2);
}
return bytes;
}
public static SecurityIdentifier ToSecurityIdentifier(this byte[] binarySid, bool bigEndianRid = false)
{
if(binarySid == null)
{
return null;
}
byte[] output = binarySid;
if (bigEndianRid)
{
// Clone the binary SID so we do not perform byte spapping on the original value.
byte[] binarySidCopy = (byte[])binarySid.Clone();
int lastByteIndex = binarySidCopy.Length -1;
// Convert RID from big endian to little endian (Reverse the order of the last 4 bytes)
binarySidCopy.SwapBytes(lastByteIndex - 3, lastByteIndex);
binarySidCopy.SwapBytes(lastByteIndex - 2, lastByteIndex - 1);
output = binarySidCopy;
}
return new SecurityIdentifier(output, 0);
}
public static byte[] Cut(this byte[] blob, int offset)
{
Validator.AssertNotNull(blob, "blob");
return blob.Cut(offset, blob.Length - offset);
}
public static byte[] Cut(this byte[] blob, int offset, int count)
{
Validator.AssertNotNull(blob, "blob");
Validator.AssertMinLength(blob, offset + count, "blob");
// TODO: Check that offset and count are positive using Validator
byte[] result = new byte[count];
Buffer.BlockCopy((Array)blob, offset, (Array)result, 0, count);
return result;
}
public static byte[] ReadToEnd(this MemoryStream stream)
{
long remainingBytes = stream.Length - stream.Position;
if(remainingBytes > int.MaxValue)
{
throw new ArgumentOutOfRangeException("stream");
}
byte[] buffer = new byte[remainingBytes];
stream.Read(buffer, 0, (int)remainingBytes);
return buffer;
}
}
}