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

165 lines
5.9 KiB
C#

using System.IO;
using System;
namespace Unosquare.Swan.Networking.Ldap {
/// <summary>
/// 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:
/// <li>(1) Only the definite form of length encoding will be used.</li>
/// <li>(2) OCTET STRING values will be encoded in the primitive form only.</li><li>
/// (3) If the value of a BOOLEAN type is true, the encoding MUST have
/// its contents octets set to hex "FF".
/// </li><li>
/// (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.
/// </li>
/// [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.
/// </summary>
internal static class LberDecoder {
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="len">The length.</param>
/// <returns>
/// Decoded Asn1Obect.
/// </returns>
/// <exception cref="EndOfStreamException">Unknown tag.</exception>
public static Asn1Object Decode(Stream stream, Int32[] len) {
Asn1Identifier asn1Id = new Asn1Identifier(stream);
Asn1Length asn1Len = new Asn1Length(stream);
Int32 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");
}
}
/// <summary>
/// Decode a boolean directly from a stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="len">Length in bytes.</param>
/// <returns>
/// Decoded boolean object.
/// </returns>
/// <exception cref="EndOfStreamException">LBER: BOOLEAN: decode error: EOF.</exception>
public static Boolean DecodeBoolean(Stream stream, Int32 len) {
SByte[] 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;
}
/// <summary>
/// Decode a Numeric type directly from a stream. Decodes INTEGER
/// and ENUMERATED types.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="len">Length in bytes.</param>
/// <returns>
/// Decoded numeric object.
/// </returns>
/// <exception cref="EndOfStreamException">
/// LBER: NUMERIC: decode error: EOF
/// or
/// LBER: NUMERIC: decode error: EOF.
/// </exception>
public static Int64 DecodeNumeric(Stream stream, Int32 len) {
Int64 l = 0;
Int32 r = stream.ReadByte();
if(r < 0) {
throw new EndOfStreamException("LBER: NUMERIC: decode error: EOF");
}
if((r & 0x80) != 0) {
// check for negative number
l = -1;
}
#pragma warning disable CS0675 // Bitweiser OR-Operator, der bei einem signaturerweiterten Operanden verwendet wurde.
l = (l << 8) | r;
#pragma warning restore CS0675 // Bitweiser OR-Operator, der bei einem signaturerweiterten Operanden verwendet wurde.
for(Int32 i = 1; i < len; i++) {
r = stream.ReadByte();
if(r < 0) {
throw new EndOfStreamException("LBER: NUMERIC: decode error: EOF");
}
#pragma warning disable CS0675 // Bitweiser OR-Operator, der bei einem signaturerweiterten Operanden verwendet wurde.
l = (l << 8) | r;
#pragma warning restore CS0675 // Bitweiser OR-Operator, der bei einem signaturerweiterten Operanden verwendet wurde.
}
return l;
}
/// <summary>
/// Decode an OctetString directly from a stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="len">Length in bytes.</param>
/// <returns>Decoded octet. </returns>
public static Object DecodeOctetString(Stream stream, Int32 len) {
SByte[] octets = new SByte[len];
Int32 totalLen = 0;
while(totalLen < len) {
// Make sure we have read all the data
totalLen += stream.ReadInput(ref octets, totalLen, len - totalLen);
}
return octets;
}
}
}