| #include <stdio.h>
#include <stdlib.h>
#include <string.h>
// packet ----------------------------------------------------------------------
typedef struct {
unsigned char* data;
int size,limit,pos,ovf;
} packet_t;
void pkt_init(packet_t *p,unsigned char* buf,int buf_size) {
p->data=buf;
p->size=0;
p->limit=buf_size;
p->pos=0;
p->ovf=0;
}
void pkt_clone(packet_t* dst,packet_t *src) {
*dst=*src;
}
int pkt_end(packet_t *p) { return p->pos>=p->size; }
int pkt_left(packet_t *p) { return p->size-p->pos; }
int pkt_get(packet_t *p) { return p->pos<p->size ? p->data[p->pos++] : 0; }
int pkt_put(packet_t *p,int v) {
if (p->pos>=p->size) {
if (p->size>=p->limit) return p->ovf=1;
p->size++;
}
if (p->pos>=p->size) p->ovf=1; else p->data[p->pos++]=v;
return p->ovf;
}
int pkt_skip(packet_t *p,int size) {
if (size>0) {
p->pos+=size;
if (p->pos>p->size) { p->ovf=1; p->pos=p->size; }
}
return p->ovf;
}
void pkt_sub(packet_t* dst,packet_t *src,int size) {
*dst=*src;
dst->size=src->pos+size;
if (dst->size>src->size) { dst->size=src->size; dst->ovf=1; }
}
int pkt_gets(packet_t* p,unsigned char* data,int size) {
int i;
for(i=0;i<size;++i) data[i]=pkt_get(p);
return p->ovf;
}
int pkt_readfile(packet_t* p,const char* name) {
FILE *f;
f=fopen(name,"rb"); if (!f) return 2;
fseek(f,0,SEEK_END);
if (ftell(f)>p->limit) p->ovf=1;
fseek(f,0,SEEK_SET);
p->size=fread(p->data,1,p->limit,f);
p->pos=0;
fclose(f);
return p->ovf;
}
// asn1 ------------------------------------------------------------------------
int asn1_len(packet_t *p) {
int x,r;
x=pkt_get(p);
if (x<128) return x;
x&=127;
for(r=0;x>0;--x) {
r<<=8;
r|=pkt_get(p);
}
return r;
}
int asn1_num(packet_t *p) {
int x,r=0;
do {
x=pkt_get(p);
r=(r<<7)|(x&127);
} while(x&128);
return r;
}
// asn1 parser -----------------------------------------------------------------
typedef struct {
int level,index;
int tag,tag_composite,tag_pos,tag_class,tag_type,tag_len;
packet_t body[1];
struct asn1_parser_tag_t *parent;
} asn1_parser_tag_t;
typedef struct {
void *tag_ctx;
int (*tag)(void* ctx,asn1_parser_tag_t* tag);
} asn1_parser_cfg_t;
typedef struct {
packet_t *packet;
asn1_parser_cfg_t *cfg;
} asn1_parser_t;
static int asn1_parse_tag(packet_t *p,
asn1_parser_cfg_t *cfg,
asn1_parser_tag_t *parent,
int stream)
{
enum {
ASN1_MASK_MULTI=0x20, ASN1_MASK_TYPE=0x1F,
ASN1_MASK_CLASS=0xC0, ASN1_SHIFT_CLASS=6
};
enum { TAG_NULL=5 };
asn1_parser_tag_t tag[1];packet_t ps[1];int rc;
tag->index=0;
tag->level=parent ? parent->level+1 : 0;
while(!pkt_end(p)) {
if (stream) {
if (p->pos+1<p->size && p->data[p->pos]==0 && p->data[p->pos+1]==0) {
p->pos+=2; break;
}
}
tag->tag_pos=p->pos;
tag->tag=pkt_get(p);
tag->tag_type=tag->tag&ASN1_MASK_TYPE;
if (tag->tag_type==ASN1_MASK_TYPE) tag->tag_type=asn1_num(p);
tag->tag_class=(tag->tag&ASN1_MASK_CLASS)>>ASN1_SHIFT_CLASS;
tag->tag_composite=tag->tag&ASN1_MASK_MULTI;
tag->tag_len=asn1_len(p);
if (tag->tag_len==0 && (tag->tag_type!=TAG_NULL || tag->tag_class!=0)) {
pkt_sub(ps,p,pkt_left(p)); // stream
if (tag->tag_composite) rc=asn1_parse_tag(ps,cfg,tag,1); else {
for(rc=2;ps->pos<ps->size;) {
while(ps->pos<ps->size && ps->data[ps->pos]!=0) ps->pos++;
if (ps->pos+1<ps->size && ps->data[++ps->pos]==0) {
ps->pos++; rc=0; break;
}
}
}
if (rc) return rc;
tag->tag_len=ps->pos-p->pos-2;
pkt_sub(tag->body,p,tag->tag_len);
rc=cfg->tag(cfg->tag_ctx,tag); if (rc) return rc;
p->pos=ps->pos;
} else {
pkt_sub(tag->body,p,tag->tag_len);
rc=cfg->tag(cfg->tag_ctx,tag); if (rc) return rc;
if (tag->tag_composite) {
pkt_sub(tag->body,p,tag->tag_len);
rc=asn1_parse_tag(tag->body,cfg,tag,0); if (rc) return rc;
}
pkt_skip(p,tag->tag_len);
}
tag->index++;
if (tag->level==0) break; // only one root element
}
return 0;
}
int asn1_parse(packet_t *p,asn1_parser_cfg_t *cfg) {
return asn1_parse_tag(p,cfg,0,0);
}
//-- base64 --------------------------------------------------------------------
static int from_base64(char c) {
if (c>='A' && c<='Z') return c-'A';
if (c>='a' && c<='z') return c-'a'+26;
if (c>='0' && c<='9') return c-'0'+52;
if (c=='+') return 62;
if (c=='/') return 63;
return -1;
}
int pkt_base64_decode(packet_t *pkt,const char* text) {
int acc=0, l1=0, l2=0, rc=0;
const char* p=text;
while(*p) {
char c=*p++;
int v=from_base64(c);
if (v<0) { if (c=='=') l2+=6; continue; }
acc=(acc<<6)|v; l1+=6; l2=0;
if (l1>=24) {
pkt_put(pkt,acc>>16);
pkt_put(pkt,acc>>8);
pkt_put(pkt,acc);
l1=0;
}
}
if (l1>0) {
if (l1+l2!=24) rc=1;
if (l2>12) { l2=12; rc=2; }
acc<<=l2; pkt_put(pkt,acc>>16);
if (l2<12) pkt_put(pkt,acc>>8);
if (l2<6) pkt_put(pkt,acc);
}
return rc;
}
//-- hex_dump ------------------------------------------------------------------
static void hex_dump(packet_t *pkt,int level) {
int w=16,i,a=0;
while(a<pkt->size) {
for(i=0;i<level;i++) printf(" ");
printf("%04X ",a);
for(i=0;i<w;i++) {
if (i+a<pkt->size) printf(" %02X",pkt->data[a+i]&255); else printf(" --");
}
printf(" |");
for(i=0;i<w;i++) {
char c='.'; if (i+a<pkt->size) c=pkt->data[a+i];
if (c<32 || c>=127) c='.';
printf("%c",c);
}
printf("|\n");
a+=w;
}
}
//-- utils ---------------------------------------------------------------------
static int wr_num(packet_t *res,unsigned n) {
if (n>=10) wr_num(res,n/10);
return pkt_put(res,'0'+n%10);
}
static int wr_oid(packet_t *res,packet_t* p) {
int x;
if (pkt_end(p)) return 1;
x=pkt_get(p);
wr_num(res,x/40);
pkt_put(res,'.');
wr_num(res,x%40);
while(!pkt_end(p)) {
x=asn1_num(p);
pkt_put(res,'.');
wr_num(res,x);
}
return res->ovf;
}
static int sprint_oid(char* buf,int buf_size,packet_t *oid) {
packet_t clone[1],text[1];
pkt_init(text,buf,buf_size);
pkt_clone(clone,oid);
wr_oid(text,clone);
return pkt_put(text,0);
}
enum {
TAG_CLASS_UNIVERSAL=0,
TAG_CLASS_APPLICATION=1,
TAG_CLASS_CONTEXT_SPECIFIC=2,
TAG_CLASS_PRIVATE=3
};
static const char* get_classname(int c) {
switch(c) {
case 0: return "Universal";
case 1: return "Application";
case 2: return "Context-specific";
case 3: return "Private";
}
return "";
}
enum {
TAG_TYPE_BOOLEAN=1,
TAG_TYPE_INTEGER=2,
TAG_TYPE_BIT_STRING=3,
TAG_TYPE_OCTET_STRING=4,
TAG_TYPE_NULL=5,
TAG_TYPE_OID=6,
TAG_TYPE_OBJECT_DESCRIPTOR=7,
TAG_TYPE_INSTANCE_OF=8,
TAG_TYPE_REAL=9,
TAG_TYPE_ENUMERATED=10,
TAG_TYPE_EMBEDDED=11,
TAG_TYPE_UTF8STRING=12,
TAG_TYPE_RELATIVE_OID=13,
TAG_TYPE_SEQUENCE=16,
TAG_TYPE_SET=17,
TAG_TYPE_NUMERIC_STRING=18,
TAG_TYPE_PRINTABLE_STRING=19,
TAG_TYPE_TELETEXT_STRING=20,
TAG_TYPE_VIDEOTEXT_STRING=21,
TAG_TYPE_IA5STRING=22,
TAG_TYPE_UTCTIME=23,
TAG_TYPE_GENERALIZEDTIME=24,
TAG_TYPE_GRAPHICS_STRING=25,
TAG_TYPE_VISIBLE_STRING=26,
TAG_TYPE_GENERAL_STRING=27,
TAG_TYPE_UNIVERSAL_STRING=28,
TAG_TYPE_CHARACTER_STRING=29,
TAG_TYPE_BMP_STRING=30
};
static const char* get_typename(int type) {
switch(type) {
case 0: return "reserved"; // for BER
case 1: return "BOOLEAN";
case 2: return "INTEGER";
case 3: return "BIT_STRING";
case 4: return "OCTET_STRING";
case 5: return "NULL";
case 6: return "OID";
case 7: return "ObjectDescriptor";
case 8: return "INSTANCE_OF";// EXTERNAL
case 9: return "REAL";
case 10: return "ENUMERATED";
case 11: return "EMBEDDED_PDV";
case 12: return "UTF8String";
case 13: return "RELATIVE-OID";
case 16: return "SEQUENCE";
case 17: return "SET";
case 18: return "NumericString";
case 19: return "PrintableString";
case 20: return "TeletexString"; // T61String
case 21: return "VideotexString";
case 22: return "IA5String";
case 23: return "UTCTime";
case 24: return "GeneralizedTime";
case 25: return "GraphicString";
case 26: return "VisibleString";// ISO646String
case 27: return "GeneralString";
case 28: return "UniversalString";
case 29: return "CHARACTER_STRING";
case 30: return "BMPString";
}
return "unknown";
}
static const char* get_oid_desc(const char* oid) {
if (strcmp(oid,"1.2.840.113549.1.1.1")==0) return "rsaEncryption";
if (strcmp(oid,"1.2.840.113549.1.1.11")==0) return "sha256WithRSAEncryption";
if (strcmp(oid,"1.3.6.1.4.1.11129.2.4.2")==0) return "Google.Extended validation certificates";
if (strcmp(oid,"1.3.6.1.5.5.7.1.1")==0) return "Certificate Authority Information Access";
if (strcmp(oid,"2.5.29.14")==0) return "Subject key identifier";
if (strcmp(oid,"2.5.29.15")==0) return "Key usage";
if (strcmp(oid,"2.5.29.17")==0) return "Subject alternative name";
if (strcmp(oid,"2.5.29.19")==0) return "Basic constraints";
if (strcmp(oid,"2.5.29.31")==0) return "Certificate Revocation List distribution points";
if (strcmp(oid,"2.5.29.32")==0) return "Certificate policies";
if (strcmp(oid,"2.5.29.35")==0) return "Authority key identifier";
if (strcmp(oid,"2.5.29.37")==0) return "Certificate extension: extKeyUsage (Extended key usage)";
if (strcmp(oid,"2.5.4.10")==0) return "Organization name";
if (strcmp(oid,"2.5.4.3")==0) return "Common name";
if (strcmp(oid,"2.5.4.6")==0) return "Country name";
return "?"; // https://oidref.com/
}
//------------------------------------------------------------------------------
static const char *rsdn_pem=
"MIIGmTCCBYGgAwIBAgIQBszIGrUjrdfJZsel39d9bDANBgkqhkiG9w0BAQsFADBZ"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTMwMQYDVQQDEypH"
"ZW9UcnVzdCBUTFMgRFYgUlNBIE1peGVkIFNIQTI1NiAyMDIwIENBLTEwHhcNMjIw"
"MTEwMDAwMDAwWhcNMjMwMjEwMjM1OTU5WjAUMRIwEAYDVQQDDAkqLnJzZG4ucnUw"
"ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTyKAV5iRFGMjIJg8Hf4fP"
"nLMocb3jkNLcCiS4Q0KMs9oP94uMdWk7TdYeN4QHDM2Z3ZniazYA7hV2uucf6MnF"
"YHQiRnjBAFdiEMW+N5srd+VnVQd+gj2PazIO1k27Qk1yOu64WwVY7oyf+TwyinLM"
"pJqR6d2puQGAmAIqEtQqqylCPUNHaoG9WNY6XT6P8kIzTSAztcgEoBbw5RUpGoUN"
"D6sBMBTvkhlUFrTxP58Fs2P4MOpQvj9pHeoShn7UHbm8yyVhtxoExBq/V4T19yOy"
"gyVJacOLnHhMz0EnNcmTcRCyghnH/r6MnUeCIg/TbzSvUH1eo9ao8TxoAQBPp1c7"
"AgMBAAGjggOgMIIDnDAfBgNVHSMEGDAWgBQSyYibL8lEen0S8d9AA0KYksck1jAd"
"BgNVHQ4EFgQUMyHFa1I+XDBMh5komaLp8sw1JvgwMwYDVR0RBCwwKoIJKi5yc2Ru"
"LnJ1ggoqLnJzZG4ub3Jnggdyc2RuLnJ1gghyc2RuLm9yZzAOBgNVHQ8BAf8EBAMC"
"BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMIGfBgNVHR8EgZcwgZQw"
"SKBGoESGQmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9HZW9UcnVzdFRMU0RWUlNB"
"TWl4ZWRTSEEyNTYyMDIwQ0EtMS0xLmNybDBIoEagRIZCaHR0cDovL2NybDQuZGln"
"aWNlcnQuY29tL0dlb1RydXN0VExTRFZSU0FNaXhlZFNIQTI1NjIwMjBDQS0xLTEu"
"Y3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQIBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93"
"d3cuZGlnaWNlcnQuY29tL0NQUzCBhQYIKwYBBQUHAQEEeTB3MCQGCCsGAQUFBzAB"
"hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTwYIKwYBBQUHMAKGQ2h0dHA6Ly9j"
"YWNlcnRzLmRpZ2ljZXJ0LmNvbS9HZW9UcnVzdFRMU0RWUlNBTWl4ZWRTSEEyNTYy"
"MDIwQ0EtMS5jcnQwCQYDVR0TBAIwADCCAX8GCisGAQQB1nkCBAIEggFvBIIBawFp"
"AHYArfe++nz/EMiLnT2cHj4YarRnKV3PsQwkyoWGNOvcgooAAAF+QyqgywAABAMA"
"RzBFAiEApies/du+wITdxwxwtcHEYiLgW0ZMe1q3Vf9nsePArOUCIE/3GJFYoCRw"
"a10NhqTSNa45/ClhcQHweu55wLfyLIouAHYANc8ZG7+xbFe/D61MbULLu7YnICZR"
"6j/hKu+oA8M71kwAAAF+QyqgvAAABAMARzBFAiAkR4+rsxVJTuRxDiA5qhO40JVj"
"aTZqzslwaQ+GT4jONgIhAI9grI0nmKt1LLvwaHqEIC4gvUlkW50RHW817jQYQOYp"
"AHcAs3N3B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAF+Qyqg8gAABAMA"
"SDBGAiEAy6boPn7iOVr2TPYKRRBA2/Ab/lTLFWxNRHjhYmImZA4CIQDYoM0Iiqfi"
"H1YI1IIql8+2XPxjIx5tUZHgUHpVfddSbTANBgkqhkiG9w0BAQsFAAOCAQEANrof"
"Ly0bSOS4XeUCrNNlHTnK+RnlRrLEHyGn7gGk4e+vyn9AcTzcEH7xJP3JFvTsDZAt"
"g4dL8eoGZOrLHW9fumV16mqHqksMCuZAo0P5Cgj9UOeppvAcnlqsMqF7BNK2AC6f"
"M08tEeJKxK375ZQut2Ge//cn/CvBt4X3VdvIkXyKh41c/AULtgiJW8aEOtUIwuf3"
"E8ICFa5OkfD3AO7QIIjn2CbFAeh72jqtzHM95wamYsjiiHnVNrtZAAWEoywyV4iK"
"eDmzT3/GXf6rVny0zoe6wWI5otHwLV/u5ATvWLVSDGUtqjh6GSgDOG4/Td0xLenK"
"DIyc4DmeunrJZX6yzw==";
|