When an EAP extension succesfully completes authentication, it can fill in the PPP_EAP_OUTPUT pUserAttributes field with MPPE key information for use by other networking components. For example, RAS can use those keys for data encryption, and WPA/WPA2 will use them for the 4-way handshake on an 802.11 link.
The PPP_EAP_OUTPUT pUserAttribute field points to an array of RAS_AUTH_ATTRIBUTE structures, terminated by an array element with raaType = raatMinimum. Documentation on the format of the RAS_AUTH_ATTRIBUTE structure can be found at http://msdn2.microsoft.com/en-gb/library/aa363535.aspx.
For MPPE Keys, there will be 2 elements in the array of RAS_AUTH_ATTRIBUTES: one for the send key and one for the receive key. For the MPPE Key attributes, the PVOID Value field of the RAS_AUTH_ATTRIBUTE will point to a structure that is described in http://msdn2.microsoft.com/en-us/library/aa363636.aspx. See the section labeled "eatVendorSpecific".
Below are some sample utility functions that can assist in the creation of the data structures that contain these keys.
struct RAS_AUTH_ATTRIBUTE_VALUE { BYTE VendorId[4]; // network byte order BYTE VendorType; BYTE Length; // number of bytes from VendorType to the end of the value BYTE VendorValue[1]; // Length-2 bytes long }; DWORD MyEapUtilAuthAttributeInsertVSA( OUT RAS_AUTH_ATTRIBUTE * pAttribute, IN DWORD VendorId, IN BYTE VendorType, IN PBYTE pVendorValue, IN BYTE cbVendorValue) // // Insert a vendor specific attribute into the location pointed to by pAttribute. // { DWORD dwResult = NO_ERROR; ASSERT(cbVendorValue < 254); pAttribute->raaType = raatVendorSpecific; pAttribute->dwLength = offsetof(struct RAS_AUTH_ATTRIBUTE_VALUE, VendorValue) + cbVendorValue; pAttribute->Value = LocalAlloc(LPTR, pAttribute->dwLength); if (pAttribute->Value == NULL) { dwResult = ERROR_OUTOFMEMORY; } else { struct RAS_AUTH_ATTRIBUTE_VALUE *pAttributeValue = (struct RAS_AUTH_ATTRIBUTE_VALUE *)pAttribute->Value; VendorId = htonl(VendorId); memcpy(&pAttributeValue->VendorId[0], &VendorId, sizeof(VendorId)); pAttributeValue->VendorType = VendorType; pAttributeValue->Length = 2 + cbVendorValue; // 1 byte for VendorType, 1 byte for Length, then cbVendorValue bytes of VendorValue memcpy(&pAttributeValue->VendorValue[0], pVendorValue, cbVendorValue ); } return dwResult; } RAS_AUTH_ATTRIBUTE * MyEapUtilAuthAttributeArrayAlloc( DWORD nAttrs) // // Allocate an array of sufficient size to hold the requested number // of authentication attributes. // { RAS_AUTH_ATTRIBUTE *pAttributes; nAttrs += 1; // Add one for the terminator pAttributes = (RAS_AUTH_ATTRIBUTE *)(LocalAlloc(LPTR, nAttrs * sizeof(RAS_AUTH_ATTRIBUTE))); if (pAttributes) { // Set the terminator array element pAttributes[nAttrs - 1].raaType = raatMinimum; } return pAttributes; } void MyEapUtilAuthAttributeArrayFree( RAS_AUTH_ATTRIBUTE *pAttributes) // // Free an array of authentication attribute values. // { if (pAttributes) { for (int i=0; pAttributes[i].raaType != raatMinimum; i++) { if (pAttributes[i].Value) { SecureZeroMemory(pAttributes[i].Value, pAttributes[i].dwLength); LocalFree(pAttributes[i].Value); pAttributes[i].Value = NULL; } } LocalFree(pAttributes); } } #define VENDOR_MICROSOFT 311 #define MS_VSA_MPPE_Send_Key 16 #define MS_VSA_MPPE_Recv_Key 17 #define MAX_MPPEKEY_LENGTH 32 // Format of the MPPE Key // Byte 1-2: Salt // Byte 3: Key Length (this is the length of the Actual Key and does not include Salt, Key Length and Padding fields) // Byte 4-35: Actual Key (Most access points require 32 byte keys for WPA) // Byte 35-50: Padding (required to make the length of the MPPE Key (not including the Salt) a multiple of 16 bytes) struct MPPEKey { BYTE Salt[2]; BYTE KeyLength; BYTE Key[MAX_MPPEKEY_LENGTH]; BYTE Padding[15]; }; DWORD MyEapUtilAuthAttributeInsertMPPEKeyVSA( OUT RAS_AUTH_ATTRIBUTE * pAttribute, IN BYTE VendorType, // MS_VSA_MPPE_Send_Key or MS_VSA_MPPE_Recv_Key IN BYTE const * const pKey, IN size_t cbKey) // // Insert an MPPEKey VSA into the location specified by pAttribute. // { DWORD dwResult; struct MPPEKey MPPEKey; if (cbKey > MAX_MPPEKEY_LENGTH) return ERROR_INVALID_PARAMETER; memset(&MPPEKey, 0, sizeof(MPPEKey)); MPPEKey.KeyLength = cbKey; memcpy(&MPPEKey.Key[0], pKey, cbKey); dwResult = MyEapUtilAuthAttributeInsertVSA(pAttribute, VENDOR_MICROSOFT, VendorType, (PBYTE)&MPPEKey, sizeof(MPPEKey) - MAX_MPPEKEY_LENGTH + cbKey); SecureZeroMemory(&MPPEKey, sizeof(MPPEKey)); return dwResult; } DWORD MyEapUtilCreateMPPEAuthAttributes( BYTE const * const pSendKey, size_t cbSendKey, BYTE const * const pRecvKey, size_t cbRecvKey, RAS_AUTH_ATTRIBUTE **ppSendRecvKeyAttr) // // Save the MPPE Send and Recv Session Keys as Auth Attributes // { DWORD dwResult; RAS_AUTH_ATTRIBUTE* pSendRecvKeyAttr; // Allocate an auth attribute array able to hold 2 attributes (send key and recv key) pSendRecvKeyAttr = MyEapUtilAuthAttributeArrayAlloc(2); if ( NULL == pSendRecvKeyAttr ) { dwResult = ERROR_OUTOFMEMORY; goto done; } // Add the send key to the attribute array at index 0 dwResult = MyEapUtilAuthAttributeInsertMPPEKeyVSA(&pSendRecvKeyAttr[0], MS_VSA_MPPE_Send_Key, pSendKey, cbSendKey); if (dwResult != NO_ERROR) goto done; // Add the receive key to the attribute array at index 1 dwResult = MyEapUtilAuthAttributeInsertMPPEKeyVSA(&pSendRecvKeyAttr[1], MS_VSA_MPPE_Recv_Key, pRecvKey, cbRecvKey); if (dwResult != NO_ERROR) goto done; done: if (dwResult != NO_ERROR) { // Free any resources MyEapUtilAuthAttributeArrayFree(pSendRecvKeyAttr); pSendRecvKeyAttr = NULL; } *ppSendRecvKeyAttr = pSendRecvKeyAttr; return dwResult; }