You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
10 KiB
336 lines
10 KiB
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) |
|
#pragma warning disable |
|
using System; |
|
using System.Diagnostics; |
|
using System.Text; |
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Math; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; |
|
|
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1 |
|
{ |
|
public class DerBitString |
|
: DerStringBase |
|
{ |
|
private static readonly char[] table |
|
= { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; |
|
|
|
/** |
|
* return a Bit string from the passed in object |
|
* |
|
* @exception ArgumentException if the object cannot be converted. |
|
*/ |
|
public static DerBitString GetInstance(object obj) |
|
{ |
|
if (obj == null || obj is DerBitString) |
|
{ |
|
return (DerBitString) obj; |
|
} |
|
if (obj is byte[]) |
|
{ |
|
try |
|
{ |
|
return (DerBitString)FromByteArray((byte[])obj); |
|
} |
|
catch (Exception e) |
|
{ |
|
throw new ArgumentException("encoding error in GetInstance: " + e.ToString()); |
|
} |
|
} |
|
|
|
throw new ArgumentException("illegal object in GetInstance: " + BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.GetTypeName(obj)); |
|
} |
|
|
|
/** |
|
* return a Bit string from a tagged object. |
|
* |
|
* @param obj the tagged object holding the object we want |
|
* @param explicitly true if the object is meant to be explicitly |
|
* tagged false otherwise. |
|
* @exception ArgumentException if the tagged object cannot |
|
* be converted. |
|
*/ |
|
public static DerBitString GetInstance(Asn1TaggedObject obj, bool isExplicit) |
|
{ |
|
Asn1Object o = obj.GetObject(); |
|
|
|
if (isExplicit || o is DerBitString) |
|
{ |
|
return GetInstance(o); |
|
} |
|
|
|
// Not copied because assumed to be a tagged implicit primitive from the parser |
|
return CreatePrimitive(((Asn1OctetString)o).GetOctets()); |
|
} |
|
|
|
internal readonly byte[] contents; |
|
|
|
public DerBitString(byte data, int padBits) |
|
{ |
|
if (padBits > 7 || padBits < 0) |
|
throw new ArgumentException("pad bits cannot be greater than 7 or less than 0", "padBits"); |
|
|
|
this.contents = new byte[] { (byte)padBits, data }; |
|
} |
|
|
|
public DerBitString(byte[] data) |
|
: this(data, 0) |
|
{ |
|
} |
|
|
|
/** |
|
* @param data the octets making up the bit string. |
|
* @param padBits the number of extra bits at the end of the string. |
|
*/ |
|
public DerBitString(byte[] data, int padBits) |
|
{ |
|
if (data == null) |
|
throw new ArgumentNullException("data"); |
|
if (padBits < 0 || padBits > 7) |
|
throw new ArgumentException("must be in the range 0 to 7", "padBits"); |
|
if (data.Length == 0 && padBits != 0) |
|
throw new ArgumentException("if 'data' is empty, 'padBits' must be 0"); |
|
|
|
this.contents = Arrays.Prepend(data, (byte)padBits); |
|
} |
|
|
|
public DerBitString(int namedBits) |
|
{ |
|
if (namedBits == 0) |
|
{ |
|
this.contents = new byte[]{ 0 }; |
|
return; |
|
} |
|
|
|
int bits = BigInteger.BitLen(namedBits); |
|
int bytes = (bits + 7) / 8; |
|
Debug.Assert(0 < bytes && bytes <= 4); |
|
|
|
byte[] data = new byte[1 + bytes]; |
|
|
|
for (int i = 1; i < bytes; i++) |
|
{ |
|
data[i] = (byte)namedBits; |
|
namedBits >>= 8; |
|
} |
|
|
|
Debug.Assert((namedBits & 0xFF) != 0); |
|
data[bytes] = (byte)namedBits; |
|
|
|
int padBits = 0; |
|
while ((namedBits & (1 << padBits)) == 0) |
|
{ |
|
++padBits; |
|
} |
|
|
|
Debug.Assert(padBits < 8); |
|
data[0] = (byte)padBits; |
|
|
|
this.contents = data; |
|
} |
|
|
|
public DerBitString(Asn1Encodable obj) |
|
: this(obj.GetDerEncoded()) |
|
{ |
|
} |
|
|
|
internal DerBitString(byte[] contents, bool check) |
|
{ |
|
if (check) |
|
{ |
|
if (null == contents) |
|
throw new ArgumentNullException("contents"); |
|
if (contents.Length < 1) |
|
throw new ArgumentException("cannot be empty", "contents"); |
|
|
|
int padBits = contents[0]; |
|
if (padBits > 0) |
|
{ |
|
if (contents.Length < 2) |
|
throw new ArgumentException("zero length data with non-zero pad bits", "contents"); |
|
if (padBits > 7) |
|
throw new ArgumentException("pad bits cannot be greater than 7 or less than 0", "contents"); |
|
} |
|
} |
|
|
|
this.contents = contents; |
|
} |
|
|
|
/** |
|
* Return the octets contained in this BIT STRING, checking that this BIT STRING really |
|
* does represent an octet aligned string. Only use this method when the standard you are |
|
* following dictates that the BIT STRING will be octet aligned. |
|
* |
|
* @return a copy of the octet aligned data. |
|
*/ |
|
public virtual byte[] GetOctets() |
|
{ |
|
if (contents[0] != 0) |
|
throw new InvalidOperationException("attempt to get non-octet aligned data from BIT STRING"); |
|
|
|
return Arrays.CopyOfRange(contents, 1, contents.Length); |
|
} |
|
|
|
public virtual byte[] GetBytes() |
|
{ |
|
if (contents.Length == 1) |
|
return Asn1OctetString.EmptyOctets; |
|
|
|
int padBits = contents[0]; |
|
byte[] rv = Arrays.CopyOfRange(contents, 1, contents.Length); |
|
// DER requires pad bits be zero |
|
rv[rv.Length - 1] &= (byte)(0xFF << padBits); |
|
return rv; |
|
} |
|
|
|
public virtual int PadBits |
|
{ |
|
get { return contents[0]; } |
|
} |
|
|
|
/** |
|
* @return the value of the bit string as an int (truncating if necessary) |
|
*/ |
|
public virtual int IntValue |
|
{ |
|
get |
|
{ |
|
int value = 0, end = System.Math.Min(5, contents.Length - 1); |
|
for (int i = 1; i < end; ++i) |
|
{ |
|
value |= (int)contents[i] << (8 * (i - 1)); |
|
} |
|
if (1 <= end && end < 5) |
|
{ |
|
int padBits = contents[0]; |
|
byte der = (byte)(contents[end] & (0xFF << padBits)); |
|
value |= (int)der << (8 * (end - 1)); |
|
} |
|
return value; |
|
} |
|
} |
|
|
|
internal override int EncodedLength(bool withID) |
|
{ |
|
return Asn1OutputStream.GetLengthOfEncodingDL(withID, contents.Length); |
|
} |
|
|
|
internal override void Encode(Asn1OutputStream asn1Out, bool withID) |
|
{ |
|
int padBits = contents[0]; |
|
int length = contents.Length; |
|
int last = length - 1; |
|
|
|
byte lastOctet = contents[last]; |
|
byte lastOctetDer = (byte)(contents[last] & (0xFF << padBits)); |
|
|
|
if (lastOctet == lastOctetDer) |
|
{ |
|
asn1Out.WriteEncodingDL(withID, Asn1Tags.BitString, contents); |
|
} |
|
else |
|
{ |
|
asn1Out.WriteEncodingDL(withID, Asn1Tags.BitString, contents, 0, last, lastOctetDer); |
|
} |
|
} |
|
|
|
protected override int Asn1GetHashCode() |
|
{ |
|
if (contents.Length < 2) |
|
return 1; |
|
|
|
int padBits = contents[0]; |
|
int last = contents.Length - 1; |
|
|
|
byte lastOctetDer = (byte)(contents[last] & (0xFF << padBits)); |
|
|
|
int hc = Arrays.GetHashCode(contents, 0, last); |
|
hc *= 257; |
|
hc ^= lastOctetDer; |
|
return hc; |
|
} |
|
|
|
protected override bool Asn1Equals(Asn1Object asn1Object) |
|
{ |
|
DerBitString that = asn1Object as DerBitString; |
|
if (null == that) |
|
return false; |
|
|
|
byte[] thisContents = this.contents, thatContents = that.contents; |
|
|
|
int length = thisContents.Length; |
|
if (thatContents.Length != length) |
|
return false; |
|
if (length == 1) |
|
return true; |
|
|
|
int last = length - 1; |
|
for (int i = 0; i < last; ++i) |
|
{ |
|
if (thisContents[i] != thatContents[i]) |
|
return false; |
|
} |
|
|
|
int padBits = thisContents[0]; |
|
byte thisLastOctetDer = (byte)(thisContents[last] & (0xFF << padBits)); |
|
byte thatLastOctetDer = (byte)(thatContents[last] & (0xFF << padBits)); |
|
|
|
return thisLastOctetDer == thatLastOctetDer; |
|
} |
|
|
|
public override string GetString() |
|
{ |
|
StringBuilder buffer = new StringBuilder("#"); |
|
|
|
byte[] str = GetDerEncoded(); |
|
|
|
for (int i = 0; i != str.Length; i++) |
|
{ |
|
uint ubyte = str[i]; |
|
buffer.Append(table[(ubyte >> 4) & 0xf]); |
|
buffer.Append(table[str[i] & 0xf]); |
|
} |
|
|
|
return buffer.ToString(); |
|
} |
|
|
|
internal static int EncodedLength(bool withID, int contentsLength) |
|
{ |
|
return Asn1OutputStream.GetLengthOfEncodingDL(withID, contentsLength); |
|
} |
|
|
|
internal static void Encode(Asn1OutputStream asn1Out, bool withID, byte[] buf, int off, int len) |
|
{ |
|
asn1Out.WriteEncodingDL(withID, Asn1Tags.BitString, buf, off, len); |
|
} |
|
|
|
internal static void Encode(Asn1OutputStream asn1Out, bool withID, byte pad, byte[] buf, int off, int len) |
|
{ |
|
asn1Out.WriteEncodingDL(withID, Asn1Tags.BitString, pad, buf, off, len); |
|
} |
|
|
|
internal static DerBitString CreatePrimitive(byte[] contents) |
|
{ |
|
int length = contents.Length; |
|
if (length < 1) |
|
throw new ArgumentException("truncated BIT STRING detected", "contents"); |
|
|
|
int padBits = contents[0]; |
|
if (padBits > 0) |
|
{ |
|
if (padBits > 7 || length < 2) |
|
throw new ArgumentException("invalid pad bits detected", "contents"); |
|
|
|
byte finalOctet = contents[length - 1]; |
|
if (finalOctet != (byte)(finalOctet & (0xFF << padBits))) |
|
{ |
|
return new BerBitString(contents, false); |
|
} |
|
} |
|
|
|
return new DerBitString(contents, false); |
|
} |
|
} |
|
} |
|
#pragma warning restore |
|
#endif
|
|
|