asn1_encoder.c (10505B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Simple encoder primitives for ASN.1 BER/DER/CER 4 * 5 * Copyright (C) 2019 James.Bottomley@HansenPartnership.com 6 */ 7 8#include <linux/asn1_encoder.h> 9#include <linux/bug.h> 10#include <linux/string.h> 11#include <linux/module.h> 12 13/** 14 * asn1_encode_integer() - encode positive integer to ASN.1 15 * @data: pointer to the pointer to the data 16 * @end_data: end of data pointer, points one beyond last usable byte in @data 17 * @integer: integer to be encoded 18 * 19 * This is a simplified encoder: it only currently does 20 * positive integers, but it should be simple enough to add the 21 * negative case if a use comes along. 22 */ 23unsigned char * 24asn1_encode_integer(unsigned char *data, const unsigned char *end_data, 25 s64 integer) 26{ 27 int data_len = end_data - data; 28 unsigned char *d = &data[2]; 29 bool found = false; 30 int i; 31 32 if (WARN(integer < 0, 33 "BUG: integer encode only supports positive integers")) 34 return ERR_PTR(-EINVAL); 35 36 if (IS_ERR(data)) 37 return data; 38 39 /* need at least 3 bytes for tag, length and integer encoding */ 40 if (data_len < 3) 41 return ERR_PTR(-EINVAL); 42 43 /* remaining length where at d (the start of the integer encoding) */ 44 data_len -= 2; 45 46 data[0] = _tag(UNIV, PRIM, INT); 47 if (integer == 0) { 48 *d++ = 0; 49 goto out; 50 } 51 52 for (i = sizeof(integer); i > 0 ; i--) { 53 int byte = integer >> (8 * (i - 1)); 54 55 if (!found && byte == 0) 56 continue; 57 58 /* 59 * for a positive number the first byte must have bit 60 * 7 clear in two's complement (otherwise it's a 61 * negative number) so prepend a leading zero if 62 * that's not the case 63 */ 64 if (!found && (byte & 0x80)) { 65 /* 66 * no check needed here, we already know we 67 * have len >= 1 68 */ 69 *d++ = 0; 70 data_len--; 71 } 72 73 found = true; 74 if (data_len == 0) 75 return ERR_PTR(-EINVAL); 76 77 *d++ = byte; 78 data_len--; 79 } 80 81 out: 82 data[1] = d - data - 2; 83 84 return d; 85} 86EXPORT_SYMBOL_GPL(asn1_encode_integer); 87 88/* calculate the base 128 digit values setting the top bit of the first octet */ 89static int asn1_encode_oid_digit(unsigned char **_data, int *data_len, u32 oid) 90{ 91 unsigned char *data = *_data; 92 int start = 7 + 7 + 7 + 7; 93 int ret = 0; 94 95 if (*data_len < 1) 96 return -EINVAL; 97 98 /* quick case */ 99 if (oid == 0) { 100 *data++ = 0x80; 101 (*data_len)--; 102 goto out; 103 } 104 105 while (oid >> start == 0) 106 start -= 7; 107 108 while (start > 0 && *data_len > 0) { 109 u8 byte; 110 111 byte = oid >> start; 112 oid = oid - (byte << start); 113 start -= 7; 114 byte |= 0x80; 115 *data++ = byte; 116 (*data_len)--; 117 } 118 119 if (*data_len > 0) { 120 *data++ = oid; 121 (*data_len)--; 122 } else { 123 ret = -EINVAL; 124 } 125 126 out: 127 *_data = data; 128 return ret; 129} 130 131/** 132 * asn1_encode_oid() - encode an oid to ASN.1 133 * @data: position to begin encoding at 134 * @end_data: end of data pointer, points one beyond last usable byte in @data 135 * @oid: array of oids 136 * @oid_len: length of oid array 137 * 138 * this encodes an OID up to ASN.1 when presented as an array of OID values 139 */ 140unsigned char * 141asn1_encode_oid(unsigned char *data, const unsigned char *end_data, 142 u32 oid[], int oid_len) 143{ 144 int data_len = end_data - data; 145 unsigned char *d = data + 2; 146 int i, ret; 147 148 if (WARN(oid_len < 2, "OID must have at least two elements")) 149 return ERR_PTR(-EINVAL); 150 151 if (WARN(oid_len > 32, "OID is too large")) 152 return ERR_PTR(-EINVAL); 153 154 if (IS_ERR(data)) 155 return data; 156 157 158 /* need at least 3 bytes for tag, length and OID encoding */ 159 if (data_len < 3) 160 return ERR_PTR(-EINVAL); 161 162 data[0] = _tag(UNIV, PRIM, OID); 163 *d++ = oid[0] * 40 + oid[1]; 164 165 data_len -= 3; 166 167 for (i = 2; i < oid_len; i++) { 168 ret = asn1_encode_oid_digit(&d, &data_len, oid[i]); 169 if (ret < 0) 170 return ERR_PTR(ret); 171 } 172 173 data[1] = d - data - 2; 174 175 return d; 176} 177EXPORT_SYMBOL_GPL(asn1_encode_oid); 178 179/** 180 * asn1_encode_length() - encode a length to follow an ASN.1 tag 181 * @data: pointer to encode at 182 * @data_len: pointer to remaining length (adjusted by routine) 183 * @len: length to encode 184 * 185 * This routine can encode lengths up to 65535 using the ASN.1 rules. 186 * It will accept a negative length and place a zero length tag 187 * instead (to keep the ASN.1 valid). This convention allows other 188 * encoder primitives to accept negative lengths as singalling the 189 * sequence will be re-encoded when the length is known. 190 */ 191static int asn1_encode_length(unsigned char **data, int *data_len, int len) 192{ 193 if (*data_len < 1) 194 return -EINVAL; 195 196 if (len < 0) { 197 *((*data)++) = 0; 198 (*data_len)--; 199 return 0; 200 } 201 202 if (len <= 0x7f) { 203 *((*data)++) = len; 204 (*data_len)--; 205 return 0; 206 } 207 208 if (*data_len < 2) 209 return -EINVAL; 210 211 if (len <= 0xff) { 212 *((*data)++) = 0x81; 213 *((*data)++) = len & 0xff; 214 *data_len -= 2; 215 return 0; 216 } 217 218 if (*data_len < 3) 219 return -EINVAL; 220 221 if (len <= 0xffff) { 222 *((*data)++) = 0x82; 223 *((*data)++) = (len >> 8) & 0xff; 224 *((*data)++) = len & 0xff; 225 *data_len -= 3; 226 return 0; 227 } 228 229 if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff")) 230 return -EINVAL; 231 232 if (*data_len < 4) 233 return -EINVAL; 234 *((*data)++) = 0x83; 235 *((*data)++) = (len >> 16) & 0xff; 236 *((*data)++) = (len >> 8) & 0xff; 237 *((*data)++) = len & 0xff; 238 *data_len -= 4; 239 240 return 0; 241} 242 243/** 244 * asn1_encode_tag() - add a tag for optional or explicit value 245 * @data: pointer to place tag at 246 * @end_data: end of data pointer, points one beyond last usable byte in @data 247 * @tag: tag to be placed 248 * @string: the data to be tagged 249 * @len: the length of the data to be tagged 250 * 251 * Note this currently only handles short form tags < 31. 252 * 253 * Standard usage is to pass in a @tag, @string and @length and the 254 * @string will be ASN.1 encoded with @tag and placed into @data. If 255 * the encoding would put data past @end_data then an error is 256 * returned, otherwise a pointer to a position one beyond the encoding 257 * is returned. 258 * 259 * To encode in place pass a NULL @string and -1 for @len and the 260 * maximum allowable beginning and end of the data; all this will do 261 * is add the current maximum length and update the data pointer to 262 * the place where the tag contents should be placed is returned. The 263 * data should be copied in by the calling routine which should then 264 * repeat the prior statement but now with the known length. In order 265 * to avoid having to keep both before and after pointers, the repeat 266 * expects to be called with @data pointing to where the first encode 267 * returned it and still NULL for @string but the real length in @len. 268 */ 269unsigned char * 270asn1_encode_tag(unsigned char *data, const unsigned char *end_data, 271 u32 tag, const unsigned char *string, int len) 272{ 273 int data_len = end_data - data; 274 int ret; 275 276 if (WARN(tag > 30, "ASN.1 tag can't be > 30")) 277 return ERR_PTR(-EINVAL); 278 279 if (!string && WARN(len > 127, 280 "BUG: recode tag is too big (>127)")) 281 return ERR_PTR(-EINVAL); 282 283 if (IS_ERR(data)) 284 return data; 285 286 if (!string && len > 0) { 287 /* 288 * we're recoding, so move back to the start of the 289 * tag and install a dummy length because the real 290 * data_len should be NULL 291 */ 292 data -= 2; 293 data_len = 2; 294 } 295 296 if (data_len < 2) 297 return ERR_PTR(-EINVAL); 298 299 *(data++) = _tagn(CONT, CONS, tag); 300 data_len--; 301 ret = asn1_encode_length(&data, &data_len, len); 302 if (ret < 0) 303 return ERR_PTR(ret); 304 305 if (!string) 306 return data; 307 308 if (data_len < len) 309 return ERR_PTR(-EINVAL); 310 311 memcpy(data, string, len); 312 data += len; 313 314 return data; 315} 316EXPORT_SYMBOL_GPL(asn1_encode_tag); 317 318/** 319 * asn1_encode_octet_string() - encode an ASN.1 OCTET STRING 320 * @data: pointer to encode at 321 * @end_data: end of data pointer, points one beyond last usable byte in @data 322 * @string: string to be encoded 323 * @len: length of string 324 * 325 * Note ASN.1 octet strings may contain zeros, so the length is obligatory. 326 */ 327unsigned char * 328asn1_encode_octet_string(unsigned char *data, 329 const unsigned char *end_data, 330 const unsigned char *string, u32 len) 331{ 332 int data_len = end_data - data; 333 int ret; 334 335 if (IS_ERR(data)) 336 return data; 337 338 /* need minimum of 2 bytes for tag and length of zero length string */ 339 if (data_len < 2) 340 return ERR_PTR(-EINVAL); 341 342 *(data++) = _tag(UNIV, PRIM, OTS); 343 data_len--; 344 345 ret = asn1_encode_length(&data, &data_len, len); 346 if (ret) 347 return ERR_PTR(ret); 348 349 if (data_len < len) 350 return ERR_PTR(-EINVAL); 351 352 memcpy(data, string, len); 353 data += len; 354 355 return data; 356} 357EXPORT_SYMBOL_GPL(asn1_encode_octet_string); 358 359/** 360 * asn1_encode_sequence() - wrap a byte stream in an ASN.1 SEQUENCE 361 * @data: pointer to encode at 362 * @end_data: end of data pointer, points one beyond last usable byte in @data 363 * @seq: data to be encoded as a sequence 364 * @len: length of the data to be encoded as a sequence 365 * 366 * Fill in a sequence. To encode in place, pass NULL for @seq and -1 367 * for @len; then call again once the length is known (still with NULL 368 * for @seq). In order to avoid having to keep both before and after 369 * pointers, the repeat expects to be called with @data pointing to 370 * where the first encode placed it. 371 */ 372unsigned char * 373asn1_encode_sequence(unsigned char *data, const unsigned char *end_data, 374 const unsigned char *seq, int len) 375{ 376 int data_len = end_data - data; 377 int ret; 378 379 if (!seq && WARN(len > 127, 380 "BUG: recode sequence is too big (>127)")) 381 return ERR_PTR(-EINVAL); 382 383 if (IS_ERR(data)) 384 return data; 385 386 if (!seq && len >= 0) { 387 /* 388 * we're recoding, so move back to the start of the 389 * sequence and install a dummy length because the 390 * real length should be NULL 391 */ 392 data -= 2; 393 data_len = 2; 394 } 395 396 if (data_len < 2) 397 return ERR_PTR(-EINVAL); 398 399 *(data++) = _tag(UNIV, CONS, SEQ); 400 data_len--; 401 402 ret = asn1_encode_length(&data, &data_len, len); 403 if (ret) 404 return ERR_PTR(ret); 405 406 if (!seq) 407 return data; 408 409 if (data_len < len) 410 return ERR_PTR(-EINVAL); 411 412 memcpy(data, seq, len); 413 data += len; 414 415 return data; 416} 417EXPORT_SYMBOL_GPL(asn1_encode_sequence); 418 419/** 420 * asn1_encode_boolean() - encode a boolean value to ASN.1 421 * @data: pointer to encode at 422 * @end_data: end of data pointer, points one beyond last usable byte in @data 423 * @val: the boolean true/false value 424 */ 425unsigned char * 426asn1_encode_boolean(unsigned char *data, const unsigned char *end_data, 427 bool val) 428{ 429 int data_len = end_data - data; 430 431 if (IS_ERR(data)) 432 return data; 433 434 /* booleans are 3 bytes: tag, length == 1 and value == 0 or 1 */ 435 if (data_len < 3) 436 return ERR_PTR(-EINVAL); 437 438 *(data++) = _tag(UNIV, PRIM, BOOL); 439 data_len--; 440 441 asn1_encode_length(&data, &data_len, 1); 442 443 if (val) 444 *(data++) = 1; 445 else 446 *(data++) = 0; 447 448 return data; 449} 450EXPORT_SYMBOL_GPL(asn1_encode_boolean); 451 452MODULE_LICENSE("GPL");