|
|
//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: hex.cpp
//
// Contents: hex encode/decode implementation
//
//---------------------------------------------------------------------------
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <dbgdef.h>
#include "pkifmt.h"
#include <tchar.h> // 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 = ERROR_SUCCESS; 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) }
|