mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-24 22:00:18 +00:00
469 lines
12 KiB
C
469 lines
12 KiB
C
/* radare2 - LGPL - Copyright 2017-2022 - wargio, pancake */
|
|
|
|
#include <r_cons.h>
|
|
#include <r_util.h>
|
|
|
|
static R_TH_LOCAL int ASN1_STD_FORMAT = 1;
|
|
static R_TH_LOCAL char temp_name[4096] = {0};
|
|
|
|
R_API void asn1_setformat(int fmt) {
|
|
ASN1_STD_FORMAT = fmt;
|
|
}
|
|
|
|
static ut32 asn1_ber_indefinite(const ut8 *buffer, ut32 length) {
|
|
if (!buffer || length < 3) {
|
|
return 0;
|
|
}
|
|
const ut8* next = buffer + 2;
|
|
const ut8* end = buffer + length - 3;
|
|
while (next + 1 < end) {
|
|
if (!next[0] && !next[1]) {
|
|
break;
|
|
}
|
|
if (next[0] == 0x80 && (next[-1] & ASN1_FORM) == FORM_CONSTRUCTED) {
|
|
next --;
|
|
int sz = asn1_ber_indefinite (next, end - next);
|
|
if (sz < 1) {
|
|
break;
|
|
}
|
|
next += sz;
|
|
}
|
|
next++;
|
|
}
|
|
return (next - buffer) + 2;
|
|
}
|
|
|
|
static RASN1Object *asn1_parse_header(const ut8 *buffer, ut32 length, const ut8 *start_pointer) {
|
|
ut8 head, length8, byte;
|
|
ut64 length64;
|
|
if (!buffer || length < 3) {
|
|
return NULL;
|
|
}
|
|
|
|
RASN1Object *object = R_NEW0 (RASN1Object);
|
|
if (!object) {
|
|
return NULL;
|
|
}
|
|
head = buffer[0];
|
|
object->offset = start_pointer ? (buffer - start_pointer) : 0;
|
|
object->klass = head & ASN1_CLASS;
|
|
object->form = head & ASN1_FORM;
|
|
object->tag = head & ASN1_TAG;
|
|
length8 = buffer[1];
|
|
if (length8 & ASN1_LENLONG) {
|
|
length64 = 0;
|
|
length8 &= ASN1_LENSHORT;
|
|
object->sector = buffer + 2;
|
|
if (length8 && length8 < length - 2) {
|
|
ut8 i8;
|
|
// can overflow.
|
|
for (i8 = 0; i8 < length8; i8++) {
|
|
byte = buffer[2 + i8];
|
|
length64 <<= 8;
|
|
length64 |= byte;
|
|
if (length64 > length) {
|
|
goto out_error;
|
|
}
|
|
}
|
|
object->sector += length8;
|
|
} else {
|
|
if (length < 3) {
|
|
goto out_error;
|
|
}
|
|
length64 = asn1_ber_indefinite (object->sector, length - 2);
|
|
}
|
|
object->length = (ut32) length64;
|
|
} else {
|
|
object->length = (ut32) length8;
|
|
object->sector = buffer + 2;
|
|
}
|
|
|
|
if (object->tag == TAG_BITSTRING && object->sector[0] == 0) {
|
|
if (object->length > 0) {
|
|
object->sector++; // real sector starts + 1
|
|
object->length--;
|
|
}
|
|
}
|
|
if (object->length > length) {
|
|
// Malformed object - overflow from data ptr
|
|
goto out_error;
|
|
}
|
|
return object;
|
|
out_error:
|
|
free (object);
|
|
return NULL;
|
|
}
|
|
|
|
static ut32 r_asn1_count_objects(const ut8 *buffer, ut32 length) {
|
|
r_return_val_if_fail (buffer, 0);
|
|
if (!length) {
|
|
return 0;
|
|
}
|
|
ut32 counter = 0;
|
|
RASN1Object *object = NULL;
|
|
const ut8 *next = buffer;
|
|
const ut8 *end = buffer + length;
|
|
while (next >= buffer && next < end) {
|
|
// i do not care about the offset now.
|
|
object = asn1_parse_header (next, end - next, 0);
|
|
if (!object || next == object->sector) {
|
|
R_FREE (object);
|
|
break;
|
|
}
|
|
next = object->sector + object->length;
|
|
counter++;
|
|
R_FREE (object);
|
|
}
|
|
R_FREE (object);
|
|
return counter;
|
|
}
|
|
|
|
R_API RASN1Object *r_asn1_create_object(const ut8 *buffer, ut32 length, const ut8 *start_pointer) {
|
|
RASN1Object *object = asn1_parse_header (buffer, length, start_pointer);
|
|
if (object && (object->form == FORM_CONSTRUCTED || object->tag == TAG_BITSTRING || object->tag == TAG_OCTETSTRING)) {
|
|
const ut8 *next = object->sector;
|
|
const ut8 *end = next + object->length;
|
|
if (end > buffer + length) {
|
|
free (object);
|
|
return NULL;
|
|
}
|
|
ut32 count = r_asn1_count_objects (object->sector, object->length);
|
|
if (count > 0) {
|
|
object->list.length = count;
|
|
object->list.objects = R_NEWS0 (RASN1Object*, count);
|
|
if (!object->list.objects) {
|
|
r_asn1_free_object (object);
|
|
return NULL;
|
|
}
|
|
ut32 i;
|
|
for (i = 0; next >= buffer && next < end && i < count; i++) {
|
|
RASN1Object *inner = r_asn1_create_object (next, end - next, start_pointer);
|
|
if (!inner || next == inner->sector) {
|
|
r_asn1_free_object (inner);
|
|
break;
|
|
}
|
|
next = inner->sector + inner->length;
|
|
object->list.objects[i] = inner;
|
|
}
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
|
|
R_API RASN1Binary *r_asn1_create_binary(const ut8 *buffer, ut32 length) {
|
|
if (!buffer || !length) {
|
|
return NULL;
|
|
}
|
|
ut8* buf = (ut8*) calloc (1, length);
|
|
if (!buf) {
|
|
return NULL;
|
|
}
|
|
RASN1Binary* bin = R_NEW0 (RASN1Binary);
|
|
if (!bin) {
|
|
free (buf);
|
|
return NULL;
|
|
}
|
|
memcpy (buf, buffer, length);
|
|
bin->binary = buf;
|
|
bin->length = length;
|
|
return bin;
|
|
}
|
|
|
|
R_API void r_asn1_print_hex(RASN1Object *object, char* buffer, ut32 size, ut32 depth) {
|
|
ut32 i;
|
|
if (!object || !object->sector) {
|
|
return;
|
|
}
|
|
char* p = buffer;
|
|
char* end = buffer + size;
|
|
if (depth > 0 && !ASN1_STD_FORMAT) {
|
|
const char *pad = r_str_pad (' ', (depth * 2) - 2);
|
|
snprintf (p, end - p, "%s", pad);
|
|
p += strlen(pad);
|
|
}
|
|
for (i = 0; i < object->length && p < end; i++) {
|
|
snprintf (p, end - p, "%02x", object->sector[i]);
|
|
p += 2;
|
|
}
|
|
if (p >= end) {
|
|
p -= 4;
|
|
snprintf (p, end - p, "...");
|
|
}
|
|
}
|
|
|
|
#if !ASN1_STD_FORMAT
|
|
static void r_asn1_print_padded(RStrBuf *sb, RASN1Object *object, int depth, const char *k, const char *v) {
|
|
const char *pad = r_str_pad (' ', (depth * 2) - 2);
|
|
if (object->form && !*v) {
|
|
return;
|
|
}
|
|
switch (object->tag) {
|
|
case TAG_NULL:
|
|
case TAG_EOC:
|
|
break;
|
|
case TAG_INTEGER:
|
|
case TAG_REAL:
|
|
if (*r_str_trim_head_ro (v)) {
|
|
r_strbuf_appendf (sb, "%s%s\n%s%s\n", pad, k, pad, v);
|
|
}
|
|
break;
|
|
case TAG_BITSTRING:
|
|
default:
|
|
if (*r_str_trim_head_ro (v)) {
|
|
r_strbuf_appendf (sb, "%s%s\n", pad, v);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static RASN1String* r_asn1_print_hexdump_padded(RASN1Object *object, ut32 depth) {
|
|
const char *pad;
|
|
ut32 i, j;
|
|
char readable[20] = {0};
|
|
if (!object || !object->sector || object->length < 1) {
|
|
return NULL;
|
|
}
|
|
RStrBuf *sb = r_strbuf_new ("");
|
|
if (ASN1_STD_FORMAT) {
|
|
pad = " : ";
|
|
} else {
|
|
pad = r_str_pad (' ', depth * 2);
|
|
r_strbuf_append (sb, " ");
|
|
}
|
|
|
|
for (i = 0, j = 0; i < object->length; i++, j++) {
|
|
ut8 c = object->sector[i];
|
|
if (i > 0 && (i % 16) == 0) {
|
|
r_strbuf_appendf (sb, "|%-16s|\n%s", readable, pad);
|
|
memset (readable, 0, sizeof (readable));
|
|
j = 0;
|
|
}
|
|
r_strbuf_appendf (sb, "%02x ", c);
|
|
readable[j] = IS_PRINTABLE(c) ? c : '.';
|
|
}
|
|
|
|
while ((i % 16) != 0) {
|
|
r_strbuf_append (sb, " ");
|
|
i++;
|
|
}
|
|
r_strbuf_appendf (sb, "|%-16s|", readable);
|
|
char* text = r_strbuf_drain (sb);
|
|
RASN1String* asn1str = r_asn1_create_string (text, true, strlen (text) + 1);
|
|
if (!asn1str) {
|
|
/* no memory left.. */
|
|
free (text);
|
|
}
|
|
return asn1str;
|
|
}
|
|
|
|
R_API char *r_asn1_to_string(RASN1Object *object, ut32 depth, RStrBuf *sb) {
|
|
ut32 i;
|
|
bool root = false;
|
|
if (!object) {
|
|
return NULL;
|
|
}
|
|
if (!sb) {
|
|
sb = r_strbuf_new ("");
|
|
root = true;
|
|
}
|
|
//this shall not be freed. it's a pointer into the buffer.
|
|
RASN1String* asn1str = NULL;
|
|
const char* name = "";
|
|
const char* string = "";
|
|
|
|
switch (object->klass) {
|
|
case CLASS_UNIVERSAL: // universal
|
|
switch (object->tag) {
|
|
case TAG_EOC:
|
|
name = "EOC";
|
|
break;
|
|
case TAG_BOOLEAN:
|
|
name = "BOOLEAN";
|
|
if (object->sector) {
|
|
string = (object->sector[0] != 0) ? "true" : "false";
|
|
}
|
|
break;
|
|
case TAG_INTEGER:
|
|
name = "INTEGER";
|
|
if (object->length < 16) {
|
|
r_asn1_print_hex (object, temp_name, sizeof (temp_name), depth);
|
|
string = temp_name;
|
|
} else {
|
|
asn1str = r_asn1_print_hexdump_padded (object, depth);
|
|
}
|
|
break;
|
|
case TAG_BITSTRING:
|
|
name = "BIT_STRING";
|
|
if (!object->list.objects) {
|
|
if (object->length < 16) {
|
|
r_asn1_print_hex (object, temp_name, sizeof (temp_name), depth);
|
|
string = temp_name;
|
|
} else {
|
|
asn1str = r_asn1_print_hexdump_padded (object, depth);
|
|
}
|
|
}
|
|
break;
|
|
case TAG_OCTETSTRING:
|
|
name = "OCTET_STRING";
|
|
if (r_str_is_printable_limited ((const char *)object->sector, object->length)) {
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
} else if (!object->list.objects) {
|
|
if (object->length < 16) {
|
|
r_asn1_print_hex (object, temp_name, sizeof (temp_name), depth);
|
|
string = temp_name;
|
|
} else {
|
|
asn1str = r_asn1_print_hexdump_padded (object, depth);
|
|
}
|
|
}
|
|
break;
|
|
case TAG_NULL:
|
|
name = "NULL";
|
|
break;
|
|
case TAG_OID:
|
|
name = "OBJECT_IDENTIFIER";
|
|
asn1str = r_asn1_stringify_oid (object->sector, object->length);
|
|
break;
|
|
case TAG_OBJDESCRIPTOR:
|
|
name = "OBJECT_DESCRIPTOR";
|
|
break;
|
|
case TAG_EXTERNAL:
|
|
name = "EXTERNAL";
|
|
break;
|
|
case TAG_REAL:
|
|
name = "REAL";
|
|
asn1str = r_asn1_print_hexdump_padded (object, depth);
|
|
break;
|
|
case TAG_ENUMERATED:
|
|
name = "ENUMERATED";
|
|
break;
|
|
case TAG_EMBEDDED_PDV:
|
|
name = "EMBEDDED_PDV";
|
|
break;
|
|
case TAG_UTF8STRING:
|
|
name = "UTF8String";
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_SEQUENCE:
|
|
name = "SEQUENCE";
|
|
break;
|
|
case TAG_SET:
|
|
name = "SET";
|
|
break;
|
|
case TAG_NUMERICSTRING:
|
|
name = "NumericString";
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_PRINTABLESTRING:
|
|
name = "PrintableString"; // ASCII subset
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_T61STRING:
|
|
name = "TeletexString"; // aka T61String
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_VIDEOTEXSTRING:
|
|
name = "VideotexString";
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_IA5STRING:
|
|
name = "IA5String"; // ASCII
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_UTCTIME:
|
|
name = "UTCTime";
|
|
asn1str = r_asn1_stringify_utctime (object->sector, object->length);
|
|
break;
|
|
case TAG_GENERALIZEDTIME:
|
|
name = "GeneralizedTime";
|
|
asn1str = r_asn1_stringify_time (object->sector, object->length);
|
|
break;
|
|
case TAG_GRAPHICSTRING:
|
|
name = "GraphicString";
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_VISIBLESTRING:
|
|
name = "VisibleString"; // ASCII subset
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_GENERALSTRING:
|
|
name = "GeneralString";
|
|
break;
|
|
case TAG_UNIVERSALSTRING:
|
|
name = "UniversalString";
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
case TAG_BMPSTRING:
|
|
name = "BMPString";
|
|
asn1str = r_asn1_stringify_string (object->sector, object->length);
|
|
break;
|
|
default:
|
|
snprintf (temp_name, sizeof (temp_name), "Universal_%u", object->tag);
|
|
name = temp_name;
|
|
break;
|
|
}
|
|
break;
|
|
case CLASS_APPLICATION:
|
|
snprintf (temp_name, sizeof (temp_name), "Application_%u", object->tag);
|
|
name = temp_name;
|
|
break;
|
|
case CLASS_CONTEXT:
|
|
snprintf (temp_name, sizeof (temp_name), "Context [%u]", object->tag); // Context
|
|
name = temp_name;
|
|
break;
|
|
case CLASS_PRIVATE:
|
|
snprintf (temp_name, sizeof (temp_name), "Private_%u", object->tag);
|
|
name = temp_name;
|
|
break;
|
|
}
|
|
if (asn1str) {
|
|
string = asn1str->string;
|
|
}
|
|
if (ASN1_STD_FORMAT) {
|
|
r_strbuf_appendf (sb, "%4"PFMT64d" ", object->offset);
|
|
r_strbuf_appendf (sb, "%4u:%2d: %s %-20s: %s\n", object->length,
|
|
depth, object->form ? "cons" : "prim", name, string);
|
|
r_asn1_free_string (asn1str);
|
|
if (object->list.objects) {
|
|
for (i = 0; i < object->list.length; i++) {
|
|
r_asn1_to_string (object->list.objects[i], depth + 1, sb);
|
|
}
|
|
}
|
|
} else {
|
|
r_asn1_print_padded (sb, object, depth, name, string);
|
|
r_asn1_free_string (asn1str);
|
|
if (object->list.objects) {
|
|
for (i = 0; i < object->list.length; i++) {
|
|
RASN1Object *obj = object->list.objects[i];
|
|
r_asn1_to_string (obj, depth + 1, sb);
|
|
}
|
|
}
|
|
}
|
|
return root? r_strbuf_drain (sb): NULL;
|
|
}
|
|
|
|
R_API void r_asn1_free_object(RASN1Object *object) {
|
|
ut32 i;
|
|
if (!object) {
|
|
return;
|
|
}
|
|
// This shall not be freed. it's a pointer into the buffer.
|
|
object->sector = NULL;
|
|
if (object->list.objects) {
|
|
for (i = 0; i < object->list.length; i++) {
|
|
r_asn1_free_object (object->list.objects[i]);
|
|
}
|
|
R_FREE (object->list.objects);
|
|
}
|
|
object->list.objects = NULL;
|
|
object->list.length = 0;
|
|
free (object);
|
|
}
|
|
|
|
R_API void r_asn1_free_binary(RASN1Binary* bin) {
|
|
if (bin) {
|
|
free (bin->binary);
|
|
free (bin);
|
|
}
|
|
}
|