namespace Unosquare.Swan.Networking.Ldap { using System.IO; /// /// This class provides LBER decoding 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 LberDecoder { /// /// Decode an LBER encoded value into an Asn1Object from an InputStream. /// This method also returns the total length of this encoded /// Asn1Object (length of type + length of length + length of content) /// in the parameter len. This information is helpful when decoding /// structured types. /// /// The stream. /// The length. /// /// Decoded Asn1Obect. /// /// Unknown tag. public static Asn1Object Decode(Stream stream, int[] len) { var asn1Id = new Asn1Identifier(stream); var asn1Len = new Asn1Length(stream); var length = asn1Len.Length; len[0] = asn1Id.EncodedLength + asn1Len.EncodedLength + length; if (asn1Id.Universal == false) return new Asn1Tagged(stream, length, (Asn1Identifier) asn1Id.Clone()); switch (asn1Id.Tag) { case Asn1Sequence.Tag: return new Asn1Sequence(stream, length); case Asn1Set.Tag: return new Asn1Set(stream, length); case Asn1Boolean.Tag: return new Asn1Boolean(stream, length); case Asn1Integer.Tag: return new Asn1Integer(stream, length); case Asn1OctetString.Tag: return new Asn1OctetString(stream, length); case Asn1Enumerated.Tag: return new Asn1Enumerated(stream, length); case Asn1Null.Tag: return new Asn1Null(); // has no content to decode. default: throw new EndOfStreamException("Unknown tag"); } } /// /// Decode a boolean directly from a stream. /// /// The stream. /// Length in bytes. /// /// Decoded boolean object. /// /// LBER: BOOLEAN: decode error: EOF. public static bool DecodeBoolean(Stream stream, int len) { var lber = new sbyte[len]; if (stream.ReadInput(ref lber, 0, lber.Length) != len) throw new EndOfStreamException("LBER: BOOLEAN: decode error: EOF"); return lber[0] != 0x00; } /// /// Decode a Numeric type directly from a stream. Decodes INTEGER /// and ENUMERATED types. /// /// The stream. /// Length in bytes. /// /// Decoded numeric object. /// /// /// LBER: NUMERIC: decode error: EOF /// or /// LBER: NUMERIC: decode error: EOF. /// public static long DecodeNumeric(Stream stream, int len) { long l = 0; var r = stream.ReadByte(); if (r < 0) throw new EndOfStreamException("LBER: NUMERIC: decode error: EOF"); if ((r & 0x80) != 0) { // check for negative number l = -1; } l = (l << 8) | r; for (var i = 1; i < len; i++) { r = stream.ReadByte(); if (r < 0) throw new EndOfStreamException("LBER: NUMERIC: decode error: EOF"); l = (l << 8) | r; } return l; } /// /// Decode an OctetString directly from a stream. /// /// The stream. /// Length in bytes. /// Decoded octet. public static object DecodeOctetString(Stream stream, int len) { var octets = new sbyte[len]; var totalLen = 0; while (totalLen < len) { // Make sure we have read all the data totalLen += stream.ReadInput(ref octets, totalLen, len - totalLen); } return octets; } } }