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