Creating MPPE Attributes in an EAP Extension
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 https://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 https://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;
}