proxmark3/client/crypto/asn1utils.c

327 lines
7.5 KiB
C
Raw Normal View History

2018-11-08 00:05:02 +08:00
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// asn.1 utils
//-----------------------------------------------------------------------------
#include "asn1utils.h"
2018-11-15 17:47:09 +08:00
#include <ctype.h>
2018-11-08 00:05:02 +08:00
#include <mbedtls/asn1.h>
2018-11-15 02:44:32 +08:00
#include <mbedtls/oid.h>
#include "util.h"
#include "emv/tlv.h"
#include "emv/emv_tags.h"
#include "emv/dump.h"
2018-11-08 00:05:02 +08:00
int ecdsa_asn1_get_signature(uint8_t *signature, size_t signaturelen, uint8_t *rval, uint8_t *sval) {
if (!signature || !signaturelen || !rval || !sval)
return 1;
int res = 0;
unsigned char *p = signature;
const unsigned char *end = p + signaturelen;
size_t len;
mbedtls_mpi xmpi;
if ((res = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) == 0) {
mbedtls_mpi_init(&xmpi);
res = mbedtls_asn1_get_mpi(&p, end, &xmpi);
if (res) {
mbedtls_mpi_free(&xmpi);
goto exit;
}
res = mbedtls_mpi_write_binary(&xmpi, rval, 32);
mbedtls_mpi_free(&xmpi);
if (res)
goto exit;
mbedtls_mpi_init(&xmpi);
res = mbedtls_asn1_get_mpi(&p, end, &xmpi);
if (res) {
mbedtls_mpi_free(&xmpi);
goto exit;
}
res = mbedtls_mpi_write_binary(&xmpi, sval, 32);
mbedtls_mpi_free(&xmpi);
if (res)
goto exit;
// check size
if (end != p)
return 2;
}
exit:
return res;
}
2018-11-15 02:44:32 +08:00
#define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, " ");}
enum asn1_tag_t {
ASN1_TAG_GENERIC,
ASN1_TAG_BOOLEAN,
ASN1_TAG_INTEGER,
ASN1_TAG_STRING,
2018-11-15 17:47:09 +08:00
ASN1_TAG_OCTET_STRING,
2018-11-15 02:44:32 +08:00
ASN1_TAG_UTC_TIME,
2018-11-15 17:47:09 +08:00
ASN1_TAG_STR_TIME,
2018-11-15 02:44:32 +08:00
ASN1_TAG_OBJECT_ID,
};
struct asn1_tag {
tlv_tag_t tag;
char *name;
enum asn1_tag_t type;
const void *data;
};
static const struct asn1_tag asn1_tags[] = {
// internal
{ 0x00 , "Unknown ???" },
// ASN.1
{ 0x01, "BOOLEAN", ASN1_TAG_BOOLEAN },
{ 0x02, "INTEGER", ASN1_TAG_INTEGER },
{ 0x03, "BIT STRING" },
2018-11-15 17:47:09 +08:00
{ 0x04, "OCTET STRING", ASN1_TAG_OCTET_STRING},
2018-11-15 02:44:32 +08:00
{ 0x05, "NULL" },
{ 0x06, "OBJECT IDENTIFIER", ASN1_TAG_OBJECT_ID },
{ 0x0C, "UTF8String", ASN1_TAG_STRING },
{ 0x10, "SEQUENCE" },
{ 0x11, "SET" },
{ 0x13, "PrintableString", ASN1_TAG_STRING },
{ 0x14, "T61String", ASN1_TAG_STRING },
{ 0x16, "IA5String", ASN1_TAG_STRING },
{ 0x17, "UTCTime", ASN1_TAG_UTC_TIME },
2018-11-15 17:47:09 +08:00
{ 0x18, "GeneralizedTime", ASN1_TAG_STR_TIME },
2018-11-15 02:44:32 +08:00
{ 0x30, "SEQUENCE" },
{ 0x31, "SET" },
{ 0xa0, "[0]" },
{ 0xa1, "[1]" },
{ 0xa2, "[2]" },
{ 0xa3, "[3]" },
{ 0xa4, "[4]" },
{ 0xa5, "[5]" },
};
static int asn1_sort_tag(tlv_tag_t tag) {
return (int)(tag >= 0x100 ? tag : tag << 8);
}
static int asn1_tlv_compare(const void *a, const void *b) {
const struct tlv *tlv = a;
const struct asn1_tag *tag = b;
return asn1_sort_tag(tlv->tag) - (asn1_sort_tag(tag->tag));
}
static const struct asn1_tag *asn1_get_tag(const struct tlv *tlv) {
struct asn1_tag *tag = bsearch(tlv, asn1_tags, sizeof(asn1_tags) / sizeof(asn1_tags[0]),
sizeof(asn1_tags[0]), asn1_tlv_compare);
return tag ? tag : &asn1_tags[0];
}
2018-11-15 17:47:09 +08:00
static void asn1_tag_dump_str_time(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level, bool *needdump){
int len = tlv->len;
*needdump = true;
if (len > 4) {
fprintf(f, "\tvalue: '");
while (true) {
// year
fwrite(tlv->value, 1, 4, f);
fprintf(f, "-");
if (len < 6)
break;
// month
fwrite(&tlv->value[4], 1, 2, f);
fprintf(f, "-");
if (len < 8)
break;
// day
fwrite(&tlv->value[6], 1, 2, f);
fprintf(f, " ");
if (len < 10)
break;
// hour
fwrite(&tlv->value[8], 1, 2, f);
fprintf(f, ":");
if (len < 12)
break;
// min
fwrite(&tlv->value[10], 1, 2, f);
fprintf(f, ":");
if (len < 14)
break;
// sec
fwrite(&tlv->value[12], 1, 2, f);
if (len < 15)
break;
// time zone
fprintf(f, " zone: %.*s", len - 14, &tlv->value[14]);
break;
}
fprintf(f, "'\n");
} else {
fprintf(f, "\n");
*needdump = true;
}
}
2018-11-15 02:44:32 +08:00
static void asn1_tag_dump_string(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level){
fprintf(f, "\tvalue: '");
fwrite(tlv->value, 1, tlv->len, f);
fprintf(f, "'\n");
}
2018-11-15 17:47:09 +08:00
static void asn1_tag_dump_octet_string(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level, bool *needdump){
*needdump = false;
for (int i = 0; i < tlv->len; i++)
if (!isspace(tlv->value[i]) && !isprint(tlv->value[i])){
*needdump = true;
break;
}
if (*needdump) {
fprintf(f, "'\n");
} else {
fprintf(f, "\t\t");
asn1_tag_dump_string(tlv, tag, f, level);
}
}
2018-11-15 02:44:32 +08:00
static unsigned long asn1_value_integer(const struct tlv *tlv, unsigned start, unsigned end) {
unsigned long ret = 0;
int i;
if (end > tlv->len * 2)
return ret;
if (start >= end)
return ret;
if (start & 1) {
ret += tlv->value[start/2] & 0xf;
i = start + 1;
} else
i = start;
for (; i < end - 1; i += 2) {
ret *= 10;
ret += tlv->value[i/2] >> 4;
ret *= 10;
ret += tlv->value[i/2] & 0xf;
}
if (end & 1) {
ret *= 10;
ret += tlv->value[end/2] >> 4;
}
return ret;
}
static void asn1_tag_dump_boolean(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level) {
PRINT_INDENT(level);
if (tlv->len > 0) {
fprintf(f, "\tvalue: %s\n", tlv->value[0]?"true":"false");
} else {
fprintf(f, "n/a\n");
}
}
static void asn1_tag_dump_integer(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level) {
PRINT_INDENT(level);
fprintf(f, "\tvalue: %lu\n", asn1_value_integer(tlv, 0, tlv->len * 2));
}
static void asn1_tag_dump_object_id(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level) {
PRINT_INDENT(level);
mbedtls_asn1_buf asn1_buf;
asn1_buf.len = tlv->len;
asn1_buf.p = (uint8_t *)tlv->value;
char pstr[300];
mbedtls_oid_get_numeric_string(pstr, sizeof(pstr), &asn1_buf);
fprintf(f, " %s\n", pstr);
}
bool asn1_tag_dump(const struct tlv *tlv, FILE *f, int level, bool *candump) {
if (!tlv) {
fprintf(f, "NULL\n");
return false;
}
const struct asn1_tag *tag = asn1_get_tag(tlv);
PRINT_INDENT(level);
fprintf(f, "--%2hx[%02zx] '%s':", tlv->tag, tlv->len, tag->name);
switch (tag->type) {
case ASN1_TAG_GENERIC:
fprintf(f, "\n");
break;
case ASN1_TAG_STRING:
asn1_tag_dump_string(tlv, tag, f, level);
*candump = false;
break;
2018-11-15 17:47:09 +08:00
case ASN1_TAG_OCTET_STRING:
asn1_tag_dump_octet_string(tlv, tag, f, level, candump);
break;
2018-11-15 02:44:32 +08:00
case ASN1_TAG_BOOLEAN:
asn1_tag_dump_boolean(tlv, tag, f, level);
*candump = false;
break;
case ASN1_TAG_INTEGER:
asn1_tag_dump_integer(tlv, tag, f, level);
*candump = false;
break;
case ASN1_TAG_UTC_TIME:
// asn1_tag_dump_utc_time(tlv, tag, f, level);
fprintf(f, "\n");
break;
2018-11-15 17:47:09 +08:00
case ASN1_TAG_STR_TIME:
asn1_tag_dump_str_time(tlv, tag, f, level, candump);
break;
2018-11-15 02:44:32 +08:00
case ASN1_TAG_OBJECT_ID:
asn1_tag_dump_object_id(tlv, tag, f, level);
*candump = false;
break;
};
return true;
}
static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) {
bool candump = true;
asn1_tag_dump(tlv, stdout, level, &candump);
if (is_leaf && candump) {
dump_buffer(tlv->value, tlv->len, stdout, level);
}
return true;
}
2018-11-15 00:44:17 +08:00
int asn1_print(uint8_t *asn1buf, size_t asn1buflen, char *indent) {
2018-11-08 00:05:02 +08:00
2018-11-15 02:44:32 +08:00
struct tlvdb *t = NULL;
t = tlvdb_parse_multi(asn1buf, asn1buflen);
if (t) {
tlvdb_visit(t, print_cb, NULL, 0);
tlvdb_free(t);
} else {
PrintAndLogEx(ERR, "Can't parse data as TLV tree.");
return 1;
}
2018-11-08 00:05:02 +08:00
return 0;
}