asn1: Teach template interp. about IMPLICIT tags
The earlier fixes to the ASN.1 compiler for IMPLICIT tags did not include the template interpreter. TBD: - TESTImplicit encoding/decoding still fails due to a bug in the template generator. - There are missing cases in the template interpreter. See XXX comments.
This commit is contained in:
@@ -138,6 +138,73 @@ _asn1_bmember_put_bit(unsigned char *p, const void *data, unsigned int bit,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to tell us if the encoding of some type per its template
|
||||
* will have an outer tag. This is needed when the caller wants to slap on an
|
||||
* IMPLICIT tag: if the inner type has a tag then we need to replace it.
|
||||
*/
|
||||
static int
|
||||
is_tagged(const struct asn1_template *t)
|
||||
{
|
||||
size_t elements = A1_HEADER_LEN(t);
|
||||
|
||||
t += A1_HEADER_LEN(t);
|
||||
if (elements != 1)
|
||||
return 0;
|
||||
switch (t->tt & A1_OP_MASK) {
|
||||
case A1_OP_SEQOF: return 0;
|
||||
case A1_OP_SETOF: return 0;
|
||||
case A1_OP_BMEMBER: return 0;
|
||||
case A1_OP_PARSE: return 0;
|
||||
case A1_OP_TAG: return 1;
|
||||
case A1_OP_CHOICE: return 1;
|
||||
case A1_OP_TYPE: return 1;
|
||||
case A1_OP_TYPE_EXTERN: {
|
||||
const struct asn1_type_func *f = t->ptr;
|
||||
|
||||
/*
|
||||
* XXX Add a boolean to struct asn1_type_func to tell us if the type is
|
||||
* tagged or not. Basically, it's not tagged if it's primitive.
|
||||
*/
|
||||
if (f->encode == (asn1_type_encode)encode_heim_any)
|
||||
return 0;
|
||||
abort(); /* XXX */
|
||||
}
|
||||
default: abort();
|
||||
}
|
||||
}
|
||||
|
||||
static size_t
|
||||
inner_type_taglen(const struct asn1_template *t)
|
||||
{
|
||||
size_t elements = A1_HEADER_LEN(t);
|
||||
|
||||
t += A1_HEADER_LEN(t);
|
||||
if (elements != 1)
|
||||
return 0;
|
||||
switch (t->tt & A1_OP_MASK) {
|
||||
case A1_OP_SEQOF: return 0;
|
||||
case A1_OP_SETOF: return 0;
|
||||
case A1_OP_BMEMBER: return 0;
|
||||
case A1_OP_PARSE: return 0;
|
||||
case A1_OP_CHOICE: return 1;
|
||||
case A1_OP_TYPE: return inner_type_taglen(t->ptr);
|
||||
case A1_OP_TAG: return der_length_tag(A1_TAG_TAG(t->tt));
|
||||
case A1_OP_TYPE_EXTERN: {
|
||||
const struct asn1_type_func *f = t->ptr;
|
||||
|
||||
/*
|
||||
* XXX Add a boolean to struct asn1_type_func to tell us if the type is
|
||||
* tagged or not. Basically, it's not tagged if it's primitive.
|
||||
*/
|
||||
if (f->encode == (asn1_type_encode)encode_heim_any)
|
||||
return 0;
|
||||
abort(); /* XXX */
|
||||
}
|
||||
default: abort();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
_asn1_decode(const struct asn1_template *t, unsigned flags,
|
||||
const unsigned char *p, size_t len, void *data, size_t *size)
|
||||
@@ -200,6 +267,7 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
|
||||
void *olddata = data;
|
||||
int is_indefinite = 0;
|
||||
int subflags = flags;
|
||||
int replace_tag = (t->tt & A1_FLAG_IMPLICIT) && is_tagged(t->ptr);
|
||||
|
||||
ret = der_match_tag_and_length(p, len, A1_TAG_CLASS(t->tt),
|
||||
&dertype, A1_TAG_TAG(t->tt),
|
||||
@@ -250,9 +318,23 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
|
||||
data = *el;
|
||||
}
|
||||
|
||||
ret = _asn1_decode(t->ptr, subflags, p, datalen, data, &newsize);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (replace_tag) {
|
||||
const struct asn1_template *subtype = t->ptr;
|
||||
|
||||
if (A1_HEADER_LEN(subtype++) != 1) {
|
||||
ret = _asn1_decode(t->ptr, subflags, p, datalen, data, &newsize);
|
||||
} else {
|
||||
subtype = subtype->ptr;
|
||||
if (A1_HEADER_LEN(subtype++) != 1)
|
||||
ret = _asn1_decode(t->ptr, subflags, p, datalen, data, &newsize);
|
||||
else
|
||||
ret = _asn1_decode(subtype->ptr, subflags, p, datalen, data, &newsize);
|
||||
}
|
||||
} else {
|
||||
ret = _asn1_decode(t->ptr, subflags, p, datalen, data, &newsize);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (is_indefinite) {
|
||||
/* If we use indefinite encoding, the newsize is the datasize. */
|
||||
@@ -477,6 +559,7 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
|
||||
case A1_OP_TAG: {
|
||||
const void *olddata = data;
|
||||
size_t l, datalen;
|
||||
int replace_tag = 0;
|
||||
|
||||
data = DPOC(data, t->offset);
|
||||
|
||||
@@ -489,21 +572,74 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
|
||||
data = *el;
|
||||
}
|
||||
|
||||
ret = _asn1_encode(t->ptr, p, len, data, &datalen);
|
||||
replace_tag = (t->tt & A1_FLAG_IMPLICIT) && is_tagged(t->ptr);
|
||||
|
||||
/* IMPLICIT tags need special handling (see gen_encode.c) */
|
||||
if (replace_tag) {
|
||||
unsigned char *pfree, *psave = p;
|
||||
Der_class found_class;
|
||||
Der_type found_type;
|
||||
unsigned int found_tag;
|
||||
size_t lensave = len;
|
||||
size_t oldtaglen;
|
||||
size_t taglen = der_length_tag(A1_TAG_TAG(t->tt));;
|
||||
|
||||
/* Allocate a buffer at least as big as we need */
|
||||
len = _asn1_length(t->ptr, data) + taglen;
|
||||
if ((p = pfree = malloc(len)) == NULL) {
|
||||
ret = ENOMEM;
|
||||
} else {
|
||||
/*
|
||||
* Encode into it (with the wrong tag, which we'll replace
|
||||
* below).
|
||||
*/
|
||||
p += len - 1;
|
||||
ret = _asn1_encode(t->ptr, p, len, data, &datalen);
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* Get the old tag and, critically, its length */
|
||||
len -= datalen; p -= datalen;
|
||||
ret = der_get_tag(p + 1, datalen, &found_class, &found_type,
|
||||
&found_tag, &oldtaglen);
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* Drop the old tag */
|
||||
len += oldtaglen; p += oldtaglen;
|
||||
/* Put the new tag */
|
||||
ret = der_put_tag(p, len,
|
||||
A1_TAG_CLASS(t->tt),
|
||||
found_type,
|
||||
A1_TAG_TAG(t->tt), &l);
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* Copy the encoding where it belongs */
|
||||
len -= l; p -= l;
|
||||
psave -= (datalen + l - oldtaglen);
|
||||
lensave -= (datalen + l - oldtaglen);
|
||||
memcpy(psave + 1, p + 1, datalen + l - oldtaglen);
|
||||
p = psave;
|
||||
len = lensave;
|
||||
}
|
||||
free(pfree);
|
||||
} else {
|
||||
/* Easy case */
|
||||
ret = _asn1_encode(t->ptr, p, len, data, &datalen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
len -= datalen; p -= datalen;
|
||||
|
||||
ret = der_put_length_and_tag(p, len, datalen,
|
||||
A1_TAG_CLASS(t->tt),
|
||||
A1_TAG_TYPE(t->tt),
|
||||
A1_TAG_TAG(t->tt), &l);
|
||||
if (ret == 0) {
|
||||
p -= l; len -= l;
|
||||
}
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
len -= datalen; p -= datalen;
|
||||
|
||||
ret = der_put_length_and_tag(p, len, datalen,
|
||||
A1_TAG_CLASS(t->tt),
|
||||
A1_TAG_TYPE(t->tt),
|
||||
A1_TAG_TAG(t->tt), &l);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
p -= l; len -= l;
|
||||
|
||||
data = olddata;
|
||||
|
||||
break;
|
||||
@@ -731,6 +867,7 @@ _asn1_length(const struct asn1_template *t, const void *data)
|
||||
case A1_OP_TAG: {
|
||||
size_t datalen;
|
||||
const void *olddata = data;
|
||||
size_t oldtaglen = 0;
|
||||
|
||||
data = DPO(data, t->offset);
|
||||
|
||||
@@ -742,9 +879,14 @@ _asn1_length(const struct asn1_template *t, const void *data)
|
||||
}
|
||||
data = *el;
|
||||
}
|
||||
|
||||
if (t->tt & A1_FLAG_IMPLICIT)
|
||||
oldtaglen = inner_type_taglen(t->ptr);
|
||||
|
||||
datalen = _asn1_length(t->ptr, data);
|
||||
ret += der_length_tag(A1_TAG_TAG(t->tt)) + der_length_len(datalen);
|
||||
ret += datalen;
|
||||
ret += der_length_tag(A1_TAG_TAG(t->tt));
|
||||
ret += oldtaglen ? -oldtaglen : der_length_len(datalen);
|
||||
data = olddata;
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user