summaryrefslogtreecommitdiffstats
path: root/src/ber_tlv_tag.c
blob: 42708760e089948b405743678efbc56ddd836c87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*-
 * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
 * Redistribution and modifications are permitted subject to BSD license.
 */
#include <asn_internal.h>
#include <ber_tlv_tag.h>
#include <errno.h>

ssize_t
ber_fetch_tag(const void *ptr, size_t size, ber_tlv_tag_t *tag_r) {
	ber_tlv_tag_t val;
	ber_tlv_tag_t tclass;
	size_t skipped;

	if(size == 0)
		return 0;

	val = *(const uint8_t *)ptr;
	tclass = (val >> 6);
	if((val &= 0x1F) != 0x1F) {
		/*
		 * Simple form: everything encoded in a single octet.
		 * Tag Class is encoded using two least significant bits.
		 */
		*tag_r = (val << 2) | tclass;
		return 1;
	}

	/*
	 * Each octet contains 7 bits of useful information.
	 * The MSB is 0 if it is the last octet of the tag.
	 */
	for(val = 0, ptr = ((const char *)ptr) + 1, skipped = 2;
			skipped <= size;
				ptr = ((const char *)ptr) + 1, skipped++) {
		unsigned int oct = *(const uint8_t *)ptr;
		if(oct & 0x80) {
			val = (val << 7) | (oct & 0x7F);
			/*
			 * Make sure there are at least 9 bits spare
			 * at the MS side of a value.
			 */
			if(val >> ((8 * sizeof(val)) - 9)) {
				/*
				 * We would not be able to accomodate
				 * any more tag bits.
				 */
				return -1;
			}
		} else {
			val = (val << 7) | oct;
			*tag_r = (val << 2) | tclass;
			return skipped;
		}
	}

	return 0;	/* Want more */
}


ssize_t
ber_tlv_tag_fwrite(ber_tlv_tag_t tag, FILE *f) {
	char buf[sizeof("[APPLICATION ]") + 32];
	ssize_t ret;

	ret = ber_tlv_tag_snprint(tag, buf, sizeof(buf));
	if(ret >= (ssize_t)sizeof(buf) || ret < 2) {
		errno = EPERM;
		return -1;
	}

	return fwrite(buf, 1, ret, f);
}

ssize_t
ber_tlv_tag_snprint(ber_tlv_tag_t tag, char *buf, size_t size) {
	char *type = 0;
	int ret;

	switch(tag & 0x3) {
	case ASN_TAG_CLASS_UNIVERSAL:	type = "UNIVERSAL ";	break;
	case ASN_TAG_CLASS_APPLICATION:	type = "APPLICATION ";	break;
	case ASN_TAG_CLASS_CONTEXT:	type = "";		break;
	case ASN_TAG_CLASS_PRIVATE:	type = "PRIVATE ";	break;
	}

	ret = snprintf(buf, size, "[%s%u]", type, ((unsigned)tag) >> 2);
	if(ret <= 0 && size) buf[0] = '\0';	/* against broken libc's */

	return ret;
}

char *
ber_tlv_tag_string(ber_tlv_tag_t tag) {
	static char buf[sizeof("[APPLICATION ]") + 32];

	(void)ber_tlv_tag_snprint(tag, buf, sizeof(buf));

	return buf;
}


size_t
ber_tlv_tag_serialize(ber_tlv_tag_t tag, void *bufp, size_t size) {
	int tclass = BER_TAG_CLASS(tag);
	ber_tlv_tag_t tval = BER_TAG_VALUE(tag);
	uint8_t *buf = (uint8_t *)bufp;
	uint8_t *end;
	size_t required_size;
	size_t i;

	if(tval <= 30) {
		/* Encoded in 1 octet */
		if(size) buf[0] = (tclass << 6) | tval;
		return 1;
	} else if(size) {
		*buf++ = (tclass << 6) | 0x1F;
		size--;
	}

	/*
	 * Compute the size of the subsequent bytes.
	 */
	for(required_size = 1, i = 7; i < 8 * sizeof(tval); i += 7) {
		if(tval >> i)
			required_size++;
		else
			break;
	}

	if(size < required_size)
		return required_size + 1;

	/*
	 * Fill in the buffer, space permitting.
	 */
	end = buf + required_size - 1;
	for(i -= 7; buf < end; i -= 7, buf++)
		*buf = 0x80 | ((tval >> i) & 0x7F);
	*buf = (tval & 0x7F);	/* Last octet without high bit */

	return required_size + 1;
}