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