// SPDX-License-Identifier: GPL-2.0-only1/*2* Simple encoder primitives for ASN.1 BER/DER/CER3*4* Copyright (C) 2019 [email protected]5*/67#include <linux/asn1_encoder.h>8#include <linux/bug.h>9#include <linux/string.h>10#include <linux/module.h>1112/**13* asn1_encode_integer() - encode positive integer to ASN.114* @data: pointer to the pointer to the data15* @end_data: end of data pointer, points one beyond last usable byte in @data16* @integer: integer to be encoded17*18* This is a simplified encoder: it only currently does19* positive integers, but it should be simple enough to add the20* negative case if a use comes along.21*/22unsigned char *23asn1_encode_integer(unsigned char *data, const unsigned char *end_data,24s64 integer)25{26int data_len = end_data - data;27unsigned char *d = &data[2];28bool found = false;29int i;3031if (WARN(integer < 0,32"BUG: integer encode only supports positive integers"))33return ERR_PTR(-EINVAL);3435if (IS_ERR(data))36return data;3738/* need at least 3 bytes for tag, length and integer encoding */39if (data_len < 3)40return ERR_PTR(-EINVAL);4142/* remaining length where at d (the start of the integer encoding) */43data_len -= 2;4445data[0] = _tag(UNIV, PRIM, INT);46if (integer == 0) {47*d++ = 0;48goto out;49}5051for (i = sizeof(integer); i > 0 ; i--) {52int byte = integer >> (8 * (i - 1));5354if (!found && byte == 0)55continue;5657/*58* for a positive number the first byte must have bit59* 7 clear in two's complement (otherwise it's a60* negative number) so prepend a leading zero if61* that's not the case62*/63if (!found && (byte & 0x80)) {64/*65* no check needed here, we already know we66* have len >= 167*/68*d++ = 0;69data_len--;70}7172found = true;73if (data_len == 0)74return ERR_PTR(-EINVAL);7576*d++ = byte;77data_len--;78}7980out:81data[1] = d - data - 2;8283return d;84}85EXPORT_SYMBOL_GPL(asn1_encode_integer);8687/* calculate the base 128 digit values setting the top bit of the first octet */88static int asn1_encode_oid_digit(unsigned char **_data, int *data_len, u32 oid)89{90unsigned char *data = *_data;91int start = 7 + 7 + 7 + 7;92int ret = 0;9394if (*data_len < 1)95return -EINVAL;9697/* quick case */98if (oid == 0) {99*data++ = 0x80;100(*data_len)--;101goto out;102}103104while (oid >> start == 0)105start -= 7;106107while (start > 0 && *data_len > 0) {108u8 byte;109110byte = oid >> start;111oid = oid - (byte << start);112start -= 7;113byte |= 0x80;114*data++ = byte;115(*data_len)--;116}117118if (*data_len > 0) {119*data++ = oid;120(*data_len)--;121} else {122ret = -EINVAL;123}124125out:126*_data = data;127return ret;128}129130/**131* asn1_encode_oid() - encode an oid to ASN.1132* @data: position to begin encoding at133* @end_data: end of data pointer, points one beyond last usable byte in @data134* @oid: array of oids135* @oid_len: length of oid array136*137* this encodes an OID up to ASN.1 when presented as an array of OID values138*/139unsigned char *140asn1_encode_oid(unsigned char *data, const unsigned char *end_data,141u32 oid[], int oid_len)142{143int data_len = end_data - data;144unsigned char *d = data + 2;145int i, ret;146147if (WARN(oid_len < 2, "OID must have at least two elements"))148return ERR_PTR(-EINVAL);149150if (WARN(oid_len > 32, "OID is too large"))151return ERR_PTR(-EINVAL);152153if (IS_ERR(data))154return data;155156157/* need at least 3 bytes for tag, length and OID encoding */158if (data_len < 3)159return ERR_PTR(-EINVAL);160161data[0] = _tag(UNIV, PRIM, OID);162*d++ = oid[0] * 40 + oid[1];163164data_len -= 3;165166for (i = 2; i < oid_len; i++) {167ret = asn1_encode_oid_digit(&d, &data_len, oid[i]);168if (ret < 0)169return ERR_PTR(ret);170}171172data[1] = d - data - 2;173174return d;175}176EXPORT_SYMBOL_GPL(asn1_encode_oid);177178/**179* asn1_encode_length() - encode a length to follow an ASN.1 tag180* @data: pointer to encode at181* @data_len: pointer to remaining length (adjusted by routine)182* @len: length to encode183*184* This routine can encode lengths up to 65535 using the ASN.1 rules.185* It will accept a negative length and place a zero length tag186* instead (to keep the ASN.1 valid). This convention allows other187* encoder primitives to accept negative lengths as singalling the188* sequence will be re-encoded when the length is known.189*/190static int asn1_encode_length(unsigned char **data, int *data_len, int len)191{192if (*data_len < 1)193return -EINVAL;194195if (len < 0) {196*((*data)++) = 0;197(*data_len)--;198return 0;199}200201if (len <= 0x7f) {202*((*data)++) = len;203(*data_len)--;204return 0;205}206207if (*data_len < 2)208return -EINVAL;209210if (len <= 0xff) {211*((*data)++) = 0x81;212*((*data)++) = len & 0xff;213*data_len -= 2;214return 0;215}216217if (*data_len < 3)218return -EINVAL;219220if (len <= 0xffff) {221*((*data)++) = 0x82;222*((*data)++) = (len >> 8) & 0xff;223*((*data)++) = len & 0xff;224*data_len -= 3;225return 0;226}227228if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff"))229return -EINVAL;230231if (*data_len < 4)232return -EINVAL;233*((*data)++) = 0x83;234*((*data)++) = (len >> 16) & 0xff;235*((*data)++) = (len >> 8) & 0xff;236*((*data)++) = len & 0xff;237*data_len -= 4;238239return 0;240}241242/**243* asn1_encode_tag() - add a tag for optional or explicit value244* @data: pointer to place tag at245* @end_data: end of data pointer, points one beyond last usable byte in @data246* @tag: tag to be placed247* @string: the data to be tagged248* @len: the length of the data to be tagged249*250* Note this currently only handles short form tags < 31.251*252* Standard usage is to pass in a @tag, @string and @length and the253* @string will be ASN.1 encoded with @tag and placed into @data. If254* the encoding would put data past @end_data then an error is255* returned, otherwise a pointer to a position one beyond the encoding256* is returned.257*258* To encode in place pass a NULL @string and -1 for @len and the259* maximum allowable beginning and end of the data; all this will do260* is add the current maximum length and update the data pointer to261* the place where the tag contents should be placed is returned. The262* data should be copied in by the calling routine which should then263* repeat the prior statement but now with the known length. In order264* to avoid having to keep both before and after pointers, the repeat265* expects to be called with @data pointing to where the first encode266* returned it and still NULL for @string but the real length in @len.267*/268unsigned char *269asn1_encode_tag(unsigned char *data, const unsigned char *end_data,270u32 tag, const unsigned char *string, int len)271{272int data_len = end_data - data;273int ret;274275if (WARN(tag > 30, "ASN.1 tag can't be > 30"))276return ERR_PTR(-EINVAL);277278if (!string && WARN(len > 127,279"BUG: recode tag is too big (>127)"))280return ERR_PTR(-EINVAL);281282if (IS_ERR(data))283return data;284285if (!string && len > 0) {286/*287* we're recoding, so move back to the start of the288* tag and install a dummy length because the real289* data_len should be NULL290*/291data -= 2;292data_len = 2;293}294295if (data_len < 2)296return ERR_PTR(-EINVAL);297298*(data++) = _tagn(CONT, CONS, tag);299data_len--;300ret = asn1_encode_length(&data, &data_len, len);301if (ret < 0)302return ERR_PTR(ret);303304if (!string)305return data;306307if (data_len < len)308return ERR_PTR(-EINVAL);309310memcpy(data, string, len);311data += len;312313return data;314}315EXPORT_SYMBOL_GPL(asn1_encode_tag);316317/**318* asn1_encode_octet_string() - encode an ASN.1 OCTET STRING319* @data: pointer to encode at320* @end_data: end of data pointer, points one beyond last usable byte in @data321* @string: string to be encoded322* @len: length of string323*324* Note ASN.1 octet strings may contain zeros, so the length is obligatory.325*/326unsigned char *327asn1_encode_octet_string(unsigned char *data,328const unsigned char *end_data,329const unsigned char *string, u32 len)330{331int data_len = end_data - data;332int ret;333334if (IS_ERR(data))335return data;336337/* need minimum of 2 bytes for tag and length of zero length string */338if (data_len < 2)339return ERR_PTR(-EINVAL);340341*(data++) = _tag(UNIV, PRIM, OTS);342data_len--;343344ret = asn1_encode_length(&data, &data_len, len);345if (ret)346return ERR_PTR(ret);347348if (data_len < len)349return ERR_PTR(-EINVAL);350351memcpy(data, string, len);352data += len;353354return data;355}356EXPORT_SYMBOL_GPL(asn1_encode_octet_string);357358/**359* asn1_encode_sequence() - wrap a byte stream in an ASN.1 SEQUENCE360* @data: pointer to encode at361* @end_data: end of data pointer, points one beyond last usable byte in @data362* @seq: data to be encoded as a sequence363* @len: length of the data to be encoded as a sequence364*365* Fill in a sequence. To encode in place, pass NULL for @seq and -1366* for @len; then call again once the length is known (still with NULL367* for @seq). In order to avoid having to keep both before and after368* pointers, the repeat expects to be called with @data pointing to369* where the first encode placed it.370*/371unsigned char *372asn1_encode_sequence(unsigned char *data, const unsigned char *end_data,373const unsigned char *seq, int len)374{375int data_len = end_data - data;376int ret;377378if (!seq && WARN(len > 127,379"BUG: recode sequence is too big (>127)"))380return ERR_PTR(-EINVAL);381382if (IS_ERR(data))383return data;384385if (!seq && len >= 0) {386/*387* we're recoding, so move back to the start of the388* sequence and install a dummy length because the389* real length should be NULL390*/391data -= 2;392data_len = 2;393}394395if (data_len < 2)396return ERR_PTR(-EINVAL);397398*(data++) = _tag(UNIV, CONS, SEQ);399data_len--;400401ret = asn1_encode_length(&data, &data_len, len);402if (ret)403return ERR_PTR(ret);404405if (!seq)406return data;407408if (data_len < len)409return ERR_PTR(-EINVAL);410411memcpy(data, seq, len);412data += len;413414return data;415}416EXPORT_SYMBOL_GPL(asn1_encode_sequence);417418/**419* asn1_encode_boolean() - encode a boolean value to ASN.1420* @data: pointer to encode at421* @end_data: end of data pointer, points one beyond last usable byte in @data422* @val: the boolean true/false value423*/424unsigned char *425asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,426bool val)427{428int data_len = end_data - data;429430if (IS_ERR(data))431return data;432433/* booleans are 3 bytes: tag, length == 1 and value == 0 or 1 */434if (data_len < 3)435return ERR_PTR(-EINVAL);436437*(data++) = _tag(UNIV, PRIM, BOOL);438data_len--;439440asn1_encode_length(&data, &data_len, 1);441442if (val)443*(data++) = 1;444else445*(data++) = 0;446447return data;448}449EXPORT_SYMBOL_GPL(asn1_encode_boolean);450451MODULE_DESCRIPTION("Simple encoder primitives for ASN.1 BER/DER/CER");452MODULE_LICENSE("GPL");453454455