RFC 3079

Deriving Keys for use with Microsoft Point-to-Point Encryption (MPPE)

πŸ“… published March 2001, from ietf

RFC 3079: Deriving Keys for use with Microsoft Point-to-Point Encryption (MPPE) πŸŽ€

Network Working Group πŸ’– This one is from the Network Working Group.

  • Author: G. Zorn, cisco Systems ✨
  • Category: Informational (it's giving "here's a helpful reference," not a rulebook πŸ“–)
  • Date: March 2001 🌸
  • Obsoletes / Updates: This header doesn't list anything it obsoletes or updates πŸ¦‹

Status of This Memo πŸ’…

Okay so this memo is here to provide information for the Internet community. It is not an Internet standard of any kind, so don't treat it like one πŸ™ˆ Distribution of this memo is unlimited, so share it with your whole group chat πŸ’œ

Copyright (C) The Internet Society (2001). All Rights Reserved. Mine, theirs, the Internet Society's 🌈

Abstract 🧚

Okay bestie, the Point-to-Point Protocol (PPP) is the standard way to carry multi-protocol datagrams over point-to-point links. Iconic. πŸ‘‘

PPP also has the Compression Control Protocol, which lets you negotiate and use compression protocols over PPP-encapsulated links. Super useful, very organised πŸ“

Now, Microsoft Point to Point Encryption (MPPE) is a way of representing PPP packets in encrypted form. It uses the RSA RC4 algorithm to keep data confidential πŸ”’ The length of the session key used to initialise the encryption tables can be negotiated, and MPPE supports 40-bit, 56-bit and 128-bit session keys. The keys change a lot: how often depends on the options you negotiated, and it may be every single packet 😭 MPPE gets negotiated within option 18 in the Compression Control Protocol.

This document explains how to derive the initial MPPE session keys from a bunch of different credential types. It's expected to get updated whenever Microsoft comes up with a new key derivation method for MPPE, because its whole purpose is to be an open, easy-to-find reference for third parties who want to interoperate with Microsoft products 🫢

MPPE itself (the negotiation protocol, the encryption details, and the algorithm for changing session keys mid-session) lives in RFC 3078. Go check that one too πŸ¦‹

Table of Contents πŸ“š

1.  Specification of Requirements ............................... 2
2.  Deriving Session Keys from MS-CHAP Credentials .............. 2
2.1.  Generating 40-bit Session Keys ............................ 3
2.2.  Generating 56-bit Session Keys ............................ 3
2.3.  Generating 128-bit Session Keys ........................... 4
2.4.  Key Derivation Functions .................................. 5
2.5.  Sample Key Derivations .................................... 6
2.5.1.  Sample 40-bit Key Derivation ............................ 6
2.5.2.  Sample 56-bit Key Derivation ............................ 6
2.5.3.  Sample 128-bit Key Derivation ........................... 7
3.  Deriving Session Keys from MS-CHAP-2 Credentials ............ 7
3.1.  Generating 40-bit Session Keys ............................ 8
3.2.  Generating 56-bit Session Keys ............................ 9
3.3.  Generating 128-bit Session Keys ...........................10
3.4.  Key Derivation Functions ..................................11
3.5.  Sample Key Derivations ....................................13
3.5.1.  Sample 40-bit Key Derivation ............................13
3.5.2.  Sample 56-bit Key Derivation ............................14
3.5.3.  Sample 128-bit Key Derivation ...........................15
4.  Deriving MPPE Session Keys from TLS Session Keys ............16
4.1.  Generating 40-bit Session Keys ............................16
4.2.  Generating 56-bit Session Keys ............................17
4.3.  Generating 128-bit Session Keys ...........................17
5.  Security Considerations .....................................18
5.1.  MS-CHAP Credentials .......................................18
5.2.  EAP-TLS Credentials .......................................19
6.  References ..................................................19
7.  Acknowledgements ............................................20
8.  Author's Address ............................................20
9.  Full Copyright Statement ....................................21

1. Specification of Requirements ✨

Quick vibe check on the key words: in this document, the key words "MAY", "MUST", "MUST NOT", "optional", "recommended", "SHOULD", and "SHOULD NOT" are to be read as described in [6]. πŸ“– Those capitalised ones are the strict-ish ones, so keep an eye out for them πŸ‘€

2. Deriving Session Keys from MS-CHAP Credentials πŸ”‘

The Microsoft Challenge-Handshake Authentication Protocol (MS-CHAP-1) [2] is a Microsoft-proprietary PPP [1] authentication protocol. It gives LAN-based users the functionality they're already used to, while bringing in the encryption and hashing algorithms that Windows networks use πŸ’»

The next sections walk through how to derive the initial session keys (40-, 56- and 128-bit) from MS-CHAP-1 credentials. Let's go πŸš€

Implementation Note πŸ“

The initial session key in both directions is derived from the credentials of the peer that initiated the call, and the challenge used (if there is one) is the challenge from the first authentication. This holds for both unilateral and bilateral authentication, and for each link in a multilink bundle. In the multi-chassis multilink case, implementations are responsible for making sure the correct keys get generated on all participating machines. Big responsibility energy πŸ‘‘

2.1. Generating 40-bit Session Keys 🌸

MPPE uses a derivative of the peer's LAN Manager password as the 40-bit session key used to initialise the RC4 encryption tables.

First, you obfuscate the peer's password using the LmPasswordHash() function (described in [2]). Then the first 8 octets of the result become the basis for the session key, generated like this:

/*
* PasswordHash is the basis for the session key
* SessionKey is a copy of PasswordHash and is the generative session key
* 8 is the length (in octets) of the key to be generated.
*
*/
Get_Key(PasswordHash, SessionKey, 8)

/*
* The effective length of the key is reduced to 40 bits by
* replacing the first three bytes as follows:
*/
SessionKey[0] = 0xd1 ;
SessionKey[1] = 0x26 ;
SessionKey[2] = 0x9e ;

2.2. Generating 56-bit Session Keys πŸ“

MPPE uses a derivative of the peer's LAN Manager password as the 56-bit session key used to initialise the RC4 encryption tables.

Same start as before: obfuscate the peer's password using the LmPasswordHash() function (described in [2]), and the first 8 octets of the result are the basis for the session key, generated the following way:

/*
* PasswordHash is the basis for the session key
* SessionKey is a copy of PasswordHash and is the generative session key
* 8 is the length (in octets) of the key to be generated.
*
*/
Get_Key(PasswordHash, SessionKey, 8)

/*
* The effective length of the key is reduced to 56 bits by
* replacing the first byte as follows:
*/
SessionKey[0] = 0xd1 ;

2.3. Generating 128-bit Session Keys πŸ¦‹

For this one, MPPE uses a derivative of the peer's Windows NT password as the 128-bit session key used to initialise the encryption tables. Different password, different vibe πŸ’œ

First, obfuscate the peer's password using the NtPasswordHash() function described in [2]. The first 16 octets of that result then get hashed again using the MD4 algorithm. The first 16 octets of the second hash become the basis for the session key, generated like so:

/*
* Challenge (as described in [9]) is sent by the PPP authenticator
* during authentication and is 8 octets long.
* NtPasswordHashHash is the basis for the session key.
* On return, InitialSessionKey contains the initial session
* key to be used.
*/
Get_Start_Key(Challenge, NtPasswordHashHash, InitialSessionKey)

/*
* CurrentSessionKey is a copy of InitialSessionKey
* and is the generative session key.
* Length (in octets) of the key to generate is 16.
*
*/
Get_Key(InitialSessionKey, CurrentSessionKey, 16)

2.4. Key Derivation Functions πŸ§ͺ

These are the procedures used to derive the session key. Get ready for the pads 🫣

/*
 * Pads used in key derivation
 */

SHApad1[40] =
   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

SHApad2[40] =
   {0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
    0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
    0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
    0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2};

/*
 * SHAInit(), SHAUpdate() and SHAFinal() functions are an
 * implementation of Secure Hash Algorithm (SHA-1) [7]. These are
 * available in public domain or can be licensed from
 * RSA Data Security, Inc.
 *
 * 1) InitialSessionKey is 8 octets long for 56- and 40-bit
 *    session keys, 16 octets long for 128 bit session keys.
 * 2) CurrentSessionKey is same as InitialSessionKey when this
 *    routine is called for the first time for the session.
 */

Get_Key(
IN     InitialSessionKey,
IN/OUT CurrentSessionKey
IN     LengthOfDesiredKey )
{
   SHAInit(Context)
   SHAUpdate(Context, InitialSessionKey, LengthOfDesiredKey)
   SHAUpdate(Context, SHAPad1, 40)
   SHAUpdate(Context, CurrentSessionKey, LengthOfDesiredKey)
   SHAUpdate(Context, SHAPad2, 40)
   SHAFinal(Context, Digest)
   memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey)
}

Get_Start_Key(
IN  Challenge,
IN  NtPasswordHashHash,
OUT InitialSessionKey)
{
   SHAInit(Context)
   SHAUpdate(Context, NtPasswordHashHash, 16)
   SHAUpdate(Context, NtPasswordHashHash, 16)
   SHAUpdate(Context, Challenge, 8)
   SHAFinal(Context, Digest)
   memcpy(InitialSessionKey, Digest, 16)
}

2.5. Sample Key Derivations πŸŽ€

Time for the worked examples! These illustrate 40-, 56- and 128-bit key derivations. All intermediate values are in hexadecimal, so hex lovers, this one's for you 🀍

2.5.1. Sample 40-bit Key Derivation πŸ’–

Initial Values
   Password = "clientPass"

Step 1: LmPasswordHash(Password, PasswordHash)
   PasswordHash = 76 a1 52 93 60 96 d7 83 0e 23 90 22 74 04 af d2

Step 2: Copy PasswordHash to SessionKey
   SessionKey = 76 a1 52 93 60 96 d7 83 0e 23 90 22 74 04 af d2

Step 3: GetKey(PasswordHash, SessionKey, 8)
   SessionKey = d8 08 01 53 8c ec 4a 08

Step 4: Reduce the effective key length to 40 bits
   SessionKey = d1 26 9e 53 8c ec 4a 08

2.5.2. Sample 56-bit Key Derivation 🌈

Initial Values
   Password = "clientPass"

Step 1: LmPasswordHash(Password, PasswordHash)
   PasswordHash = 76 a1 52 93 60 96 d7 83 0e 23 90 22 74 04 af d2

Step 2: Copy PasswordHash to SessionKey
   SessionKey = 76 a1 52 93 60 96 d7 83 0e 23 90 22 74 04 af d2

Step 3: GetKey(PasswordHash, SessionKey, 8)
   SessionKey = d8 08 01 53 8c ec 4a 08

Step 4: Reduce the effective key length to 56 bits
   SessionKey = d1 08 01 53 8c ec 4a 08

2.5.3. Sample 128-bit Key Derivation ⭐

Initial Values
   Password = "clientPass"
   Challenge = 10 2d b5 df 08 5d 30 41

Step 1: NtPasswordHash(Password, PasswordHash)
   PasswordHash = 44 eb ba 8d 53 12 b8 d6 11 47 44 11 f5 69 89 ae

Step 2: PasswordHashHash = MD4(PasswordHash)
   PasswordHashHash = 41 c0 0c 58 4b d2 d9 1c 40 17 a2 a1 2f a5 9f 3f

Step 3: GetStartKey(Challenge, PasswordHashHash, InitialSessionKey)
   InitialSessionKey = a8 94 78 50 cf c0 ac ca d1 78 9f b6 2d dc dd b0

Step 4: Copy InitialSessionKey to CurrentSessionKey
   CurrentSessionKey = a8 94 78 50 cf c0 ac c1 d1 78 9f b6 2d dc dd b0

Step 5: GetKey(InitialSessionKey, CurrentSessionKey, 16)
   CurrentSessionKey = 59 d1 59 bc 09 f7 6f 1d a2 a8 6a 28 ff ec 0b 1e

3. Deriving Session Keys from MS-CHAP-2 Credentials ✨

okay so, MS-CHAP-2 [8] is Microsoft's own PPP authentication protocol πŸ’… It gives LAN-style users the experience they're used to, and it plugs into the encryption and hashing algorithms that run on Windows networks. It's giving familiar πŸ–₯οΈπŸ”

Here's the tea 🍡 40-bit, 56-bit and 128-bit keys all come from the exact same algorithm, using the authenticating peer's Windows NT password. The only real difference is how long the keys are and how strong they are. 40- and 56-bit keys are 8 octets long, and 128-bit keys are 16 octets long πŸŽ€ Each session also gets a separate key for the send direction and the receive direction, so they never share one 🫢

Implementation Note

The initial session keys in both directions come from the credentials of the peer that started the call, and the challenges used are the ones from the first authentication. This goes for every link in a multilink bundle too 🌈 In the multi-chassis multilink case, implementations are responsible for making sure the correct keys get generated on all participating machines πŸ’œ

3.1. Generating 40-bit Session Keys

bestie, this is the 40-bit version πŸ¦‹ MS-CHAP-2 is in play, and the starting MPPE session keys come from the peer's Windows NT password.

First up, we scramble the peer's password with the NtPasswordHash() function, which is described in [8] 🧁

NtPasswordHash(Password, PasswordHash)

Then we take the first 16 octets of that result and hash them again with MD4 πŸ”

PasswordHashHash = md4(PasswordHash)

Next, the first 16 octets of this second hash get combined with the NT-Response field from the MS-CHAP-2 Response packet [8]. Together they make the master session key πŸ‘‘

GetMasterKey(PasswordHashHash, NtResponse, MasterKey)

Once the master key exists, we use it to make two 40-bit session keys, one for sending and one for receiving ✨

GetAsymmetricStartKey(MasterKey, MasterSendKey, 8, TRUE, TRUE)
GetAsymmetricStartKey(MasterKey, MasterReceiveKey, 8, FALSE, TRUE)

Plot twist πŸ™ˆ The master session keys are never used to encrypt or decrypt any data. They only exist to derive the transient session keys. To get those, we call GetNewKeyFromSHA() (described in [3]) πŸ¦„

GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 8, SendSessionKey)
GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 8,
                                               ReceiveSessionKey)

Now the effective strength of both keys gets lowered on purpose by setting the first three octets to known constants πŸ™ƒ

SendSessionKey[0] = ReceiveSessionKey[0] = 0xd1
SendSessionKey[1] = ReceiveSessionKey[1] = 0x26
SendSessionKey[2] = ReceiveSessionKey[2] = 0x9e

Last step: we initialize the RC4 tables with the new session keys 🌸

rc4_key(SendRC4key, 8, SendSessionKey)
rc4_key(ReceiveRC4key, 8, ReceiveSessionKey)

3.2. Generating 56-bit Session Keys

this one is the 56-bit version, and it's giving the same vibes as 40-bit with one tiny twist πŸŽ€ Once again MS-CHAP-2 is the authentication, and the starting MPPE session keys come from the peer's Windows NT password.

Same opening move: scramble the password with NtPasswordHash() as described in [8] 🧼

NtPasswordHash(Password, PasswordHash)

Then MD4 the first 16 octets of that result again πŸ“

PasswordHashHash = md4(PasswordHash)

Next, the first 16 octets of this second hash team up with the NT-Response field from the MS-CHAP-2 Response packet [8] to form the master session key πŸ‘‘

GetMasterKey(PasswordHashHash, NtResponse, MasterKey)

From the master key, we derive two 56-bit session keys, one for sending and one for receiving πŸ’–

GetAsymmetricStartKey(MasterKey, MasterSendKey, 8, TRUE, TRUE)
GetAsymmetricStartKey(MasterKey, MasterReceiveKey, 8, FALSE, TRUE)

Reminder, bestie 🫢 The master session keys never touch the actual data. They're only there to make the transient session keys. Those come from GetNewKeyFromSHA() (described in [3]) 🧚

GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 8, SendSessionKey)
GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 8,
                                               ReceiveSessionKey)

For the strength reduction, only the first octet gets set to a known constant. Much less than the 40-bit version, but still a slay πŸ’…

SendSessionKey[0] = ReceiveSessionKey[0] = 0xd1

Finally, the RC4 tables get initialized with the new session keys ✨

rc4_key(SendRC4key, 8, SendSessionKey)
rc4_key(ReceiveRC4key, 8, ReceiveSessionKey)

3.3. Generating 128-bit Session Keys

and now the 128-bit version, which is the full-strength glow-up 🌈 MS-CHAP-2 is the authentication again, and the starting MPPE session keys come from the peer's Windows NT password.

Scramble the password with NtPasswordHash() as described in [8] 🫧

NtPasswordHash(Password, PasswordHash)

MD4 the first 16 octets of that result again 🌷

PasswordHashHash = md4(PasswordHash)

The first 16 octets of this second hash get combined with the NT-Response field from the MS-CHAP-2 Response packet [8] to make the master session key πŸ‘‘

GetMasterKey(PasswordHashHash, NtResponse, MasterKey)

This time we derive two 128-bit master session keys, one for sending and one for receiving πŸ’œ

GetAsymmetricStartKey(MasterKey, MasterSendKey, 16, TRUE, TRUE)
GetAsymmetricStartKey(MasterKey, MasterReceiveKey, 16, FALSE, TRUE)

Same rule as always πŸ™ˆ The master session keys never encrypt or decrypt data. They just make the transient session keys, which come from GetNewKeyFromSHA() (described in [3]) πŸ¦‹

GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 16, SendSessionKey)
GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 16,
                                                ReceiveSessionKey)

No strength reduction this time, so the keys stay at full length πŸ”₯ The last step is initializing the RC4 tables with the new session keys πŸŽ€

rc4_key(SendRC4key, 16, SendSessionKey)
rc4_key(ReceiveRC4key, 16, ReceiveSessionKey)

3.4. Key Derivation Functions

okay, this is the part where we go over the procedures used to derive the session key 🧠 Get ready for the pads and magic constants, bestie πŸͺ„

/*
 * Pads used in key derivation
 */

SHSpad1[40] =
   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

SHSpad2[40] =
   {0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
    0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
    0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
    0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2};

/*
 * "Magic" constants used in key derivations
 */

Magic1[27] =
   {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
    0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
    0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79};

Magic2[84] =
   {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
    0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
    0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
    0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
    0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
    0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
    0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
    0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
    0x6b, 0x65, 0x79, 0x2e};

Magic3[84] =
   {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
    0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
    0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
    0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
    0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
    0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
    0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
    0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
    0x6b, 0x65, 0x79, 0x2e};


   GetMasterKey(
   IN  16-octet  PasswordHashHash,
   IN  24-octet  NTResponse,
   OUT 16-octet  MasterKey )
   {
      20-octet Digest

      ZeroMemory(Digest, sizeof(Digest));

      /*
       * SHSInit(), SHSUpdate() and SHSFinal()
       * are an implementation of the Secure Hash Standard [7].
       */

      SHSInit(Context);
      SHSUpdate(Context, PasswordHashHash, 16);
      SHSUpdate(Context, NTResponse, 24);
      SHSUpdate(Context, Magic1, 27);
      SHSFinal(Context, Digest);

      MoveMemory(MasterKey, Digest, 16);
   }

   VOID
   GetAsymetricStartKey(
   IN   16-octet      MasterKey,
   OUT  8-to-16 octet SessionKey,
   IN   INTEGER       SessionKeyLength,
   IN   BOOLEAN       IsSend,
   IN   BOOLEAN       IsServer )
   {

      20-octet Digest;

      ZeroMemory(Digest, 20);

      if (IsSend) {
         if (IsServer) {
            s = Magic3
         } else {
            s = Magic2
         }
      } else {
         if (IsServer) {

            s = Magic2
         } else {
            s = Magic3
         }
      }

      /*
       * SHSInit(), SHSUpdate() and SHSFinal()
       * are an implementation of the Secure Hash Standard [7].
       */

      SHSInit(Context);
      SHSUpdate(Context, MasterKey, 16);
      SHSUpdate(Context, SHSpad1, 40);
      SHSUpdate(Context, s, 84);
      SHSUpdate(Context, SHSpad2, 40);
      SHSFinal(Context, Digest);

      MoveMemory(SessionKey, Digest, SessionKeyLength);
   }

3.5. Sample Key Derivations

next up are the worked examples 🧁 We walk through 40-, 56- and 128-bit key derivations, and every intermediate value is written in hexadecimal, so grab your calculator bestie πŸ”’

3.5.1. Sample 40-bit Key Derivation

Initial Values
   UserName = "User"
            =  55 73 65 72

   Password = "clientPass"
            = 63 00 6C 00 69 00 65 00 6E 00
              74 00 50 00 61 00 73 00 73 00

   AuthenticatorChallenge = 5B 5D 7C 7D 7B 3F 2F 3E 3C 2C
                            60 21 32 26 26 28
   PeerChallenge = 21 40 23 24 25 5E 26 2A 28 29 5F 2B 3A 33 7C 7E

   Challenge = D0 2E 43 86 BC E9 12 26

   NT-Response =
   82 30 9E CD 8D 70 8B 5E A0 8F AA 39 81 CD 83 54 42 33
   11 4A 3D 85 D6 DF

Step 1: NtPasswordHash(Password, PasswordHash)
   PasswordHash = 44 EB BA 8D 53 12 B8 D6 11 47 44 11 F5 69 89 AE

Step 2: PasswordHashHash = MD4(PasswordHash)
   PasswordHashHash = 41 C0 0C 58 4B D2 D9 1C 40 17 A2 A1 2F A5 9F 3F

Step 3: Derive the master key (GetMasterKey())
   MasterKey = FD EC E3 71 7A 8C 83 8C B3 88 E5 27 AE 3C DD 31

Step 4: Derive the master send session key (GetAsymmetricStartKey())
   SendStartKey40 = 8B 7C DC 14 9B 99 3A 1B

Step 5: Derive the initial send session key (GetNewKeyFromSHA())
   SendSessionKey40 = D1 26 9E C4 9F A6 2E 3E

Sample Encrypted Message
   rc4(SendSessionKey40, "test message") = 92 91 37 91 7E 58 03 D6
                                           68 D7 58 98

3.5.2. Sample 56-bit Key Derivation

Initial Values
   UserName = "User"
            =  55 73 65 72

   Password = "clientPass"
            = 63 00 6C 00 69 00 65 00 6E 00 74 00 50
              00 61 00 73 00 73 00

   AuthenticatorChallenge = 5B 5D 7C 7D 7B 3F 2F 3E 3C 2C
                            60 21 32 26 26 28
   PeerChallenge = 21 40 23 24 25 5E 26 2A 28 29 5F 2B 3A 33 7C 7E

   Challenge = D0 2E 43 86 BC E9 12 26

   NT-Response =
   82 30 9E CD 8D 70 8B 5E A0 8F AA 39 81 CD 83 54 42 33
   11 4A 3D 85 D6 DF

Okay, so we're picking up the 56-bit walkthrough right where we left off πŸ’…

Step 1: NtPasswordHash(Password, PasswordHash)

   PasswordHash = 44 EB BA 8D 53 12 B8 D6 11 47 44 11 F5 69 89 AE

Step 2: PasswordHashHash = MD4(PasswordHash)

   PasswordHashHash = 41 C0 0C 58 4B D2 D9 1C 40 17 A2 A1 2F A5 9F 3F

Step 3: Derive the master key (GetMasterKey())

   MasterKey = FD EC E3 71 7A 8C 83 8C B3 88 E5 27 AE 3C DD 31

Step 4: Derive the master send session key (GetAsymmetricStartKey())

   SendStartKey56 = 8B 7C DC 14 9B 99 3A 1B

Step 5: Derive the initial send session key (GetNewKeyFromSHA())

   SendSessionKey56 = D1 5C 00 C4 9F A6 2E 3E

Sample Encrypted Message

   rc4(SendSessionKey40, "test message") = 3F 10 68 33 FA 44 8D
                                           A8 42 BC 57 58

3.5.3. Sample 128-bit Key Derivation 🌸

Initial Values

   UserName = "User"
            =  55 73 65 72

   Password = "clientPass"
            = 63 00 6C 00 69 00 65 00 6E 00
              74 00 50 00 61 00 73 00 73 00

   AuthenticatorChallenge = 5B 5D 7C 7D 7B 3F 2F 3E 3C 2C
                            60 21 32 26 26 28

   PeerChallenge = 21 40 23 24 25 5E 26 2A 28 29 5F 2B 3A 33 7C 7E

   Challenge = D0 2E 43 86 BC E9 12 26

   NT-Response =
   82 30 9E CD 8D 70 8B 5E A0 8F AA 39 81 CD 83 54 42 33
   11 4A 3D 85 D6 DF

Step 1: NtPasswordHash(Password, PasswordHash)

   PasswordHash = 44 EB BA 8D 53 12 B8 D6 11 47 44 11 F5 69 89 AE

Step 2: PasswordHashHash = MD4(PasswordHash)

   PasswordHashHash = 41 C0 0C 58 4B D2 D9 1C 40 17 A2 A1 2F A5 9F 3F

Step 2: Derive the master key (GetMasterKey())

   MasterKey = FD EC E3 71 7A 8C 83 8C B3 88 E5 27 AE 3C DD 31

Step 3: Derive the send master session key (GetAsymmetricStartKey())

   SendStartKey128 = 8B 7C DC 14 9B 99 3A 1B A1 18 CB 15 3F 56 DC CB

Step 4: Derive the initial send session key (GetNewKeyFromSHA())

   SendSessionKey128 = 40 5C B2 24 7A 79 56 E6 E2 11 00 7A E2 7B 22 D4

Sample Encrypted Message

  rc4(SendSessionKey128, "test message") = 81 84 83 17 DF 68
                                           84 62 72 FB 5A BE

4. Deriving MPPE Session Keys from TLS Session Keys ✨

So, EAP [10] is a PPP extension that lets you plug in extra authentication methods, and TLS [11] gives you mutual authentication, integrity-protected ciphersuite negotiation, and key exchange between two endpoints. EAP-TLS [12] is the EAP authentication type that brings TLS into the PPP authentication framework πŸ¦‹ The next sections cover how to derive the initial session keys from TLS session keys. The 56-, 40- and 128-bit keys all use the same algorithm, and the only real difference is their length and effective strength: the 56- and 40-bit keys are 8 octets long, while the 128-bit keys are 16 octets. Separate keys get derived for the send and receive directions of the session.

4.1. Generating 40-bit Session Keys πŸ”‘

When MPPE is used together with EAP-TLS authentication, the TLS master secret is used as the master session key.

The algorithm for deriving the asymmetrical master session keys from the TLS master secret is described in [12]. The master session keys are never used to encrypt or decrypt data. They're only used in deriving the transient session keys, so they're basically the behind-the-scenes VIPs πŸ‘‘

Implementation Note

If the asymmetrical master keys are less than 8 octets in length, they MUST be padded on the left with zeroes before being used to derive the initial transient session keys. Conversely, if the asymmetrical master keys are more than 8 octets in length, they must be truncated to 8 octets before being used to derive the initial transient session keys.

The initial transient session keys come from calling the function GetNewKeyFromSHA() (described in [3]):

GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 8, SendSessionKey)
GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 8,
ReceiveSessionKey)

Next, we reduce the effective strength of both keys by setting the first three octets to known constants πŸ₯€

   SendSessionKey[0] = ReceiveSessionKey[0] = 0xD1
   SendSessionKey[1] = ReceiveSessionKey[1] = 0x26
   SendSessionKey[2] = ReceiveSessionKey[2] = 0x9E

Finally, the RC4 tables get initialized using the new session keys:

   rc4_key(SendRC4key, 8, SendSessionKey)
   rc4_key(ReceiveRC4key, 8, ReceiveSessionKey)

4.2. Generating 56-bit Session Keys 🌈

When MPPE is used together with EAP-TLS authentication, the TLS master secret is used as the master session key.

The algorithm for deriving the asymmetrical master session keys from the TLS master secret is described in [12]. The master session keys are never used to encrypt or decrypt data. They're only used in deriving the transient session keys, so they stay backstage πŸŽ€

Implementation Note

If the asymmetrical master keys are less than 8 octets in length, they MUST be padded on the left with zeroes before being used to derive the initial transient session keys. Conversely, if the asymmetrical master keys are more than 8 octets in length, they must be truncated to 8 octets before being used to derive the initial transient session keys.

The initial transient session keys come from calling the function GetNewKeyFromSHA() (described in [3]):

GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 8, SendSessionKey)
GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 8,
ReceiveSessionKey)

Next, the effective strength of both keys gets reduced by setting the initial octet to a known constant:

   SendSessionKey[0] = ReceiveSessionKey[0] = 0xD1

Finally, the RC4 tables get initialized using the new session keys:

   rc4_key(SendRC4key, 8, SendSessionKey)
   rc4_key(ReceiveRC4key, 8, ReceiveSessionKey)

4.3. Generating 128-bit Session Keys πŸ’œ

When MPPE is used together with EAP-TLS authentication, the TLS master secret is used as the master session key.

The algorithm for deriving the asymmetrical master session keys from the TLS master secret is described in [12]. Note that the send key on one side is the receive key on the other 🫢

The master session keys are never used to encrypt or decrypt data. They're only used in deriving the transient session keys.

Implementation Note

If the asymmetrical master keys are less than 16 octets in length, they MUST be padded on the left with zeroes before being used to derive the initial transient session keys. Conversely, if the asymmetrical master keys are more than 16 octets in length, they must be truncated to 16 octets before being used to derive the initial transient session keys.

The initial transient session keys come from calling the function GetNewKeyFromSHA() (described in [3]):

GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 16, SendSessionKey)
GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 16,
ReceiveSessionKey)

Finally, the RC4 tables get initialized using the new session keys:

   rc4_key(SendRC4key, 16, SendSessionKey)
   rc4_key(ReceiveRC4key, 16, ReceiveSessionKey)

5. Security Considerations πŸ”₯

5.1. MS-CHAP Credentials πŸ™ˆ

Because of the way 40-bit keys are derived from MS-CHAP-1 credentials, the initial 40-bit session key will be identical in every session established under the same peer credentials. For this reason, and because RC4 with a 40-bit key length is believed to be a relatively weak cipher, peers SHOULD NOT use 40-bit keys derived from the LAN Manager password hash (as described above) if it can be avoided. That's a big no-no, bestie πŸ’€

Since the MPPE session keys are derived from user passwords (in the MS-CHAP-1 and MS-CHAP-2 cases), care should be taken to pick strong passwords and to change them frequently. Main character password energy only πŸ”

5.2. EAP-TLS Credentials ✨

The strength of the session keys depends on the security of the TLS protocol.

The EAP server may be on a separate machine from the PPP authenticator. If that's the case, adequate care must be taken when transmitting the EAP-TLS master keys to the authenticator 😭

6. References πŸ“š

  • [1] Simpson, W., "The Point-to-Point Protocol (PPP)", STD 51, RFC 1661, July 1994.
  • [2] Zorn, G. and S. Cobb, "Microsoft PPP CHAP Extensions", RFC 2433, October 1998.
  • [3] Pall, G. and G. Zorn, "Microsoft Point-to-Point Encryption (MPPE) RFC 3078, March 2001.
  • [4] 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
  • [5] Pall, G., "Microsoft Point-to-Point Compression (MPPC) Protocol", RFC 2118, March 1997.
  • [6] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
  • [7] "Secure Hash Standard", Federal Information Processing Standards Publication 180-1, National Institute of Standards and Technology, April 1995.
  • [8] Zorn, G., "Microsoft PPP CHAP Extensions, Version 2", RFC 2759, January 2000.
  • [9] Simpson, W., "PPP Challenge Handshake Authentication Protocol (CHAP)", RFC 1994, August 1996.
  • [10] Blunk, L. and J. Vollbrecht, "PPP Extensible Authentication Protocol (EAP)", RFC 2284, March 1998.
  • [11] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0", RFC 2246, January 1999.
  • [12] Aboba, B. and D. Simon, "PPP EAP TLS Authentication Protocol", RFC 2716, October 1999.

7. Acknowledgements 🫢

Anthony Bell, Richard B. Ward, Terence Spies and Thomas Dimitri, all of Microsoft Corporation, made significant contributions to the design and development of MPPE. Absolute legends πŸ‘‘

Additional thanks to Robert Friend, Joe Davies, Jody Terrill, Archie Cobbs, Mark Deuser, Vijay Baliga, Brad Robel-Forrest and Jeff Haag for useful feedback πŸ’–

The technical portions of this memo were completed while the author was employed by Microsoft Corporation.

8. Author's Address πŸ’Œ

Questions about this memo can also be directed to:

   Glen Zorn
   cisco Systems
   500 108th Avenue N.E.
   Suite 500
   Bellevue, Washington 98004
   USA

   Phone: +1 425 438 8218
   FAX:   +1 425 438 1848
   EMail: gwz@cisco.com

Copyright (C) The Internet Society (2001). 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.

Acknowledgement ✨

okay so the RFC Editor team, the folks who keep all these documents tidy and published, are currently being funded by the Internet Society 🫢 like, the ISOC is literally the one keeping the lights on for them rn πŸ’– and "currently" is doing a lot of work there, so it's not forever, just the current arrangement πŸ’… iykyk 🌸

πŸ“¦ where this page comes from