/*++ Copyright (c) Microsoft Corporation Description: Implementation of routines to calculate the signature given a series of piecemeal data blobs Module Name: xcalcsig.c --*/ #include "basedll.h" #pragma hdrstop #include #include // // Define the actual context structure // typedef struct { DWORD dwFlags; // Flags PBYTE pbTitleKey; // Per-title key XSHAHMAC_CONTEXT shactx; // SHA1 context } XCALCSIG_CONTEXT, *PXCALCSIG_CONTEXT; // // Define some macros to abstract memory management // #define XCalcSigAlloc(n) LocalAlloc(LMEM_FIXED, (n)) #define XCalcSigFree(p) LocalFree(p) // // Define some debug facilities to track invalid contexts // #if DBG #define XCALCSIG_FLAG_INVALID_CONTEXT (0x80000000) #endif // // Define the K padding size for SHA1-Hmac // #define HMAC_K_PADSIZE 64 // // Define the well-known XOR factors for Kipad and Kopad // #define HMAC_KI_XOR_FACTOR ((DWORD)0x36363636) #define HMAC_KO_XOR_FACTOR ((DWORD)0x5C5C5C5C) // // Function to initialize a piecemeal SHA1-Hmac evaluation // VOID WINAPI XShaHmacInitialize( IN PBYTE pbKey, IN DWORD cbKey, IN OUT XSHAHMAC_CONTEXT Shactx ) { HRESULT hr = S_OK; BYTE rgbKipad[HMAC_K_PADSIZE]; ULONG dwBlock; RIP_ON_NOT_TRUE("XShaHmacInitialize", (pbKey != NULL)); RIP_ON_NOT_TRUE("XShaHmacInitialize", (cbKey >= sizeof(DWORD))); RIP_ON_NOT_TRUE("XShaHmacInitialize", (Shactx != NULL)); // Shorten length if longer than our K padding if (cbKey > HMAC_K_PADSIZE) cbKey = HMAC_K_PADSIZE; // Build our Kipad memset(rgbKipad, 0, HMAC_K_PADSIZE); memcpy(rgbKipad, pbKey, cbKey); for (dwBlock = 0; dwBlock < (HMAC_K_PADSIZE/sizeof(DWORD)); dwBlock++) { ((DWORD*)rgbKipad)[dwBlock] ^= HMAC_KI_XOR_FACTOR; } // Initialize our SHA1 Hmac context XcSHAInit(Shactx); // Run our Kipad through this ... XcSHAUpdate(Shactx, rgbKipad, HMAC_K_PADSIZE); } // // Function to update the Hmac // VOID WINAPI XShaHmacUpdate( IN XSHAHMAC_CONTEXT Shactx, IN PBYTE pbData, IN DWORD cbData ) { RIP_ON_NOT_TRUE("XShaHmacUpdate", (Shactx != NULL)); RIP_ON_NOT_TRUE("XShaHmacUpdate", (pbData != NULL)); RIP_ON_NOT_TRUE("XShaHmacUpdate", (cbData > 0)); XcSHAUpdate(Shactx, pbData, cbData); } // // Function to calculate the final Hmac // VOID WINAPI XShaHmacComputeFinal( IN XSHAHMAC_CONTEXT Shactx, IN PBYTE pbKey, IN DWORD cbKey, OUT PBYTE pbHmac ) { HRESULT hr = S_OK; BYTE rgbKopad[HMAC_K_PADSIZE + A_SHA_DIGEST_LEN]; ULONG dwBlock; RIP_ON_NOT_TRUE("XShaHmacComputeFinal", (Shactx != NULL)); RIP_ON_NOT_TRUE("XShaHmacComputeFinal", (pbHmac != NULL)); // Shorten length if longer than our K padding if (cbKey > HMAC_K_PADSIZE) cbKey = HMAC_K_PADSIZE; // Build our Kipad memset(rgbKopad, 0, HMAC_K_PADSIZE); memcpy(rgbKopad, pbKey, cbKey); for (dwBlock = 0; dwBlock < (HMAC_K_PADSIZE/sizeof(DWORD)); dwBlock++) { ((DWORD*)rgbKopad)[dwBlock] ^= HMAC_KO_XOR_FACTOR; } // Finish off the hash, and place the result right // after the Kopad data XcSHAFinal(Shactx, rgbKopad + HMAC_K_PADSIZE); // Do another hash, now with the Kopad data XcSHAInit(Shactx); XcSHAUpdate(Shactx, rgbKopad, sizeof(rgbKopad)); XcSHAFinal(Shactx, pbHmac); } // // Function to make a signature non-roamable. The two // signature buffers must be different and must not overlap // BOOL XapiConvertSignatureToNonRoamable( PXCALCSIG_CONTEXT pCalcSig, PXCALCSIG_SIGNATURE psignatureRoamable, PXCALCSIG_SIGNATURE psignatureNonRoamable ) { // Compute the Keyed SHA Hmac XShaHmacInitialize((LPBYTE)(*XboxHDKey), XBOX_KEY_LENGTH, pCalcSig->shactx); XShaHmacUpdate(pCalcSig->shactx, psignatureRoamable->Signature, XCALCSIG_SIGNATURE_SIZE); XShaHmacComputeFinal(pCalcSig->shactx, (LPBYTE)(*XboxHDKey), XBOX_KEY_LENGTH, psignatureNonRoamable->Signature); return(TRUE); } // // Wrapper to XCalculateSignatureBeginEx() that does not take a // title ID parameter (uses the current title's ID implicitly) // HANDLE WINAPI XCalculateSignatureBegin( IN DWORD dwFlags ) { return XCalculateSignatureBeginEx(dwFlags, XeImageHeader()->Certificate->TitleID); } // // Function to begin the piecemeal process to calculate the // signature of a blob of data // // Arguments: // dwFlags - Optional flags. Currently defined flags are: // XCALCSIG_FLAG_NON_ROAMABLE - this forces the resulting // signature to be unusable on any XBox other than // the one on which the signature is computed. // dwAltTitleId - Alternate title id or the current title id // // Return Values: // On success, this function returns a HANDLE that can be used // in subsequent calls to calculate a signature. On failure, // function returns INVALID_HANDLE_VALUE, extended error // information can be retrieved using GetLastError(). // // Remarks: // On success, this fucntion allocates memory for the // returned HANDLE. The caller must explicitly call // XCalculateSignatureEnd to free the handle resources, // regardless of any intermediate failures. // HANDLE WINAPI XCalculateSignatureBeginEx( IN DWORD dwFlags, IN DWORD dwAltTitleId ) { PXCALCSIG_CONTEXT pCalcSig; PXBEIMAGE_CERTIFICATE Certificate = XeImageHeader()->Certificate; RIP_ON_NOT_TRUE("XCalculateSignatureBegin", ((dwFlags & (~XCALCSIG_FLAG_NON_ROAMABLE)) == 0)); // Allocate the context pCalcSig = XCalcSigAlloc(sizeof(XCALCSIG_CONTEXT)); if (!pCalcSig) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Error; } // Initialize pCalcSig->dwFlags = dwFlags; // // Get the per-title key // If dwAltTitleId is the current title's id, use XboxSignatureKey // Otherwise, try to user XboxAlternateSignatureKeys // if (Certificate->TitleID != dwAltTitleId) { int i; for (i = 0; i < ARRAYSIZE(Certificate->AlternateTitleIDs); i++) { if (0 == Certificate->AlternateTitleIDs[i]) { SetLastError(ERROR_ACCESS_DENIED); goto Error; } if (dwAltTitleId == Certificate->AlternateTitleIDs[i]) { break; } } if (i >= sizeof(Certificate->AlternateTitleIDs)) { SetLastError(ERROR_ACCESS_DENIED); goto Error; } ASSERT(i < XBEIMAGE_ALTERNATE_TITLE_ID_COUNT); pCalcSig->pbTitleKey = (PBYTE)XboxAlternateSignatureKeys[i]; } else { pCalcSig->pbTitleKey = (PBYTE)XboxSignatureKey; } // Initialize SHA Hmac XShaHmacInitialize(pCalcSig->pbTitleKey, XC_SYMMETRIC_KEY_SIZE, pCalcSig->shactx); // Return context as opaque handle return((HANDLE)pCalcSig); Error: return(INVALID_HANDLE_VALUE); } // // This function continues to to update the calculation // for the specified signature context handle. // // Arguments: // hCalcSig - the context to update // pbData - the next chunk of data to update with // cbData - the size of the data chunk // // Return Values: // This function returns ERROR_SUCCESS on success, and if this // function failed, an appropriate Win32 error code is returned. // Either case, XCalculateSignatureEnd must be called to free // any resources associated with the HANDLE. // DWORD WINAPI XCalculateSignatureUpdate( IN HANDLE hCalcSig, IN const BYTE *pbData, IN ULONG cbData ) { PXCALCSIG_CONTEXT pCalcSig = (PXCALCSIG_CONTEXT)hCalcSig; RIP_ON_NOT_TRUE("XCalculateSignatureUpdate", (pCalcSig != NULL)); RIP_ON_NOT_TRUE("XCalculateSignatureUpdate", (pbData != NULL)); #if DBG RIP_ON_NOT_TRUE("XCalculateSignatureUpdate", ((pCalcSig->dwFlags & XCALCSIG_FLAG_INVALID_CONTEXT) == 0)); #endif // Call update XShaHmacUpdate(pCalcSig->shactx, (PBYTE)pbData, cbData); return(ERROR_SUCCESS); } // // This function performs the last bit of precessing // required to calculate the signature, returns the // signature blob, and releases the context. // // Arguments: // hCalcSig - the context to close // Signature - the buffer to receive the final signature // // Return Values: // On success, this function returns ERROR_SUCCESS, and the // final computed signature is returned in signature. If this // function failed, an appropriate Win32 error code is returned. // // Remarks: // The context specified by hCalcSig will be released // regardless of whether this function call succeeded // or not. hCalcSig should not be used after this function // returns unless it is reinitialized by XCalculateSignatureBegin. // This function can be used to just free the context. In // this case, NULL should be specified for the Signature argument. // DWORD WINAPI XCalculateSignatureEnd( IN HANDLE hCalcSig, OUT PXCALCSIG_SIGNATURE pSignature ) { BOOL fResult = TRUE; PXCALCSIG_CONTEXT pCalcSig = (PXCALCSIG_CONTEXT)hCalcSig; XCALCSIG_SIGNATURE xcsSignature; BOOL fNonRoamable; RIP_ON_NOT_TRUE("XCalculateSignatureEnd", (pCalcSig != NULL)); #if DBG RIP_ON_NOT_TRUE("XCalculateSignatureEnd", ((pCalcSig->dwFlags & XCALCSIG_FLAG_INVALID_CONTEXT) == 0)); #endif fNonRoamable = ((pCalcSig->dwFlags & XCALCSIG_FLAG_NON_ROAMABLE) != 0); // Finish the calculation if (pSignature) { // Figure out the Hmac XShaHmacComputeFinal(pCalcSig->shactx, pCalcSig->pbTitleKey, XC_SYMMETRIC_KEY_SIZE, (fNonRoamable)?xcsSignature.Signature:pSignature->Signature); if (fNonRoamable) { fResult = XapiConvertSignatureToNonRoamable( pCalcSig, &xcsSignature, pSignature); } } // Well, free the context either case #if DBG pCalcSig->dwFlags |= XCALCSIG_FLAG_INVALID_CONTEXT; #endif XCalcSigFree(pCalcSig); return(fResult?ERROR_SUCCESS:GetLastError()); }