using System; using System.IO; namespace Unosquare.Swan.Networking.Ldap { /// /// This class provides LBER encoding routines for ASN.1 Types. LBER is a /// subset of BER as described in the following taken from 5.1 of RFC 2251: /// 5.1. Mapping Onto BER-based Transport Services /// The protocol elements of Ldap are encoded for exchange using the /// Basic Encoding Rules (BER) [11] of ASN.1 [3]. However, due to the /// high overhead involved in using certain elements of the BER, the /// following additional restrictions are placed on BER-encodings of Ldap /// protocol elements: ///
  • (1) Only the definite form of length encoding will be used.
  • ///
  • (2) OCTET STRING values will be encoded in the primitive form only.
  • /// (3) If the value of a BOOLEAN type is true, the encoding MUST have /// its contents octets set to hex "FF". ///
  • /// (4) If a value of a type is its default value, it MUST be absent. /// Only some BOOLEAN and INTEGER types have default values in this /// protocol definition. /// These restrictions do not apply to ASN.1 types encapsulated inside of /// OCTET STRING values, such as attribute values, unless otherwise /// noted. ///
  • /// [3] ITU-T Rec. X.680, "Abstract Syntax Notation One (ASN.1) - /// Specification of Basic Notation", 1994. /// [11] ITU-T Rec. X.690, "Specification of ASN.1 encoding rules: Basic, /// Canonical, and Distinguished Encoding Rules", 1994. ///
    internal static class LberEncoder { /// /// BER Encode an Asn1Boolean directly into the specified output stream. /// /// The Asn1Boolean object to encode. /// The stream. public static void Encode(Asn1Boolean b, Stream stream) { Encode(b.GetIdentifier(), stream); stream.WriteByte(0x01); stream.WriteByte((Byte)(b.BooleanValue() ? 0xff : 0x00)); } /// /// Encode an Asn1Numeric directly into the specified outputstream. /// Use a two's complement representation in the fewest number of octets /// possible. /// Can be used to encode INTEGER and ENUMERATED values. /// /// The Asn1Numeric object to encode. /// The stream. public static void Encode(Asn1Numeric n, Stream stream) { SByte[] octets = new SByte[8]; SByte len; Int64 longValue = n.LongValue(); Int64 endValue = longValue < 0 ? -1 : 0; Int64 endSign = endValue & 0x80; for(len = 0; len == 0 || longValue != endValue || (octets[len - 1] & 0x80) != endSign; len++) { octets[len] = (SByte)(longValue & 0xFF); longValue >>= 8; } Encode(n.GetIdentifier(), stream); stream.WriteByte((Byte)len); for(Int32 i = len - 1; i >= 0; i--) { stream.WriteByte((Byte)octets[i]); } } /// /// Encode an Asn1OctetString directly into the specified outputstream. /// /// The Asn1OctetString object to encode. /// The stream. public static void Encode(Asn1OctetString os, Stream stream) { Encode(os.GetIdentifier(), stream); EncodeLength(os.ByteValue().Length, stream); SByte[] tempSbyteArray = os.ByteValue(); stream.Write(tempSbyteArray.ToByteArray(), 0, tempSbyteArray.Length); } public static void Encode(Asn1Object obj, Stream stream) { switch(obj) { case Asn1Boolean b: Encode(b, stream); break; case Asn1Numeric n: Encode(n, stream); break; case Asn1Null n: Encode(n.GetIdentifier(), stream); stream.WriteByte(0x00); // Length (with no Content) break; case Asn1OctetString n: Encode(n, stream); break; case Asn1Structured n: Encode(n, stream); break; case Asn1Tagged n: Encode(n, stream); break; case Asn1Choice n: Encode(n.ChoiceValue, stream); break; default: throw new InvalidDataException(); } } /// /// Encode an Asn1Structured into the specified outputstream. This method /// can be used to encode SET, SET_OF, SEQUENCE, SEQUENCE_OF. /// /// The Asn1Structured object to encode. /// The stream. public static void Encode(Asn1Structured c, Stream stream) { Encode(c.GetIdentifier(), stream); Asn1Object[] arrayValue = c.ToArray(); using(MemoryStream output = new MemoryStream()) { foreach(Asn1Object obj in arrayValue) { Encode(obj, output); } EncodeLength((Int32)output.Length, stream); Byte[] tempSbyteArray = output.ToArray(); stream.Write(tempSbyteArray, 0, tempSbyteArray.Length); } } /// /// Encode an Asn1Tagged directly into the specified outputstream. /// /// The Asn1Tagged object to encode. /// The stream. public static void Encode(Asn1Tagged t, Stream stream) { if(!t.Explicit) { Encode(t.TaggedValue, stream); return; } Encode(t.GetIdentifier(), stream); // determine the encoded length of the base type. using(MemoryStream encodedContent = new MemoryStream()) { Encode(t.TaggedValue, encodedContent); EncodeLength((Int32)encodedContent.Length, stream); SByte[] tempSbyteArray = encodedContent.ToArray().ToSByteArray(); stream.Write(tempSbyteArray.ToByteArray(), 0, tempSbyteArray.Length); } } /// /// Encode an Asn1Identifier directly into the specified outputstream. /// /// The Asn1Identifier object to encode. /// The stream. public static void Encode(Asn1Identifier id, Stream stream) { Int32 c = (Int32)id.Asn1Class; Int32 t = id.Tag; SByte ccf = (SByte)((c << 6) | (id.Constructed ? 0x20 : 0)); if(t < 30) { #pragma warning disable CS0675 // Bitweiser OR-Operator, der bei einem signaturerweiterten Operanden verwendet wurde. stream.WriteByte((Byte)(ccf | t)); #pragma warning restore CS0675 // Bitweiser OR-Operator, der bei einem signaturerweiterten Operanden verwendet wurde. } else { stream.WriteByte((Byte)(ccf | 0x1F)); EncodeTagInteger(t, stream); } } /// /// Encodes the length. /// /// The length. /// The stream. private static void EncodeLength(Int32 length, Stream stream) { if(length < 0x80) { stream.WriteByte((Byte)length); } else { SByte[] octets = new SByte[4]; // 4 bytes sufficient for 32 bit int. SByte n; for(n = 0; length != 0; n++) { octets[n] = (SByte)(length & 0xFF); length >>= 8; } stream.WriteByte((Byte)(0x80 | n)); for(Int32 i = n - 1; i >= 0; i--) { stream.WriteByte((Byte)octets[i]); } } } /// /// Encodes the provided tag into the stream. /// /// The value. /// The stream. private static void EncodeTagInteger(Int32 val, Stream stream) { SByte[] octets = new SByte[5]; Int32 n; for(n = 0; val != 0; n++) { octets[n] = (SByte)(val & 0x7F); val >>= 7; } for(Int32 i = n - 1; i > 0; i--) { stream.WriteByte((Byte)(octets[i] | 0x80)); } stream.WriteByte((Byte)octets[0]); } } }