2727// fork from Apache HttpComponents
2828package org .asynchttpclient .ntlm ;
2929
30+ import org .jetbrains .annotations .Contract ;
31+ import org .jetbrains .annotations .Nullable ;
32+
3033import javax .crypto .Cipher ;
3134import javax .crypto .spec .SecretKeySpec ;
3235import java .io .UnsupportedEncodingException ;
3336import java .nio .charset .Charset ;
34- import java .nio .charset .UnsupportedCharsetException ;
37+ import java .nio .charset .StandardCharsets ;
3538import java .security .Key ;
3639import java .security .MessageDigest ;
3740import java .security .SecureRandom ;
@@ -55,17 +58,7 @@ public final class NtlmEngine {
5558 /**
5659 * Unicode encoding
5760 */
58- private static final Charset UNICODE_LITTLE_UNMARKED ;
59-
60- static {
61- Charset c ;
62- try {
63- c = Charset .forName ("UnicodeLittleUnmarked" );
64- } catch (UnsupportedCharsetException e ) {
65- c = null ;
66- }
67- UNICODE_LITTLE_UNMARKED = c ;
68- }
61+ private static final Charset UNICODE_LITTLE_UNMARKED = StandardCharsets .UTF_16LE ;
6962
7063 private static final byte [] MAGIC_CONSTANT = "KGS!@#$%" .getBytes (US_ASCII );
7164
@@ -92,7 +85,7 @@ public final class NtlmEngine {
9285 /**
9386 * Secure random generator
9487 */
95- private static final SecureRandom RND_GEN ;
88+ private static final @ Nullable SecureRandom RND_GEN ;
9689
9790 static {
9891 SecureRandom rnd = null ;
@@ -132,17 +125,14 @@ public final class NtlmEngine {
132125 * @throws NtlmEngineException If {@encrypt(byte[],byte[])} fails.
133126 */
134127 private static String getType3Message (final String user , final String password , final String host , final String domain , final byte [] nonce ,
135- final int type2Flags , final String target , final byte [] targetInformation ) {
128+ final int type2Flags , final @ Nullable String target , final byte @ Nullable [] targetInformation ) {
136129 return new Type3Message (domain , host , user , password , nonce , type2Flags , target , targetInformation ).getResponse ();
137130 }
138131
139132 /**
140133 * Strip dot suffix from a name
141134 */
142135 private static String stripDotSuffix (final String value ) {
143- if (value == null ) {
144- return null ;
145- }
146136 final int index = value .indexOf ('.' );
147137 if (index != -1 ) {
148138 return value .substring (0 , index );
@@ -153,14 +143,16 @@ private static String stripDotSuffix(final String value) {
153143 /**
154144 * Convert host to standard form
155145 */
156- private static String convertHost (final String host ) {
146+ @ Contract (value = "!null -> !null" , pure = true )
147+ private static @ Nullable String convertHost (final String host ) {
157148 return host != null ? stripDotSuffix (host ).toUpperCase () : null ;
158149 }
159150
160151 /**
161152 * Convert domain to standard form
162153 */
163- private static String convertDomain (final String domain ) {
154+ @ Contract (value = "!null -> !null" , pure = true )
155+ private static @ Nullable String convertDomain (final String domain ) {
164156 return domain != null ? stripDotSuffix (domain ).toUpperCase () : null ;
165157 }
166158
@@ -223,36 +215,36 @@ private static class CipherGen {
223215 protected final String user ;
224216 protected final String password ;
225217 protected final byte [] challenge ;
226- protected final String target ;
227- protected final byte [] targetInformation ;
218+ protected final @ Nullable String target ;
219+ protected final byte @ Nullable [] targetInformation ;
228220
229221 // Information we can generate but may be passed in (for testing)
230- protected byte [] clientChallenge ;
231- protected byte [] clientChallenge2 ;
232- protected byte [] secondaryKey ;
233- protected byte [] timestamp ;
222+ protected byte @ Nullable [] clientChallenge ;
223+ protected byte @ Nullable [] clientChallenge2 ;
224+ protected byte @ Nullable [] secondaryKey ;
225+ protected byte @ Nullable [] timestamp ;
234226
235227 // Stuff we always generate
236- protected byte [] lmHash ;
237- protected byte [] lmResponse ;
238- protected byte [] ntlmHash ;
239- protected byte [] ntlmResponse ;
240- protected byte [] ntlmv2Hash ;
241- protected byte [] lmv2Hash ;
242- protected byte [] lmv2Response ;
243- protected byte [] ntlmv2Blob ;
244- protected byte [] ntlmv2Response ;
245- protected byte [] ntlm2SessionResponse ;
246- protected byte [] lm2SessionResponse ;
247- protected byte [] lmUserSessionKey ;
248- protected byte [] ntlmUserSessionKey ;
249- protected byte [] ntlmv2UserSessionKey ;
250- protected byte [] ntlm2SessionResponseUserSessionKey ;
251- protected byte [] lanManagerSessionKey ;
252-
253- CipherGen (final String domain , final String user , final String password , final byte [] challenge , final String target ,
254- final byte [] targetInformation , final byte [] clientChallenge , final byte [] clientChallenge2 , final byte [] secondaryKey ,
255- final byte [] timestamp ) {
228+ protected byte @ Nullable [] lmHash ;
229+ protected byte @ Nullable [] lmResponse ;
230+ protected byte @ Nullable [] ntlmHash ;
231+ protected byte @ Nullable [] ntlmResponse ;
232+ protected byte @ Nullable [] ntlmv2Hash ;
233+ protected byte @ Nullable [] lmv2Hash ;
234+ protected byte @ Nullable [] lmv2Response ;
235+ protected byte @ Nullable [] ntlmv2Blob ;
236+ protected byte @ Nullable [] ntlmv2Response ;
237+ protected byte @ Nullable [] ntlm2SessionResponse ;
238+ protected byte @ Nullable [] lm2SessionResponse ;
239+ protected byte @ Nullable [] lmUserSessionKey ;
240+ protected byte @ Nullable [] ntlmUserSessionKey ;
241+ protected byte @ Nullable [] ntlmv2UserSessionKey ;
242+ protected byte @ Nullable [] ntlm2SessionResponseUserSessionKey ;
243+ protected byte @ Nullable [] lanManagerSessionKey ;
244+
245+ CipherGen (final String domain , final String user , final String password , final byte [] challenge , final @ Nullable String target ,
246+ final byte @ Nullable [] targetInformation , final byte @ Nullable [] clientChallenge , final byte @ Nullable [] clientChallenge2 ,
247+ final byte @ Nullable [] secondaryKey , final byte @ Nullable [] timestamp ) {
256248 this .domain = domain ;
257249 this .target = target ;
258250 this .user = user ;
@@ -265,8 +257,8 @@ private static class CipherGen {
265257 this .timestamp = timestamp ;
266258 }
267259
268- CipherGen (final String domain , final String user , final String password , final byte [] challenge , final String target ,
269- final byte [] targetInformation ) {
260+ CipherGen (final String domain , final String user , final String password , final byte [] challenge , final @ Nullable String target ,
261+ final byte @ Nullable [] targetInformation ) {
270262 this (domain , user , password , challenge , target , targetInformation , null , null , null , null );
271263 }
272264
@@ -380,8 +372,11 @@ public byte[] getTimestamp() {
380372
381373 /**
382374 * Calculate the NTLMv2Blob
375+ *
376+ * @param targetInformation this parameter is the same object as the field targetInformation,
377+ * but guaranteed to be not null. This is done to satisfy NullAway requirements
383378 */
384- public byte [] getNTLMv2Blob () {
379+ public byte [] getNTLMv2Blob (byte [] targetInformation ) {
385380 if (ntlmv2Blob == null ) {
386381 ntlmv2Blob = createBlob (getClientChallenge2 (), targetInformation , getTimestamp ());
387382 }
@@ -390,10 +385,13 @@ public byte[] getNTLMv2Blob() {
390385
391386 /**
392387 * Calculate the NTLMv2Response
388+ *
389+ * @param targetInformation this parameter is the same object as the field targetInformation,
390+ * but guaranteed to be not null. This is done to satisfy NullAway requirements
393391 */
394- public byte [] getNTLMv2Response () {
392+ public byte [] getNTLMv2Response (byte [] targetInformation ) {
395393 if (ntlmv2Response == null ) {
396- ntlmv2Response = lmv2Response (getNTLMv2Hash (), challenge , getNTLMv2Blob ());
394+ ntlmv2Response = lmv2Response (getNTLMv2Hash (), challenge , getNTLMv2Blob (targetInformation ));
397395 }
398396 return ntlmv2Response ;
399397 }
@@ -457,12 +455,15 @@ public byte[] getNTLMUserSessionKey() {
457455
458456 /**
459457 * GetNTLMv2UserSessionKey
458+ *
459+ * @param targetInformation this parameter is the same object as the field targetInformation,
460+ * but guaranteed to be not null. This is done to satisfy NullAway requirements
460461 */
461- public byte [] getNTLMv2UserSessionKey () {
462+ public byte [] getNTLMv2UserSessionKey (byte [] targetInformation ) {
462463 if (ntlmv2UserSessionKey == null ) {
463464 final byte [] ntlmv2hash = getNTLMv2Hash ();
464465 final byte [] truncatedResponse = new byte [16 ];
465- System .arraycopy (getNTLMv2Response (), 0 , truncatedResponse , 0 , 16 );
466+ System .arraycopy (getNTLMv2Response (targetInformation ), 0 , truncatedResponse , 0 , 16 );
466467 ntlmv2UserSessionKey = hmacMD5 (truncatedResponse , ntlmv2hash );
467468 }
468469 return ntlmv2UserSessionKey ;
@@ -597,9 +598,6 @@ private static byte[] lmHash(final String password) {
597598 * the NTLM Response and the NTLMv2 and LMv2 Hashes.
598599 */
599600 private static byte [] ntlmHash (final String password ) {
600- if (UNICODE_LITTLE_UNMARKED == null ) {
601- throw new NtlmEngineException ("Unicode not supported" );
602- }
603601 final byte [] unicodePassword = password .getBytes (UNICODE_LITTLE_UNMARKED );
604602 final MD4 md4 = new MD4 ();
605603 md4 .update (unicodePassword );
@@ -613,9 +611,6 @@ private static byte[] ntlmHash(final String password) {
613611 * Responses.
614612 */
615613 private static byte [] lmv2Hash (final String domain , final String user , final byte [] ntlmHash ) {
616- if (UNICODE_LITTLE_UNMARKED == null ) {
617- throw new NtlmEngineException ("Unicode not supported" );
618- }
619614 final HMACMD5 hmacMD5 = new HMACMD5 (ntlmHash );
620615 // Upper case username, upper case domain!
621616 hmacMD5 .update (user .toUpperCase (Locale .ROOT ).getBytes (UNICODE_LITTLE_UNMARKED ));
@@ -632,9 +627,6 @@ private static byte[] lmv2Hash(final String domain, final String user, final byt
632627 * Responses.
633628 */
634629 private static byte [] ntlmv2Hash (final String domain , final String user , final byte [] ntlmHash ) {
635- if (UNICODE_LITTLE_UNMARKED == null ) {
636- throw new NtlmEngineException ("Unicode not supported" );
637- }
638630 final HMACMD5 hmacMD5 = new HMACMD5 (ntlmHash );
639631 // Upper case username, mixed case target!!
640632 hmacMD5 .update (user .toUpperCase (Locale .ROOT ).getBytes (UNICODE_LITTLE_UNMARKED ));
@@ -774,10 +766,11 @@ private static void oddParity(final byte[] bytes) {
774766 * NTLM message generation, base class
775767 */
776768 private static class NTLMMessage {
769+ private static final byte [] EMPTY_BYTE_ARRAY = new byte []{};
777770 /**
778771 * The current response
779772 */
780- private byte [] messageContents ;
773+ private byte [] messageContents = EMPTY_BYTE_ARRAY ;
781774
782775 /**
783776 * The current output position
@@ -902,7 +895,7 @@ protected void addByte(final byte b) {
902895 *
903896 * @param bytes the bytes to add.
904897 */
905- protected void addBytes (final byte [] bytes ) {
898+ protected void addBytes (final byte @ Nullable [] bytes ) {
906899 if (bytes == null ) {
907900 return ;
908901 }
@@ -1022,8 +1015,8 @@ String getResponse() {
10221015 */
10231016 static class Type2Message extends NTLMMessage {
10241017 protected byte [] challenge ;
1025- protected String target ;
1026- protected byte [] targetInfo ;
1018+ protected @ Nullable String target ;
1019+ protected byte @ Nullable [] targetInfo ;
10271020 protected int flags ;
10281021
10291022 Type2Message (final String message ) {
@@ -1090,14 +1083,14 @@ byte[] getChallenge() {
10901083 /**
10911084 * Retrieve the target
10921085 */
1093- String getTarget () {
1086+ @ Nullable String getTarget () {
10941087 return target ;
10951088 }
10961089
10971090 /**
10981091 * Retrieve the target info
10991092 */
1100- byte [] getTargetInfo () {
1093+ byte @ Nullable [] getTargetInfo () {
11011094 return targetInfo ;
11021095 }
11031096
@@ -1117,19 +1110,19 @@ static class Type3Message extends NTLMMessage {
11171110 // Response flags from the type2 message
11181111 protected int type2Flags ;
11191112
1120- protected byte [] domainBytes ;
1121- protected byte [] hostBytes ;
1113+ protected byte @ Nullable [] domainBytes ;
1114+ protected byte @ Nullable [] hostBytes ;
11221115 protected byte [] userBytes ;
11231116
11241117 protected byte [] lmResp ;
11251118 protected byte [] ntResp ;
1126- protected byte [] sessionKey ;
1119+ protected byte @ Nullable [] sessionKey ;
11271120
11281121 /**
11291122 * Constructor. Pass the arguments we will need
11301123 */
11311124 Type3Message (final String domain , final String host , final String user , final String password , final byte [] nonce ,
1132- final int type2Flags , final String target , final byte [] targetInformation ) {
1125+ final int type2Flags , final @ Nullable String target , final byte @ Nullable [] targetInformation ) {
11331126 // Save the flags
11341127 this .type2Flags = type2Flags ;
11351128
@@ -1149,12 +1142,12 @@ static class Type3Message extends NTLMMessage {
11491142 // been tested
11501143 if ((type2Flags & FLAG_TARGETINFO_PRESENT ) != 0 && targetInformation != null && target != null ) {
11511144 // NTLMv2
1152- ntResp = gen .getNTLMv2Response ();
1145+ ntResp = gen .getNTLMv2Response (targetInformation );
11531146 lmResp = gen .getLMv2Response ();
11541147 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY ) != 0 ) {
11551148 userSessionKey = gen .getLanManagerSessionKey ();
11561149 } else {
1157- userSessionKey = gen .getNTLMv2UserSessionKey ();
1150+ userSessionKey = gen .getNTLMv2UserSessionKey (targetInformation );
11581151 }
11591152 } else {
11601153 // NTLMv1
@@ -1198,9 +1191,6 @@ static class Type3Message extends NTLMMessage {
11981191 } else {
11991192 sessionKey = null ;
12001193 }
1201- if (UNICODE_LITTLE_UNMARKED == null ) {
1202- throw new NtlmEngineException ("Unicode not supported" );
1203- }
12041194 hostBytes = unqualifiedHost != null ? unqualifiedHost .getBytes (UNICODE_LITTLE_UNMARKED ) : null ;
12051195 domainBytes = unqualifiedDomain != null ? unqualifiedDomain .toUpperCase (Locale .ROOT ).getBytes (UNICODE_LITTLE_UNMARKED ) : null ;
12061196 userBytes = user .getBytes (UNICODE_LITTLE_UNMARKED );
0 commit comments