reject non-minimally encoded ASN.1 DER data
split scan_asn1derlength into scan_asn1derlength and scan_asn1derlengthvaluemaster
parent
f4414a2e80
commit
24d1ccb1b7
@ -1,25 +1,30 @@
|
|||||||
#include "scan.h"
|
#include "scan.h"
|
||||||
|
|
||||||
size_t scan_asn1derlength(const char* src,size_t len,unsigned long long* length) {
|
size_t scan_asn1derlengthvalue(const char* src,size_t len,unsigned long long* value) {
|
||||||
const char* orig=src;
|
if (len==0 || len>=-(uintptr_t)src) return 0;
|
||||||
const char* max=orig+len;
|
unsigned char i,c=*src;
|
||||||
if (src>=max) return 0;
|
unsigned long long l;
|
||||||
/* If the highest bit of the first byte is clear, the byte is the length.
|
if ((c&0x80)==0) {
|
||||||
* Otherwise the next n bytes are the length (n being the lower 7 bits) */
|
*value=c&0x7f;
|
||||||
if (*src&0x80) {
|
return 1;
|
||||||
int chars=*src&0x7f;
|
}
|
||||||
unsigned long long l=0;
|
/* Highest bit set: lower 7 bits is the length of the length value in bytes. */
|
||||||
while (chars>0) {
|
c&=0x7f;
|
||||||
if (++src>=max) return 0;
|
if (!c) return 0; /* length 0x80 means indefinite length encoding, not supported here */
|
||||||
if (l>(((unsigned long long)-1)>>8)) return 0; /* catch integer overflow */
|
l=(unsigned char)src[1];
|
||||||
l=l*256+(unsigned char)*src;
|
if (l==0) return 0; /* not minimally encoded: 0x81 0x00 instead of 0x00 */
|
||||||
--chars;
|
if (c>sizeof(l)) return 0; /* too many bytes, does not fit into target integer type */
|
||||||
}
|
for (i=2; i<=c; ++i)
|
||||||
*length=l;
|
l=l*256+(unsigned char)src[i];
|
||||||
} else
|
if (l<0x7f) return 0; /* not minimally encoded: 0x81 0x70 instead of 0x70 */
|
||||||
*length=*src&0x7f;
|
*value=l;
|
||||||
src++;
|
return i;
|
||||||
if (src+*length>max) return 0; /* catch integer overflow */
|
}
|
||||||
if ((uintptr_t)src+*length<(uintptr_t)src) return 0; /* gcc 4.1 removes this check without the cast to uintptr_t */
|
|
||||||
return (size_t)(src-orig);
|
size_t scan_asn1derlength(const char* src,size_t len,unsigned long long* value) {
|
||||||
|
unsigned long long l;
|
||||||
|
size_t i=scan_asn1derlengthvalue(src,len,&l);
|
||||||
|
if (l > len-i) return 0; /* make sure data would fit into buffer */
|
||||||
|
*value=l;
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
.TH scan_asn1derlength 3
|
||||||
|
.SH NAME
|
||||||
|
scan_asn1derlengthvalue \- decode an unsigned integer from ASN.1 DER length encoding
|
||||||
|
.SH SYNTAX
|
||||||
|
.B #include <scan.h>
|
||||||
|
|
||||||
|
size_t \fBscan_asn1derlengthvalue\fP(const char *\fIsrc\fR,size_t \fIlen\fR,unsigned long long *\fIdest\fR);
|
||||||
|
.SH DESCRIPTION
|
||||||
|
scan_asn1derlengthvalue decodes an unsigned integer in ASN.1 DER length encoding
|
||||||
|
from a memory area holding binary data. It writes the decode value in
|
||||||
|
\fIdest\fR and returns the number of bytes it read from \fIsrc\fR.
|
||||||
|
|
||||||
|
scan_asn1derlength never reads more than \fIlen\fR bytes from \fIsrc\fR. If the
|
||||||
|
sequence is longer than that, or the memory area contains an invalid
|
||||||
|
sequence, scan_asn1derlength returns 0 and does not touch \fIdest\fR.
|
||||||
|
|
||||||
|
The length of the longest spec-compliant ASN.1 DER length is 128 bytes,
|
||||||
|
but this implementation will return an error if the value does not fit
|
||||||
|
into the target integer type. In practice the largest sequence is
|
||||||
|
sizeof(*dest)+1.
|
||||||
|
|
||||||
|
This implementation will reject values that are not encoded in the
|
||||||
|
minimum amount of bytes.
|
||||||
|
|
||||||
|
If you need to decode the length value so you can parse actual ASN.1
|
||||||
|
tag/length/value structures, please consider using scan_asn1derlength
|
||||||
|
instead, as it will do additional checking for you.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
fmt_asn1derlength(3), scan_asn1derlengthvalue(3)
|
Loading…
Reference in New Issue