1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* include/k5-der.h - Distinguished Encoding Rules (DER) declarations */
3 /*
4 * Copyright (C) 2023 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * Most ASN.1 encoding and decoding is done using the table-driven framework in
35 * libkrb5. When that is not an option, these helpers can be used to encode
36 * and decode simple types.
37 */
38
39 #ifndef K5_DER_H
40 #define K5_DER_H
41
42 #include <stdint.h>
43 #include <stdbool.h>
44 #include "k5-buf.h"
45 #include "k5-input.h"
46
47 /* Return the number of bytes needed to encode len as a DER encoding length. */
48 static inline size_t
k5_der_len_len(size_t len)49 k5_der_len_len(size_t len)
50 {
51 size_t llen;
52
53 if (len < 128)
54 return 1;
55 llen = 1;
56 while (len > 0) {
57 len >>= 8;
58 llen++;
59 }
60 return llen;
61 }
62
63 /* Return the number of bytes needed to encode a DER value (with identifier
64 * byte and length) for a given contents length. */
65 static inline size_t
k5_der_value_len(size_t contents_len)66 k5_der_value_len(size_t contents_len)
67 {
68 return 1 + k5_der_len_len(contents_len) + contents_len;
69 }
70
71 /* Add a DER identifier byte (composed by the caller, including the ASN.1
72 * class, tag, and constructed bit) and length. */
73 static inline void
k5_der_add_taglen(struct k5buf * buf,uint8_t idbyte,size_t len)74 k5_der_add_taglen(struct k5buf *buf, uint8_t idbyte, size_t len)
75 {
76 uint8_t *p;
77 size_t llen = k5_der_len_len(len);
78
79 p = k5_buf_get_space(buf, 1 + llen);
80 if (p == NULL)
81 return;
82 *p++ = idbyte;
83 if (len < 128) {
84 *p = len;
85 } else {
86 *p = 0x80 | (llen - 1);
87 /* Encode the length bytes backwards so the most significant byte is
88 * first. */
89 p += llen;
90 while (len > 0) {
91 *--p = len & 0xFF;
92 len >>= 8;
93 }
94 }
95 }
96
97 /* Add a DER value (identifier byte, length, and contents). */
98 static inline void
k5_der_add_value(struct k5buf * buf,uint8_t idbyte,const void * contents,size_t len)99 k5_der_add_value(struct k5buf *buf, uint8_t idbyte, const void *contents,
100 size_t len)
101 {
102 k5_der_add_taglen(buf, idbyte, len);
103 k5_buf_add_len(buf, contents, len);
104 }
105
106 /*
107 * If the next byte in in matches idbyte and the subsequent DER length is
108 * valid, advance in past the tag and length, set *len_out to the decoded
109 * length, and return true. Otherwise return false. Only set an error on in
110 * if the next byte matches idbyte but the ensuing length is invalid.
111 */
112 static inline bool
k5_der_get_taglen(struct k5input * in,uint8_t idbyte,size_t * len_out)113 k5_der_get_taglen(struct k5input *in, uint8_t idbyte, size_t *len_out)
114 {
115 uint8_t lenbyte, i;
116 size_t len;
117
118 /* Do nothing if in is empty or the next byte doesn't match idbyte. */
119 if (in->status || in->len == 0 || *in->ptr != idbyte)
120 return false;
121
122 /* Advance past the identifier byte and decode the length. */
123 (void)k5_input_get_byte(in);
124 lenbyte = k5_input_get_byte(in);
125 if (lenbyte < 128) {
126 len = lenbyte;
127 } else {
128 len = 0;
129 for (i = 0; i < (lenbyte & 0x7F); i++) {
130 if (len > (SIZE_MAX >> 8)) {
131 k5_input_set_status(in, EOVERFLOW);
132 return false;
133 }
134 len = (len << 8) | k5_input_get_byte(in);
135 }
136 }
137
138 if (in->status)
139 return false;
140
141 *len_out = len;
142 return true;
143 }
144
145 /*
146 * If the next byte in in matches idbyte and the subsequent DER length is
147 * valid, advance in past the value, set *contents_out to the value contents,
148 * and return true. Otherwise return false. Only set an error on in if the
149 * next byte matches idbyte but the ensuing length is invalid. contents_out
150 * may be aliased to in; it will only be written to on successful decoding of a
151 * value.
152 */
153 static inline bool
k5_der_get_value(struct k5input * in,uint8_t idbyte,struct k5input * contents_out)154 k5_der_get_value(struct k5input *in, uint8_t idbyte,
155 struct k5input *contents_out)
156 {
157 size_t len;
158 const void *bytes;
159
160 if (!k5_der_get_taglen(in, idbyte, &len))
161 return false;
162 bytes = k5_input_get_bytes(in, len);
163 if (bytes == NULL)
164 return false;
165 k5_input_init(contents_out, bytes, len);
166 return true;
167 }
168
169 #endif /* K5_DER_H */
170