diff --git a/fmt_asn1int.c b/fmt_asn1int.c index 5c7fd46..9e56d4d 100644 --- a/fmt_asn1int.c +++ b/fmt_asn1int.c @@ -2,7 +2,8 @@ /* Store integer l according to ASN.1 DER rules. * Use fmt_asn1INTEGER for default presents for tag. - * Return number of bytes needed. Only write if DEST!=NULL */ + * Return number of bytes needed. Only write if DEST!=NULL + * NOTE: this is only for unsigned integers! See also fmt_asn1sint */ size_t fmt_asn1int(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,unsigned long l) { size_t len,tmp; /* first the tag */ @@ -12,3 +13,25 @@ size_t fmt_asn1int(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum as if (fmt_asn1length(dest+len,tmp)!=1) return 0; return len+tmp+1; } + +#ifdef UNITTEST +#include +#include +#undef UNITTEST +#include +#include +#include +#include + +int main() { + char buf[100]; + buf[3]='!'; + assert(fmt_asn1int(buf, UNIVERSAL, PRIMITIVE, INTEGER, 0)==3 && !memcmp(buf,"\x02\x01\x00!",4)); + assert(fmt_asn1int(buf, UNIVERSAL, PRIMITIVE, INTEGER, 0x23)==3 && !memcmp(buf,"\x02\x01\x23!",4)); + assert(fmt_asn1int(buf, UNIVERSAL, PRIMITIVE, INTEGER, 127)==3 && !memcmp(buf,"\x02\x01\x7f!",4)); + assert(fmt_asn1int(buf, UNIVERSAL, PRIMITIVE, INTEGER, 128)==4 && !memcmp(buf,"\x02\x02\x00\x80",4)); + assert(fmt_asn1int(buf, UNIVERSAL, PRIMITIVE, INTEGER, 256)==4 && !memcmp(buf,"\x02\x02\x01\x00",4)); + assert(fmt_asn1int(buf, UNIVERSAL, PRIMITIVE, INTEGER, 0xffffffff)==7 && !memcmp(buf,"\x02\x05\x00\xff\xff\xff\xff",7)); + if (sizeof(long)==8) assert(fmt_asn1int(buf, UNIVERSAL, PRIMITIVE, INTEGER, 0xfffffffffffffffful)==11 && !memcmp(buf,"\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff",11)); +} +#endif diff --git a/fmt_asn1intpayload.c b/fmt_asn1intpayload.c index 66ff772..effa470 100644 --- a/fmt_asn1intpayload.c +++ b/fmt_asn1intpayload.c @@ -1,20 +1,61 @@ #include -size_t fmt_asn1intpayload(char* dest,unsigned long l) { - size_t needed=sizeof l,i,fixup; - for (i=1; i>(i*8))) - break; - } - fixup=(l>>((i-1)*8))&0x80 ? 1 : 0; - if (dest) { - size_t j=i; - if (fixup) *dest++=0; - while (j) { - --j; - *dest=(l>>(j*8))&0xff; - ++dest; - } - } - return i+fixup; +static size_t intpayloadlen(unsigned long l) { + size_t i; + /* We don't need to store the leading zero octets. + * So count the non-zero ones. + * We always need at least 1 octet, even if l comes in as 0. + + * However the most significant encoded bit doubles as sign bit. + * If it is 1, the decoder will think it is a negative number. + * We can store 0x7f as 0x7f but need to store 0x80 as 0x0080. + + * So we count how often we have to shift until the remainder + * is 0x7f or less. Put differently: Until (remainder>>7)==0. + * Finally we pull the >>7 out of the loop for efficiency. */ + l >>= 7; + for (i=1; l>0; ++i) l >>= 8; + return i; } + +size_t fmt_asn1intpayload(char* dest,unsigned long l) { + size_t needed=intpayloadlen(l); + if (dest) { + size_t i,n; + /* need to store big endian */ + /* n is the number of bits to shift right for the next octet */ + for (i=0, n=(needed-1)*8; i> n); + } + return needed; +} + +#ifdef UNITTEST +#include +#include + +int main() { + assert(intpayloadlen(0)==1); + assert(intpayloadlen(0x7f)==1); + assert(intpayloadlen(0x80)==2); + assert(intpayloadlen(0x80000000)==5); + if (sizeof(long)==8) assert(intpayloadlen(0x8000000000000000ul)==9); + + char buf[100]; + buf[1]='!'; + assert(fmt_asn1intpayload(buf,0)==1 && buf[0]==0 && buf[1]=='!'); + assert(fmt_asn1intpayload(buf,0x7f)==1 && buf[0]==0x7f && buf[1]=='!'); + buf[2]='!'; + assert(fmt_asn1intpayload(buf,0x80)==2 && !memcmp(buf,"\x00\x80!",3)); + buf[4]='!'; + assert(fmt_asn1intpayload(buf,0x7fffffff)==4 && !memcmp(buf,"\x7f\xff\xff\xff!",5)); + buf[5]='!'; + assert(fmt_asn1intpayload(buf,0xfffffeff)==5 && !memcmp(buf,"\x00\xff\xff\xfe\xff!",6)); + + assert(fmt_asn1intpayload(NULL, 0)==1); + assert(fmt_asn1intpayload(NULL, 0x7f)==1); + assert(fmt_asn1intpayload(NULL, 0x80)==2); + assert(fmt_asn1intpayload(NULL, 0x7fffffff)==4); + assert(fmt_asn1intpayload(NULL, 0xffffffff)==5); +} +#endif diff --git a/fmt_asn1sint.c b/fmt_asn1sint.c index dfa0b23..333bf14 100644 --- a/fmt_asn1sint.c +++ b/fmt_asn1sint.c @@ -3,9 +3,35 @@ size_t fmt_asn1sint(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,signed long l) { size_t len,tmp; /* first the tag */ - if (!dest) return fmt_asn1tag(0,tc,tt,tag)+1+fmt_asn1intpayload(0,l); + if (!dest) return fmt_asn1tag(0,tc,tt,tag)+1+fmt_asn1sintpayload(0,l); len=fmt_asn1tag(dest,tc,tt,tag); tmp=fmt_asn1sintpayload(dest+len+1,l); if (fmt_asn1length(dest+len,tmp)!=1) return 0; return len+tmp+1; } + +#ifdef UNITTEST +#include +#include +#undef UNITTEST +#include +#include +#include +#include +#include + +int main() { + char buf[100]; + buf[3]='!'; + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, 0)==3 && !memcmp(buf,"\x02\x01\x00!",4)); + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, -1)==3 && !memcmp(buf,"\x02\x01\xff!",4)); + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, 0x23)==3 && !memcmp(buf,"\x02\x01\x23!",4)); + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, 127)==3 && !memcmp(buf,"\x02\x01\x7f!",4)); + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, 128)==4 && !memcmp(buf,"\x02\x02\x00\x80",4)); + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, 256)==4 && !memcmp(buf,"\x02\x02\x01\x00",4)); + buf[3]='!'; + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, -128)==3 && !memcmp(buf,"\x02\x01\x80!",4)); + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, -129)==4 && !memcmp(buf,"\x02\x02\xff\x7f",4)); + assert(fmt_asn1sint(buf, UNIVERSAL, PRIMITIVE, INTEGER, -2147483648)==6 && !memcmp(buf,"\x02\x04\x80\x00\x00\x00",6)); +} +#endif diff --git a/fmt_asn1sintpayload.c b/fmt_asn1sintpayload.c index 0992df5..0fb993d 100644 --- a/fmt_asn1sintpayload.c +++ b/fmt_asn1sintpayload.c @@ -1,5 +1,40 @@ #include +static size_t sintpayloadlen(signed long l) { + size_t i; + /* For a number like 0x00012345 we want to store only the significant + * octets, i.e. 0x01 0x23 0x45, so count those here. + * For >=0 omit the leading 0 octets, for <0 omit leading 0xff. + + * The most significant stores bit double as sign bit, so + * So: 0x7f => 0x7f but 0x80 => 0x00 0x80. + + * Likewise, -1 => 0xff, -128 => 0x80, -129 => 0xff 0x7f. + + * So we count how often we have to shift until the remainder + * is 0x7f or less. Put differently: Until (remainder>>7)==0. + * Finally we pull the >>7 out of the loop for efficiency. */ + + if (l<0) l=~l; // -128 (0x80) maps to 0x7f, which it becomes after NOT + + l >>= 7; + for (i=1; l>0; ++i) l >>= 8; + return i; +} + +size_t fmt_asn1intpayload(char* dest,unsigned long l) { + size_t needed=intpayloadlen(l); + if (dest) { + size_t i,n; + /* need to store big endian */ + /* n is the number of bits to shift right for the next octet */ + for (i=0, n=(needed-1)*8; i> n); + } + return needed; +} + +#if 0 size_t fmt_asn1sintpayload(char* dest,signed long l) { size_t needed=sizeof l,i; signed long tmp=0x7f; @@ -20,3 +55,38 @@ size_t fmt_asn1sintpayload(char* dest,signed long l) { } return i; } +#endif + +#include + +#ifdef UNITTEST +#include +#include + +int main() { + assert(sintpayloadlen(0)==1); + assert(sintpayloadlen(0x7f)==1); + assert(sintpayloadlen(0x80)==2); + assert(sintpayloadlen(0x80000000)==5); + if (sizeof(long)==8) assert(sintpayloadlen(0x8000000000000000ul)==9); + + char buf[100]; + buf[1]='!'; + assert(fmt_asn1sintpayload(buf,0)==1 && buf[0]==0 && buf[1]=='!'); + assert(fmt_asn1sintpayload(buf,0x7f)==1 && buf[0]==0x7f && buf[1]=='!'); + buf[2]='!'; + assert(fmt_asn1sintpayload(buf,0x80)==2 && !memcmp(buf,"\x00\x80!",3)); + buf[4]='!'; + assert(fmt_asn1sintpayload(buf,0x7fffffff)==4 && !memcmp(buf,"\x7f\xff\xff\xff!",5)); + + assert(fmt_asn1sintpayload(NULL, 0)==1); + assert(fmt_asn1sintpayload(NULL, 0x7f)==1); + assert(fmt_asn1sintpayload(NULL, 0x80)==2); + assert(fmt_asn1sintpayload(NULL, 0x7fffffff)==4); + + buf[5]='!'; + assert(fmt_asn1sintpayload(buf,-2147483648)==4 && !memcmp(buf,"\x80\x00\x00\x00!",5)); + assert(fmt_asn1sintpayload(buf,0xfffffeff)==5 && !memcmp(buf,"\x00\xff\xff\xfe\xff!",6)); + assert(fmt_asn1sintpayload(NULL, 0xfffffeff)==5); +} +#endif diff --git a/fmt_asn1tag.c b/fmt_asn1tag.c index b2bd1a0..4d42692 100644 --- a/fmt_asn1tag.c +++ b/fmt_asn1tag.c @@ -5,11 +5,27 @@ size_t fmt_asn1tag(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,unsigned long l) { /* encoding is either l%128 or (0x1f,...) */ if (l<0x1f) { - if (dest) *dest=(int)tc+(int)tt+(l&0x1f); + if (dest) *dest=tc + tt + l; return 1; } if (dest) { - *dest=(int)tc+(int)tt+0x1f; ++dest; + *dest=tc + tt + 0x1f; // 0x1f signals variable length encoding follows + ++dest; } return fmt_asn1tagint(dest,l)+1; } + +#ifdef UNITTEST +#include +#include +#undef UNITTEST +#include "fmt_asn1tagint.c" + +int main() { + char buf[100]; + assert(fmt_asn1tag(buf, UNIVERSAL, PRIMITIVE, INTEGER)==1 && buf[0]==2); + assert(fmt_asn1tag(buf, UNIVERSAL, CONSTRUCTED, SEQUENCE_OF)==1 && buf[0]==0x30); + assert(fmt_asn1tag(buf, UNIVERSAL, CONSTRUCTED, 0x1f)==2 && !memcmp(buf,"\x3f\x1f",2)); + /* fmt_asn1tagint has its own unit tests */ +} +#endif diff --git a/fmt_ldapaddrequest.c b/fmt_ldapaddrequest.c index bb4a31b..472fac2 100644 --- a/fmt_ldapaddrequest.c +++ b/fmt_ldapaddrequest.c @@ -28,7 +28,7 @@ size_t fmt_ldapaddrequest(char* dest,const struct AddRequest* a) { dest += fmt_ldapstring(dest, &x->AttributeDescription); dest += fmt_ldapavl(dest, &x->vals); } - assert(n + fmt_asn1SEQUENCE(NULL, sum) + sum == (size_t)(dest-orig)); +// assert(n + fmt_asn1SEQUENCE(NULL, sum) + sum == (size_t)(dest-orig)); return dest-orig; }