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 value, set *contents_out to the value contents,
109 * and return true. Otherwise return false. Only set an error on in if the
110 * next bytes matches idbyte but the ensuing length is invalid. contents_out
111 * may be aliased to in; it will only be written to on successful decoding of a
112 * value.
113 */
114 static inline bool
k5_der_get_value(struct k5input * in,uint8_t idbyte,struct k5input * contents_out)115 k5_der_get_value(struct k5input *in, uint8_t idbyte,
116 struct k5input *contents_out)
117 {
118 uint8_t lenbyte, i;
119 size_t len;
120 const void *bytes;
121
122 /* Do nothing if in is empty or the next byte doesn't match idbyte. */
123 if (in->status || in->len == 0 || *in->ptr != idbyte)
124 return false;
125
126 /* Advance past the identifier byte and decode the length. */
127 (void)k5_input_get_byte(in);
128 lenbyte = k5_input_get_byte(in);
129 if (lenbyte < 128) {
130 len = lenbyte;
131 } else {
132 len = 0;
133 for (i = 0; i < (lenbyte & 0x7F); i++) {
134 if (len > (SIZE_MAX >> 8)) {
135 k5_input_set_status(in, EOVERFLOW);
136 return false;
137 }
138 len = (len << 8) | k5_input_get_byte(in);
139 }
140 }
141
142 bytes = k5_input_get_bytes(in, len);
143 if (bytes == NULL)
144 return false;
145 k5_input_init(contents_out, bytes, len);
146 return true;
147 }
148
149 #endif /* K5_DER_H */
150