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 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;
}

Comments (0)