//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: hex.cpp // // Contents: hex encode/decode implementation // //--------------------------------------------------------------------------- #include #include #include #include #include "pkifmt.h" #include // must be included after dbgdef.h __inline BOOL _IsWhiteSpaceChar( TCHAR ch) { return( TEXT(' ') == ch || TEXT('\t') == ch || TEXT('\r') == ch); } DWORD _DigToHex( IN TCHAR ch, OUT BYTE *pb) { BYTE b; DWORD dwErr = ERROR_SUCCESS; if (!_istxdigit(ch)) { dwErr = ERROR_INVALID_DATA; #if DBG DbgPrintf(DBG_SS_TRACE, "bad hex data: %02x\n", ch); #endif //DBG goto BadHexDataError; } if (_istdigit(ch)) { b = ch - TEXT('0'); } else if (_istupper(ch)) { b = ch - TEXT('A') + 10; } else { b = ch - TEXT('a') + 10; } *pb = b; ErrorReturn: return (dwErr); SET_ERROR(BadHexDataError, dwErr) } __inline BOOL _IsSkipChar( TCHAR ch) { return( TEXT(' ') == ch || TEXT('\t') == ch || TEXT('\r') == ch || TEXT('\n') == ch || TEXT(',') == ch || TEXT('-') == ch); } DWORD _HexDecodeSimple( IN TCHAR const *pchIn, IN DWORD cchIn, OPTIONAL OUT BYTE *pbOut, IN OUT DWORD *pcbOut) { DWORD dwErr; TCHAR const *pch = pchIn; TCHAR const *pchEnd; DWORD cb = 0; BOOL fOverFlow = FALSE; pchEnd = &pchIn[cchIn]; while (pch < pchEnd) { BYTE blo; BYTE bhi; if (_IsSkipChar(*pch)) { pch++; continue; } dwErr = _DigToHex(*pch, &bhi); if (ERROR_SUCCESS != dwErr) { goto _DigToHexError; } pch++; if (pch >= pchEnd) { dwErr = ERROR_INVALID_DATA; goto BadHexDataError; } dwErr = _DigToHex(*pch, &blo); if (ERROR_SUCCESS != dwErr) { goto _DigToHexError; } pch++; if (NULL != pbOut) { if (cb >= *pcbOut) { fOverFlow = TRUE; pbOut = NULL; } else { *pbOut++ = blo | (bhi << 4); } } cb++; } *pcbOut = cb; if (fOverFlow) { dwErr = ERROR_MORE_DATA; goto MoreDataError; } ErrorReturn: return (dwErr); SET_ERROR(MoreDataError, dwErr) SET_ERROR(BadHexDataError, dwErr) TRACE_ERROR(_DigToHexError) } DWORD _HexParse( IN OUT TCHAR const **ppch, IN TCHAR const *pchEnd, IN DWORD cDigitMin, IN DWORD cDigitMax, OUT DWORD *pdwValue) { DWORD dwErr = ERROR_SUCCESS; TCHAR const *pch = *ppch; DWORD Value = 0; DWORD cDigit = 0; BYTE b; *pdwValue = 0; while (pch < pchEnd && cDigit <= cDigitMax) { //printf("HexParse %u/%u-%u, ch=%02x\n", cDigit, cDigitMin, cDigitMax, *pch); dwErr = _DigToHex(*pch, &b); if (ERROR_SUCCESS != dwErr) { break; } Value = b | (Value << 4); pch++; cDigit++; } //printf("HexParse %u/%u-%u, val=%x\n", cDigit, cDigitMin, cDigitMax, Value); if (cDigit < cDigitMin || cDigit > cDigitMax) { dwErr = ERROR_INVALID_DATA; goto BadHexDataError; } *pdwValue = Value; *ppch = pch; dwErr = ERROR_SUCCESS; ErrorReturn: return (dwErr); SET_ERROR(BadHexDataError, dwErr) } #define HS_ADDRESS 0 #define HS_HEXDATA 1 #define HS_ASCIIDATA 2 #define HS_NEWLINE 3 DWORD _HexDecodeComplex( IN TCHAR const *pchIn, IN DWORD cchIn, IN DWORD Flags, OPTIONAL OUT BYTE *pbOut, IN OUT DWORD *pcbOut) { TCHAR const *pch = pchIn; TCHAR const *pchEnd; DWORD cb = 0; DWORD dwErr; DWORD LastAddress = 0; DWORD Address; DWORD i; BOOL fOverFlow = FALSE; BOOL fPartialLastLine = FALSE; int *pStateBase; int *pState; int s_aASCIIADDRState[] = { HS_ADDRESS, HS_HEXDATA, HS_ASCIIDATA, HS_NEWLINE }; int s_aASCIIState[] = { HS_HEXDATA, HS_ASCIIDATA, HS_NEWLINE }; int s_aADDRState[] = { HS_ADDRESS, HS_HEXDATA, HS_NEWLINE }; switch (Flags) { case CRYPT_STRING_HEXASCII: // 5 pStateBase = s_aASCIIState; break; case CRYPT_STRING_HEXADDR: // 0xa pStateBase = s_aADDRState; break; case CRYPT_STRING_HEXASCIIADDR: // 0xb pStateBase = s_aASCIIADDRState; break; default: dwErr = ERROR_INVALID_DATA; //hr = E_INVALIDARG; goto FlagsError; } pState = pStateBase; pchEnd = &pchIn[cchIn]; while (pch < pchEnd) { //printf("f=%x: *pState: %u ch=%02x\n", Flags, *pState, *pch); switch (*pState++) { case HS_ADDRESS: // decode 4 to 8 digit address: while (pch < pchEnd && _IsWhiteSpaceChar(*pch)) { pch++; } if (pch >= pchEnd) { continue; // Done: no more data } dwErr = _HexParse(&pch, pchEnd, 4, 8, &Address); if (ERROR_SUCCESS != dwErr) { goto _HexParseError; } //printf("f=%x: Address: %x\n", Flags, Address); if (!fPartialLastLine && 0 != LastAddress && LastAddress + 16 != Address) { dwErr = ERROR_INVALID_DATA; goto BadHexDataError; } LastAddress = Address; break; case HS_HEXDATA: // decode up to 16 bytes of hex data for (i = 0; i < 16; i++) { DWORD Data; // decode 2 digit byte value: while (pch < pchEnd && _IsSkipChar(*pch)) { pch++; } if (pch >= pchEnd) { break; // Done: no more data } if (fPartialLastLine) { //printf("f=%x: fPartialLastLine extra data: %02x\n", Flags, *pch); dwErr = ERROR_INVALID_DATA; goto DataAfterEndError; } dwErr = _HexParse(&pch, pchEnd, 2, 2, &Data); if (ERROR_SUCCESS != dwErr) { // Must be a partial last line. The only additional // data should be an optional partial ascii display on // the right, a newline, and possibly one more address // line. //printf("f=%x: fPartialLastLine = TRUE: %02x\n", Flags, *pch); fPartialLastLine = TRUE; break; } //printf("f=%x: Data[%u]: %02x\n", Flags, i, Data); if (NULL != pbOut) { if (cb >= *pcbOut) { fOverFlow = TRUE; pbOut = NULL; } else { *pbOut++ = (BYTE) Data; } } cb++; } break; case HS_ASCIIDATA: // skip up to 16 non-whitespace characters while (pch < pchEnd && _IsWhiteSpaceChar(*pch)) { pch++; } for (i = 0; i < 16; i++) { if (pch >= pchEnd || TEXT(' ') > *pch || TEXT('~') < *pch) { break; } //printf("f=%x: Ascii[%u]: %c\n", Flags, i, *pch); pch++; } break; case HS_NEWLINE: // skip whitespace characters and a newline while (pch < pchEnd && _IsWhiteSpaceChar(*pch)) { //printf("f=%x: NL skip: %02x\n", Flags, *pch); pch++; } //printf("f=%x: NL: %02x\n", Flags, *pch); if (pch >= pchEnd) { continue; // Done: no more data } if (TEXT('\n') != *pch) { //printf("f=%x: Extra Data: %02x\n", Flags, *pch); dwErr = ERROR_INVALID_DATA; goto ExtraDataOnLineError; } //printf("f=%x: NewLine\n", Flags); pch++; pState = pStateBase; break; default: assert(!"Bad *pState"); } } *pcbOut = cb; if (fOverFlow) { dwErr = ERROR_MORE_DATA; goto MoreDataError; } dwErr = ERROR_SUCCESS; ErrorReturn: return(dwErr); SET_ERROR(MoreDataError, dwErr) SET_ERROR(ExtraDataOnLineError, dwErr) SET_ERROR(BadHexDataError, dwErr) TRACE_ERROR(_HexParseError) SET_ERROR(DataAfterEndError, dwErr) SET_ERROR(FlagsError, dwErr) } DWORD HexDecode( IN TCHAR const *pchIn, IN DWORD cchIn, IN DWORD Flags, OPTIONAL OUT BYTE *pbOut, IN OUT DWORD *pcbOut) { DWORD dwErr; if (CRYPT_STRING_HEX == Flags) { dwErr = _HexDecodeSimple(pchIn, cchIn, pbOut, pcbOut); if (ERROR_SUCCESS != dwErr) { #if DBG //skip ERROR_INVALID_DATA dbg print if (ERROR_INVALID_DATA == dwErr) { SetLastError(dwErr); goto ErrorReturn; } #endif goto _HexDecodeSimpleError; } } else { dwErr = _HexDecodeComplex(pchIn, cchIn, Flags, pbOut, pcbOut); if (ERROR_SUCCESS != dwErr) { #if DBG //skip ERROR_INVALID_DATA dbg print if (ERROR_INVALID_DATA == dwErr) { SetLastError(dwErr); goto ErrorReturn; } #endif goto _HexDecodeComplexError; } } ErrorReturn: return(dwErr); TRACE_ERROR(_HexDecodeSimpleError) TRACE_ERROR(_HexDecodeComplexError) } TCHAR _IsPrintableChar(TCHAR ch) { if (ch < TEXT(' ') || ch > TEXT('~')) { ch = TEXT('.'); } return(ch); } // Encode a BYTE array into text as a hex dump. // Use CR-LF pairs for line breaks, unless CRYPT_STRING_NOCR is set. // Do not '\0' terminate the text string -- that's handled by the caller. DWORD HexEncode( IN BYTE const *pbIn, IN DWORD cbIn, IN DWORD Flags, OPTIONAL OUT TCHAR *pchOut, IN OUT DWORD *pcchOut) { TCHAR const *pszsep; TCHAR const *psznl; DWORD r; DWORD i; DWORD cbremain; DWORD cchOut = 0; DWORD cch; TCHAR *pch = pchOut; DWORD dwErr = ERROR_MORE_DATA; BOOL fAscii = FALSE; BOOL fAddress = FALSE; TCHAR szAddress[32]; BOOL fNoCR = 0 != (CRYPT_STRING_NOCR & Flags); switch (~CRYPT_STRING_NOCR & Flags) { case CRYPT_STRING_HEX: break; case CRYPT_STRING_HEXASCII: fAscii = TRUE; break; case CRYPT_STRING_HEXADDR: fAddress = TRUE; break; case CRYPT_STRING_HEXASCIIADDR: fAscii = TRUE; fAddress = TRUE; break; default: dwErr = ERROR_INVALID_DATA; //hr = E_INVALIDARG; goto FlagsError; } for (r = 0; r < cbIn; r += 16) { DWORD iEnd; cbremain = cbIn - r; iEnd = min(cbremain, 16); for (i = 0; i < iEnd; i++) { psznl = TEXT(""); szAddress[0] = TEXT('\0'); pszsep = TEXT(" "); if ((i % 8) == 0) // 0 or 8 { pszsep = TEXT(" "); if (i == 0) // 0 { if (fAddress) { _stprintf(szAddress, TEXT("%04x"), r); } pszsep = TEXT("\t"); if (r != 0) // starting new line { psznl = fNoCR? TEXT("\n") : TEXT("\r\n"); pszsep = TEXT("\t"); } } } cch = _tcslen(psznl) + _tcslen(szAddress) + _tcslen(pszsep) + 2; if (NULL != pchOut) { if (cchOut + cch + 1 > *pcchOut) { goto MoreDataError; } _stprintf( pch, TEXT("%s%s%s%02x"), psznl, szAddress, pszsep, pbIn[r + i]); pch += cch; assert(TEXT('\0') == *pch); assert(pch == &pchOut[cchOut + cch]); } cchOut += cch; } if (fAscii && 0 != i) { cch = 3 + (16 - i)*3 + ((i <= 8)? 1 : 0); if (NULL != pchOut) { if (cchOut + cch + iEnd + 1 > *pcchOut) { goto MoreDataError; } _stprintf(pch, TEXT("%*s"), cch, TEXT("")); pch += cch; assert(TEXT('\0') == *pch); assert(pch == &pchOut[cchOut + cch]); for (i = 0; i < iEnd; i++) { *pch++ = _IsPrintableChar(pbIn[r + i]); assert(pch == &pchOut[cchOut + cch + i + 1]); } } cchOut += cch + iEnd; } } if (r != 0) { DWORD cchnl = fNoCR? 1 : 2; if (NULL != pchOut) { if (cchOut + cchnl > *pcchOut) { goto MoreDataError; } if (!fNoCR) { *pch++ = TEXT('\r'); } *pch++ = TEXT('\n'); assert(pch == &pchOut[cchOut + cchnl]); } cchOut += cchnl; } *pcchOut = cchOut; dwErr = ERROR_SUCCESS; ErrorReturn: return(dwErr); SET_ERROR(MoreDataError, dwErr) SET_ERROR(FlagsError, dwErr) }