private void CalculateSignature( ReadOnlySpan <byte> message, uint sequenceNumber, ReadOnlySpan <byte> signingKey, RC4 seal, Span <byte> signature) { BinaryPrimitives.WriteInt32LittleEndian(signature, 1); BinaryPrimitives.WriteUInt32LittleEndian(signature.Slice(12), sequenceNumber); using (var hmac = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, signingKey)) { hmac.AppendData(signature.Slice(12, 4)); hmac.AppendData(message); Span <byte> hmacResult = stackalloc byte[hmac.HashLengthInBytes]; hmac.GetHashAndReset(hmacResult); if (_negotiatedFlags.HasFlag(Flags.NegotiateKeyExchange)) { seal.Transform(hmacResult.Slice(0, 8), signature.Slice(4, 8)); } else { hmacResult.Slice(0, 8).CopyTo(signature.Slice(4, 8)); } } }
private void ValidateAuthentication(byte[] incomingBlob) { ReadOnlySpan <byte> lmChallengeResponse = GetField(incomingBlob, 12); ReadOnlySpan <byte> ntChallengeResponse = GetField(incomingBlob, 20); ReadOnlySpan <byte> encryptedRandomSessionKey = GetField(incomingBlob, 52); ReadOnlySpan <byte> mic = incomingBlob.AsSpan(72, 16); Flags flags = (Flags)BinaryPrimitives.ReadUInt32LittleEndian(incomingBlob.AsSpan(60)); Assert.Equal(_requiredFlags, (flags & _requiredFlags)); // Only one encoding can be selected by the client Assert.True((flags & (Flags.NegotiateOEM | Flags.NegotiateUnicode)) != 0); Assert.True((flags & (Flags.NegotiateOEM | Flags.NegotiateUnicode)) != (Flags.NegotiateOEM | Flags.NegotiateUnicode)); Encoding encoding = flags.HasFlag(Flags.NegotiateUnicode) ? Encoding.Unicode : Encoding.ASCII; string domainName = encoding.GetString(GetField(incomingBlob, 28)); string userName = encoding.GetString(GetField(incomingBlob, 36)); string workstation = encoding.GetString(GetField(incomingBlob, 44)); Assert.Equal(_expectedCredential.UserName, userName); Assert.Equal(_expectedCredential.Domain, domainName); byte[] ntlm2hash = MakeNtlm2Hash(); Span <byte> sessionBaseKey = stackalloc byte[16]; using (IncrementalHash hmac = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, ntlm2hash)) { hmac.AppendData(_serverChallenge); hmac.AppendData(ntChallengeResponse.Slice(16)); // If this matches then the password matched IsAuthenticated = hmac.GetHashAndReset().AsSpan().SequenceEqual(ntChallengeResponse.Slice(0, 16)); if (!IsAuthenticated) { // Bail out return; } // Compute sessionBaseKey hmac.AppendData(ntChallengeResponse.Slice(0, 16)); hmac.GetHashAndReset(sessionBaseKey); } ReadOnlySpan <byte> avPairs = ntChallengeResponse.Slice(16 + 28); AvFlags avFlags = 0; while (avPairs[0] != (byte)AvId.EOL) { AvId id = (AvId)avPairs[0]; Assert.Equal(0, avPairs[1]); ushort length = BinaryPrimitives.ReadUInt16LittleEndian(avPairs.Slice(2, 2)); if (id == AvId.Flags) { Assert.Equal(4, length); avFlags = (AvFlags)BinaryPrimitives.ReadUInt32LittleEndian(avPairs.Slice(4, 4)); } else if (id == AvId.TargetName) { ClientSpecifiedSpn = Encoding.Unicode.GetString(avPairs.Slice(4, length)); } avPairs = avPairs.Slice(length + 4); } // Decrypt exportedSessionKey with sessionBaseKey Span <byte> exportedSessionKey = stackalloc byte[16]; if (flags.HasFlag(Flags.NegotiateKeyExchange) && (flags.HasFlag(Flags.NegotiateSeal) || flags.HasFlag(Flags.NegotiateSign))) { using (RC4 rc4 = new RC4(sessionBaseKey)) { rc4.Transform(encryptedRandomSessionKey, exportedSessionKey); } } else { sessionBaseKey.CopyTo(exportedSessionKey); } // Calculate and verify message integrity if enabled if (avFlags.HasFlag(AvFlags.MICPresent)) { IsMICPresent = true; Assert.NotNull(_negotiateMessage); Assert.NotNull(_challengeMessage); byte[] calculatedMic = new byte[16]; using (var hmacMic = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, exportedSessionKey)) { hmacMic.AppendData(_negotiateMessage); hmacMic.AppendData(_challengeMessage); // Authenticate message with the MIC erased hmacMic.AppendData(incomingBlob.AsSpan(0, 72)); hmacMic.AppendData(new byte[16]); hmacMic.AppendData(incomingBlob.AsSpan(72 + 16)); hmacMic.GetHashAndReset(calculatedMic); } Assert.Equal(mic.ToArray(), calculatedMic); } // Derive signing keys _clientSigningKey = DeriveKey(exportedSessionKey, ClientSigningKeyMagic); _serverSigningKey = DeriveKey(exportedSessionKey, ServerSigningKeyMagic); _clientSealingKey = DeriveKey(exportedSessionKey, ClientSealingKeyMagic); _serverSealingKey = DeriveKey(exportedSessionKey, ServerSealingKeyMagic); ResetKeys(); _negotiatedFlags = flags; CryptographicOperations.ZeroMemory(exportedSessionKey); }