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"
|
||||
|
||||
size_t scan_asn1derlength(const char* src,size_t len,unsigned long long* length) {
|
||||
const char* orig=src;
|
||||
const char* max=orig+len;
|
||||
if (src>=max) return 0;
|
||||
/* If the highest bit of the first byte is clear, the byte is the length.
|
||||
* Otherwise the next n bytes are the length (n being the lower 7 bits) */
|
||||
if (*src&0x80) {
|
||||
int chars=*src&0x7f;
|
||||
unsigned long long l=0;
|
||||
while (chars>0) {
|
||||
if (++src>=max) return 0;
|
||||
if (l>(((unsigned long long)-1)>>8)) return 0; /* catch integer overflow */
|
||||
l=l*256+(unsigned char)*src;
|
||||
--chars;
|
||||
size_t scan_asn1derlengthvalue(const char* src,size_t len,unsigned long long* value) {
|
||||
if (len==0 || len>=-(uintptr_t)src) return 0;
|
||||
unsigned char i,c=*src;
|
||||
unsigned long long l;
|
||||
if ((c&0x80)==0) {
|
||||
*value=c&0x7f;
|
||||
return 1;
|
||||
}
|
||||
*length=l;
|
||||
} else
|
||||
*length=*src&0x7f;
|
||||
src++;
|
||||
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);
|
||||
/* 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 */
|
||||
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 */
|
||||
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 */
|
||||
*value=l;
|
||||
return i;
|
||||
}
|
||||
|
||||
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