public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) { byte[] inputData = { 0x87, 2, 0, 0x80 }; AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); AssertExtensions.Throws <ArgumentException>( "expectedTag", () => reader.ReadEnumeratedValue <ShortBacked>(Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); Assert.Throws <CryptographicException>(() => reader.ReadEnumeratedValue <ShortBacked>()); Assert.True(reader.HasData, "HasData after default tag"); Assert.Throws <CryptographicException>( () => reader.ReadEnumeratedValue <ShortBacked>(new Asn1Tag(TagClass.Application, 0))); Assert.True(reader.HasData, "HasData after wrong custom class"); Assert.Throws <CryptographicException>( () => reader.ReadEnumeratedValue <ShortBacked>(new Asn1Tag(TagClass.ContextSpecific, 1))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); ShortBacked value = reader.ReadEnumeratedValue <ShortBacked>(new Asn1Tag(TagClass.ContextSpecific, 7)); Assert.Equal((ShortBacked)0x80, value); Assert.False(reader.HasData, "HasData after reading value"); }
public static void ReadEnumeratedValue_NonEnumType(PublicEncodingRules ruleSet) { byte[] data = { 0x0A, 0x01, 0x00 }; AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); Assert.Throws <ArgumentException>(() => reader.ReadEnumeratedValue <Guid>()); }
public static void ReadEnumeratedValue_Invalid_ULong(PublicEncodingRules ruleSet, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); Assert.Throws <CryptographicException>(() => reader.ReadEnumeratedValue <ULongBacked>()); }
public static void ReadEnumeratedValue_Invalid_Long(AsnEncodingRules ruleSet, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); AsnReader reader = new AsnReader(inputData, ruleSet); Assert.Throws <AsnContentException>(() => reader.ReadEnumeratedValue <LongBacked>()); }
public static void ReadEnumeratedValue_FlagsEnum(PublicEncodingRules ruleSet) { byte[] data = { 0x0A, 0x01, 0x00 }; AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); AssertExtensions.Throws <ArgumentException>( "tEnum", () => reader.ReadEnumeratedValue <AssemblyFlags>()); }
public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) { byte[] inputData = { 0x0A, 1, 0x7E }; AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); AssertExtensions.Throws <ArgumentException>( "expectedTag", () => reader.ReadEnumeratedValue <ShortBacked>(Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); Assert.Throws <CryptographicException>( () => reader.ReadEnumeratedValue <ShortBacked>(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); ShortBacked value = reader.ReadEnumeratedValue <ShortBacked>(); Assert.Equal((ShortBacked)0x7E, value); Assert.False(reader.HasData, "HasData after read"); }
private static void GetExpectedValue <TEnum>( PublicEncodingRules ruleSet, TEnum expectedValue, string inputHex) where TEnum : struct { byte[] inputData = inputHex.HexToByteArray(); AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); TEnum value = reader.ReadEnumeratedValue <TEnum>(); Assert.Equal(expectedValue, value); }
public SearchRequest TryDecode(AsnReader reader, byte[] input) { SearchRequest searchRequest = new SearchRequest { RawPacket = input, }; Asn1Tag bindRequestApplication = new Asn1Tag(TagClass.Application, 3); AsnReader subReader = reader.ReadSequence(bindRequestApplication); searchRequest.BaseObject = System.Text.Encoding.ASCII.GetString(subReader.ReadOctetString()); SearchRequest.ScopeEnum scope = subReader.ReadEnumeratedValue <SearchRequest.ScopeEnum>(); SearchRequest.DerefAliasesEnum deref = subReader.ReadEnumeratedValue <SearchRequest.DerefAliasesEnum>(); BigInteger sizeLimit = subReader.ReadInteger(); BigInteger timeLimit = subReader.ReadInteger(); bool typesOnly = subReader.ReadBoolean(); searchRequest.Filter = DecodeSearchFilter(subReader); return(searchRequest); }
public static void ExpectedTag_IgnoresConstructed( AsnEncodingRules ruleSet, string inputHex, TagClass tagClass, int tagValue) { Asn1Tag primitiveTag = new Asn1Tag(tagClass, tagValue, false); Asn1Tag constructedTag = new Asn1Tag(tagClass, tagValue, true); byte[] inputData = inputHex.HexToByteArray(); AsnReader reader = new AsnReader(inputData, ruleSet); ShortBacked val1 = reader.ReadEnumeratedValue <ShortBacked>(constructedTag); Assert.False(reader.HasData); reader = new AsnReader(inputData, ruleSet); ShortBacked val2 = reader.ReadEnumeratedValue <ShortBacked>(primitiveTag); Assert.False(reader.HasData); Assert.Equal(val1, val2); reader = new AsnReader(inputData, ruleSet); ShortBacked val3 = (ShortBacked)reader.ReadEnumeratedValue(typeof(ShortBacked), constructedTag); Assert.False(reader.HasData); Assert.Equal(val1, val3); reader = new AsnReader(inputData, ruleSet); ShortBacked val4 = (ShortBacked)reader.ReadEnumeratedValue(typeof(ShortBacked), primitiveTag); Assert.False(reader.HasData); Assert.Equal(val1, val4); reader = new AsnReader(inputData, ruleSet); ReadOnlyMemory <byte> bytes1 = reader.ReadEnumeratedBytes(constructedTag); Assert.False(reader.HasData); reader = new AsnReader(inputData, ruleSet); ReadOnlyMemory <byte> bytes2 = reader.ReadEnumeratedBytes(primitiveTag); Assert.False(reader.HasData); Assert.Equal(bytes1.ByteArrayToHex(), bytes2.ByteArrayToHex()); Assert.Equal("FF", bytes1.ByteArrayToHex()); }
public static void ExpectedTag_IgnoresConstructed( PublicEncodingRules ruleSet, string inputHex, PublicTagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); ShortBacked val1 = reader.ReadEnumeratedValue <ShortBacked>(new Asn1Tag((TagClass)tagClass, tagValue, true)); Assert.False(reader.HasData); reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); ShortBacked val2 = reader.ReadEnumeratedValue <ShortBacked>(new Asn1Tag((TagClass)tagClass, tagValue, false)); Assert.False(reader.HasData); Assert.Equal(val1, val2); }
public byte[]? GetOutgoingBlob(byte[]?incomingBlob) { if (_spnegoMechList == null && incomingBlob.AsSpan().StartsWith(NtlmHeader)) { _ntlmPassthrough = true; // Windows often sends pure NTLM instead of proper Negotiate, handle that as passthrough byte[]? outgoingBlob = _ntlmServer.GetOutgoingBlob(incomingBlob); IsAuthenticated = _ntlmServer.IsAuthenticated; return(outgoingBlob); } Assert.False(_ntlmPassthrough); AsnReader reader = new AsnReader(incomingBlob, AsnEncodingRules.DER); if (_spnegoMechList == null) { AsnReader initialContextTokenReader = reader.ReadSequence(new Asn1Tag(TagClass.Application, 0)); string spNegoOid = initialContextTokenReader.ReadObjectIdentifier(); Assert.Equal(SpnegoOid, spNegoOid); AsnReader negTokenInitReader = initialContextTokenReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenInit)).ReadSequence(); AsnReader mechTypesOuterReader = negTokenInitReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenInit.MechTypes)); _spnegoMechList = mechTypesOuterReader.PeekEncodedValue().ToArray(); bool hasNtlm = false; bool isNtlmPreferred = false; bool first = true; AsnReader mechTypesReader = mechTypesOuterReader.ReadSequence(); while (mechTypesReader.HasData) { string mechType = mechTypesReader.ReadObjectIdentifier(); if (mechType == NtlmOid) { hasNtlm = true; isNtlmPreferred = first; } first = false; } // Skip context flags, if present if (negTokenInitReader.HasData && negTokenInitReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenInit.ReqFlags))) { negTokenInitReader.ReadSequence(); } byte[]? mechToken = null; if (negTokenInitReader.HasData && negTokenInitReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenInit.MechToken))) { AsnReader mechTokenReader = negTokenInitReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenInit.MechToken)); mechToken = mechTokenReader.ReadOctetString(); Assert.False(mechTokenReader.HasData); } byte[]? mechListMIC = null; if (negTokenInitReader.HasData && negTokenInitReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenInit.MechListMIC))) { AsnReader mechListMICReader = negTokenInitReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenInit.MechListMIC)); mechListMIC = mechListMICReader.ReadOctetString(); Assert.False(mechListMICReader.HasData); } Assert.True(hasNtlm); // If the preferred mechanism was NTLM then proceed with the given token byte[]? outgoingBlob = null; if (isNtlmPreferred && mechToken != null) { Assert.Null(mechListMIC); outgoingBlob = _ntlmServer.GetOutgoingBlob(mechToken); } // Generate reply AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp))) { using (writer.PushSequence()) { using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.NegState))) { if (RequestMIC) { writer.WriteEnumeratedValue(NegState.RequestMic); } else { writer.WriteEnumeratedValue(NegState.AcceptIncomplete); } } using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.SupportedMech))) { writer.WriteObjectIdentifier(NtlmOid); } if (outgoingBlob != null) { using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.ResponseToken))) { writer.WriteOctetString(outgoingBlob); } } } } return(writer.Encode()); } else { AsnReader negTokenRespReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp)).ReadSequence(); Assert.True(negTokenRespReader.HasData); NegState?clientState; if (negTokenRespReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.NegState))) { AsnReader valueReader = negTokenRespReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.NegState)); clientState = valueReader.ReadEnumeratedValue <NegState>(); Assert.False(valueReader.HasData); Assert.NotEqual(NegState.Reject, clientState); Assert.NotEqual(NegState.RequestMic, clientState); } // Client should not send mechanism Assert.False(negTokenRespReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.SupportedMech))); byte[]? mechToken = null; if (negTokenRespReader.HasData && negTokenRespReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.ResponseToken))) { AsnReader mechTokenReader = negTokenRespReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.ResponseToken)); mechToken = mechTokenReader.ReadOctetString(); Assert.False(mechTokenReader.HasData); } byte[]? mechListMIC = null; if (negTokenRespReader.HasData && negTokenRespReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.MechListMIC))) { AsnReader mechListMICReader = negTokenRespReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.MechListMIC)); mechListMIC = mechListMICReader.ReadOctetString(); Assert.False(mechListMICReader.HasData); } Assert.NotNull(mechToken); byte[]? outgoingBlob = _ntlmServer.GetOutgoingBlob(mechToken); if (_ntlmServer.IsAuthenticated) { if (RequestMIC) { Assert.NotNull(mechListMIC); } // Validate mechListMIC, if present if (mechListMIC is not null) { _ntlmServer.VerifyMIC(_spnegoMechList, mechListMIC); } } else { Assert.Null(mechListMIC); } // Generate reply AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp))) { using (writer.PushSequence()) { using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.NegState))) { if (_ntlmServer.IsAuthenticated) { writer.WriteEnumeratedValue(NegState.AcceptCompleted); } else if (outgoingBlob != null) { writer.WriteEnumeratedValue(NegState.AcceptIncomplete); } else { writer.WriteEnumeratedValue(NegState.Reject); } } if (outgoingBlob != null) { using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.ResponseToken))) { writer.WriteOctetString(outgoingBlob); } } if (mechListMIC != null) { using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.MechListMIC))) { Span <byte> mic = stackalloc byte[16]; _ntlmServer.GetMIC(_spnegoMechList, mic); writer.WriteOctetString(mic); // MS-SPNG section 3.2.5.1 NTLM RC4 Key State for MechListMIC and First Signed Message // specifies that the RC4 sealing keys are reset back to the initial state for the // first message. _ntlmServer.ResetKeys(); } } } } IsAuthenticated = _ntlmServer.IsAuthenticated; return(writer.Encode()); } }
public unsafe string ProcessNegotiateChallenge(string challengeString) { Console.WriteLine($"ChallengesString {challengeString}"); NegState state = NegState.Unknown; string mech = null; byte[] blob = null; byte[] data = Convert.FromBase64String(challengeString); AsnReader reader = new AsnReader(data, AsnEncodingRules.DER); AsnReader challengeReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp)); // NegTokenResp::= SEQUENCE { // negState[0] ENUMERATED { // accept - completed(0), // accept - incomplete(1), // reject(2), // request - mic(3) // } OPTIONAL, // --REQUIRED in the first reply from the target // supportedMech[1] MechType OPTIONAL, // --present only in the first reply from the target // responseToken[2] OCTET STRING OPTIONAL, // mechListMIC[3] OCTET STRING OPTIONAL, // ... // } challengeReader = challengeReader.ReadSequence(); while (challengeReader.HasData) { Asn1Tag tag = challengeReader.PeekTag(); if (tag.TagClass == TagClass.ContextSpecific) { NegTokenResp dataType = (NegTokenResp)tag.TagValue; AsnReader specificValue = new AsnReader(challengeReader.PeekContentBytes(), AsnEncodingRules.DER); switch (dataType) { case NegTokenResp.NegState: state = specificValue.ReadEnumeratedValue <NegState>(); break; case NegTokenResp.SupportedMech: mech = specificValue.ReadObjectIdentifier(); break; case NegTokenResp.ResponseToken: blob = specificValue.ReadOctetString(); break; default: // Ignore everything else break; } } challengeReader.ReadEncodedValue(); } if (Diag) { Console.WriteLine("Negotiate challenege: {0} - {1} in {2}", challengeString, mech, state); } // Mechanism should be set on first message. That means always // as NTLM has only one challenege message. if (!NtlmOid.Equals(mech)) { throw new NotSupportedException($"'{mech}' mechanism is not supported"); } if (state != NegState.Unknown && state != NegState.AcceptIncomplete) { // If state was set, it should be AcceptIncomplete for us to proseed. return(""); } if (blob?.Length > 0) { // Process decoded NTLM blob. byte[] response = ProcessChallengeMessage(blob); if (response?.Length > 0) { AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp))) { writer.PushSequence(); using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenInit.MechToken))) { writer.WriteOctetString(response); } writer.PopSequence(); } return("Negotiate " + Convert.ToBase64String(writer.Encode(), Base64FormattingOptions.None)); } } return(""); }