RFC 2433

Microsoft PPP CHAP Extensions

๐Ÿ“… published October 1998, from ietf

Microsoft PPP CHAP Extensions ๐ŸŽ€

  • RFC: 2433
  • Series: Network Working Group
  • Authors: G. Zorn and S. Cobb, both at Microsoft Corporation ๐Ÿ’ป
  • Category: Informational
  • Date: October 1998
  • Obsoletes / Updates: The header doesn't list any RFCs that this one obsoletes or updates ๐ŸŒธ

Status of This Memo โœจ

Okay so this memo gives information to the Internet community. It does not specify an Internet standard of any kind ๐Ÿงš Distribution is unlimited, so share it with everyone!

Copyright (C) The Internet Society (1998). All Rights Reserved. ๐Ÿ’œ

IESG Note ๐Ÿšจ

The protocol described here has significant vulnerabilities. People planning on implementing or using this protocol should read section 12, "Security Considerations". ๐Ÿ˜ญ

1. Abstract โœจ

Okay, so PPP [1] is the standard way to send multi-protocol datagrams over point-to-point links ๐ŸŒˆ It also has an extensible Link Control Protocol and a whole family of Network Control Protocols (NCPs) for setting up and configuring different network-layer protocols. Iconic behaviour from PPP, honestly ๐Ÿ‘‘

This document describes Microsoft's PPP CHAP dialect, called MS-CHAP. It extends the user authentication that Windows networks already offer so it works for remote workstations ๐Ÿ’… MS-CHAP is closely derived from the PPP Challenge Handshake Authentication Protocol in RFC 1994 [2], so keep that one handy, bestie ๐Ÿ“š

The algorithms used to generate the various MS-CHAP protocol fields are described in an appendix. ๐ŸŽ€

2. Introduction ๐ŸŒธ

Microsoft made MS-CHAP to authenticate remote Windows workstations. It gives you the same experience LAN users are used to, and it uses the encryption and hashing algorithms that Windows networks already use ๐Ÿฆ‹

Where possible, MS-CHAP matches standard CHAP. The main differences between MS-CHAP and standard CHAP are:

  • MS-CHAP is enabled by negotiating CHAP Algorithm 0x80 in LCP option 3, Authentication Protocol.
  • The MS-CHAP Response packet is in a format designed for compatibility with Microsoft's Windows NT 3.5, 3.51 and 4.0, and Windows95 networking products. The MS-CHAP format does not require the authenticator to store a clear-text or reversibly encrypted password. That's a big deal ๐Ÿ”
  • MS-CHAP provides authenticator-controlled authentication retry and password changing mechanisms. ๐Ÿ”
  • MS-CHAP defines a set of reason-for-failure codes returned in the Failure packet Message field. ๐Ÿ™ˆ

3. Specification of Requirements ๐Ÿ“

In this document, the key words "MAY", "MUST, "MUST NOT", "optional", "recommended", "SHOULD", and "SHOULD NOT" are to be interpreted as described in [2]. These are the official requirement words, so read them carefully, bestie ๐Ÿซถ

4. LCP Configuration ๐Ÿงš

The LCP configuration for MS-CHAP is identical to the one for standard CHAP. The only difference is that the Algorithm field has the value 0x80, instead of the MD5 value 0x05. PPP implementations that don't support MS-CHAP, but that correctly implement LCP Config-Rej, should have no problem dealing with this non-standard option. โœจ

5. Challenge Packet ๐Ÿ“

The MS-CHAP Challenge packet is identical in format to the standard CHAP Challenge packet. Easy ๐Ÿ’–

MS-CHAP authenticators send an 8-octet challenge Value field. Peers don't need to copy Microsoft's algorithm for picking the 8-octet value. The standard guidelines on randomness [1,2,7] SHOULD be observed, though. ๐ŸŽฒ

Microsoft authenticators don't currently put anything in the Name field. This may change in the future. ๐Ÿ”ฎ

6. Response Packet ๐Ÿ’…

The MS-CHAP Response packet is identical in format to the standard CHAP Response packet. The main difference is that the Value field is sub-formatted differently, like this:

24 octets: LAN Manager compatible challenge response
24 octets: Windows NT compatible challenge response
 1 octet : "Use Windows NT compatible challenge response" flag

The LAN Manager compatible challenge response is an encoded function of the password and the received challenge. It's output by the routine LmChallengeResponse() (see section A.1, below). LAN Manager passwords are limited to 14 case-insensitive OEM characters. Also, use of the LAN Manager compatible challenge response has been deprecated, so peers SHOULD NOT generate it, and the sub-field SHOULD be zero-filled. The algorithm used to generate the LAN Manager compatible challenge response is described here for informational purposes only. ๐Ÿ™ƒ

The Windows NT compatible challenge response is an encoded function of the password and the received challenge too. It's output by the routine NTChallengeResponse() (see section A.5, below). A Windows NT password is a string of 0 to (theoretically) 256 case-sensitive Unicode [8] characters. Current versions of Windows NT limit passwords to 14 characters, mainly for compatibility reasons. This may change in the future. ๐ŸŒˆ

The "use Windows NT compatible challenge response" flag, if 1, means the Windows NT response is provided and should be used in preference to the LAN Manager response. The LAN Manager response will still be used if the account doesn't have a Windows NT password hash. For example, that happens if the password hasn't been changed since the account was uploaded from a LAN Manager 2.x account database. If the flag is 0, the Windows NT response is ignored and the LAN Manager response is used. Since LAN Manager authentication has been deprecated, this flag SHOULD always be set (1), and the LAN Manager compatible challenge response field SHOULD be zero-filled. ๐Ÿ”ฅ

The Name field identifies the peer's user account name. The Windows NT domain name may come before the user's account name. For example, BIGCO\johndoe where "BIGCO" is a Windows NT domain containing the user account "john-doe". If a domain is not provided, the backslash should also be left out, like johndoe. ๐Ÿงฉ

7. Success Packet ๐ŸŽ‰

The Success packet is identical in format to the standard CHAP Success packet. Nothing new here, just vibes โœจ

8. Failure Packet ๐Ÿšซ

The Failure packet is identical in format to the standard CHAP Failure packet. But here's the twist: there's formatted text stored in the Message field, and, contrary to the standard CHAP rules, that text affects the protocol ๐Ÿ‘€ The Message field format is:

"E=eeeeeeeeee R=r C=cccccccccccccccc V=vvvvvvvvvv"

Here's what each part means:

  • The "eeeeeeeeee" is the decimal error code (it doesn't need to be 10 digits). It corresponds to one of the codes listed below. Implementations should deal with codes that aren't on this list gracefully. ๐Ÿงธ
646 ERROR_RESTRICTED_LOGON_HOURS
647 ERROR_ACCT_DISABLED
648 ERROR_PASSWD_EXPIRED
649 ERROR_NO_DIALIN_PERMISSION
691 ERROR_AUTHENTICATION_FAILURE
709 ERROR_CHANGING_PASSWORD
  • The "r" is a flag set to "1" if a retry is allowed, and "0" if not. When the authenticator sets this flag to "1", it disables short timeouts. It expects the peer to prompt the user for new credentials and resubmit the response. โณ
  • The "cccccccccccccccc" is 16 hexadecimal digits, shown as an ASCII representation of a new challenge value. This field is optional. If it isn't sent, the authenticator expects the resubmitted response to be calculated based on the previous challenge value plus decimal 23 in the first octet, i.e. the one immediately following the Value Size field. Windows 95 authenticators may send this field. Windows NT authenticators don't, but they may in the future. Both systems implement peer support for this field. ๐Ÿ”„
  • The "vvvvvvvvvv" is the decimal version code (it doesn't need to be 10 digits). It indicates the MS-CHAP protocol version supported on the server. Right now, this only matters for selecting a Change Password packet type. If the field isn't present, the version should be assumed to be 1. Since use of the version 1 Change Password packet has been deprecated, this field SHOULD always contain a value greater than or equal to 2. ๐Ÿ”–

Implementations should accept but ignore any additional text they don't recognize. ๐Ÿซถ

9. Change Password Packet (version 1) ๐Ÿ”

The version 1 Change Password packet doesn't appear in standard CHAP. It lets the peer change the password on the account specified in the previous Response packet. The version 1 Change Password packet should be sent only if the authenticator reports ERROR_PASSWD_EXPIRED (E=648) and V is either missing or equal to one in the Message field of the Failure packet. ๐Ÿ™ˆ

The use of the Change Password Packet (version 1) has been deprecated. Its format is described here for informational purposes, but peers SHOULD NOT transmit it. ๐Ÿšซ

The format of this packet is as follows:

    1 octet : Code (=5)
    1 octet : Identifier
    2 octets: Length (=72)
   16 octets: Encrypted LAN Manager Old password Hash
   16 octets: Encrypted LAN Manager New Password Hash
   16 octets: Encrypted Windows NT Old Password Hash
   16 octets: Encrypted Windows NT New Password Hash
    2 octets: Password Length
    2 octets: Flags

Here's what each field does:

  • Code: 5
  • Identifier: The Identifier field is one octet and helps match requests and replies. Its value is the Identifier of the received Failure packet this packet responds to, plus 1.
  • Length: 72
  • Encrypted LAN Manager New Password Hash and Encrypted LAN Manager Old Password Hash: These fields contain the LAN Manager password hash of the new and old passwords, encrypted with the last received challenge value. This is output by the routine LmEncryptedPasswordHash() (see section A.8, below).
  • Encrypted Windows NT New Password Hash and Encrypted Windows NT Old Password Hash: These fields contain the Windows NT password hash of the new and old passwords, encrypted with the last received challenge value. This is output by the pseudo-code routine NtEncryptedPasswordHash() (see section A.10, below). ๐Ÿง
  • Password Length: The length in octets of the LAN Manager compatible form of the new password. If this value is greater than or equal to zero and less than or equal to 14, it's assumed the encrypted LAN Manager password hash fields are valid. Otherwise, it's assumed these fields aren't valid. In that case, the Windows NT compatible passwords MUST be provided. ๐Ÿšจ
  • Flags: This field is two octets long. It's a bit field of option flags, where 0 is the least significant bit of the 16-bit quantity:
    • Bit 0: If this bit is set (1), it means the encrypted Windows NT hashed passwords are valid and should be used. If this bit is cleared (0), the Windows NT fields aren't used and the LAN Manager fields must be provided. ๐ŸŒŸ
    • Bits 1-15: Reserved, always clear (0). ๐ŸŽ€

10. Change Password Packet (version 2) ๐ŸŽ€

okay bestie, this one is the version 2 Change Password packet and it is NOT in standard CHAP ๐Ÿ’… It lets the peer change the password on the account named in the previous Response packet. You should only send it if the authenticator reports ERROR_PASSWD_EXPIRED (E=648) and a version of 2 or greater in the Message field of the Failure packet. โœจ

This packet type is supported by Windows NT 3.51, 4.0 and recent versions of Windows 95 ๐Ÿฆ‹ It is not supported by Windows NT 3.5 or early versions of Windows 95 ๐Ÿ’€

Here's the layout of the packet ๐Ÿงš

           1 octet  : Code
           1 octet  : Identifier
           2 octets : Length
         516 octets : Password Encrypted with Old NT Hash

          16 octets : Old NT Hash Encrypted with New NT Hash
         516 octets : Password Encrypted with Old LM Hash
          16 octets : Old LM Hash Encrypted With New NT Hash
          24 octets : LAN Manager compatible challenge response
          24 octets : Windows NT compatible challenge response
           2-octet  : Flags

Code

The Code value is 6 ๐ŸŒธ

Identifier

The Identifier is one octet and helps match requests with replies ๐Ÿซถ Its value is the Identifier of the received Failure packet this one answers, plus 1.

Length

The Length is 1118 ๐Ÿ”ฅ

Password Encrypted with Old NT Hash

This field holds the PWBLOCK form of the new Windows NT password, encrypted with the old Windows NT password hash. It's the output of the NewPasswordEncryptedWithOldNtPasswordHash() routine (see section A.11, below) ๐Ÿ”

Old NT Hash Encrypted with New NT Hash

This field holds the old Windows NT password hash, encrypted with the new Windows NT password hash. It's the output of the OldNtPasswordHashEncryptedWithNewNtPasswordHash() routine (see section A.14, below) ๐Ÿ’–

Password Encrypted with Old LM Hash

This field holds the PWBLOCK form of the new Windows NT password, encrypted with the old LAN Manager password hash. It's the output of the NewPasswordEncryptedWithOldLmPasswordHash() routine described in section A.15, below. BUT the use of this field has been deprecated ๐Ÿ™ˆ peers SHOULD NOT generate it, and this field SHOULD be zero-filled.

Old LM Hash Encrypted With New NT Hash

This field holds the old LAN Manager password hash, encrypted with the new Windows NT password hash. It's the output of the OldLmPasswordHashEncryptedWithNewNtPasswordHash() routine (see section A.16, below). Same deal though, the use of this field has been deprecated ๐Ÿ˜ญ peers SHOULD NOT generate it, and this field SHOULD be zero-filled.

LAN Manager compatible challenge response

Windows NT compatible challenge response

This is the challenge response field (the same one described in the Response packet), but calculated on the new password and the same challenge that was used in the last response ๐ŸŒˆ The LAN Manager compatible challenge response is deprecated too, so peers SHOULD NOT generate it, and the field SHOULD be zero-filled.

Flags

This field is two octets long ๐Ÿฆ‹ It's a bit field of option flags, where 0 is the least significant bit of the 16-bit quantity. Here's how it's laid out:

                   1
         5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         |                           | |
         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • Bit 0: this is the "use Windows NT compatible challenge response" flag, same as in the Response packet ๐Ÿ’…
  • Bit 1: if it's set (1), the "Password Encrypted with Old LM Hash" and "Old LM Hash Encrypted With New NT Hash" fields are valid and should be used. If it's clear (0), those fields are not valid. This bit SHOULD always be clear (0) ๐Ÿ™…โ€โ™€๏ธ
  • Bits 2-15: reserved, always clear (0) ๐Ÿ“

11. Security Considerations ๐Ÿ’€

okay this part is SERIOUS bestie, don't skip it ๐Ÿ˜ญ As an implementation detail, the authenticator SHOULD limit the number of password retries allowed, so brute-force password guessing attacks are harder to pull off ๐Ÿ”’

The challenge value gets encrypted with the password hash to form the response, and the challenge is sent in clear text. That means both passive known-plaintext and active chosen-plaintext attacks against the password hash are possible ๐Ÿšจ Suitable precautions (i.e., frequent password changes) SHOULD be taken in environments where eavesdropping is likely.

The Change Password (version 1) packet is vulnerable to a passive eavesdropping attack that can easily reveal the new password hash ๐Ÿ’” For this reason, it MUST NOT be sent if eavesdropping is possible.


12. References ๐Ÿ“š

[1] Simpson, W., "The Point-to-Point Protocol (PPP)", STD 51, RFC 1661, July 1994.

[2] Simpson, W., "PPP Challenge Handshake Authentication Protocol (CHAP)", RFC 1994, August 1996.

[3] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[4] "Data Encryption Standard (DES)", Federal Information Processing Standard Publication 46-2, National Institute of Standards and Technology, December 1993.

[5] Rivest, R., "MD4 Message Digest Algorithm", RFC 1320, April 1992.

[6] RC4 is a proprietary encryption algorithm available under license from RSA Data Security Inc. For licensing information, contact: RSA Data Security, Inc. 100 Marine Parkway Redwood City, CA 94065-1031

[7] Eastlake, D., Crocker, S., and J. Schiller, "Randomness Recomnendations for Security", RFC 1750, December 1994.

[8] "The Unicode Standard, Version 2.0", The Unicode Consortium, Addison-Wesley, 1996. ISBN 0-201-48345-9.

[9] "DES Modes of Operation", Federal Information Processing Standards Publication 81, National Institute of Standards and Technology, December 1980


13. Acknowledgements ๐Ÿซถ

Thanks (in no particular order) to Jeff Haag (Jeff_Haag@3com.com), Bill Palter (palter@network-alchemy.com), Bruce Johnson (bjohnson@microsoft.com), Tony Bell (tonybe@microsoft.com), Benoit Martin (ehlija@vircom.com), and Joe Davies (josephd@microsoft.com) for useful suggestions and feedback ๐Ÿ’œ


14. Chair's Address ๐ŸŒท

The PPP Extensions Working Group can be contacted via the current chair:

Karl Fox
Ascend Communications
3518 Riverside Drive
Suite 101
Columbus, OH 43221

Phone: +1 614 326 6841
EMail: karl@ascend.com

15. Authors' Addresses โœจ

Questions about this memo can also be directed to:

Glen Zorn
Microsoft Corporation
One Microsoft Way
Redmond, Washington 98052

Phone: +1 425 703 1559
Fax:   +1 425 936 7329
EMail: glennz@microsoft.com


Steve Cobb
Microsoft Corporation
One Microsoft Way
Redmond, Washington 98052

EMail: stevec@microsoft.com

Appendix A - Pseudocode ๐Ÿงš

The routines mentioned in the text are described in pseudocode below ๐Ÿ“

A.1 LmChallengeResponse()

   LmChallengeResponse(
   IN  8-octet          Challenge,
   IN  0-to-14-oem-char Password,
   OUT 24-octet         Response )
   {
      LmPasswordHash( Password, giving PasswordHash )
      ChallengeResponse( Challenge, PasswordHash, giving Response )
   }

A.2 LmPasswordHash()

   LmPasswordHash(
   IN  0-to-14-oem-char Password,
   OUT 16-octet         PasswordHash )
   {
      Set UcasePassword to the uppercased Password
      Zero pad UcasePassword to 14 characters

      DesHash( 1st 7-octets of UcasePassword,
               giving 1st 8-octets of PasswordHash )

      DesHash( 2nd 7-octets of UcasePassword,
               giving 2nd 8-octets of PasswordHash )
   }

A.3 DesHash()

   DesHash(
   IN  7-octet Clear,
   OUT 8-octet Cypher )
   {
      /*
       * Make Cypher an irreversibly encrypted form of Clear by
       * encrypting known text using Clear as the secret key.
       * The known text consists of the string
       *
       *              KGS!@#$%
       */

      Set StdText to "KGS!@#$%"
      DesEncrypt( StdText, Clear, giving Cypher )
   }

A.4 DesEncrypt()

   DesEncrypt(
   IN  8-octet Clear,
   IN  7-octet Key,
   OUT 8-octet Cypher )
   {
      /*
       * Use the DES encryption algorithm [4] in ECB mode [9]
       * to encrypt Clear into Cypher such that Cypher can
       * only be decrypted back to Clear by providing Key.
       * Note that the DES algorithm takes as input a 64-bit
       * stream where the 8th, 16th, 24th, etc.  bits are
       * parity bits ignored by the encrypting algorithm.
       * Unless you write your own DES to accept 56-bit input
       * without parity, you will need to insert the parity bits
       * yourself.
       */
   }

A.5 NtChallengeResponse()

   NtChallengeResponse(
   IN  8-octet               Challenge,
   IN  0-to-256-unicode-char Password,
   OUT 24-octet              Response )
   {
      NtPasswordHash( Password, giving PasswordHash )
      ChallengeResponse( Challenge, PasswordHash, giving Response )
   }

A.6 NtPasswordHash()

   NtPasswordHash(
   IN  0-to-256-unicode-char Password,
   OUT 16-octet              PasswordHash )
   {
      /*
       * Use the MD4 algorithm [5] to irreversibly hash Password
       * into PasswordHash.  Only the password is hashed without
       * including any terminating 0.
       */

   }

A.7 ChallengeResponse()

   ChallengeResponse(
   IN  8-octet  Challenge,
   IN  16-octet PasswordHash,
   OUT 24-octet Response )
   {
      Set ZPasswordHash to PasswordHash zero-padded to 21 octets

      DesEncrypt( Challenge,
                  1st 7-octets of ZPasswordHash,
                  giving 1st 8-octets of Response )

      DesEncrypt( Challenge,
                  2nd 7-octets of ZPasswordHash,
                  giving 2nd 8-octets of Response )

      DesEncrypt( Challenge,
                  3rd 7-octets of ZPasswordHash,
                  giving 3rd 8-octets of Response )
   }

A.8 LmEncryptedPasswordHash()

   LmEncryptedPasswordHash(
   IN  0-to-14-oem-char Password,
   IN  8-octet          KeyValue,
   OUT 16-octet         Cypher )
   {
      LmPasswordHash( Password, giving PasswordHash )

      PasswordHashEncryptedWithBlock( PasswordHash,
                                      KeyValue,
                                      giving Cypher )
   }

A.9 PasswordHashEncryptedWithBlock()

   PasswordHashEncryptedWithBlock(
   IN  16-octet PasswordHash,
   IN  8-octet  Block,
   OUT 16-octet Cypher )
   {

      DesEncrypt( 1st 8-octets PasswordHash,
                  1st 7-octets Block,
                  giving 1st 8-octets Cypher )

      DesEncrypt( 2nd 8-octets PasswordHash,
                  1st 7-octets Block,
                  giving 2nd 8-octets Cypher )
   }

A.10 NtEncryptedPasswordHash()

   NtEncryptedPasswordHash(  IN   0-to-14-oem-char  Password IN  8-octet
   Challenge OUT 16-octet         Cypher ) {
      NtPasswordHash( Password, giving PasswordHash )

      PasswordHashEncryptedWithBlock( PasswordHash,
                                      Challenge,
                                      giving Cypher )
   }

A.11 NewPasswordEncryptedWithOldNtPasswordHash() ๐Ÿ’…

Okay bestie, this one is a combo move โœจ It runs NtPasswordHash() on the OldPassword first, then uses that hash to lock up the NewPassword with EncryptPwBlockWithPasswordHash(). Iconic, honestly ๐Ÿ”ฅ

datatype-PWBLOCK
{
   256-unicode-char Password
   4-octets         PasswordLength
}

NewPasswordEncryptedWithOldNtPasswordHash(
IN  0-to-256-unicode-char NewPassword,
IN  0-to-256-unicode-char OldPassword,
OUT datatype-PWBLOCK      EncryptedPwBlock )
{
   NtPasswordHash( OldPassword, giving PasswordHash )

   EncryptPwBlockWithPasswordHash( NewPassword,
                                   PasswordHash,
                                   giving EncryptedPwBlock )
}

A.12 EncryptPwBlockWithPasswordHash() ๐ŸŽ€

This is where the password gets wrapped up, and it's giving secret-keeper energy ๐Ÿคซ The PWBLOCK gets filled with random octets first, then the password gets tucked into the END of it (right-aligned, babe), its length goes in the PasswordLength field, and then the whole block is RC4 encrypted using the PasswordHash as the key.

EncryptPwBlockWithPasswordHash(
IN  0-to-256-unicode-char Password,
IN  16-octet              PasswordHash,
OUT datatype-PWBLOCK      PwBlock )
{

   Fill ClearPwBlock with random octet values
   PwSize = lstrlenW( Password ) * sizeof( unicode-char )
   PwOffset = sizeof( ClearPwBlock.Password ) - PwSize
   Move PwSize octets to (ClearPwBlock.Password + PwOffset ) from Password
   ClearPwBlock.PasswordLength = PwSize
   Rc4Encrypt( ClearPwBlock,
               sizeof( ClearPwBlock ),
               PasswordHash,
               sizeof( PasswordHash ),
               giving PwBlock )
}

A.13 Rc4Encrypt() ๐Ÿฆ‹

RC4 time! ๐Ÿงš This is the tool doing the actual scrambling. The comment in the original says it all, so here it is, unedited:

Rc4Encrypt(
IN  x-octet Clear,
IN  integer ClearLength,
IN  y-octet Key,
IN  integer KeyLength,
OUT x-octet Cypher )
{
   /*
    * Use the RC4 encryption algorithm [6] to encrypt Clear of
    * length ClearLength octets into a Cypher of the same length
    * such that the Cypher can only be decrypted back to Clear
    * by providing a Key of length KeyLength octets.
    */
}

A.14 OldNtPasswordHashEncryptedWithNewNtPasswordHash() ๐ŸŒธ

Ngl the name is a whole mood, but the vibe is simple: hash both passwords with NT, then encrypt the OLD hash using the NEW hash as the block ๐Ÿซถ

OldNtPasswordHashEncryptedWithNewNtPasswordHash(
IN  0-to-256-unicode-char NewPassword,
IN  0-to-256-unicode-char OldPassword,
OUT 16-octet              EncryptedPasswordHash )
{
   NtPasswordHash( OldPassword, giving OldPasswordHash )
   NtPasswordHash( NewPassword, giving NewPasswordHash )
   NtPasswordHashEncryptedWithBlock( OldPasswordHash,
                                     NewPasswordHash,
                                     giving EncryptedPasswordHash )
}

A.15 NewPasswordEncryptedWithOldLmPasswordHash() ๐Ÿ“

Same energy as A.11, just with the LM hash doing the heavy lifting this time ๐Ÿ’– It runs LmPasswordHash() on the OldPassword, then uses it to encrypt the NewPassword with EncryptPwBlockWithPasswordHash().

NewPasswordEncryptedWithOldLmPasswordHash(
IN  0-to-256-unicode-char NewPassword,
IN  0-to-256-unicode-char OldPassword,
OUT datatype-PWBLOCK      EncryptedPwBlock )
{
   LmPasswordHash( OldPassword, giving PasswordHash )

   EncryptPwBlockWithPasswordHash( NewPassword, PasswordHash,
                                   giving EncryptedPwBlock )
}

A.16 OldLmPasswordHashEncryptedWithNewNtPasswordHash() ๐ŸŒˆ

Okay this one mixes both worlds ๐ŸŒ The OLD password gets the LM hash, the NEW password gets the NT hash, and then the old LM hash is encrypted using the new NT hash as the block. Lowkey clever.

OldLmPasswordHashEncryptedWithNewNtPasswordHash(
IN  0-to-256-unicode-char NewPassword,
IN  0-to-256-unicode-char OldPassword,
OUT 16-octet              EncryptedPasswordHash )
{
   LmPasswordHash( OldPassword, giving OldPasswordHash )

   NtPasswordHash( NewPassword, giving NewPasswordHash )

   NtPasswordHashEncryptedWithBlock( OldPasswordHash, NewPasswordHash,
                                   giving EncrytptedPasswordHash )
}

A.17 NtPasswordHashEncryptedWithBlock() ๐Ÿฆ„

This is the DES step, and it's a two-for-one ๐Ÿ‘ฏโ€โ™€๏ธ The 16-octet PasswordHash gets split in half, and each 8-octet half gets DES encrypted with the matching 7-octet piece of the Block.

NtPasswordHashEncryptedWithBlock(
IN  16-octet PasswordHash,
IN  16-octet Block,
OUT 16-octet Cypher )
{
   DesEncrypt( 1st 8-octets PasswordHash,
               1st 7-octets Block,
               giving 1st 8-octets Cypher )

   DesEncrypt( 2nd 8-octets PasswordHash,
               2nd 7-octets Block,
               giving 2nd 8-octets Cypher )
}

Appendix B - Examples ๐ŸŒŸ

B.1 Negotiation Examples ๐Ÿฆ‹

Here are some examples of typical negotiations, bestie ๐Ÿ’… The peer is on the left and the authenticator is on the right.

The packet sequence ID gets incremented on each authentication retry Response and on the change password response. Every case where the packet sequence ID gets updated is noted below ๐Ÿ“

Response retry is never allowed after Change Password. Change Password may occur after Response retry. The implied challenge form is shown in the examples, though all cases of first challenge+23 should be replaced by the C=cccccccccccccccc challenge if authenticator supplies it in the Failure packet. Read that last part twice, it matters ๐Ÿ‘€

B.1.1 Successful authentication โœจ

        <- Challenge
    Response ->
        <- Success

B.1.2 Failed authentication with no retry allowed ๐Ÿ’€

        <- Challenge
    Response ->
        <- Failure (E=691 R=0)

B.1.3 Successful authentication after retry ๐ŸŽ€

        <- Challenge
    Response ->
        <- Failure (E=691 R=1), disable short timeout
    Response (++ID) to first challenge+23 ->
        <- Success

B.1.4 Failed hack attack with 3 attempts allowed ๐Ÿ™ˆ

        <- Challenge
    Response ->
        <- Failure (E=691 R=1), disable short timeout
    Response (++ID) to first challenge+23 ->
        <- Failure (E=691 R=1), disable short timeout
    Response (++ID) to first challenge+23+23 ->

        <- Failure (E=691 R=0)

B.1.5 Successful authentication with password change ๐Ÿ“

        <- Challenge
    Response ->
        <- Failure (E=648 R=0 V=2), disable short timeout
    ChangePassword (++ID) to first challenge ->
        <- Success

B.1.6 Successful authentication with retry and password change ๐Ÿ‘‘

        <- Challenge
    Response ->
        <- Failure (E=691 R=1), disable short timeout
    Response (++ID) to first challenge+23 ->
        <- Failure (E=648 R=0 V=2), disable short timeout
    ChangePassword (++ID) to first challenge+23 ->
        <- Success

B.2 Hash Example ๐Ÿงช

Here are the intermediate values for the password "MyPw" ๐Ÿ’–

8-octet Challenge:
10 2D B5 DF 08 5D 30 41

0-to-256-unicode-char NtPassword:
4D 00 79 00 50 00 77 00

16-octet NtPasswordHash:
FC 15 6A F7 ED CD 6C 0E DD E3 33 7D 42 7F 4E AC

24-octet NtChallengeResponse:
4E 9D 3C 8F 9C FD 38 5D 5B F4 D3 24 67 91 95 6C
A4 C3 51 AB 40 9A 3D 61

B.3 Example of DES Key Generation ๐Ÿ”‘

DES uses 56-bit keys, which get expanded to 64 bits by inserting parity bits. Once the key's parity is fixed, every eighth bit is a parity bit, and the number of bits set (1) in each octet is odd, aka odd parity. Heads up: lots of DES engines don't check parity at all and just strip the parity bits out ๐Ÿ™ˆ

This example shows the values that come out when you use the 16-octet NTPasswordHash from Appendix B.2 to generate a pair of DES keys (for example, for use in the NtPasswordHashEncryptedWithBlock() described in Appendix A.17) ๐Ÿงš

16-octet NtPasswordHash:
FC 15 6A F7 ED CD 6C 0E DD E3 33 7D 42 7F 4E AC

First "raw" DES key (initial 7 octets of password hash):
FC 15 6A F7 ED CD 6C

First parity-corrected DES key (eight octets):
FD 0B 5B 5E 7F 6E 34 D9

Second "raw" DES key (second 7 octets of password hash)
0E DD E3 33 7D 42 7F

Second parity-corrected DES key (eight octets):
0E 6E 79 67 37 EA 08 FE

Okay, the legal bit. Please read it, it's important, and I'm keeping every word exactly as it is ๐Ÿซถ

   Copyright (C) The Internet Society (1998).  All Rights Reserved.

   This document and translations of it may be copied and furnished to
   others, and derivative works that comment on or otherwise explain it
   or assist in its implementation may be prepared, copied, published
   and distributed, in whole or in part, without restriction of any
   kind, provided that the above copyright notice and this paragraph are
   included on all such copies and derivative works.  However, this
   document itself may not be modified in any way, such as by removing
   the copyright notice or references to the Internet Society or other
   Internet organizations, except as needed for the purpose of
   developing Internet standards in which case the procedures for
   copyrights defined in the Internet Standards process must be
   followed, or as required to translate it into languages other than
   English.

   The limited permissions granted above are perpetual and will not be
   revoked by the Internet Society or its successors or assigns.

   This document and the information contained herein is provided on an
   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
๐Ÿ“ฆ where this page comes from