1# Copyright: (c) 2018, Jordan Borean (@jborean93) <jborean93@gmail.com>
2# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
3
4import hashlib
5import hmac
6
7from ntlm_auth.des import DES
8from ntlm_auth.constants import NegotiateFlags
9
10
11def _get_exchange_key_ntlm_v1(negotiate_flags, session_base_key,
12 server_challenge, lm_challenge_response,
13 lm_hash):
14 """
15 [MS-NLMP] v28.0 2016-07-14
16
17 3.4.5.1 KXKEY
18 Calculates the Key Exchange Key for NTLMv1 authentication. Used for signing
19 and sealing messages
20
21 :param negotiate_flags: The negotiated NTLM flags
22 :param session_base_key: A session key calculated from the user password
23 challenge
24 :param server_challenge: A random 8-byte response generated by the server
25 in the CHALLENGE_MESSAGE
26 :param lm_challenge_response: The LmChallengeResponse value computed in
27 ComputeResponse
28 :param lm_hash: The LMOWF computed in Compute Response
29 :return: The Key Exchange Key (KXKEY) used to sign and seal messages and
30 compute the ExportedSessionKey
31 """
32 if negotiate_flags & \
33 NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
34 key_exchange_key = hmac.new(
35 session_base_key, server_challenge + lm_challenge_response[:8],
36 digestmod=hashlib.md5
37 ).digest()
38 elif negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY:
39 des_handler = DES(DES.key56_to_key64(lm_hash[:7]))
40 first_des = des_handler.encrypt(lm_challenge_response[:8])
41
42 second_des_key = lm_hash[7:8] + b"\xbd\xbd\xbd\xbd\xbd\xbd"
43 des_handler = DES(DES.key56_to_key64(second_des_key))
44 second_des = des_handler.encrypt(lm_challenge_response[:8])
45
46 key_exchange_key = first_des + second_des
47 elif negotiate_flags & NegotiateFlags.NTLMSSP_REQUEST_NON_NT_SESSION_KEY:
48 key_exchange_key = lm_hash[:8] + b'\0' * 8
49 else:
50 key_exchange_key = session_base_key
51
52 return key_exchange_key
53
54
55def _get_exchange_key_ntlm_v2(session_base_key):
56 """
57 [MS-NLMP] v28.0 2016-07-14
58
59 4.3.5.1 KXKEY
60 Calculates the Key Exchange Key for NTLMv2 authentication. Used for signing
61 and sealing messages. According to docs, 'If NTLM v2 is used,
62 KeyExchangeKey MUST be set to the given 128-bit SessionBaseKey
63
64 :param session_base_key: A session key calculated from the user password
65 challenge
66 :return key_exchange_key: The Key Exchange Key (KXKEY) used to sign and
67 seal messages
68 """
69 return session_base_key
70
71
72def get_sign_key(exported_session_key, magic_constant):
73 """
74 3.4.5.2 SIGNKEY
75
76 :param exported_session_key: A 128-bit session key used to derive signing
77 and sealing keys
78 :param magic_constant: A constant value set in the MS-NLMP documentation
79 (constants.SignSealConstants)
80 :return sign_key: Key used to sign messages
81 """
82
83 sign_key = hashlib.md5(exported_session_key + magic_constant).digest()
84
85 return sign_key
86
87
88def get_seal_key(negotiate_flags, exported_session_key, magic_constant):
89 """
90 3.4.5.3. SEALKEY
91 Main method to use to calculate the seal_key used to seal (encrypt)
92 messages. This will determine the correct method below to use based on the
93 compatibility flags set and should be called instead of the others
94
95 :param exported_session_key: A 128-bit session key used to derive signing
96 and sealing keys
97 :param negotiate_flags: The negotiate_flags structure sent by the server
98 :param magic_constant: A constant value set in the MS-NLMP documentation
99 (constants.SignSealConstants)
100 :return seal_key: Key used to seal messages
101 """
102
103 if negotiate_flags & \
104 NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
105 seal_key = _get_seal_key_ntlm2(negotiate_flags,
106 exported_session_key,
107 magic_constant)
108 elif negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY:
109 seal_key = _get_seal_key_ntlm1(negotiate_flags, exported_session_key)
110 else:
111 seal_key = exported_session_key
112
113 return seal_key
114
115
116def _get_seal_key_ntlm1(negotiate_flags, exported_session_key):
117 """
118 3.4.5.3 SEALKEY
119 Calculates the seal_key used to seal (encrypt) messages. This for
120 authentication where NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY has not
121 been negotiated. Will weaken the keys if NTLMSSP_NEGOTIATE_56 is not
122 negotiated it will default to the 40-bit key
123
124 :param negotiate_flags: The negotiate_flags structure sent by the server
125 :param exported_session_key: A 128-bit session key used to derive signing
126 and sealing keys
127 :return seal_key: Key used to seal messages
128 """
129 if negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_56:
130 seal_key = exported_session_key[:7] + b"\xa0"
131 else:
132 seal_key = exported_session_key[:5] + b"\xe5\x38\xb0"
133
134 return seal_key
135
136
137def _get_seal_key_ntlm2(negotiate_flags, exported_session_key, magic_constant):
138 """
139 3.4.5.3 SEALKEY
140 Calculates the seal_key used to seal (encrypt) messages. This for
141 authentication where NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY has been
142 negotiated. Will weaken the keys if NTLMSSP_NEGOTIATE_128 is not
143 negotiated, will try NEGOTIATE_56 and then will default to the 40-bit key
144
145 :param negotiate_flags: The negotiate_flags structure sent by the server
146 :param exported_session_key: A 128-bit session key used to derive signing
147 and sealing keys
148 :param magic_constant: A constant value set in the MS-NLMP documentation
149 (constants.SignSealConstants)
150 :return seal_key: Key used to seal messages
151 """
152 if negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_128:
153 seal_key = exported_session_key
154 elif negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_56:
155 seal_key = exported_session_key[:7]
156 else:
157 seal_key = exported_session_key[:5]
158
159 seal_key = hashlib.md5(seal_key + magic_constant).digest()
160
161 return seal_key