/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* include/k5-der.h - Distinguished Encoding Rules (DER) declarations */2/*3* Copyright (C) 2023 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* * Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in15* the documentation and/or other materials provided with the16* distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS21* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE22* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,23* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES24* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR25* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,27* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED29* OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132/*33* Most ASN.1 encoding and decoding is done using the table-driven framework in34* libkrb5. When that is not an option, these helpers can be used to encode35* and decode simple types.36*/3738#ifndef K5_DER_H39#define K5_DER_H4041#include <stdint.h>42#include <stdbool.h>43#include "k5-buf.h"44#include "k5-input.h"4546/* Return the number of bytes needed to encode len as a DER encoding length. */47static inline size_t48k5_der_len_len(size_t len)49{50size_t llen;5152if (len < 128)53return 1;54llen = 1;55while (len > 0) {56len >>= 8;57llen++;58}59return llen;60}6162/* Return the number of bytes needed to encode a DER value (with identifier63* byte and length) for a given contents length. */64static inline size_t65k5_der_value_len(size_t contents_len)66{67return 1 + k5_der_len_len(contents_len) + contents_len;68}6970/* Add a DER identifier byte (composed by the caller, including the ASN.171* class, tag, and constructed bit) and length. */72static inline void73k5_der_add_taglen(struct k5buf *buf, uint8_t idbyte, size_t len)74{75uint8_t *p;76size_t llen = k5_der_len_len(len);7778p = k5_buf_get_space(buf, 1 + llen);79if (p == NULL)80return;81*p++ = idbyte;82if (len < 128) {83*p = len;84} else {85*p = 0x80 | (llen - 1);86/* Encode the length bytes backwards so the most significant byte is87* first. */88p += llen;89while (len > 0) {90*--p = len & 0xFF;91len >>= 8;92}93}94}9596/* Add a DER value (identifier byte, length, and contents). */97static inline void98k5_der_add_value(struct k5buf *buf, uint8_t idbyte, const void *contents,99size_t len)100{101k5_der_add_taglen(buf, idbyte, len);102k5_buf_add_len(buf, contents, len);103}104105/*106* If the next byte in in matches idbyte and the subsequent DER length is107* valid, advance in past the tag and length, set *len_out to the decoded108* length, and return true. Otherwise return false. Only set an error on in109* if the next byte matches idbyte but the ensuing length is invalid.110*/111static inline bool112k5_der_get_taglen(struct k5input *in, uint8_t idbyte, size_t *len_out)113{114uint8_t lenbyte, i;115size_t len;116117/* Do nothing if in is empty or the next byte doesn't match idbyte. */118if (in->status || in->len == 0 || *in->ptr != idbyte)119return false;120121/* Advance past the identifier byte and decode the length. */122(void)k5_input_get_byte(in);123lenbyte = k5_input_get_byte(in);124if (lenbyte < 128) {125len = lenbyte;126} else {127len = 0;128for (i = 0; i < (lenbyte & 0x7F); i++) {129if (len > (SIZE_MAX >> 8)) {130k5_input_set_status(in, EOVERFLOW);131return false;132}133len = (len << 8) | k5_input_get_byte(in);134}135}136137if (in->status)138return false;139140*len_out = len;141return true;142}143144/*145* If the next byte in in matches idbyte and the subsequent DER length is146* valid, advance in past the value, set *contents_out to the value contents,147* and return true. Otherwise return false. Only set an error on in if the148* next byte matches idbyte but the ensuing length is invalid. contents_out149* may be aliased to in; it will only be written to on successful decoding of a150* value.151*/152static inline bool153k5_der_get_value(struct k5input *in, uint8_t idbyte,154struct k5input *contents_out)155{156size_t len;157const void *bytes;158159if (!k5_der_get_taglen(in, idbyte, &len))160return false;161bytes = k5_input_get_bytes(in, len);162if (bytes == NULL)163return false;164k5_input_init(contents_out, bytes, len);165return true;166}167168#endif /* K5_DER_H */169170171