asn1: Teach template backend to DEFAULT

This commit is contained in:
Nicolas Williams
2021-01-28 00:05:14 -06:00
parent 3da24c19ad
commit 783b632f1f
5 changed files with 453 additions and 26 deletions

View File

@@ -38,6 +38,11 @@
#ifndef __TEMPLATE_H__
#define __TEMPLATE_H__
/* header:
* HF flags if not a BIT STRING type
* HBF flags if a BIT STRING type
*/
/* tag:
* 0..20 tag
* 21 type
@@ -53,6 +58,11 @@
* 28..31 op
*/
/* defval: (next template entry is defaulted)
*
* DV flags (ptr is or points to defval)
*/
#define A1_OP_MASK (0xf0000000)
#define A1_OP_TYPE (0x10000000)
#define A1_OP_TYPE_EXTERN (0x20000000)
@@ -62,10 +72,12 @@
#define A1_OP_SETOF (0x60000000)
#define A1_OP_BMEMBER (0x70000000)
#define A1_OP_CHOICE (0x80000000)
#define A1_OP_DEFVAL (0x90000000)
#define A1_FLAG_MASK (0x0f000000)
#define A1_FLAG_OPTIONAL (0x01000000)
#define A1_FLAG_IMPLICIT (0x02000000)
#define A1_FLAG_DEFAULT (0x04000000)
#define A1_TAG_T(CLASS,TYPE,TAG) ((A1_OP_TAG) | (((CLASS) << 22) | ((TYPE) << 21) | (TAG)))
#define A1_TAG_CLASS(x) (((x) >> 22) & 0x3)
@@ -87,6 +99,12 @@
#define A1_HBF_RFC1510 0x1
#define A1_DV_BOOLEAN 0x01
#define A1_DV_INTEGER 0x02
#define A1_DV_INTEGER32 0x04
#define A1_DV_INTEGER64 0x08
#define A1_DV_UTF8STRING 0x10
struct asn1_template {
uint32_t tt;

View File

@@ -1888,6 +1888,71 @@ test_seqof5(void)
return ret;
}
static int
cmp_default(void *a, void *b)
{
TESTDefault *aa = a;
TESTDefault *ab = b;
COMPARE_STRING(aa,ab,name);
COMPARE_INTEGER(aa,ab,version);
COMPARE_INTEGER(aa,ab,maxint);
COMPARE_INTEGER(aa,ab,works);
return 0;
}
static int
test_default(void)
{
struct test_case tests[] = {
{ NULL, 2, "\x30\x00", NULL },
{ NULL, 25,
"\x30\x17\x0c\x07\x68\x65\x69\x6d\x64\x61"
"\x6c\xa0\x03\x02\x01\x07\x02\x04\x7f\xff"
"\xff\xff\x01\x01\x00",
NULL
},
{ NULL, 10,
"\x30\x08\xa0\x03\x02\x01\x07\x01\x01\x00",
NULL
},
{ NULL, 17,
"\x30\x0f\x0c\x07\x68\x65\x69\x6d\x64\x61\x6c\x02\x04"
"\x7f\xff\xff\xff",
NULL
}
};
TESTDefault values[] = {
{ "Heimdal", 8, 9223372036854775807, 1 },
{ "heimdal", 7, 2147483647, 0 },
{ "Heimdal", 7, 9223372036854775807, 0 },
{ "heimdal", 8, 2147483647, 1 },
};
int i, ret;
int ntests = sizeof(tests) / sizeof(*tests);
for (i = 0; i < ntests; ++i) {
tests[i].val = &values[i];
if (asprintf (&tests[i].name, "TESTDefault %d", i) < 0)
errx(1, "malloc");
if (tests[i].name == NULL)
errx(1, "malloc");
}
ret = generic_test (tests, ntests, sizeof(TESTDefault),
(generic_encode)encode_TESTDefault,
(generic_length)length_TESTDefault,
(generic_decode)decode_TESTDefault,
(generic_free)free_TESTDefault,
cmp_default,
(generic_copy)copy_TESTDefault);
for (i = 0; i < ntests; ++i)
free(tests[i].name);
return ret;
}
static int
test_x690sample(void)
{
@@ -2005,5 +2070,7 @@ main(int argc, char **argv)
DO_ONE(test_x690sample);
DO_ONE(test_default);
return ret;
}

View File

@@ -54,6 +54,7 @@
*/
#include "gen_locl.h"
#include <vis.h>
static const char *symbol_name(const char *, const Type *);
static void generate_template_type(const char *, const char **, const char *, const char *, const char *,
@@ -182,8 +183,7 @@ bitstring_symbol(const char *basename, const Type *t)
/* XXX Make sure this is sorted by `type' and can just index this by type */
/* XXX Make this const! */
struct {
const struct {
enum typetype type;
const char *(*symbol_name)(const char *, const Type *);
int is_struct;
@@ -499,7 +499,109 @@ compact_tag(const Type *t)
}
static void
template_members(struct templatehead *temp, const char *basetype, const char *name, const Type *t, int optional, int implicit, int isstruct, int need_offset)
defval(struct templatehead *temp, Member *m)
{
switch (m->defval->type) {
case booleanvalue:
add_line(temp, "{ A1_OP_DEFVAL|A1_DV_BOOLEAN, ~0, (void *)%u }",
m->defval->u.booleanvalue);
break;
case nullvalue:
add_line(temp, "{ A1_OP_DEFVAL|A1_DV_NULL, ~0, (void *)0 }");
break;
case integervalue: {
const char *dv = "A1_DV_INTEGER";
Type *t = m->type;
for (;;) {
if (t->range)
break;
if (t->type == TInteger && t->members)
break;
if (t->type == TEnumerated)
break;
if (t->subtype)
t = t->subtype;
else if (t->symbol && t->symbol->type)
t = t->symbol->type;
else
errx(1, "DEFAULT values for unconstrained INTEGER members not supported");
}
if (t->members)
dv = "A1_DV_INTEGER32"; /* XXX Enum size assumptions! No good! */
else if (t->range->min < INT_MIN && t->range->max <= INT64_MAX)
dv = "A1_DV_INTEGER64";
else if (t->range->min >= 0 && t->range->max > UINT_MAX)
dv = "A1_DV_INTEGER64";
else if (t->range->min >= INT_MIN && t->range->max <= INT_MAX)
dv = "A1_DV_INTEGER32";
else if (t->range->min >= 0 && t->range->max <= UINT_MAX)
dv = "A1_DV_INTEGER32";
else
errx(1, "unsupported range %lld -> %lld",
(long long)m->type->range->min, (long long)m->type->range->max);
add_line(temp, "{ A1_OP_DEFVAL|%s, ~0, (void *)%llu }",
dv, (long long)m->defval->u.integervalue);
break;
}
case stringvalue: {
char *quoted;
if (rk_strasvis(&quoted, m->defval->u.stringvalue,
VIS_CSTYLE | VIS_DQ | VIS_NL, "") < 0)
err(1, "Could not quote a string");
add_line(temp, "{ A1_OP_DEFVAL|A1_DV_UTF8STRING, ~0, (void *)\"%s\" }",
quoted);
free(quoted);
break;
}
case objectidentifiervalue: {
struct objid *o;
size_t sz = sizeof("{ }");
char *s, *p;
int len;
for (o = m->defval->u.objectidentifiervalue; o != NULL; o = o->next) {
if ((len = snprintf(0, 0, " %d", o->value)) < 0)
err(1, "Could not format integer");
sz += len;
}
if ((p = s = malloc(sz)) == NULL)
err(1, "Could not allocate string");
len = snprintf(p, sz, "{");
sz -= len;
p += len;
for (o = m->defval->u.objectidentifiervalue; o != NULL; o = o->next) {
if ((len = snprintf(p, sz, " %d", o->value)) < 0 || len > sz - 1)
err(1, "Could not format integer");
sz -= len;
p += len;
}
len = snprintf(p, sz, " }");
sz -= len;
p += len;
add_line(temp, "{ A1_OP_DEFVAL|A1_DV_INTEGER, ~0, (void *)\"%s\" }", s);
free(s);
break;
}
default: abort();
}
}
static void
template_members(struct templatehead *temp,
const char *basetype,
const char *name,
const Type *t,
int optional,
int defaulted,
int implicit,
int isstruct,
int need_offset)
{
char *poffset = NULL;
@@ -511,15 +613,17 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
switch (t->type) {
case TType:
if (use_extern(t->symbol)) {
add_line(temp, "{ A1_OP_TYPE_EXTERN %s%s, %s, &asn1_extern_%s}",
optional ? "|A1_FLAG_OPTIONAL" : "",
implicit ? "|A1_FLAG_IMPLICIT" : "",
add_line(temp, "{ A1_OP_TYPE_EXTERN %s%s%s, %s, &asn1_extern_%s}",
optional ? "|A1_FLAG_OPTIONAL" : "",
defaulted ? "|A1_FLAG_DEFAULT" : "",
implicit ? "|A1_FLAG_IMPLICIT" : "",
poffset, t->symbol->gen_name);
} else {
add_line_pointer(temp, t->symbol->gen_name, poffset,
"A1_OP_TYPE %s%s",
optional ? "|A1_FLAG_OPTIONAL" : "",
implicit ? "|A1_FLAG_IMPLICIT" : "");
"A1_OP_TYPE %s%s%s",
optional ? "|A1_FLAG_OPTIONAL" : "",
defaulted ? "|A1_FLAG_DEFAULT" : "",
implicit ? "|A1_FLAG_IMPLICIT" : "");
}
break;
@@ -650,7 +754,10 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
if (newbasename == NULL)
errx(1, "malloc");
template_members(temp, newbasename, m->gen_name, m->type, m->optional, 0, isstruct, 1);
if (m->defval)
defval(temp, m);
template_members(temp, newbasename, m->gen_name, m->type, m->optional, m->defval ? 1 : 0, 0, isstruct, 1);
free(newbasename);
}
@@ -676,7 +783,10 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
if (newbasename == NULL)
errx(1, "malloc");
template_members(temp, newbasename, m->gen_name, m->type, m->optional, 0, isstruct, 1);
if (m->defval)
defval(temp, m);
template_members(temp, newbasename, m->gen_name, m->type, m->optional, m->defval ? 1 : 0, 0, isstruct, 1);
free(newbasename);
}
@@ -720,11 +830,12 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
t->subtype, 0, subtype_is_struct, 0);
add_line_pointer(temp, dupname, poffset,
"A1_TAG_T(%s,%s,%s)%s%s",
"A1_TAG_T(%s,%s,%s)%s%s%s",
classname(t->tag.tagclass),
prim ? "PRIM" : "CONS",
valuename(t->tag.tagclass, t->tag.tagvalue),
optional ? "|A1_FLAG_OPTIONAL" : "",
optional ? "|A1_FLAG_OPTIONAL" : "",
defaulted ? "|A1_FLAG_DEFAULT" : "",
tagimplicit ? "|A1_FLAG_IMPLICIT" : "");
free(tname);
@@ -900,7 +1011,9 @@ generate_template_type(const char *varname,
const char *basetype,
const char *name,
Type *type,
int optional, int isstruct, int need_offset)
int optional,
int isstruct,
int need_offset)
{
struct tlist *tl;
const char *d;
@@ -920,7 +1033,8 @@ generate_template_type(const char *varname,
implicit = (type->tag.tagenv == TE_IMPLICIT);
}
template_members(&tl->template, basetype, name, type, optional, implicit, isstruct, need_offset);
template_members(&tl->template, basetype, name, type, optional, 0,
implicit, isstruct, need_offset);
/* if its a sequence or set type, check if there is a ellipsis */
if (type->type == TSequence || type->type == TSet) {

View File

@@ -209,6 +209,7 @@ int
_asn1_decode(const struct asn1_template *t, unsigned flags,
const unsigned char *p, size_t len, void *data, size_t *size)
{
const struct asn1_template *tdefval = NULL;
size_t elements = A1_HEADER_LEN(t);
size_t oldlen = len;
int ret = 0;
@@ -223,6 +224,9 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_DEFVAL:
tdefval = t;
break;
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
size_t newsize, elsize;
@@ -250,10 +254,45 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
}
if (ret) {
if (t->tt & A1_FLAG_OPTIONAL) {
/*
* Optional field not present in encoding, presumably,
* though we should really look more carefully at `ret'.
*/
free(*pel);
*pel = NULL;
break;
}
} else if (t->tt & A1_FLAG_DEFAULT) {
/*
* Defaulted field not present in encoding, presumably,
* though we should really look more carefully at `ret'.
*/
if (tdefval->tt & A1_DV_BOOLEAN) {
int *i = (void *)(char *)el;
*i = tdefval->ptr ? 1 : 0;
} else if (tdefval->tt & A1_DV_INTEGER64) {
int64_t *i = (void *)(char *)el;
*i = (int64_t)(intptr_t)tdefval->ptr;
} else if (tdefval->tt & A1_DV_INTEGER32) {
int32_t *i = (void *)(char *)el;
*i = (int32_t)(intptr_t)tdefval->ptr;
} else if (tdefval->tt & A1_DV_INTEGER) {
struct heim_integer *i = (void *)(char *)el;
if ((ret = der_copy_heim_integer(tdefval->ptr, i)))
return ret;
} else if (tdefval->tt & A1_DV_UTF8STRING) {
char **s = el;
if ((*s = strdup(tdefval->ptr)) == NULL)
return ENOMEM;
} else {
abort();
}
break;
}
return ret;
}
p += newsize; len -= newsize;
@@ -269,6 +308,8 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
int subflags = flags;
int replace_tag = (t->tt & A1_FLAG_IMPLICIT) && is_tagged(t->ptr);
data = DPO(data, t->offset);
/*
* XXX If this type (chasing t->ptr through IMPLICIT tags, if this
* one is too, till we find a non-TTag) is a [UNIVERSAL SET] type,
@@ -283,8 +324,42 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
&dertype, A1_TAG_TAG(t->tt),
&datalen, &l);
if (ret) {
if (t->tt & A1_FLAG_OPTIONAL)
if (t->tt & A1_FLAG_OPTIONAL) {
data = olddata;
break;
} else if (t->tt & A1_FLAG_DEFAULT) {
/*
* Defaulted field not present in encoding, presumably,
* though we should really look more carefully at `ret'.
*/
if (tdefval->tt & A1_DV_BOOLEAN) {
int *i = (void *)(char *)data;
*i = tdefval->ptr ? 1 : 0;
} else if (tdefval->tt & A1_DV_INTEGER64) {
int64_t *i = (void *)(char *)data;
*i = (int64_t)(intptr_t)tdefval->ptr;
} else if (tdefval->tt & A1_DV_INTEGER32) {
int32_t *i = (void *)(char *)data;
*i = (int32_t)(intptr_t)tdefval->ptr;
} else if (tdefval->tt & A1_DV_INTEGER) {
struct heim_integer *i = (void *)(char *)data;
if ((ret = der_copy_heim_integer(tdefval->ptr, i)))
return ret;
} else if (tdefval->tt & A1_DV_UTF8STRING) {
char **s = data;
if ((*s = strdup(tdefval->ptr)) == NULL)
return ENOMEM;
} else {
abort();
}
data = olddata;
break;
}
return ret;
}
@@ -316,8 +391,6 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
} else if (datalen > len)
return ASN1_OVERRUN;
data = DPO(data, t->offset);
if (t->tt & A1_FLAG_OPTIONAL) {
void **el = (void **)data;
size_t ellen = _asn1_sizeofType(t->ptr);
@@ -484,7 +557,7 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
/* provide a saner value as default, we should have a NO element value */
*element = 1;
for (i = 1; i < A1_HEADER_LEN(choice) + 1; i++) {
/* should match first tag instead, store it in choice.tt */
ret = _asn1_decode(choice[i].ptr, 0, p, len,
@@ -556,6 +629,7 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_DEFVAL: break;
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
size_t newsize;
@@ -566,7 +640,40 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
if (*pel == NULL)
break;
el = *pel;
}
} else if ((t->tt & A1_FLAG_DEFAULT) && elements > 1) {
const struct asn1_template *tdefval = t - 1;
/* Compare tdefval to whatever's at `el' */
if (tdefval->tt & A1_DV_BOOLEAN) {
const int *i = (void *)(char *)el;
if ((*i && tdefval->ptr) || (!*i && !tdefval->ptr))
break;
} else if (tdefval->tt & A1_DV_INTEGER64) {
const int64_t *i = (void *)(char *)el;
if (*i == (int64_t)(intptr_t)tdefval->ptr)
break;
} else if (tdefval->tt & A1_DV_INTEGER32) {
const int32_t *i = (void *)(char *)el;
if ((int64_t)(intptr_t)tdefval->ptr <= INT_MAX &&
(int64_t)(intptr_t)tdefval->ptr >= INT_MIN &&
*i == (int32_t)(intptr_t)tdefval->ptr)
break;
} else if (tdefval->tt & A1_DV_INTEGER) {
const struct heim_integer *i = (void *)(char *)el;
if (der_heim_integer_cmp(i, tdefval->ptr) == 0)
break;
} else if (tdefval->tt & A1_DV_UTF8STRING) {
const char * const *s = el;
if (*s && strcmp(*s, tdefval->ptr) == 0)
break;
} else {
abort();
}
}
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
ret = _asn1_encode(t->ptr, p, len, el, &newsize);
@@ -605,7 +712,46 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
break;
}
data = *el;
}
} else if ((t->tt & A1_FLAG_DEFAULT) && elements > 1) {
const struct asn1_template *tdefval = t - 1;
int exclude = 0;
/* Compare tdefval to whatever's at `data' */
if (tdefval->tt & A1_DV_BOOLEAN) {
const int *i = (void *)(char *)data;
if ((*i && tdefval->ptr) || (!*i && !tdefval->ptr))
exclude = 1;
} else if (tdefval->tt & A1_DV_INTEGER64) {
const int64_t *i = (void *)(char *)data;
if (*i == (int64_t)(intptr_t)tdefval->ptr)
exclude = 1;
} else if (tdefval->tt & A1_DV_INTEGER32) {
const int32_t *i = (void *)(char *)data;
if ((int64_t)(intptr_t)tdefval->ptr <= INT_MAX &&
(int64_t)(intptr_t)tdefval->ptr >= INT_MIN &&
*i == (int32_t)(intptr_t)tdefval->ptr)
exclude = 1;
} else if (tdefval->tt & A1_DV_INTEGER) {
const struct heim_integer *i = (void *)(char *)data;
if (der_heim_integer_cmp(i, tdefval->ptr) == 0)
break;
} else if (tdefval->tt & A1_DV_UTF8STRING) {
const char * const *s = data;
if (*s && strcmp(*s, tdefval->ptr) == 0)
exclude = 1;
} else {
abort();
}
if (exclude) {
data = olddata;
break;
}
}
replace_tag = (t->tt & A1_FLAG_IMPLICIT) && is_tagged(t->ptr);
@@ -880,6 +1026,7 @@ _asn1_length(const struct asn1_template *t, const void *data)
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_DEFVAL: break;
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
const void *el = DPOC(data, t->offset);
@@ -889,7 +1036,41 @@ _asn1_length(const struct asn1_template *t, const void *data)
if (*pel == NULL)
break;
el = *pel;
}
} else if ((t->tt & A1_FLAG_DEFAULT) && elements > 1) {
const struct asn1_template *tdefval = t - 1;
/* Compare tdefval to whatever's at `el' */
if (tdefval->tt & A1_DV_BOOLEAN) {
const int *i = (void *)(char *)el;
if ((*i && tdefval->ptr) || (!*i && !tdefval->ptr))
break;
} else if (tdefval->tt & A1_DV_INTEGER64) {
const int64_t *i = (void *)(char *)el;
if (*i == (int64_t)(intptr_t)tdefval->ptr)
break;
} else if (tdefval->tt & A1_DV_INTEGER32) {
const int32_t *i = (void *)(char *)el;
if ((int64_t)(intptr_t)tdefval->ptr <= INT_MAX &&
(int64_t)(intptr_t)tdefval->ptr >= INT_MIN &&
*i == (int32_t)(intptr_t)tdefval->ptr)
break;
} else if (tdefval->tt & A1_DV_INTEGER) {
const struct heim_integer *i = (void *)(char *)el;
if (der_heim_integer_cmp(i, tdefval->ptr) == 0)
break;
} else if (tdefval->tt & A1_DV_UTF8STRING) {
const char * const *s = el;
if (*s && strcmp(*s, tdefval->ptr) == 0)
break;
} else {
abort();
}
}
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
ret += _asn1_length(t->ptr, el);
@@ -913,7 +1094,46 @@ _asn1_length(const struct asn1_template *t, const void *data)
break;
}
data = *el;
}
} else if ((t->tt & A1_FLAG_DEFAULT) && elements > 1) {
const struct asn1_template *tdefval = t - 1;
int exclude = 0;
/* Compare tdefval to whatever's at `data' */
if (tdefval->tt & A1_DV_BOOLEAN) {
const int *i = (void *)(char *)data;
if ((*i && tdefval->ptr) || (!*i && !tdefval->ptr))
exclude = 1;
} else if (tdefval->tt & A1_DV_INTEGER64) {
const int64_t *i = (void *)(char *)data;
if (*i == (int64_t)(intptr_t)tdefval->ptr)
exclude = 1;
} else if (tdefval->tt & A1_DV_INTEGER32) {
const int32_t *i = (void *)(char *)data;
if ((int64_t)(intptr_t)tdefval->ptr <= INT_MAX &&
(int64_t)(intptr_t)tdefval->ptr >= INT_MIN &&
*i == (int32_t)(intptr_t)tdefval->ptr)
exclude = 1;
} else if (tdefval->tt & A1_DV_INTEGER) {
const struct heim_integer *i = (void *)(char *)data;
if (der_heim_integer_cmp(i, tdefval->ptr) == 0)
exclude = 1;
} else if (tdefval->tt & A1_DV_UTF8STRING) {
const char * const *s = data;
if (*s && strcmp(*s, tdefval->ptr) == 0)
exclude = 1;
} else {
abort();
}
if (exclude) {
data = olddata;
break;
}
}
if (t->tt & A1_FLAG_IMPLICIT)
oldtaglen = inner_type_taglen(t->ptr);
@@ -1011,6 +1231,7 @@ _asn1_free(const struct asn1_template *t, void *data)
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_DEFVAL: break;
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
void *el = DPO(data, t->offset);
@@ -1121,6 +1342,7 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to)
while (elements) {
switch (t->tt & A1_OP_MASK) {
case A1_OP_DEFVAL: break;
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
const void *fel = DPOC(from, t->offset);

View File

@@ -26,13 +26,19 @@ TESTOutOfOrderBar ::= SEQUENCE {
-- which we well might since XDR's syntax is a dual of a strict subset of
-- ASN.1, and since XDR the encoding is fairly straightforward.
--
-- Note that the `next' member has to be OPTIONAL or DEFAULTed for this to
-- work.
-- Note that the `next' member has to be OPTIONAL for this to work.
TESTCircular ::= SEQUENCE {
name UTF8String,
next TESTCircular OPTIONAL
}
TESTDefault ::= SEQUENCE {
name UTF8String DEFAULT "Heimdal",
version [0] TESTuint32 DEFAULT 8,
maxint TESTuint64 DEFAULT 9223372036854775807,
works BOOLEAN DEFAULT TRUE
}
TESTuint32 ::= INTEGER (0..4294967295)
TESTuint64 ::= INTEGER(0..9223372036854775807)
TESTint64 ::= INTEGER(-9223372036854775808..9223372036854775807)