xref: /freebsd/crypto/krb5/src/include/k5-der.h (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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