diff --git a/scan_asn1length.c b/scan_asn1length.c index 9e20fae..7dbd4a9 100644 --- a/scan_asn1length.c +++ b/scan_asn1length.c @@ -12,17 +12,66 @@ size_t scan_asn1length(const char* src,const char* max,size_t* value) { } else { /* Highest bit set: lower 7 bits is the length of the length value in bytes. */ c&=0x7f; - if (!c) return 0; /* length 0x80 means indefinite length encoding, not supported here */ + if (!c) + return 0; /* length 0x80 means indefinite length encoding, not supported here */ l=(unsigned char)src[1]; - if (l==0) return 0; /* not minimally encoded: 0x81 0x00 instead of 0x00 */ - if (c>sizeof(l)) return 0; /* too many bytes, does not fit into target integer type */ - if (c+1>len) return 0; /* not enough data in input buffer */ + if (l==0) + return 0; /* not minimally encoded: 0x81 0x00 instead of 0x00 */ + if (c>sizeof(l)) + return 0; /* too many bytes, does not fit into target integer type */ + if (c+1>len) + return 0; /* not enough data in input buffer */ for (i=2; i<=c; ++i) l=l*256+(unsigned char)src[i]; - if (l<0x7f) return 0; /* not minimally encoded: 0x81 0x70 instead of 0x70 */ + if (l<0x7f) + return 0; /* not minimally encoded: 0x81 0x70 instead of 0x70 */ } - if (l>len-i) return 0; /* if the length would not fit into the buffer, return 0 */ + if (l>len-i) + return 0; /* if the length would not fit into the buffer, return 0 */ *value=l; return i; } +#ifdef UNITTEST +#include +#include + +int main() { + char buf[10]; + unsigned long l; + /* empty input */ + assert(scan_asn1length(buf,buf,&l)==0); + /* regular 1-byte encoding */ + strcpy(buf,"\x23"); + assert(scan_asn1length(buf,buf+1,&l)==0); // length fits but value doesn't + assert(scan_asn1length(buf,buf+0x23,&l)==0); // length fits but value doesn't + assert(scan_asn1length(buf,buf+0x24,&l)==1 && l==0x23); // OK + /* not minimally encoded */ + strcpy(buf,"\x81\x23"); // not minimal, should have been "\x23" + assert(scan_asn1length(buf,buf+10,&l)==0); + /* indefinite length encoding not supported */ + strcpy(buf,"\x80"); + assert(scan_asn1length(buf,buf+1,&l)==0); + /* regular 2-byte encoding */ + strcpy(buf,"\x81\x97"); + assert(scan_asn1length(buf,buf+2,&l)==0); // length fits but value doesn't + assert(scan_asn1length(buf,buf+255,&l)==2 && l==0x97); + /* non-minimal multi-byte */ + memcpy(buf,"\x82\x00\x97",3); // not minimal, should have been "\x81\x97" + assert(scan_asn1length(buf,buf+3,&l)==0); + /* value not representable */ + memcpy(buf,"\x89\x01\x02\x03\x04\x05\x06\x07\x08\x09",10); // can't fit 9 bytes into long + // this will also fail the "length bytes fit in input buffer" + assert(scan_asn1length(buf,buf+10,&l)==0); + /* value does not fit in input buffer */ + memcpy(buf,"\x81\x80",2); // length 0x80 + assert(scan_asn1length(buf,buf+10,&l)==0); // length fits, value doesn't + assert(scan_asn1length(buf,buf+0x90,&l)==2 && l==0x80); // OK + assert(scan_asn1length(buf,buf+1,&l)==0); // length doesn't fit + // three byte encoding + memcpy(buf,"\x82\x80\x00",3); // length 0x8000 + assert(scan_asn1length(buf,buf+10,&l)==0); // length fits, value doesn't + assert(scan_asn1length(buf,buf+0x8010,&l)==3 && l==0x8000); // OK + assert(scan_asn1length(buf,buf+2,&l)==0); // length doesn't fit +} +#endif diff --git a/scan_asn1tag.c b/scan_asn1tag.c index e124950..21b7782 100644 --- a/scan_asn1tag.c +++ b/scan_asn1tag.c @@ -7,7 +7,8 @@ #endif size_t scan_asn1tag(const char* src,const char* max,enum asn1_tagclass* tc,enum asn1_tagtype* tt,unsigned long* tag) { - if (max<=src) return 0; + if (max<=src) + return 0; *tc=(*src&0xC0); *tt=(*src&0x20); /* The lower 5 bits are the tag, unless it's 0x1f, in which case the @@ -15,6 +16,8 @@ size_t scan_asn1tag(const char* src,const char* max,enum asn1_tagclass* tc,enum * in the sequence is marked by a cleared high bit */ if ((*src & 0x1f) == 0x1f) { size_t res=scan_asn1tagint(src+1,max,tag); + if (res && *tag < 0x1f) // non-minimal encoding + return 0; return res+!!res; /* add 1 unless it's 0, then leave 0 */ } else { *tag=*src&0x1f; @@ -30,7 +33,28 @@ int main() { enum asn1_tagclass tc; enum asn1_tagtype tt; unsigned long tag; - char buf[10]; + char buf[15]; + assert(scan_asn1tag(buf,buf,&tc,&tt,&tag)==0); // empty input strcpy(buf,"\x01"); assert(scan_asn1tag(buf,buf+10,&tc,&tt,&tag)==1 && tc==UNIVERSAL && tt==PRIMITIVE && tag==BOOLEAN); + /* incomplete input */ + strcpy(buf,"\x1f"); assert(scan_asn1tag(buf,buf+1,&tc,&tt,&tag)==0); + /* long-form encoding when short-form would have sufficed */ + strcpy(buf,"\x1f\x1e"); + assert(scan_asn1tag(buf,buf+10,&tc,&tt,&tag)==0); + /* OK */ + strcpy(buf,"\x1f\x1f"); + assert(scan_asn1tag(buf,buf+10,&tc,&tt,&tag)==2 && tc==UNIVERSAL && tt==PRIMITIVE && tag==0x1f); + /* non-minimal encoding */ + strcpy(buf,"\x1f\x80\x01"); + assert(scan_asn1tag(buf,buf+10,&tc,&tt,&tag)==0); + /* incomplete encoding */ + assert(scan_asn1tag(buf,buf+2,&tc,&tt,&tag)==0); + strcpy(buf,"\x1f\x81\x00"); + assert(scan_asn1tag(buf,buf+10,&tc,&tt,&tag)==3 && tc==UNIVERSAL && tt==PRIMITIVE && tag==0x80); + /* value not representable */ + memcpy(buf,"\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f",11); + assert(scan_asn1tag(buf,buf+12,&tc,&tt,&tag)==0); + memcpy(buf,"\x1f\x8f\xff\xff\xff\x7f",7); + assert(scan_asn1tag(buf,buf+10,&tc,&tt,&tag)==6 && tc==UNIVERSAL && tt==PRIMITIVE && tag==0xffffffff); } #endif diff --git a/scan_asn1tagint.c b/scan_asn1tagint.c index 8711136..3050688 100644 --- a/scan_asn1tagint.c +++ b/scan_asn1tagint.c @@ -4,12 +4,15 @@ size_t scan_asn1tagint(const char* src,const char* bounds,unsigned long* val) { const char* orig=src; unsigned long l=0; if (src>=bounds || /* empty input */ - (unsigned char)src[0]==0x80) return 0; /* catch non-minimal encoding */ + (unsigned char)src[0]==0x80) + return 0; /* catch non-minimal encoding */ for (;; ++src) { if (src>=bounds || /* incomplete input */ - l>>(sizeof(l)*8-7)) return 0; /* catch integer overflow */ + l>>(sizeof(l)*8-7)) + return 0; /* catch integer overflow */ l=l*128+(*src&0x7F); - if (!(*src&0x80)) break; + if (!(*src&0x80)) + break; } *val=l; return src-orig+1;