RaspberryIO/Unosquare.Swan/Networking/Ldap/Asn1.cs
2019-12-03 18:44:25 +01:00

542 lines
17 KiB
C#

using System;
using System.IO;
using System.Text;
namespace Unosquare.Swan.Networking.Ldap {
/// <summary>
/// The Asn1Set class can hold an unordered collection of components with
/// identical type. This class inherits from the Asn1Structured class
/// which already provides functionality to hold multiple Asn1 components.
/// </summary>
/// <seealso cref="Asn1Structured" />
internal class Asn1SetOf
: Asn1Structured {
public const Int32 Tag = 0x11;
public static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, true, Tag);
public Asn1SetOf(Int32 size = 10)
: base(Id, size) {
}
public override String ToString() => this.ToString("SET OF: { ");
}
/// <summary>
/// The Asn1Choice object represents the choice of any Asn1Object. All
/// Asn1Object methods are delegated to the object this Asn1Choice contains.
/// </summary>
/// <seealso cref="Asn1Object" />
internal class Asn1Choice
: Asn1Object {
private Asn1Object _content;
public Asn1Choice(Asn1Object content = null) => this._content = content;
protected internal virtual Asn1Object ChoiceValue {
get => this._content;
set => this._content = value;
}
public override Asn1Identifier GetIdentifier() => this._content.GetIdentifier();
public override void SetIdentifier(Asn1Identifier id) => this._content.SetIdentifier(id);
public override String ToString() => this._content.ToString();
}
/// <summary>
/// This class is used to encapsulate an ASN.1 Identifier.
/// An Asn1Identifier is composed of three parts:
/// <li> a class type,</li><li> a form, and</li><li> a tag.</li>
/// The class type is defined as:
/// <pre>
/// bit 8 7 TAG CLASS
/// ------- -----------
/// 0 0 UNIVERSAL
/// 0 1 APPLICATION
/// 1 0 CONTEXT
/// 1 1 PRIVATE
/// </pre>
/// The form is defined as:
/// <pre>
/// bit 6 FORM
/// ----- --------
/// 0 PRIMITIVE
/// 1 CONSTRUCTED
/// </pre>
/// Note: CONSTRUCTED types are made up of other CONSTRUCTED or PRIMITIVE
/// types.
/// The tag is defined as:.
/// <pre>
/// bit 5 4 3 2 1 TAG
/// ------------- ---------------------------------------------
/// 0 0 0 0 0
/// . . . . .
/// 1 1 1 1 0 (0-30) single octet tag
/// 1 1 1 1 1 (&gt; 30) multiple octet tag, more octets follow
/// </pre></summary>
internal sealed class Asn1Identifier {
public Asn1Identifier(Asn1IdentifierTag tagClass, Boolean constructed, Int32 tag) {
this.Asn1Class = tagClass;
this.Constructed = constructed;
this.Tag = tag;
}
public Asn1Identifier(LdapOperation tag)
: this(Asn1IdentifierTag.Application, true, (Int32)tag) {
}
public Asn1Identifier(Int32 contextTag, Boolean constructed = false)
: this(Asn1IdentifierTag.Context, constructed, contextTag) {
}
public Asn1Identifier(Stream stream) {
Int32 r = stream.ReadByte();
this.EncodedLength++;
if(r < 0) {
throw new EndOfStreamException("BERDecoder: decode: EOF in Identifier");
}
this.Asn1Class = (Asn1IdentifierTag)(r >> 6);
this.Constructed = (r & 0x20) != 0;
this.Tag = r & 0x1F; // if tag < 30 then its a single octet identifier.
if(this.Tag == 0x1F) {
// if true, its a multiple octet identifier.
this.Tag = this.DecodeTagNumber(stream);
}
}
public Asn1IdentifierTag Asn1Class {
get;
}
public Boolean Constructed {
get;
}
public Int32 Tag {
get;
}
public Int32 EncodedLength {
get; private set;
}
public Boolean Universal => this.Asn1Class == Asn1IdentifierTag.Universal;
public Object Clone() => this.MemberwiseClone();
private Int32 DecodeTagNumber(Stream stream) {
Int32 n = 0;
while(true) {
Int32 r = stream.ReadByte();
this.EncodedLength++;
if(r < 0) {
throw new EndOfStreamException("BERDecoder: decode: EOF in tag number");
}
n = (n << 7) + (r & 0x7F);
if((r & 0x80) == 0) {
break;
}
}
return n;
}
}
/// <summary>
/// This is the base class for all other Asn1 types.
/// </summary>
internal abstract class Asn1Object {
private static readonly String[] ClassTypes = { "[UNIVERSAL ", "[APPLICATION ", "[", "[PRIVATE " };
private Asn1Identifier _id;
protected Asn1Object(Asn1Identifier id = null) => this._id = id;
public virtual Asn1Identifier GetIdentifier() => this._id;
public virtual void SetIdentifier(Asn1Identifier identifier) => this._id = identifier;
public override String ToString() {
Asn1Identifier identifier = this.GetIdentifier();
return $"{ClassTypes[(Int32)identifier.Asn1Class]}{identifier.Tag}]";
}
}
/// <summary>
/// This class encapsulates the OCTET STRING type.
/// </summary>
/// <seealso cref="Asn1Object" />
internal sealed class Asn1OctetString
: Asn1Object {
public const Int32 Tag = 0x04;
private static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, false, Tag);
private readonly SByte[] _content;
public Asn1OctetString(SByte[] content)
: base(Id) => this._content = content;
public Asn1OctetString(String content)
: base(Id) => this._content = Encoding.UTF8.GetSBytes(content);
public Asn1OctetString(Stream stream, Int32 len)
: base(Id) => this._content = len > 0 ? (SByte[])LberDecoder.DecodeOctetString(stream, len) : new SByte[0];
public SByte[] ByteValue() => this._content;
public String StringValue() => Encoding.UTF8.GetString(this._content);
public override String ToString() => base.ToString() + "OCTET STRING: " + this.StringValue();
}
/// <summary>
/// The Asn1Tagged class can hold a base Asn1Object with a distinctive tag
/// describing the type of that base object. It also maintains a boolean value
/// indicating whether the value should be encoded by EXPLICIT or IMPLICIT
/// means. (Explicit is true by default.)
/// If the type is encoded IMPLICITLY, the base types form, length and content
/// will be encoded as usual along with the class type and tag specified in
/// the constructor of this Asn1Tagged class.
/// If the type is to be encoded EXPLICITLY, the base type will be encoded as
/// usual after the Asn1Tagged identifier has been encoded.
/// </summary>
/// <seealso cref="Asn1Object" />
internal class Asn1Tagged : Asn1Object {
private Asn1Object _content;
public Asn1Tagged(Asn1Identifier identifier, Asn1Object obj = null, Boolean isExplicit = true)
: base(identifier) {
this._content = obj;
this.Explicit = isExplicit;
if(!isExplicit) {
// replace object's id with new tag.
this._content?.SetIdentifier(identifier);
}
}
public Asn1Tagged(Asn1Identifier identifier, SByte[] vals)
: base(identifier) {
this._content = new Asn1OctetString(vals);
this.Explicit = false;
}
public Asn1Tagged(Stream stream, Int32 len, Asn1Identifier identifier)
: base(identifier) =>
// If we are decoding an implicit tag, there is no way to know at this
// low level what the base type really is. We can place the content
// into an Asn1OctetString type and pass it back to the application who
// will be able to create the appropriate ASN.1 type for this tag.
this._content = new Asn1OctetString(stream, len);
public Asn1Object TaggedValue {
get => this._content;
set {
this._content = value;
if(!this.Explicit) {
// replace object's id with new tag.
value?.SetIdentifier(this.GetIdentifier());
}
}
}
public Boolean Explicit {
get;
}
public override String ToString() => this.Explicit ? base.ToString() + this._content : this._content.ToString();
}
/// <summary>
/// This class serves as the base type for all ASN.1
/// structured types.
/// </summary>
/// <seealso cref="Asn1Object" />
internal abstract class Asn1Structured : Asn1Object {
private Asn1Object[] _content;
private Int32 _contentIndex;
protected internal Asn1Structured(Asn1Identifier id, Int32 size = 10)
: base(id) => this._content = new Asn1Object[size];
public Asn1Object[] ToArray() {
Asn1Object[] cloneArray = new Asn1Object[this._contentIndex];
Array.Copy(this._content, 0, cloneArray, 0, this._contentIndex);
return cloneArray;
}
public void Add(String s) => this.Add(new Asn1OctetString(s));
public void Add(Asn1Object obj) {
if(this._contentIndex == this._content.Length) {
// Array too small, need to expand it, double length
Asn1Object[] newArray = new Asn1Object[this._contentIndex + this._contentIndex];
Array.Copy(this._content, 0, newArray, 0, this._contentIndex);
this._content = newArray;
}
this._content[this._contentIndex++] = obj;
}
public void Set(Int32 index, Asn1Object value) {
if(index >= this._contentIndex || index < 0) {
throw new IndexOutOfRangeException($"Asn1Structured: get: index {index}, size {this._contentIndex}");
}
this._content[index] = value;
}
public Asn1Object Get(Int32 index) {
if(index >= this._contentIndex || index < 0) {
throw new IndexOutOfRangeException($"Asn1Structured: set: index {index}, size {this._contentIndex}");
}
return this._content[index];
}
public Int32 Size() => this._contentIndex;
public String ToString(String type) {
StringBuilder sb = new StringBuilder().Append(type);
for(Int32 i = 0; i < this._contentIndex; i++) {
_ = sb.Append(this._content[i]);
if(i != this._contentIndex - 1) {
_ = sb.Append(", ");
}
}
_ = sb.Append(" }");
return base.ToString() + sb;
}
protected internal void DecodeStructured(Stream stream, Int32 len) {
Int32[] componentLen = new Int32[1]; // collects length of component
while(len > 0) {
this.Add(LberDecoder.Decode(stream, componentLen));
len -= componentLen[0];
}
}
}
/// <summary>
/// This class encapsulates the ASN.1 BOOLEAN type.
/// </summary>
/// <seealso cref="Asn1Object" />
internal class Asn1Boolean
: Asn1Object {
public const Int32 Tag = 0x01;
public static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, false, Tag);
private readonly Boolean _content;
public Asn1Boolean(Boolean content)
: base(Id) => this._content = content;
public Asn1Boolean(Stream stream, Int32 len)
: base(Id) => this._content = LberDecoder.DecodeBoolean(stream, len);
public Boolean BooleanValue() => this._content;
public override String ToString() => $"{base.ToString()}BOOLEAN: {this._content}";
}
/// <summary>
/// This class represents the ASN.1 NULL type.
/// </summary>
/// <seealso cref="Asn1Object" />
internal sealed class Asn1Null
: Asn1Object {
public const Int32 Tag = 0x05;
public static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, false, Tag);
public Asn1Null()
: base(Id) {
}
public override String ToString() => $"{base.ToString()}NULL: \"\"";
}
/// <summary>
/// This abstract class is the base class
/// for all Asn1 numeric (integral) types. These include
/// Asn1Integer and Asn1Enumerated.
/// </summary>
/// <seealso cref="Asn1Object" />
internal abstract class Asn1Numeric : Asn1Object {
private readonly Int64 _content;
internal Asn1Numeric(Asn1Identifier id, Int32 numericValue)
: base(id) => this._content = numericValue;
internal Asn1Numeric(Asn1Identifier id, Int64 numericValue)
: base(id) => this._content = numericValue;
public Int32 IntValue() => (Int32)this._content;
public Int64 LongValue() => this._content;
}
/// <summary>
/// This class provides a means to manipulate ASN.1 Length's. It will
/// be used by Asn1Encoder's and Asn1Decoder's by composition.
/// </summary>
internal sealed class Asn1Length {
public Asn1Length(Stream stream) {
Int32 r = stream.ReadByte();
this.EncodedLength++;
if(r == 0x80) {
this.Length = -1;
} else if(r < 0x80) {
this.Length = r;
} else {
this.Length = 0;
for(r &= 0x7F; r > 0; r--) {
Int32 part = stream.ReadByte();
this.EncodedLength++;
if(part < 0) {
throw new EndOfStreamException("BERDecoder: decode: EOF in Asn1Length");
}
this.Length = (this.Length << 8) + part;
}
}
}
public Int32 Length {
get;
}
public Int32 EncodedLength {
get;
}
}
/// <summary>
/// The Asn1Sequence class can hold an ordered collection of components with
/// distinct type.
/// This class inherits from the Asn1Structured class which
/// provides functionality to hold multiple Asn1 components.
/// </summary>
/// <seealso cref="Asn1Structured" />
internal class Asn1Sequence
: Asn1Structured {
public const Int32 Tag = 0x10;
private static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, true, Tag);
public Asn1Sequence(Int32 size)
: base(Id, size) {
}
public Asn1Sequence(Stream stream, Int32 len)
: base(Id) => this.DecodeStructured(stream, len);
public override String ToString() => this.ToString("SEQUENCE: { ");
}
/// <summary>
/// The Asn1Set class can hold an unordered collection of components with
/// distinct type. This class inherits from the Asn1Structured class
/// which already provides functionality to hold multiple Asn1 components.
/// </summary>
/// <seealso cref="Asn1Structured" />
internal sealed class Asn1Set
: Asn1Structured {
public const Int32 Tag = 0x11;
public static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, true, Tag);
public Asn1Set(Stream stream, Int32 len)
: base(Id) => this.DecodeStructured(stream, len);
public override String ToString() => this.ToString("SET: { ");
}
/// <summary>
/// This class encapsulates the ASN.1 INTEGER type.
/// </summary>
/// <seealso cref="Asn1Numeric" />
internal class Asn1Integer
: Asn1Numeric {
public const Int32 Tag = 0x02;
public static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, false, Tag);
public Asn1Integer(Int32 content)
: base(Id, content) {
}
public Asn1Integer(Stream stream, Int32 len)
: base(Id, LberDecoder.DecodeNumeric(stream, len)) {
}
public override String ToString() => base.ToString() + "INTEGER: " + this.LongValue();
}
/// <summary>
/// This class encapsulates the ASN.1 ENUMERATED type.
/// </summary>
/// <seealso cref="Asn1Numeric" />
internal sealed class Asn1Enumerated : Asn1Numeric {
public const Int32 Tag = 0x0a;
public static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, false, Tag);
public Asn1Enumerated(LdapScope content)
: base(Id, (Int32)content) {
}
public Asn1Enumerated(Int32 content)
: base(Id, content) {
}
/// <summary>
/// Initializes a new instance of the <see cref="Asn1Enumerated"/> class.
/// Constructs an Asn1Enumerated object by decoding data from an
/// input stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="len">The length.</param>
public Asn1Enumerated(Stream stream, Int32 len)
: base(Id, LberDecoder.DecodeNumeric(stream, len)) {
}
public override String ToString() => base.ToString() + "ENUMERATED: " + this.LongValue();
}
/// <summary>
/// The Asn1SequenceOf class is used to hold an ordered collection
/// of components with identical type. This class inherits
/// from the Asn1Structured class which already provides
/// functionality to hold multiple Asn1 components.
/// </summary>
/// <seealso cref="Asn1Structured" />
internal class Asn1SequenceOf : Asn1Structured {
public const Int32 Tag = 0x10;
public static readonly Asn1Identifier Id = new Asn1Identifier(Asn1IdentifierTag.Universal, true, Tag);
public Asn1SequenceOf(Int32 size)
: base(Id, size) {
}
public Asn1SequenceOf(Stream stream, Int32 len)
: base(Id) => this.DecodeStructured(stream, len);
public override String ToString() => this.ToString("SEQUENCE OF: { ");
}
}