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 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 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 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 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 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 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