Hello,

I attached an improved version of my ASN1 test tool. You give a number
and the tool displays the encoding and decoding using both the old and
new code.

2009/12/13 Andreas Jellinghaus <a...@dungeon.inka.de>:
> Am Sonntag 13 Dezember 2009 14:49:31 schrieb Ludovic Rousseau:
>> 2009/12/13 Andreas Jellinghaus <a...@dungeon.inka.de>:
>> > I tried all four combinations of old/new code for init and for signing.
>> >
>> > only cards initialized with old code and signing with new code is broken,
>> > so we could as quick fix revert the change.
>>
>> I remember doing this patch.
>> The problem was that the ASN.1 encoder/decoder routines were bogus.
>> Negative values and "special" values like 128 were not encoded
>> correctly.
>
> can you explain how the encoding rules exactly are?
> I saw that the old code encodes 145 = 0x91 as integer as
> "02 01 91" (02 = integer tag, 01 = length byte), while the
> new code uses "02 02 00 91" (02 = integer tag, 02 = length byte,
> 00 91 = value 0x0091 = 145).
>
> do the encoding rules give us any clue, maybe "91" is an illegal
> payload?

0x91 is a legal payload but is the encoding of -111.

$ ./asn1 145
converting: 145 (old code)
objsize: 1 byte(s)
91
145 (new code -111)
converting: 145 (new code)
objsize: 2 byte(s)
00 91
145 (old code 145)

The 7th bit of the first byte is the sign. The sign bit was not
correctly managed by the previous code.
$ ./asn1 -1
converting: -1 (old code)
objsize: 4 byte(s)
FF FF FF FF
-1 (new code -1)
converting: -1 (new code)
objsize: 1 byte(s)
FF
-1 (old code 255)

> or does anyone know if for example keyReference needs to be a
> positive integer, so we can run a "fixup" later?
>
> we would need to check all integer in all asn.1 encodings I fear,
> as the same bug could be in all of them. hmm, not possible a grep
> shows 36 entries :(
>
> so I wonder how we can detect the problem and fix it.

The problem is that you can't tell (except one case, see bellow) if an
encoding is using the old or new encoding without knowing that output
values are illegal, like negative values for a keyReference.

> worst case: for starcos our "guess" is keyReference should
> be positive if it is set, so for every value from -2 to -128
> we could simply add 128.

Add 256 instead of 128.

$ ./asn1 -2
converting: -2 (old code)
objsize: 4 byte(s)
FF FF FF FE
-2 (new code -2)
converting: -2 (new code)
objsize: 1 byte(s)
FE
-2 (old code 254)

$ ./asn1 -128
converting: -128 (old code)
objsize: 4 byte(s)
FF FF FF 80
-128 (new code -128)
converting: -128 (new code)
objsize: 1 byte(s)
80
-128 (old code 128)

$ ./asn1 128
converting: 128 (old code)
objsize: 1 byte(s)
80
128 (new code -128)
converting: 128 (new code)
objsize: 2 byte(s)
00 80
128 (old code 128)

$ ./asn1 255
converting: 255 (old code)
objsize: 1 byte(s)
FF
255 (new code -1)
converting: 255 (new code)
objsize: 2 byte(s)
00 FF
255 (old code 255)

> (I'm no starcos expert, at least I hope that key Reference is
> a positive value)
>
> ah, SPK2.4 manual page 28, dummy values are 000xxxxxb,
> real values 100xxxxxb.
>
> So we can implement a hack for key ID, if we find no generic solution.
>
> any ideas for a generic one?

An encoding on more than 1 byte and starting with 0x00 is using the
new/corrected encoder. And it should be correctly decoded by both
decoders. On the other cases I don't think you can differentiate the
encoding.

OpenSC would try to decode using both the old and new code. And decide
which values to use using heuristics (like negative values are
forbidden) but I guess that will soon be a nightmare to maintain.

Sorry for all the problems because of my change.

Regards,

-- 
 Dr. Ludovic Rousseau
#include <stdio.h>
#include <stdlib.h>

typedef unsigned char u8;
#define SC_ERROR_OUT_OF_MEMORY 1
#define SC_ERROR_INVALID_ASN1_OBJECT 2

u8 OBJ[10];

static int old_sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out)
{
	int    a = 0;
	size_t i;

	if (inlen > sizeof(int))
		return SC_ERROR_INVALID_ASN1_OBJECT;
	for (i = 0; i < inlen; i++) {
		a <<= 8;
		a |= *inbuf++;
	}
	*out = a;
	return 0;
}

static int old_asn1_encode_integer(int in, u8 ** obj, size_t * objsize)
{
	int i = sizeof(in) * 8, skip = 1;
	u8 *p, b;

	*obj = p = (u8 *) malloc(sizeof(in));
	if (*obj == NULL)
		return SC_ERROR_OUT_OF_MEMORY;
	do {
		i -= 8;
		b = in >> i;
		if (b == 0 && skip)
			continue;
		skip = 0;
		*p++ = b;
	} while (i > 0);
	*objsize = p - *obj;
	if (*objsize == 0) {
		*objsize = 1;
		(*obj)[0] = 0;
	}
	return 0;
}

static int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out)
{
	int    a = 0;
	size_t i;

	if (inlen > sizeof(int))
		return SC_ERROR_INVALID_ASN1_OBJECT;
	if (inbuf[0] & 0x80)
		a = -1;
	for (i = 0; i < inlen; i++) {
		a <<= 8;
		a |= *inbuf++;
	}
	*out = a;
	return 0;
}


static int asn1_encode_integer(int in, u8 ** obj, size_t * objsize)
{
	int i = sizeof(in) * 8, skip_zero, skip_sign;
	u8 *p, b;

	if (in < 0)
	{
		skip_sign = 1;
		skip_zero= 0;
	}
	else
	{
		skip_sign = 0;
		skip_zero= 1;
	}
	*obj = p = (u8 *) malloc(sizeof(in)+1);
	if (*obj == NULL)
		return SC_ERROR_OUT_OF_MEMORY;
	do {
		i -= 8;
		b = in >> i;
		if (skip_sign)
		{
			if (b != 0xff)
				skip_sign = 0;
			if (b & 0x80)
			{
				*p = b;
				if (0xff == b)
					continue;
			}
			else
			{
				p++;
				skip_sign = 0;
			}
		}
		if (b == 0 && skip_zero)
			continue;
		if (skip_zero) {
			skip_zero = 0;
			/* prepend 0x00 if MSb is 1 and integer positive */
			if ((b & 0x80) != 0 && in > 0)
				*p++ = 0;
		}
		*p++ = b;
	} while (i > 0);
	if (skip_sign)
		p++;
	*objsize = p - *obj;
	if (*objsize == 0) {
		*objsize = 1;
		(*obj)[0] = 0;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	u8 *obj;
	size_t objsize = 0;
	int i, n;

	if (argc != 2)
	{
		printf("usage: %s integer\n", argv[0]);
		return -1;
	}

	//while(1)
	{
	n = atoi(argv[1]);
	//n = rand();
	printf("converting: %d (old code)\n", n);

	old_asn1_encode_integer(n, &obj, &objsize);
	printf("objsize: %d byte(s)\n", objsize);
	for (i=0; i<objsize; i++)
		printf("%02X ", obj[i]);
	printf("\n");

	old_sc_asn1_decode_integer(obj, objsize, &i);
	printf("%d ", i);

	sc_asn1_decode_integer(obj, objsize, &i);
	printf("(new code %d)\n", i);

	printf("converting: %d (new code)\n", n);

	asn1_encode_integer(n, &obj, &objsize);
	printf("objsize: %d byte(s)\n", objsize);
	for (i=0; i<objsize; i++)
		printf("%02X ", obj[i]);
	printf("\n");

	sc_asn1_decode_integer(obj, objsize, &i);
	printf("%d ", i);

	old_sc_asn1_decode_integer(obj, objsize, &i);
	printf("(old code %d)\n", i);

#if 0
	if (i != n)
		printf("ERROR %d %d\n", i, n);
#endif
	}
	return 0;
}
_______________________________________________
opensc-devel mailing list
opensc-devel@lists.opensc-project.org
http://www.opensc-project.org/mailman/listinfo/opensc-devel

Reply via email to