ASN1 lax bit string decoding

Some ASN1 objects stored on some smartcards (for instance the
IASECC/CPX ones) do not comply strictly with the rules
8.6.2.3 and 8.6.2.3 from the ITU.

Since these rules are not some strict ones, let's have a loose
decoding option that can be displayed by the command:
opensc-explorer
  asn1 7001 # for instance

Fix: issue #2224
This commit is contained in:
Vincent JARDIN 2021-02-07 17:25:48 +00:00 committed by Frank Morgner
parent b508349010
commit 4119b2c3e7
3 changed files with 34 additions and 23 deletions

View File

@ -253,10 +253,15 @@ static void sc_asn1_print_bit_string(const u8 * buf, size_t buflen, size_t depth
if (buflen > sizeof(a) + 1) { if (buflen > sizeof(a) + 1) {
print_hex(buf, buflen, depth); print_hex(buf, buflen, depth);
} else { } else {
r = sc_asn1_decode_bit_string(buf, buflen, &a, sizeof(a)); r = sc_asn1_decode_bit_string(buf, buflen, &a, sizeof(a), 1);
if (r < 0) { if (r < 0) {
printf("decode error"); printf("decode error, ");
return; /* try again without the strict mode */
r = sc_asn1_decode_bit_string(buf, buflen, &a, sizeof(a), 0);
if (r < 0) {
printf("even for lax decoding");
return ;
}
} }
for (i = r - 1; i >= 0; i--) { for (i = r - 1; i >= 0; i--) {
printf("%c", ((a >> i) & 1) ? '1' : '0'); printf("%c", ((a >> i) & 1) ? '1' : '0');
@ -567,7 +572,7 @@ const u8 *sc_asn1_verify_tag(sc_context_t *ctx, const u8 * buf, size_t buflen,
} }
static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf, static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf,
size_t outlen, int invert) size_t outlen, int invert, const int strict)
{ {
const u8 *in = inbuf; const u8 *in = inbuf;
u8 *out = (u8 *) outbuf; u8 *out = (u8 *) outbuf;
@ -577,13 +582,19 @@ static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf,
if (inlen < 1) if (inlen < 1)
return SC_ERROR_INVALID_ASN1_OBJECT; return SC_ERROR_INVALID_ASN1_OBJECT;
/* 8.6.2.3 If the bitstring is empty, there shall be no subsequent octets,
* and the initial octet shall be zero. */ /* The formatting is only enforced by SHALL keyword so we should accept
if (inlen == 1 && *in != 0) * by default also non-strict values. */
return SC_ERROR_INVALID_ASN1_OBJECT; if (strict) {
/* ITU-T Rec. X.690 8.6.2.2: The number shall be in the range zero to seven. */ /* 8.6.2.3 If the bitstring is empty, there shall be no
if ((*in & ~0x07) != 0) * subsequent octets,and the initial octet shall be zero. */
return SC_ERROR_INVALID_ASN1_OBJECT; if (inlen == 1 && *in != 0)
return SC_ERROR_INVALID_ASN1_OBJECT;
/* ITU-T Rec. X.690 8.6.2.2: The number shall be in the range zero to seven. */
if ((*in & ~0x07) != 0)
return SC_ERROR_INVALID_ASN1_OBJECT;
}
memset(outbuf, 0, outlen); memset(outbuf, 0, outlen);
zero_bits = *in & 0x07; zero_bits = *in & 0x07;
in++; in++;
@ -622,15 +633,15 @@ static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf,
} }
int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen, int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen,
void *outbuf, size_t outlen) void *outbuf, size_t outlen, const int strict)
{ {
return decode_bit_string(inbuf, inlen, outbuf, outlen, 1); return decode_bit_string(inbuf, inlen, outbuf, outlen, 1, strict);
} }
int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen,
void *outbuf, size_t outlen) void *outbuf, size_t outlen, const int strict)
{ {
return decode_bit_string(inbuf, inlen, outbuf, outlen, 0); return decode_bit_string(inbuf, inlen, outbuf, outlen, 0, strict);
} }
static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf, static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf,
@ -675,7 +686,7 @@ static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf,
* Bitfields are just bit strings, stored in an unsigned int * Bitfields are just bit strings, stored in an unsigned int
* (taking endianness into account) * (taking endianness into account)
*/ */
static int decode_bit_field(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen) static int decode_bit_field(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen, const int strict)
{ {
u8 data[sizeof(unsigned int)]; u8 data[sizeof(unsigned int)];
unsigned int field = 0; unsigned int field = 0;
@ -684,7 +695,7 @@ static int decode_bit_field(const u8 * inbuf, size_t inlen, void *outbuf, size_t
if (outlen != sizeof(data)) if (outlen != sizeof(data))
return SC_ERROR_BUFFER_TOO_SMALL; return SC_ERROR_BUFFER_TOO_SMALL;
n = decode_bit_string(inbuf, inlen, data, sizeof(data), 1); n = decode_bit_string(inbuf, inlen, data, sizeof(data), 1, strict);
if (n < 0) if (n < 0)
return n; return n;
@ -1538,7 +1549,7 @@ static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry,
*len = objlen-1; *len = objlen-1;
parm = *buf; parm = *buf;
} }
r = decode_bit_string(obj, objlen, (u8 *) parm, *len, invert); r = decode_bit_string(obj, objlen, (u8 *) parm, *len, invert, 0);
if (r >= 0) { if (r >= 0) {
*len = r; *len = r;
r = 0; r = 0;
@ -1547,7 +1558,7 @@ static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry,
break; break;
case SC_ASN1_BIT_FIELD: case SC_ASN1_BIT_FIELD:
if (parm != NULL) if (parm != NULL)
r = decode_bit_field(obj, objlen, (u8 *) parm, *len); r = decode_bit_field(obj, objlen, (u8 *) parm, *len, 0);
break; break;
case SC_ASN1_OCTET_STRING: case SC_ASN1_OCTET_STRING:
if (parm != NULL) { if (parm != NULL) {

View File

@ -96,10 +96,10 @@ void sc_asn1_print_tags(const u8 * buf, size_t buflen);
int sc_asn1_utf8string_to_ascii(const u8 * buf, size_t buflen, int sc_asn1_utf8string_to_ascii(const u8 * buf, size_t buflen,
u8 * outbuf, size_t outlen); u8 * outbuf, size_t outlen);
int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen, int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen,
void *outbuf, size_t outlen); void *outbuf, size_t outlen, const int strict);
/* non-inverting version */ /* non-inverting version */
int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen,
void *outbuf, size_t outlen); void *outbuf, size_t outlen, const int strict);
int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out, int strict); int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out, int strict);
int sc_asn1_decode_object_id(const u8 * inbuf, size_t inlen, int sc_asn1_decode_object_id(const u8 * inbuf, size_t inlen,
struct sc_object_id *id); struct sc_object_id *id);

View File

@ -195,7 +195,7 @@ TORTURE_INTEGER(negative, "\xff\x20", -224)
size_t value_len = sizeof(value); \ size_t value_len = sizeof(value); \
int rv; \ int rv; \
\ \
rv = decode_bit_field(data, datalen, &value, value_len); \ rv = decode_bit_field(data, datalen, &value, value_len, 1); \
assert_int_equal(rv, SC_SUCCESS); \ assert_int_equal(rv, SC_SUCCESS); \
assert_int_equal(value, int_value); \ assert_int_equal(value, int_value); \
} }
@ -208,7 +208,7 @@ TORTURE_INTEGER(negative, "\xff\x20", -224)
size_t value_len = sizeof(value); \ size_t value_len = sizeof(value); \
int rv; \ int rv; \
\ \
rv = decode_bit_field(data, datalen, &value, value_len); \ rv = decode_bit_field(data, datalen, &value, value_len, 1); \
assert_int_equal(rv, error); \ assert_int_equal(rv, error); \
} }
/* Without the Tag (0x03) and Length */ /* Without the Tag (0x03) and Length */