59 #define UPDATE_STRING "12 November 2010"
65 #define TRUE ( !FALSE )
89 #define EXIT_FAILURE 1
90 #define EXIT_SUCCESS ( !EXIT_FAILURE )
93 #define FILENAME_MAX 512
95 #if FILENAME_MAX < 128
97 #define FILENAME_MAX 512
105 #if ( defined( _WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) || \
106 defined( __WIN32__ ) )
117 #if defined( linux ) || defined( __linux__ ) || defined( sun ) || \
118 defined( __bsdi__ ) || defined( __FreeBSD__ ) || defined( __NetBSD__ ) || \
119 defined( __OpenBSD__ ) || defined( __hpux ) || defined( _M_XENIX ) || \
120 defined( __osf__ ) || defined( _AIX ) || defined( __MACH__ )
123 #if defined( linux ) || defined( __linux__ )
141 #ifdef _GUARDIAN_TARGET
142 #define __TANDEM_NSK__
151 #define min(a,b) ( ( a ) < ( b ) ? ( a ) : ( b ) )
156 #define byteToInt( x ) ( ( unsigned char ) ( x ) )
162 extern unsigned _stklen = 16384;
168 #pragma warning( disable: 4018 )
175 #define LENGTH_MAGIC 177545L
179 #define CLASS_MASK 0xC0
180 #define UNIVERSAL 0x00
181 #define APPLICATION 0x40
187 #define FORM_MASK 0x20
188 #define PRIMITIVE 0x00
189 #define CONSTRUCTED 0x20
193 #define TAG_MASK 0x1F
197 #define BITSTRING 0x03
198 #define OCTETSTRING 0x04
201 #define OBJDESCRIPTOR 0x07
202 #define EXTERNAL 0x08
204 #define ENUMERATED 0x0A
205 #define EMBEDDED_PDV 0x0B
206 #define UTF8STRING 0x0C
207 #define SEQUENCE 0x10
209 #define NUMERICSTRING 0x12
210 #define PRINTABLESTRING 0x13
211 #define T61STRING 0x14
212 #define VIDEOTEXSTRING 0x15
213 #define IA5STRING 0x16
215 #define GENERALIZEDTIME 0x18
216 #define GRAPHICSTRING 0x19
217 #define VISIBLESTRING 0x1A
218 #define GENERALSTRING 0x1B
219 #define UNIVERSALSTRING 0x1C
220 #define BMPSTRING 0x1E
224 #define LEN_XTND 0x80
225 #define LEN_MASK 0x7F
248 unsigned char header[ 8 ];
253 static int printDots =
FALSE;
254 static int doPure =
FALSE;
255 static int doDumpHeader =
FALSE;
256 static int extraOIDinfo =
FALSE;
257 static int doHexValues =
FALSE;
258 static int useStdin =
FALSE;
259 static int zeroLengthAllowed =
FALSE;
260 static int dumpText =
FALSE;
261 static int printAllData =
FALSE;
262 static int checkEncaps =
TRUE;
263 static int checkCharset =
TRUE;
265 static int reverseBitString =
TRUE;
267 static int reverseBitString =
FALSE;
269 static int rawTimeString =
FALSE;
270 static int shallowIndent =
FALSE;
271 static int outputWidth = 80;
272 static int maxNestLevel = 100;
273 static int doOutlineOnly =
FALSE;
277 #define INDENT_SIZE 11
278 #define INDENT_STRING " : "
282 static int noErrors = 0;
283 static int noWarnings = 0;
295 #define MAX_OID_SIZE 32
305 static OIDINFO *oidList = NULL;
313 #ifdef __TANDEM_NSK__
314 #define CONFIG_NAME "asn1cfg"
316 #define CONFIG_NAME "dumpasn1.cfg"
319 #if defined( __TANDEM_NSK__ )
321 static const char *configPaths[] = {
322 "$system.security",
"$system.system",
327 #elif defined( __WIN32__ )
329 static const char *configPaths[] = {
333 "c:\\dos\\",
"d:\\dos\\",
"c:\\windows\\",
"d:\\windows\\",
334 "c:\\winnt\\",
"d:\\winnt\\",
338 "c:\\program files\\bin\\",
339 "c:\\program files (x86)\\bin\\",
342 "c:\\program files\\utilities\\",
343 "c:\\program files (x86)\\utilities\\",
351 #elif defined( __OS390__ )
353 static const char *configPaths[] = {
362 static const char *configPaths[] = {
365 "/usr/bin/",
"/usr/local/bin/",
"/etc/dumpasn1/",
368 "$HOME/",
"$HOME/bin/",
377 "$HOME/.",
"/etc/dumpasn1/",
387 #define isEnvTerminator( c ) \
388 ( ( ( c ) == '/' ) || ( ( c ) == '.' ) || ( ( c ) == '$' ) || \
389 ( ( c ) == '\0' ) || ( ( c ) == '~' ) )
399 static char *idstr(
const int tagID )
404 return(
"End-of-contents octets" );
410 return(
"BIT STRING" );
412 return(
"OCTET STRING" );
416 return(
"OBJECT IDENTIFIER" );
418 return(
"ObjectDescriptor" );
420 return(
"EXTERNAL" );
424 return(
"ENUMERATED" );
426 return(
"EMBEDDED PDV" );
428 return(
"UTF8String" );
430 return(
"SEQUENCE" );
434 return(
"NumericString" );
436 return(
"PrintableString" );
438 return(
"TeletexString" );
440 return(
"VideotexString" );
442 return(
"IA5String" );
446 return(
"GeneralizedTime" );
448 return(
"GraphicString" );
450 return(
"VisibleString" );
452 return(
"GeneralString" );
454 return(
"UniversalString" );
456 return(
"BMPString" );
458 return(
"Unknown (Reserved)" );
468 for( oidPtr = oidList; oidPtr != NULL; oidPtr = oidPtr->
next )
470 if( oidLength == oidPtr->
oidLength - 2 && \
471 !memcmp( oidPtr->
oid + 2, oid, oidLength ) )
482 if( ( *buffer = (
char * ) malloc( strlen( attribute ) + 1 ) ) == NULL )
484 puts(
"Out of memory." );
487 strcpy( *buffer, attribute );
500 static int charFlags[] = {
502 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
504 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
506 PI,
I,
I,
I,
I,
I,
I,
PI,
PI,
PI,
I,
PI,
PI,
PI,
PI,
PI,
508 PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
I,
I,
PI,
I,
PI,
510 I,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
512 PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
I,
I,
I,
I,
I,
514 I,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
516 PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
PI,
I,
I,
I,
I, 0
519 static int isPrintable(
int ch )
521 if( ch >= 128 || !( charFlags[ ch ] &
P ) )
526 static int isIA5(
int ch )
528 if( ch >= 128 || !( charFlags[ ch ] &
I ) )
546 #define MAX_LINESIZE 512
552 static int readLine( FILE *file,
char *buffer )
554 int bufCount = 0,
ch;
557 while( ( ( ch = getc( file ) ) ==
' ' || ch ==
'\t' ) && !feof( file ) );
560 while( ch !=
'\r' && ch !=
'\n' && ch !=
CPM_EOF && !feof( file ) )
567 printf(
"Bad character '%c' in config file line %d.\n",
573 if( ch ==
'#' && !bufCount )
576 while( ch !=
'\r' && ch !=
'\n' && ch !=
CPM_EOF && !feof( file ) )
584 printf(
"Config file line %d too long.\n", lineNo );
589 buffer[ bufCount++ ] =
ch;
598 if( ( ch = getc( file ) ) !=
'\n' )
603 while( bufCount > 0 &&
604 ( ( ch = buffer[ bufCount - 1 ] ) ==
' ' || ch ==
'\t' ) )
606 buffer[ bufCount ] =
'\0';
611 while( !feof( file ) )
623 static int processOID(
OIDINFO *oidInfo,
char *
string )
626 int firstValue,
value, valueIndex = 0, oidIndex = 3;
629 binaryOID[ 0 ] =
OID;
634 printf(
"Excessively long OID in config file line %d.\n",
638 if( sscanf(
string,
"%d", &value ) != 1 || value < 0 )
640 printf(
"Invalid value in config file line %d.\n", lineNo );
643 if( valueIndex == 0 )
650 if( valueIndex == 1 )
652 if( firstValue < 0 || firstValue > 2 || value < 0 || \
653 ( ( firstValue < 2 && value > 39 ) || \
654 ( firstValue == 2 && value > 175 ) ) )
656 printf(
"Invalid value in config file line %d.\n",
660 binaryOID[ 2 ] = ( firstValue * 40 ) + value;
665 int hasHighBits =
FALSE;
667 if( value >= 0x200000L )
669 binaryOID[ oidIndex++ ] = 0x80 | ( value >> 21 );
673 if( ( value >= 0x4000 ) || hasHighBits )
675 binaryOID[ oidIndex++ ] = 0x80 | ( value >> 14 );
679 if( ( value >= 0x80 ) || hasHighBits )
681 binaryOID[ oidIndex++ ] = 0x80 | ( value >> 7 );
684 binaryOID[ oidIndex++ ] =
value;
687 while( *
string && isdigit(
byteToInt( *
string ) ) )
689 if( *
string && *
string++ !=
' ' )
691 printf(
"Invalid OID string in config file line %d.\n", lineNo );
695 binaryOID[ 1 ] = oidIndex - 2;
696 memcpy( oidInfo->
oid, binaryOID, oidIndex );
702 static int processHexOID(
OIDINFO *oidInfo,
char *
string )
708 if( sscanf(
string,
"%x", &value ) != 1 || value > 255 )
710 printf(
"Invalid hex value in config file line %d.\n", lineNo );
715 if( *
string && *
string++ !=
' ' )
717 printf(
"Invalid hex string in config file line %d.\n", lineNo );
725 printf(
"OID value in config file line %d too long.\n", lineNo );
733 static int readConfig(
const char *
path,
const int isDefaultConfig )
735 OIDINFO dummyOID = { NULL,
"Dummy", 0,
"Dummy",
"Dummy", 1 }, *oidPtr;
737 int seenHexOID =
FALSE;
742 if( ( file = fopen( path,
"rb" ) ) == NULL )
746 if( isDefaultConfig )
748 puts(
"Cannot open config file 'dumpasn1.cfg', which should be in the same" );
749 puts(
"directory as the dumpasn1 program, a standard system directory, or" );
750 puts(
"in a location pointed to by the DUMPASN1_PATH environment variable." );
751 puts(
"Operation will continue without the ability to display Object " );
752 puts(
"Identifier information." );
754 puts(
"If the config file is located elsewhere, you can set the environment" );
755 puts(
"variable DUMPASN1_PATH to the path to the file." );
759 printf(
"Cannot open config file '%s'.\n", path );
764 if( oidList == NULL )
767 for( oidPtr = oidList; oidPtr->
next != NULL; oidPtr = oidPtr->
next );
771 while( ( status = readLine( file, buffer ) ) ==
TRUE && !feof( file ) )
781 if( !strncmp( buffer,
"OID = ", 6 ) )
787 printf(
"OID ending on config file line %d has no "
788 "description attribute.\n", lineNo - 1 );
795 puts(
"Out of memory." );
798 oidPtr = oidPtr->
next;
799 if( oidList == NULL )
801 memset( oidPtr, 0,
sizeof(
OIDINFO ) );
804 if( !strncmp( buffer + 6,
"06", 2 ) )
807 if( !processHexOID( oidPtr, buffer + 6 ) )
812 if( !processOID( oidPtr, buffer + 6 ) )
816 else if( !strncmp( buffer,
"Description = ", 14 ) )
820 printf(
"Duplicate OID description in config file line %d.\n",
824 if( !addAttribute( &oidPtr->
description, buffer + 14 ) )
827 else if( !strncmp( buffer,
"Comment = ", 10 ) )
831 printf(
"Duplicate OID comment in config file line %d.\n",
835 if( !addAttribute( &oidPtr->
comment, buffer + 10 ) )
838 else if( !strncmp( buffer,
"Warning", 7 ) )
842 printf(
"Duplicate OID warning in config file line %d.\n",
850 printf(
"Unrecognised attribute '%s', line %d.\n", buffer,
863 puts(
"\nWarning: Use of old-style hex OIDs detected in "
864 "configuration file, please\n update your dumpasn1 "
865 "configuration file.\n" );
874 static int testConfigPath(
const char *path )
879 if( ( file = fopen( path,
"rb" ) ) == NULL )
888 static void buildConfigPath(
char *path,
const char *pathTemplate )
891 int pathLen, pathPos = 0, newPathPos = 0;
894 strcpy( pathBuffer, pathTemplate );
896 pathLen = strlen( pathBuffer );
898 while( pathPos < pathLen )
904 if( ( strPtr = strstr( pathBuffer + pathPos,
"$" ) ) != NULL )
905 substringSize = ( int ) ( ( strPtr - pathBuffer ) - pathPos );
907 substringSize = pathLen - pathPos;
908 if( substringSize > 0 )
910 memcpy( newPath + newPathPos, pathBuffer + pathPos,
913 newPathPos += substringSize;
914 pathPos += substringSize;
926 memcpy( envName, pathBuffer + pathPos, i );
930 if( ( envString = getenv( envName ) ) != NULL )
932 const int envStrLen = strlen( envString );
936 memcpy( newPath + newPathPos, envString, envStrLen );
937 newPathPos += envStrLen;
943 newPath[ newPathPos ] =
'\0';
946 strcpy( path, newPath );
951 static int readGlobalConfig(
const char *path )
954 char *searchPos = (
char * ) path, *namePos, *lastPos = NULL;
959 char filePath[ _MAX_PATH ];
975 lastPos = strstr( searchPos,
"dumpasn1" );
976 if( lastPos == NULL )
977 lastPos = strstr( searchPos,
"DUMPASN1" );
978 searchPos = lastPos + 1;
980 while( lastPos != NULL );
982 if( namePos == NULL && ( namePos = strrchr( path,
'/' ) ) != NULL )
984 const int endPos = ( int ) ( namePos - path ) + 1;
993 memcpy( buffer, path, endPos );
995 if( testConfigPath( buffer ) )
996 return( readConfig( buffer,
TRUE ) );
1003 if( strlen( path ) <
FILENAME_MAX - 13 && namePos != NULL )
1005 strcpy( buffer, path );
1006 strcpy( buffer + (
int ) ( namePos - (
char * ) path ),
CONFIG_NAME );
1007 if( testConfigPath( buffer ) )
1008 return( readConfig( buffer,
TRUE ) );
1012 for( i = 0; configPaths[ i ] != NULL; i++ )
1014 buildConfigPath( buffer, configPaths[ i ] );
1015 if( testConfigPath( buffer ) )
1016 return( readConfig( buffer,
TRUE ) );
1021 if( ( envPath = getenv(
"PATH" ) ) != NULL )
1023 char *pathPtr = strtok( envPath,
":" );
1028 if( testConfigPath( buffer ) )
1029 return( readConfig( buffer,
TRUE ) );
1030 pathPtr = strtok( NULL,
":" );
1032 while( pathPtr != NULL );
1038 count = GetModuleFileName ( NULL, filePath, _MAX_PATH );
1041 char *progNameStart = strrchr( filePath,
'\\' );
1042 if( progNameStart != NULL && \
1043 ( progNameStart - filePath ) < _MAX_PATH - 13 )
1047 if( testConfigPath( filePath ) )
1048 return( readConfig( filePath,
TRUE ) );
1062 static void freeConfig(
void )
1066 while( oidPtr != NULL )
1070 oidPtr = oidPtr->
next;
1071 if( oidCursor->
comment != NULL )
1087 static int asciiToEbcdic(
const int ch )
1089 char convBuffer[ 2 ];
1091 convBuffer[ 0 ] =
ch;
1092 convBuffer[ 1 ] =
'\0';
1093 __atoe( convBuffer );
1094 return( convBuffer[ 0 ] );
1100 static int printString(
const int level,
const char *format, ... )
1105 if( level >= maxNestLevel )
1107 va_start( argPtr, format );
1108 length = vfprintf(
output, format, argPtr );
1116 static void doIndent(
const int level )
1120 if( level >= maxNestLevel )
1122 for( i = 0; i < level; i++ )
1124 fprintf(
output, printDots ?
". " : \
1125 shallowIndent ?
" " :
" " );
1131 static void complain(
const char *
message,
const int level )
1133 if( level < maxNestLevel )
1137 doIndent( level + 1 );
1139 fprintf(
output,
"Error: %s.\n", message );
1147 static int adjustLevel(
const int level,
const int maxLevel )
1157 if( level > maxLevel )
1168 int warnNegative =
FALSE, warnNonDER =
FALSE, i;
1170 value = getc( inFile );
1172 warnNegative =
TRUE;
1173 for( i = 0; i < valueLength - 1; i++ )
1175 int ch = getc( inFile );
1180 if( ( value == 0x00 ) && ( ( ch & 0x80 ) == 0x00 ) )
1182 if( ( value == 0xFF ) && ( ( ch & 0x80 ) == 0x80 ) )
1185 value = ( value << 8 ) | ch;
1190 printString( level,
" %ld\n", value );
1192 complain(
"Integer has non-DER encoding", level );
1193 if( warnNegative < 0 )
1194 complain(
"Integer has a negative value", level );
1199 static void dumpHex( FILE *inFile,
long length,
int level,
int isInteger )
1201 const int lineLength = ( dumpText ) ? 8 : 16;
1202 char printable[ 9 ];
1204 int warnPadding =
FALSE, warnNegative = isInteger, singleLine =
FALSE;
1209 if( ( ( doPure ) ? 0 :
INDENT_SIZE ) + ( level * 2 ) + 12 + \
1210 ( length * 3 ) < outputWidth )
1213 if( noBytes > 128 && !printAllData )
1215 level = adjustLevel( level, ( doPure ) ? 15 : 8 );
1216 printable[ 8 ] = printable[ 0 ] =
'\0';
1217 for( i = 0; i < noBytes; i++ )
1221 if( !( i % lineLength ) )
1224 printString( level,
"%c",
' ' );
1231 printString( level,
"%s",
" " );
1232 printString( level,
"%s", printable );
1234 printString( level,
"%c",
'\n' );
1237 doIndent( level + 1 );
1240 ch = getc( inFile );
1241 printString( level,
"%s%02X", ( i % lineLength ) ?
" " :
"", ch );
1242 printable[ i % 8 ] = ( ch >=
' ' && ch < 127 ) ? ch :
'.';
1249 if( !( ch & 0x80 ) )
1250 warnNegative =
FALSE;
1255 if( ( prevCh == 0x00 ) && ( ( ch & 0x80 ) == 0x00 ) )
1257 if( ( prevCh == 0xFF ) && ( ( ch & 0x80 ) == 0x80 ) )
1265 printable[ i ] =
'\0';
1266 while( i < lineLength )
1268 printString( level,
"%s",
" " );
1271 printString( level,
"%s",
" " );
1272 printString( level,
"%s", printable );
1274 if( length > 128 && !printAllData )
1277 printString( level,
"%c",
'\n' );
1280 doIndent( level + 5 );
1281 printString( level,
"[ Another %ld bytes skipped ]", length );
1291 printString( level,
"%c",
'\n' );
1296 complain(
"Integer has non-DER encoding", level );
1298 complain(
"Integer has a negative value", level );
1304 static int oidToString(
char *textOID,
int *textOIDlength,
1305 const unsigned char *oid,
const int oidLength )
1307 unsigned char uuidBuffer[ 32 ];
1309 int length = 0, uuidBufPos = -1, uuidBitCount = 5, i;
1310 int validEncoding =
TRUE, isUUID =
FALSE;
1312 for( i = 0, value = 0; i <
oidLength; i++ )
1314 const unsigned char data = oid[ i ];
1315 const long valTmp = value << 7;
1321 if( value == 0 && data == 0x80 )
1324 validEncoding =
FALSE;
1329 if( uuidBitCount == 0 )
1330 uuidBuffer[ uuidBufPos ] = data << 1;
1333 if( uuidBufPos >= 0 )
1334 uuidBuffer[ uuidBufPos ] |= ( data & 0x7F ) >> ( 7 - uuidBitCount );
1336 if( uuidBitCount < 7 )
1337 uuidBuffer[ uuidBufPos ] = data << ( uuidBitCount + 1 );
1340 if( uuidBitCount > 7 )
1342 if( !( data & 0x80 ) )
1351 if( uuidBufPos != 16 )
1353 validEncoding =
FALSE;
1356 length += sprintf( textOID + length,
1357 " { %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x }",
1358 uuidBuffer[ 0 ], uuidBuffer[ 1 ],
1359 uuidBuffer[ 2 ], uuidBuffer[ 3 ],
1360 uuidBuffer[ 4 ], uuidBuffer[ 5 ],
1361 uuidBuffer[ 6 ], uuidBuffer[ 7 ],
1362 uuidBuffer[ 8 ], uuidBuffer[ 9 ],
1363 uuidBuffer[ 10 ], uuidBuffer[ 11 ],
1364 uuidBuffer[ 12 ], uuidBuffer[ 13 ],
1365 uuidBuffer[ 14 ], uuidBuffer[ 15 ] );
1370 if( value >= ( LONG_MAX >> 7 ) || \
1371 valTmp >= LONG_MAX - ( data & 0x7F ) )
1373 validEncoding =
FALSE;
1376 value = valTmp | ( data & 0x7F );
1379 validEncoding =
FALSE;
1382 if( !( data & 0x80 ) )
1397 y += ( x - 2 ) * 40;
1400 if( x < 0 || x > 2 || y < 0 || \
1401 ( ( x < 2 && y > 39 ) || \
1402 ( x == 2 && ( y > 50 && y != 100 ) ) ) )
1411 validEncoding =
FALSE;
1414 length = sprintf( textOID,
"%ld %ld", x, y );
1425 length += sprintf( textOID + length,
" %ld", value );
1432 validEncoding =
FALSE;
1434 textOID[
length ] =
'\0';
1437 return( validEncoding );
1443 static void dumpBitString( FILE *inFile,
const int length,
const int unused,
1446 unsigned int bitString = 0, currentBitMask = 0x80, remainderMask = 0xFF;
1447 int bitFlag, value = 0, noBits, bitNo = -1, i;
1448 char *errorStr = NULL;
1450 if( unused < 0 || unused > 7 )
1451 complain(
"Invalid number of unused bits", level );
1452 noBits = ( length * 8 ) - unused;
1458 bitString = fgetc( inFile );
1461 for( i = noBits - 8; i > 0; i -= 8 )
1463 bitString = ( bitString << 8 ) | fgetc( inFile );
1464 currentBitMask <<= 8;
1465 remainderMask = ( remainderMask << 8 ) | 0xFF;
1468 if( reverseBitString )
1470 for( i = 0, bitFlag = 1; i < noBits; i++ )
1472 if( bitString & currentBitMask )
1474 if( !( bitString & remainderMask ) )
1477 errorStr =
"Spurious zero bits in bitstring";
1482 if( noBits <
sizeof(
int ) && \
1483 ( ( remainderMask << noBits ) & value ) )
1488 errorStr =
"Spurious one bits in bitstring";
1498 printString( level,
"%c",
'\n' );
1501 doIndent( level + 1 );
1502 printString( level,
"%c",
'\'' );
1503 if( reverseBitString )
1504 currentBitMask = 1 << ( noBits - 1 );
1505 for( i = 0; i < noBits; i++ )
1507 if( value & currentBitMask )
1509 bitNo = ( bitNo == -1 ) ? ( noBits - 1 ) - i : -2;
1510 printString( level,
"%c",
'1' );
1513 printString( level,
"%c",
'0' );
1514 currentBitMask >>= 1;
1517 printString( level,
"'B (bit %d)\n", bitNo );
1519 printString( level,
"%s",
"'B\n" );
1521 if( errorStr != NULL )
1522 complain( errorStr, level );
1534 static void displayString( FILE *inFile,
long length,
int level,
1539 int lineLength = 48, i;
1543 if( noBytes > 384 && !printAllData )
1547 if( ( strOption ==
STR_UTCTIME && length != 13 ) || \
1551 doTimeStr = rawTimeString ?
FALSE :
TRUE;
1553 if( !doTimeStr && length <= 40 )
1554 printString( level,
"%s",
" '" );
1555 level = adjustLevel( level, ( doPure ) ? 15 : 8 );
1556 for( i = 0; i < noBytes; i++ )
1562 if( length > 40 && !( i % lineLength ) )
1565 printString( level,
"%c",
'\'' );
1566 printString( level,
"%c",
'\n' );
1569 doIndent( level + 1 );
1570 printString( level,
"%c",
'\'' );
1573 ch = getc( inFile );
1574 #if defined( __WIN32__ ) || defined( __UNIX__ ) || defined( __OS390__ )
1577 if( i == noBytes - 1 && ( noBytes & 1 ) )
1582 const wchar_t wCh = ( ch << 8 ) | getc( inFile );
1596 outLen = wcstombs( outBuf, &wCh, 1 );
1601 ungetc( wCh & 0xFF, inFile );
1607 #if defined( __WIN32__ )
1608 if( level < maxNestLevel )
1610 #elif defined( __UNIX__ ) && !( defined( __MACH__ ) || defined( __OpenBSD__ ) )
1621 if( level < maxNestLevel )
1623 if( fwide(
output, 1 ) > 0 )
1633 if( level < maxNestLevel )
1636 for( p = outBuf; *p !=
'\0'; p++ )
1637 *p = asciiToEbcdic( *p );
1640 printString( level,
"%s", outBuf );
1654 warnPrintable =
TRUE;
1655 if( strOption ==
STR_IA5 && !isIA5( ch ) )
1659 if( !isprint( ch & 0x7F ) )
1664 if( !isprint( ch ) )
1668 ch = asciiToEbcdic( ch );
1674 if( !isdigit( ch ) && ch !=
'Z' )
1677 if( !isprint( ch ) )
1681 ch = asciiToEbcdic( ch );
1686 if( i == noBytes - 1 && ( noBytes & 1 ) )
1705 if( !isprint( ch ) )
1708 ch = asciiToEbcdic( ch );
1714 printString( level,
"%c", ch );
1717 if( length > 384 && !printAllData )
1720 printString( level,
"%s",
"'\n" );
1723 doIndent( level + 5 );
1724 printString( level,
"[ Another %ld characters skipped ]", length );
1728 int ch = getc( inFile );
1731 warnPrintable =
TRUE;
1732 if( strOption ==
STR_IA5 && !isIA5( ch ) )
1740 const char *timeStrPtr = ( strOption ==
STR_UTCTIME ) ? \
1741 timeStr : timeStr + 2;
1743 printString( level,
" %c%c/%c%c/",
1744 timeStrPtr[ 4 ], timeStrPtr[ 5 ],
1745 timeStrPtr[ 2 ], timeStrPtr[ 3 ] );
1748 printString( level,
"%s",
1749 ( timeStr[ 0 ] <
'5' ) ?
"20" :
"19" );
1753 printString( level,
"%c%c", timeStr[ 0 ], timeStr[ 1 ] );
1755 printString( level,
"%c%c %c%c:%c%c:%c%c GMT",
1756 timeStrPtr[ 0 ], timeStrPtr[ 1 ], timeStrPtr[ 6 ],
1757 timeStrPtr[ 7 ], timeStrPtr[ 8 ], timeStrPtr[ 9 ],
1758 timeStrPtr[ 10 ], timeStrPtr[ 11 ] );
1761 printString( level,
"%c",
'\'' );
1763 printString( level,
"%c",
'\n' );
1767 complain(
"PrintableString contains illegal character(s)", level );
1769 complain(
"IA5String contains illegal character(s)", level );
1771 complain(
"Time is encoded incorrectly", level );
1773 complain(
"BMPString has missing final byte/half character", level );
1786 static int getItem( FILE *inFile,
ASN1_ITEM *item )
1792 tag = item->
header[ index++ ] = fgetc( inFile );
1805 value = fgetc( inFile );
1806 tag = ( tag << 7 ) | ( value & 0x7F );
1810 while( value &
LEN_XTND && index < 5 && !feof( inFile ) );
1818 if( feof( inFile ) )
1824 length = item->
header[ index++ ] = fgetc( inFile );
1841 for( i = 0; i <
length; i++ )
1843 int ch = fgetc( inFile );
1858 static int checkEncapsulate( FILE *inFile,
const int length )
1861 const int currentPos = fPos;
1869 status = getItem( inFile, &nestedItem );
1870 diffPos = fPos - currentPos;
1872 fseek( inFile, -diffPos,
SEEK_CUR );
1895 status = getItem( inFile, &nestedItem );
1897 status = getItem( inFile, &nestedItem );
1898 diffPos = fPos - currentPos;
1900 fseek( inFile, -diffPos,
SEEK_CUR );
1906 if( ( nestedItem.
tag <= 0 || nestedItem.
tag > 0x31 ) || \
1907 ( nestedItem.
length >= length ) )
1914 if( nestedItem.
length != length - diffPos )
1918 if( nestedItem.
tag <= 0 || nestedItem.
tag > 0x31 )
1943 static int zeroLengthOK(
const ASN1_ITEM *item )
1969 if( !zeroLengthAllowed )
1994 static STR_OPTION checkForText( FILE *inFile,
const int length )
1998 int sampleLength =
min( length, 16 ), i;
2002 if( sampleLength < 4 )
2005 if( sampleLength <= 2 )
2011 sampleLength = fread( buffer, 1, sampleLength, inFile );
2012 fseek( inFile, -sampleLength,
SEEK_CUR );
2013 for( i = 0; i < sampleLength; i++ )
2015 const int ch =
byteToInt( buffer[ i ] );
2017 if( !( isalpha( ch ) || isdigit( ch ) || isspace( ch ) ) )
2024 sampleLength = fread( buffer, 1, sampleLength, inFile );
2025 fseek( inFile, -sampleLength,
SEEK_CUR );
2026 if( isdigit(
byteToInt( buffer[ 0 ] ) ) && \
2027 ( length == 13 || length == 15 ) && \
2028 buffer[ length - 1 ] ==
'Z' )
2031 for( i = 0; i < length - 1; i++ )
2033 if( !isdigit(
byteToInt( buffer[ i ] ) ) )
2036 if( i == length - 1 )
2039 for( i = 0; i < sampleLength; i++ )
2059 if( i < sampleLength - 2 )
2097 if( buffer[ i ] < 0x20 || buffer[ i ] > 0x7E )
2108 static void dumpHeader( FILE *inFile,
const ASN1_ITEM *item,
const int level )
2114 printString( level,
"%s",
" " );
2115 printString( level,
"<%02X", *item->
header );
2117 printString( level,
" %02X", item->
header[ i ] );
2122 if( extraLen > 0 && doDumpHeader > 1 )
2128 extraLen = ( int ) item->
length;
2130 for( i = 0; i < extraLen; i++ )
2132 int ch = fgetc( inFile );
2134 if( feof( inFile ) )
2137 printString( level,
" %02X", ch );
2139 fseek( inFile, -extraLen,
SEEK_CUR );
2142 printString( level,
"%s",
">\n" );
2147 static int printAsn1( FILE *inFile,
const int level,
long length,
2148 const int isIndefinite );
2150 static void printConstructed( FILE *inFile,
int level,
const ASN1_ITEM *item )
2157 printString( level,
"%s",
" {}\n" );
2161 printString( level,
"%s",
" {\n" );
2165 fprintf(
output,
"Error: Inconsistent object length, %d byte%s "
2166 "difference.\n", result, ( result > 1 ) ?
"s" :
"" );
2171 printString( level,
"%s", ( printDots ) ?
". " :
" " );
2173 printString( level,
"%s",
"}\n" );
2178 static void printASN1object( FILE *inFile,
ASN1_ITEM *item,
int level )
2183 const int nonOutlineObject = \
2189 static const char *
const classtext[] =
2190 {
"UNIVERSAL ",
"APPLICATION ",
"",
"PRIVATE " };
2193 if( !nonOutlineObject )
2195 printString( level,
"[%s%d]",
2205 fprintf( stderr,
"\nError: Object has bad length field, tag = %02X, "
2206 "length = %lX, value =", item->
tag, item->
length );
2207 fprintf( stderr,
"<%02X", *item->
header );
2209 fprintf( stderr,
" %02X", item->
header[ i ] );
2210 fputs(
">.\n", stderr );
2216 printString( level,
"%c",
'\n' );
2217 complain(
"Object has zero length", level );
2224 printConstructed( inFile, level, item );
2232 if( nonOutlineObject )
2235 printString( level,
"%c",
'\n' );
2242 ( stringType = checkForText( inFile, item->
length ) ) !=
STR_NONE )
2245 displayString( inFile, item->
length, level, stringType );
2257 printString( level,
"%s", idstr( item->
tag ) );
2265 fprintf( stderr,
"\nError: Object has bad length field, tag = %02X, "
2266 "length = %lX, value =", item->
tag, item->
length );
2267 fprintf( stderr,
"<%02X", *item->
header );
2269 fprintf( stderr,
" %02X", item->
header[ i ] );
2270 fputs(
">.\n", stderr );
2277 printConstructed( inFile, level, item );
2289 if( !item->
length && !zeroLengthOK( item ) )
2291 printString( level,
"%c",
'\n' );
2292 complain(
"Object has zero length", level );
2301 ch = getc( inFile );
2302 printString( level,
" %s\n", ch ?
"TRUE" :
"FALSE" );
2303 if( ch != 0 && ch != 0xFF )
2304 complain(
"BOOLEAN has non-DER encoding", level );
2312 dumpHex( inFile, item->
length, level,
TRUE );
2314 printValue( inFile, item->
length, level );
2321 if( ( ch = getc( inFile ) ) != 0 )
2323 printString( level,
" %d unused bit%s",
2324 ch, ( ch != 1 ) ?
"s" :
"" );
2327 if( !--item->
length && !ch )
2329 printString( level,
"%c",
'\n' );
2330 complain(
"Object has zero length", level );
2333 if( item->
length <=
sizeof(
int ) )
2337 dumpBitString( inFile, (
int ) item->
length, ch, level );
2344 if( checkEncapsulate( inFile, item->
length ) )
2348 printString( level,
"%s",
", encapsulates" );
2349 printConstructed( inFile, level, item );
2352 if( !useStdin && !dumpText && \
2353 ( stringType = checkForText( inFile, item->
length ) ) !=
STR_NONE )
2360 displayString( inFile, item->
length, level, \
2361 ( !checkCharset && ( stringType ==
STR_IA5 || \
2371 char textOID[ 128 ];
2378 fprintf( stderr,
"\nError: Object identifier length %ld too "
2379 "large.\n", item->
length );
2382 fread( buffer, 1, (
size_t ) item->
length, inFile );
2384 if( ( oidInfo = getOIDinfo( buffer, (
int ) item->
length ) ) != NULL )
2387 isValid = oidToString( textOID, &length, buffer,
2392 if( ( ( doPure ) ? 0 : INDENT_SIZE ) + ( level * 2 ) + 18 + \
2393 strlen( oidInfo->
description ) + 2 + length >= outputWidth )
2395 printString( level,
"%c",
'\n' );
2398 doIndent( level + 1 );
2401 printString( level,
"%c",
' ' );
2402 printString( level,
"%s (%s)\n", oidInfo->
description, textOID );
2405 if( extraOIDinfo && oidInfo->
comment != NULL )
2409 doIndent( level + 1 );
2410 printString( level,
"(%s)\n", oidInfo->
comment );
2413 complain(
"OID has invalid encoding", level );
2424 isValid = oidToString( textOID, &length, buffer, (
int ) item->
length );
2425 printString( level,
" '%s'\n", textOID );
2427 complain(
"OID has invalid encoding", level );
2433 printString( level,
"%c",
'\n' );
2466 printString( level,
"%c",
'\n' );
2469 doIndent( level + 1 );
2470 printString( level,
"%s",
"Unrecognised primitive, hex value is:");
2478 static int printAsn1( FILE *inFile,
const int level,
long length,
2479 const int isIndefinite )
2482 long lastPos = fPos;
2486 if( !length && !isIndefinite )
2489 while( ( status = getItem( inFile, &item ) ) > 0 )
2491 int nonOutlineObject =
FALSE;
2508 checkEncaps =
FALSE;
2509 puts(
"Warning: Input is non-seekable, some functionality "
2510 "has been disabled." );
2518 dumpHeader( inFile, &item, level );
2523 nonOutlineObject =
TRUE;
2532 complain(
"Spurious EOC in definite-length item", level );
2534 if( !doPure && !nonOutlineObject )
2537 printString( level, ( doHexValues ) ?
"%04lX NDEF: " :
2538 "%4ld NDEF: ", lastPos );
2542 printString( level, ( doHexValues ) ?
"%04lX %4lX: " :
2543 "%4ld %4ld: ", lastPos, item.
length );
2550 if( !nonOutlineObject )
2552 printASN1object( inFile, &item, level );
2560 length -= fPos - lastPos;
2572 return( (
int ) -length );
2579 const int ch = fgetc( inFile );
2588 if( ch && ch <= 0x31 )
2589 ungetc( ch, inFile );
2604 fprintf( stderr,
"\nError: Invalid data encountered at position "
2607 fprintf( stderr,
" %02X", item.
header[ i ] );
2608 fprintf( stderr,
".\n" );
2616 fprintf(
output,
"Error: Inconsistent object length, %ld byte%s "
2617 "difference.\n", length, ( length > 1 ) ?
"s" :
"" );
2625 static void usageExit(
void )
2627 puts(
"DumpASN1 - ASN.1 object dump/syntax check program." );
2628 puts(
"Copyright Peter Gutmann 1997 - 2010. Last updated " UPDATE_STRING "." );
2631 puts(
"Usage: dumpasn1 [-acdefghilmoprstuvwxz] <file>" );
2632 puts(
" Input options:" );
2633 puts(
" - = Take input from stdin (some options may not work properly)" );
2634 puts(
" -<number> = Start <number> bytes into the file" );
2635 puts(
" -- = End of arg list" );
2636 puts(
" -c<file> = Read Object Identifier info from alternate config file" );
2637 puts(
" (values will override equivalents in global config file)" );
2640 puts(
" Output options:" );
2641 puts(
" -f<file> = Dump object at offset -<number> to file (allows data to be" );
2642 puts(
" extracted from encapsulating objects)" );
2643 puts(
" -w<number> = Set width of output, default = 80 columns" );
2646 puts(
" Display options:" );
2647 puts(
" -a = Print all data in long data blocks, not just the first 128 bytes" );
2648 puts(
" -d = Print dots to show column alignment" );
2649 puts(
" -g = Display ASN.1 structure outline only (no primitive objects)" );
2650 puts(
" -h = Hex dump object header (tag+length) before the decoded output" );
2651 puts(
" -hh = Same as -h but display more of the object as hex data" );
2652 puts(
" -i = Use shallow indenting, for deeply-nested objects" );
2653 puts(
" -l = Long format, display extra info about Object Identifiers" );
2654 puts(
" -m<number> = Maximum nesting level for which to display content" );
2655 puts(
" -p = Pure ASN.1 output without encoding information" );
2656 puts(
" -t = Display text values next to hex dump of data" );
2657 puts(
" -v = Verbose mode, equivalent to -ahlt" );
2660 puts(
" Format options:" );
2661 puts(
" -e = Don't print encapsulated data inside OCTET/BIT STRINGs" );
2662 puts(
" -r = Print bits in BIT STRING as encoded in reverse order" );
2663 puts(
" -u = Don't format UTCTime/GeneralizedTime string data" );
2664 puts(
" -x = Display size and offset in hex not decimal" );
2667 puts(
" Checking options:" );
2668 puts(
" -o = Don't check validity of character strings hidden in octet strings" );
2669 puts(
" -s = Syntax check only, don't dump ASN.1 structures" );
2670 puts(
" -z = Allow zero-length items" );
2673 puts(
"Warnings generated by deprecated OIDs require the use of '-l' to be displayed." );
2674 puts(
"Program return code is the number of errors found or EXIT_SUCCESS." );
2682 CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
2687 char *pathPtr = argv[ 0 ];
2690 int moreArgs =
TRUE, doCheckOnly =
FALSE;
2693 memset( pathPtr,
'\0',
sizeof( pathPtr ) );
2694 getcwd( pathPtr,
sizeof( pathPtr ) );
2695 strcat( pathPtr,
"/" );
2709 if( GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ),
2711 outputWidth = csbiInfo.dwSize.X;
2715 while( argc && *argv[ 0 ] ==
'-' && moreArgs )
2717 char *argPtr = argv[ 0 ] + 1;
2725 offset = atol( argPtr );
2728 switch( toupper(
byteToInt( *argPtr ) ) )
2735 printAllData =
TRUE;
2739 if( !readConfig( argPtr + 1,
FALSE ) )
2741 while( argPtr[ 1 ] )
2750 checkEncaps =
FALSE;
2754 if( ( outFile = fopen( argPtr + 1,
"wb" ) ) == NULL )
2756 perror( argPtr + 1 );
2759 while( argPtr[ 1 ] )
2764 doOutlineOnly =
TRUE;
2772 shallowIndent =
TRUE;
2776 extraOIDinfo =
TRUE;
2780 maxNestLevel = atoi( argPtr + 1 );
2781 if( maxNestLevel < 1 || maxNestLevel > 100 )
2783 puts(
"Invalid maximum nesting level." );
2786 while( argPtr[ 1 ] )
2791 checkCharset =
TRUE;
2799 reverseBitString = !reverseBitString;
2804 #if defined( __WIN32__ )
2811 freopen(
"nul",
"w", stdout );
2812 #elif defined( __UNIX__ )
2815 freopen(
"/dev/null",
"w", stdout );
2826 rawTimeString =
TRUE;
2830 printAllData = doDumpHeader =
TRUE;
2831 extraOIDinfo = dumpText =
TRUE;
2835 outputWidth = atoi( argPtr + 1 );
2836 if( outputWidth < 40 || outputWidth > 500 )
2838 puts(
"Invalid output width." );
2841 while( argPtr[ 1 ] )
2850 zeroLengthAllowed =
TRUE;
2854 printf(
"Unknown argument '%c'.\n", *argPtr );
2864 if( useStdin && ( doDumpHeader || outFile != NULL ) )
2866 puts(
"Can't use -f or -h when taking input from stdin" );
2875 if( argc != 1 && !useStdin )
2877 if( !readGlobalConfig( pathPtr ) )
2885 if( ( inFile = fopen( argv[ 0 ],
"rb" ) ) == NULL )
2887 perror( argv[ 0 ] );
2899 if( outFile != NULL )
2907 status = getItem( inFile, &item );
2910 puts(
"Non-ASN.1 data encountered." );
2916 puts(
"Nothing to read." );
2922 puts(
"Cannot process indefinite-length item." );
2929 putc( item.
header[ i ], outFile );
2930 for( length = 0; length < item.
length && !feof( inFile ); length++ )
2931 putc( getc( inFile ),
outFile );
2937 if( !useStdin && offset == 0 )
2939 unsigned char buffer[ 16 ];
2951 fread( buffer, 1, 8, inFile );
2952 if( !feof( inFile ) )
2954 fprintf(
output,
"Warning: Further data follows ASN.1 data at "
2955 "position %ld.\n", position );
2967 fputc(
'\n', stderr );
2968 fprintf( stderr,
"%d warning%s, %d error%s.\n", noWarnings,
2969 ( noWarnings != 1 ) ?
"s" :
"", noErrors,
2970 ( noErrors != 1 ) ?
"s" :
"" );