165 lines
5.9 KiB
C#
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;
|
|
}
|
|
}
|
|
} |