#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <ntsdexts.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ber.h"

#define iso_member          0x2a,               // iso(1) memberbody(2)
#define us                  0x86, 0x48,         // us(840)
#define rsadsi              0x86, 0xf7, 0x0d,   // rsadsi(113549)
#define pkcs                0x01,               // pkcs(1)

#define rsa_                iso_member us rsadsi
#define rsa_len             6
#define rsa_text            "iso(2) member-body(2) us(840) rsadsi(113549) "
#define pkcs_1              iso_member us rsadsi pkcs
#define pkcs_len            7
#define pkcs_text           "iso(2) member-body(2) us(840) rsadsi(113549) pkcs(1) "


#define joint_iso_ccitt_ds  0x55,
#define attributetype       0x04,

#define attributeType       joint_iso_ccitt_ds attributetype
#define attrtype_len        2

typedef struct _ObjectId {
    UCHAR       Sequence[16];
    DWORD       SequenceLen;
    PSTR        Name;
} ObjectId;

ObjectId    KnownObjectIds[] = {
    { {pkcs_1 1, 1}, pkcs_len + 2, pkcs_text "RSA"},
    { {pkcs_1 1, 2}, pkcs_len + 2, pkcs_text "MD2/RSA"},
    { {pkcs_1 1, 4}, pkcs_len + 2, pkcs_text "MD5/RSA"},
    { {rsa_ 3, 4}, rsa_len + 2, rsa_text "RC4"},
    { {attributeType 3}, attrtype_len + 1, "CN="},
    { {attributeType 6}, attrtype_len + 1, "C="},
    { {attributeType 7}, attrtype_len + 1, "L="},
    { {attributeType 8}, attrtype_len + 1, "S="},
    { {attributeType 10}, attrtype_len + 1, "O="},
    { {attributeType 11}, attrtype_len + 1, "OU="},
    };

ObjectId    KnownPrefixes[] = {
    { {pkcs_1}, pkcs_len, pkcs_text},
    { {iso_member us rsadsi}, pkcs_len - 1, "iso(2) member-body(2) us(840) rsadsi(113549) "},
    { {iso_member us}, pkcs_len - 4, "iso(2) member-body(2) us(840) "},
    { {iso_member}, pkcs_len - 6, "iso(2) member-body(2) " }
    };


typedef struct _NameTypes {
    PSTR        Prefix;
    UCHAR       Sequence[8];
    DWORD       SequenceLen;
} NameTypes;

NameTypes   KnownNameTypes[] = { {"CN=", {attributeType 3}, attrtype_len + 1},
                                 {"C=", {attributeType 6}, attrtype_len + 1},
                                 {"L=", {attributeType 7}, attrtype_len + 1},
                                 {"S=", {attributeType 8}, attrtype_len + 1},
                                 {"O=", {attributeType 10}, attrtype_len + 1},
                                 {"OU=", {attributeType 11}, attrtype_len + 1}
                               };
BYTE        Buffer[1024];

BOOL        BerVerbose = FALSE ;

char maparray[] = "0123456789abcdef";

#define MAX_OID_VALS    32

typedef struct _OID {
    unsigned cVal;
    unsigned Val[MAX_OID_VALS];
} OID;

typedef enum _OidResult {
    OidExact,
    OidPartial,
    OidMiss,
    OidError
} OidResult;


extern  PNTSD_EXTENSION_APIS    pExtApis;
extern  HANDLE                  hDbgThread;
extern  HANDLE                  hDbgProcess;

#define DebuggerOut     (pExtApis->lpOutputRoutine)
#define GetSymbol       (pExtApis->lpGetSymbolRoutine)
#define GetExpr         (PVOID) (pExtApis->lpGetExpressionRoutine)
#define InitDebugHelp(hProc,hThd,pApis) {hDbgProcess = hProc; hDbgThread = hThd; pExtApis = pApis;}

#define ExitIfCtrlC()   if (pExtApis->lpCheckControlCRoutine()) return;
#define BreakIfCtrlC()  if (pExtApis->lpCheckControlCRoutine()) break;


#define LINE_SIZE   192
#define INDENT_SIZE 4

#define OID_VERBOSE 0x0002
#define OID_PARTIAL 0x0001

char * DefaultTree =
   "1 iso\n"
   "    2 memberbody\n"
   "        840 us\n"
   "            113549 rsadsi\n"
   "                1 pkcs\n"
   "                    1 RSA\n"
   "                    3 pkcs-3\n"
   "                        1 dhKeyAgreement\n"
   "                2 digestAlgorithm\n"
   "                    2 MD2\n"
   "                    4 MD4\n"
   "                    5 MD5\n"
   "            113554 mit\n"
   "                1 infosys\n"
   "                    2 gssapi\n"
   "                        1 generic\n"
   "                            1 user_name\n"
   "                            2 machine_uid_name\n"
   "                            3 string_uid_name\n"
   "            113556 microsoft\n"
   "                1 ds\n"
   "    3 org\n"
   "        6 dod\n"
   "            1 internet\n"
   "                4 private\n"
   "                    1 enterprise\n"
   "                        311 microsoft\n"
   "                            1 software\n"
   "                                1 systems\n"
   "                                2 wins\n"
   "                                3 dhcp\n"
   "                                4 apps\n"
   "                                5 mos\n"
   "                                7 InternetServer\n"
   "                                8 ipx\n"
   "                                9 ripsap\n"
   "                            2 security\n"
   "                                1 certificates\n"
   "                                2 mechanisms\n"
   "                                    9 Negotiator\n"
   "                                    10 NTLM\n"
   "                                    12 SSL\n"
   "                5 security\n"
   "                    3 integrity\n"
   "                        1 md5-DES-CBC\n"
   "                        2 sum64-DES-CBC\n"
   "                    5 mechanisms\n"
   "                        1 spkm\n"
   "                            1 spkm-1\n"
   "                            2 spkm-2\n"
   "                            10 spkmGssTokens\n"
   "                    6 nametypes\n"
   "                        2 gss-host-based-services\n"
   "                        3 gss-anonymous-name\n"
   "                        4 gss-api-exported-name\n"
   "        14 oiw\n"
   "            3 secsig\n"
   "                2 algorithm\n"
   "                    7 DES-CBC\n"
   "                    10 DES-MAC\n"
   "                    18 SHA\n"
   "                    22 id-rsa-key-transport\n"
   "2 joint-iso-ccitt\n"
   "    5 ds\n"
   "        4 attribute-type\n"
   "            3 CommonName\n"
   "            6 Country/region\n"
   "            7 Locality\n"
   "            8 State\n"
   "            10 Organization\n"
   "            11 OrgUnit\n"
    ;


typedef struct _TreeFile {
    CHAR *  Buffer;
    CHAR *  Line;
    CHAR *  CurNul;
} TreeFile, * PTreeFile ;


BOOL
TreeFileInit(
    PTreeFile   pFile,
    PSTR        pStr)
{
    int l;


    l = strlen( pStr );

    if ( (pStr[l - 1] != '\r') &&
         (pStr[l - 1] != '\n') )
    {
        l++;
    }

    pFile->Buffer = LocalAlloc( LMEM_FIXED, l );

    if ( pFile->Buffer )
    {
        strcpy( pFile->Buffer, pStr );
        pFile->Line = pFile->Buffer ;
        pFile->CurNul = NULL ;
    }

    return (pFile->Buffer != NULL);

}

VOID
TreeFileDelete(
    PTreeFile   pFile
    )
{
    LocalFree( pFile->Buffer );

}

PSTR
TreeFileGetLine(
    PTreeFile   pFile )
{
    PSTR    Scan;
    PSTR    Line;

    if ( !pFile->Line )
    {
        return( NULL );
    }

    if ( pFile->CurNul )
    {
        *pFile->CurNul = '\n';
    }

    pFile->CurNul = NULL ;

    Scan = pFile->Line ;

    while ( *Scan && (*Scan != '\n') && (*Scan != '\r'))
    {
        Scan++;
    }

    //
    // Okay, get the line to return
    //

    Line = pFile->Line;

    //
    // If this is not the end, touch up the pointers:
    //

    if ( *Scan )
    {
        *Scan = '\0';

        pFile->CurNul = Scan;

        Scan += 1;

        while ( *Scan && ( (*Scan == '\r' ) || ( *Scan == '\n') ))
        {
            Scan++ ;
        }

        //
        // If this is the end, reset line
        //

        if ( *Scan == '\0' )
        {
            pFile->Line = NULL ;
        }
        else
        {
            pFile->Line = Scan;
        }

    }
    else
    {
        pFile->Line = NULL ;
    }

    return( Line );

}

VOID
TreeFileRewind(
    PTreeFile   pFile )
{

    if ( pFile->CurNul )
    {
        *pFile->CurNul = '\n';
    }

    pFile->CurNul = NULL ;

    pFile->Line = pFile->Buffer ;
}

int
tohex(
    BYTE    b,
    PSTR    psz)
{
    BYTE b1, b2;

    b1 = b >> 4;
    b2 = b & 0xF;

    *psz++ = maparray[b1];
    *psz = maparray[b2];

    return(3);
}


//+---------------------------------------------------------------------------
//
//  Function:   DecodeOID
//
//  Synopsis:   Decodes an OID into a simple structure
//
//  Arguments:  [pEncoded] --
//              [len]      --
//              [pOID]     --
//
//  History:    8-07-96   RichardW   Stolen directly from DonH
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
DecodeOID(unsigned char *pEncoded, int len, OID *pOID)
{
    unsigned cval;
    unsigned val;
    int i, j;

    if (len <=2) {
        return FALSE;
    }


    // The first two values are encoded in the first octet.

    pOID->Val[0] = pEncoded[0] / 40;
    pOID->Val[1] = pEncoded[0] % 40;

    //DebuggerOut("Encoded value %02x turned into %d and %d\n", pEncoded[0],
    //          pOID->Val[0], pOID->Val[1] );

    cval = 2;
    i = 1;

    while (i < len) {
        j = 0;
        val = pEncoded[i] & 0x7f;
        while (pEncoded[i] & 0x80) {
            val <<= 7;
            ++i;
            if (++j > 4 || i >= len) {
                // Either this value is bigger than we can handle (we
                // don't handle values that span more than four octets)
                // -or- the last octet in the encoded string has its
                // high bit set, indicating that it's not supposed to
                // be the last octet.  In either case, we're sunk.
                return FALSE;
            }
            val |= pEncoded[i] & 0x7f;
        }
        //ASSERT(i < len);
        pOID->Val[cval] = val;
        ++cval;
        ++i;
    }
    pOID->cVal = cval;

    return TRUE;
}

PSTR
GetLineWithIndent(
    PTreeFile   ptf,
    DWORD       i)
{
    PSTR    Scan;
    DWORD   test;


    do
    {
        Scan = TreeFileGetLine( ptf );


        if ( Scan && i )
        {
            if ( i < INDENT_SIZE )
            {
                test = 0;
            }
            else
            {
                test = i - INDENT_SIZE ;
            }

            if ( Scan[ test ] != ' ' )
            {
                {
                    Scan = NULL ;
                    break;
                }
            }

        }
        else
            test = 0;

    } while ( Scan && (Scan[i] == ' ')  );

    return( Scan );
}

OidResult
scan_oid_table(
    char *  Table,
    DWORD   Flags,
    PUCHAR  ObjectId,
    DWORD   Len,
    PSTR    pszRep,
    DWORD   MaxRep)
{
    CHAR    OidPath[ MAX_PATH ];
    OID     Oid;
    DWORD   i;
    DWORD   Indent;
    TreeFile    tf;
    PSTR    Scan;
    PSTR    Tag;
    PSTR    SubScan;
    DWORD   Index;
    DWORD   size;
    DWORD   TagSize;

    if (!DecodeOID( ObjectId, Len, &Oid ))
    {
        return( OidError );
    }


    i = 0;

    Indent = 0;

    if ( !TreeFileInit( &tf, Table ) )
    {
        DebuggerOut("Unable to load prefix table\n");
        return OidError ;
    }

    Tag = OidPath;

    size = 0;

    TagSize = 0;


    if ( (Flags & OID_VERBOSE) == 0 )
    {
        while ( i < Oid.cVal )
        {
            TagSize = _snprintf( Tag, MAX_PATH - size, "%d.",
                            Oid.Val[i] );

            size += TagSize;

            Tag += TagSize;

            i++;

        }

        strncpy( pszRep, OidPath, MaxRep );

        TreeFileDelete( &tf );

        return( OidExact );
    }

    while ( i < Oid.cVal )
    {

        do
        {

            Scan = GetLineWithIndent( &tf, Indent );


            if ( Scan )
            {
                Index = atoi(Scan);
            }
            else
            {
                Index = (DWORD) -1;
            }

            if ( Index == Oid.Val[i] )
            {
                break;
            }

        } while ( Scan );


        //
        // If Scan is NULL, we didn't get a match
        //

        if ( !Scan )
        {
            if ( i > 0 )
            {
                if ( Flags & OID_PARTIAL )
                {
                    while ( i < Oid.cVal )
                    {
                        TagSize = _snprintf( Tag, MAX_PATH - size, "%d ",
                                        Oid.Val[i] );

                        size += TagSize;

                        Tag += TagSize;

                        i++;

                    }
                    strncpy( pszRep, OidPath, MaxRep );
                }

                TreeFileDelete( &tf );

                return( OidPartial );

            }

            TreeFileDelete( &tf );

            return( OidMiss );
        }

        //
        // Got a hit:
        //

        SubScan = &Scan[Indent];

        while (*SubScan != ' ')
        {
            SubScan++;
        }

        SubScan++;

        TagSize = _snprintf( Tag, MAX_PATH - size, "%s(%d) ", SubScan, Index );

        size += TagSize;

        Tag += TagSize ;

        Indent += INDENT_SIZE ;

        i ++;


    }

    strncpy( pszRep, OidPath, MaxRep );

    TreeFileDelete( &tf );

    return( OidExact );


}

decode_to_string(
    LPBYTE  pBuffer,
    DWORD   Flags,
    DWORD   Type,
    DWORD   Len,
    PSTR    pszRep,
    DWORD   RepLen)
{
    PSTR    pstr;
    PSTR    lineptr;
    DWORD   i;


    switch (Type)
    {
        case BER_NULL:
            strcpy(pszRep, "<empty>");
            break;

        case BER_OBJECT_ID:
            scan_oid_table( DefaultTree,
                            OID_PARTIAL | (Flags & DECODE_VERBOSE_OIDS ? OID_VERBOSE : 0 ),
                            pBuffer, Len, pszRep, RepLen );

            break;

        case BER_PRINTABLE_STRING:
        case BER_TELETEX_STRING:
        case BER_GRAPHIC_STRING:
        case BER_VISIBLE_STRING:
        case BER_GENERAL_STRING:
            CopyMemory(pszRep, pBuffer, min(Len, RepLen - 1) );
            pszRep[min(Len, RepLen - 1)] = '\0';
            break;

        default:

            pstr = &pszRep[30];
            lineptr = pszRep;
            for (i = 0; i < min(Len, 8) ; i++ )
            {
                lineptr += tohex(*pBuffer, lineptr);
                if ((*pBuffer >= ' ') && (*pBuffer <= '|'))
                {
                    *pstr++ = *pBuffer;
                }
                else
                {
                    *pstr++ = '.';
                }

                pBuffer++;

            }
            *pstr++ = '\0';
    }
    return(0);
}

int
ber_decode(
    OutputFn Out,
    StopFn  Stop,
    LPBYTE  pBuffer,
    DWORD   Flags,
    int   Indent,
    int   Offset,
    int   TotalLength,
    int   BarDepth)
{
    char *  TypeName = NULL;
    char    msg[32];
    char *  pstr;
    int     i;
    int     Len;
    int     ByteCount;
    int     Accumulated;
    DWORD   Type;
    int     subsize;
    char    line[ LINE_SIZE ];
    BOOL    Nested;
    BOOL    Leaf;
    int     NewBarDepth;
    char    nonuniversal[ LINE_SIZE ];



    if ((Stop)())
    {
        return(0);
    }

    Type = *pBuffer;


    if ( (Type & 0xC0) == 0 )
    {
        switch ( Type & 0x1F )
        {
            case BER_BOOL:
                TypeName = "Bool";
                break;

            case BER_INTEGER:
                TypeName = "Integer";
                break;

            case BER_BIT_STRING:
                TypeName = "Bit String";
                break;

            case BER_OCTET_STRING:
                TypeName = "Octet String";
                if ( Flags & DECODE_NEST_OCTET_STRINGS )
                {
                    TypeName = "Octet String (Expanding)";
                    Type |= BER_CONSTRUCTED ;
                    Flags &= ~( DECODE_NEST_OCTET_STRINGS );
                }
                break;

            case BER_NULL:
                TypeName = "Null";
                break;

            case BER_OBJECT_ID:
                TypeName = "Object ID";
                break;

            case BER_OBJECT_DESC:
                TypeName = "Object Descriptor";
                break;

            case BER_SEQUENCE:
                TypeName = "Sequence";
                break;

            case BER_SET:
                TypeName = "Set";
                break;

            case BER_NUMERIC_STRING:
                TypeName = "Numeric String";
                break;

            case BER_PRINTABLE_STRING:
                TypeName = "Printable String";
                break;

            case BER_TELETEX_STRING:
                TypeName = "TeleTex String";
                break;

            case BER_VIDEOTEX_STRING:
                TypeName = "VideoTex String";
                break;

            case BER_VISIBLE_STRING:
                TypeName = "Visible String";
                break;

            case BER_GENERAL_STRING:
                TypeName = "General String";
                break;

            case BER_GRAPHIC_STRING:
                TypeName = "Graphic String";
                break;

            case BER_UTC_TIME:
                TypeName = "UTC Time";
                break;


            default:
                TypeName = "Unknown";
                break;
        }
    }
    else
    {
        //
        // Not universal
        //

        switch ( Type & 0xC0 )
        {
            case BER_UNIVERSAL:
                TypeName = "Internal Error!";
                break;

            case BER_APPLICATION:
                sprintf( nonuniversal, "[Application %d]", Type & 0x1F);
                TypeName = nonuniversal;
                break;

            case BER_CONTEXT_SPECIFIC:
                sprintf( nonuniversal, "[Context Specific %d]", Type & 0x1F);
                TypeName = nonuniversal;
                break;

            case BER_PRIVATE:
                sprintf( nonuniversal, "[Private %d]", Type & 0x1F);
                TypeName = nonuniversal ;
                break;

        }
    }


    pstr = msg;
    for (i = 0; i < Indent ; i++ )
    {
        if (i < BarDepth)
        {
            *pstr++ = '\263';
        }
        else
        {
            *pstr++ = ' ';
        }
        *pstr++ = ' ';
    }
    *pstr++ = '\0';

    pBuffer ++;
    Len = 0;

    if (*pBuffer & 0x80)
    {
        ByteCount = *pBuffer++ & 0x7f;

        for (i = 0; i < ByteCount ; i++ )
        {
            Len <<= 8;
            Len += *pBuffer++;
        }
    }
    else
    {
        ByteCount = 0;
        Len = *pBuffer++;
    }

    if (Offset + Len + 2 + ByteCount == TotalLength)
    {
        Leaf = TRUE;
    }
    else
    {
        Leaf = FALSE;
    }
    if (Type & BER_CONSTRUCTED)
    {
        Nested = TRUE;
    }
    else
    {
        Nested = FALSE;
    }

    (Out)("%s%c\304%c[%x] %s(%d) ", msg,
                    Leaf ? 192 : 195,
                        Nested ? 194 : 196,
                        Type, TypeName, Len);

    if ( Type & BER_CONSTRUCTED )
    {
        (Out)("\n");
        Accumulated = 0;
        while (Accumulated < Len)
        {
            if (BarDepth < Indent)
            {
                NewBarDepth = BarDepth;
            }
            else
            {
                NewBarDepth = (Nested && Leaf) ? BarDepth : Indent + 1;
            }

            subsize = ber_decode(Out, Stop, pBuffer, Flags, Indent + 1,
                                    Accumulated, Len, NewBarDepth);
            Accumulated += subsize;
            pBuffer += subsize;
        }
        (Out)("%s%c\n", msg, ((Indent <= BarDepth) && !Leaf) ? 179 : 32);
    }
    else
    {
        memset(line, ' ', LINE_SIZE - 1);
        line[ LINE_SIZE - 1 ] = '\0';

        decode_to_string(pBuffer, Flags, Type, Len, line, LINE_SIZE);

        (Out)("%s\n", line);

    }

    return(Len + 2 + ByteCount);
}

BOOL
NeverStop(void)
{
    return(FALSE);
}