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 */ 23 unsigned char * 24 asn1_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 } 86 EXPORT_SYMBOL_GPL(asn1_encode_integer); 87 88 /* calculate the base 128 digit values setting the top bit of the first octet */ 89 static 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 */ 140 unsigned char * 141 asn1_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 ret = 0; 168 169 for (i = 2; i < oid_len; i++) { 170 ret = asn1_encode_oid_digit(&d, &data_len, oid[i]); 171 if (ret < 0) 172 return ERR_PTR(ret); 173 } 174 175 data[1] = d - data - 2; 176 177 return d; 178 } 179 EXPORT_SYMBOL_GPL(asn1_encode_oid); 180 181 /** 182 * asn1_encode_length() - encode a length to follow an ASN.1 tag 183 * @data: pointer to encode at 184 * @data_len: pointer to remaning length (adjusted by routine) 185 * @len: length to encode 186 * 187 * This routine can encode lengths up to 65535 using the ASN.1 rules. 188 * It will accept a negative length and place a zero length tag 189 * instead (to keep the ASN.1 valid). This convention allows other 190 * encoder primitives to accept negative lengths as singalling the 191 * sequence will be re-encoded when the length is known. 192 */ 193 static int asn1_encode_length(unsigned char **data, int *data_len, int len) 194 { 195 if (*data_len < 1) 196 return -EINVAL; 197 198 if (len < 0) { 199 *((*data)++) = 0; 200 (*data_len)--; 201 return 0; 202 } 203 204 if (len <= 0x7f) { 205 *((*data)++) = len; 206 (*data_len)--; 207 return 0; 208 } 209 210 if (*data_len < 2) 211 return -EINVAL; 212 213 if (len <= 0xff) { 214 *((*data)++) = 0x81; 215 *((*data)++) = len & 0xff; 216 *data_len -= 2; 217 return 0; 218 } 219 220 if (*data_len < 3) 221 return -EINVAL; 222 223 if (len <= 0xffff) { 224 *((*data)++) = 0x82; 225 *((*data)++) = (len >> 8) & 0xff; 226 *((*data)++) = len & 0xff; 227 *data_len -= 3; 228 return 0; 229 } 230 231 if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff")) 232 return -EINVAL; 233 234 if (*data_len < 4) 235 return -EINVAL; 236 *((*data)++) = 0x83; 237 *((*data)++) = (len >> 16) & 0xff; 238 *((*data)++) = (len >> 8) & 0xff; 239 *((*data)++) = len & 0xff; 240 *data_len -= 4; 241 242 return 0; 243 } 244 245 /** 246 * asn1_encode_tag() - add a tag for optional or explicit value 247 * @data: pointer to place tag at 248 * @end_data: end of data pointer, points one beyond last usable byte in @data 249 * @tag: tag to be placed 250 * @string: the data to be tagged 251 * @len: the length of the data to be tagged 252 * 253 * Note this currently only handles short form tags < 31. 254 * 255 * Standard usage is to pass in a @tag, @string and @length and the 256 * @string will be ASN.1 encoded with @tag and placed into @data. If 257 * the encoding would put data past @end_data then an error is 258 * returned, otherwise a pointer to a position one beyond the encoding 259 * is returned. 260 * 261 * To encode in place pass a NULL @string and -1 for @len and the 262 * maximum allowable beginning and end of the data; all this will do 263 * is add the current maximum length and update the data pointer to 264 * the place where the tag contents should be placed is returned. The 265 * data should be copied in by the calling routine which should then 266 * repeat the prior statement but now with the known length. In order 267 * to avoid having to keep both before and after pointers, the repeat 268 * expects to be called with @data pointing to where the first encode 269 * returned it and still NULL for @string but the real length in @len. 270 */ 271 unsigned char * 272 asn1_encode_tag(unsigned char *data, const unsigned char *end_data, 273 u32 tag, const unsigned char *string, int len) 274 { 275 int data_len = end_data - data; 276 int ret; 277 278 if (WARN(tag > 30, "ASN.1 tag can't be > 30")) 279 return ERR_PTR(-EINVAL); 280 281 if (!string && WARN(len > 127, 282 "BUG: recode tag is too big (>127)")) 283 return ERR_PTR(-EINVAL); 284 285 if (IS_ERR(data)) 286 return data; 287 288 if (!string && len > 0) { 289 /* 290 * we're recoding, so move back to the start of the 291 * tag and install a dummy length because the real 292 * data_len should be NULL 293 */ 294 data -= 2; 295 data_len = 2; 296 } 297 298 if (data_len < 2) 299 return ERR_PTR(-EINVAL); 300 301 *(data++) = _tagn(CONT, CONS, tag); 302 data_len--; 303 ret = asn1_encode_length(&data, &data_len, len); 304 if (ret < 0) 305 return ERR_PTR(ret); 306 307 if (!string) 308 return data; 309 310 if (data_len < len) 311 return ERR_PTR(-EINVAL); 312 313 memcpy(data, string, len); 314 data += len; 315 316 return data; 317 } 318 EXPORT_SYMBOL_GPL(asn1_encode_tag); 319 320 /** 321 * asn1_encode_octet_string() - encode an ASN.1 OCTET STRING 322 * @data: pointer to encode at 323 * @end_data: end of data pointer, points one beyond last usable byte in @data 324 * @string: string to be encoded 325 * @len: length of string 326 * 327 * Note ASN.1 octet strings may contain zeros, so the length is obligatory. 328 */ 329 unsigned char * 330 asn1_encode_octet_string(unsigned char *data, 331 const unsigned char *end_data, 332 const unsigned char *string, u32 len) 333 { 334 int data_len = end_data - data; 335 int ret; 336 337 if (IS_ERR(data)) 338 return data; 339 340 /* need minimum of 2 bytes for tag and length of zero length string */ 341 if (data_len < 2) 342 return ERR_PTR(-EINVAL); 343 344 *(data++) = _tag(UNIV, PRIM, OTS); 345 data_len--; 346 347 ret = asn1_encode_length(&data, &data_len, len); 348 if (ret) 349 return ERR_PTR(ret); 350 351 if (data_len < len) 352 return ERR_PTR(-EINVAL); 353 354 memcpy(data, string, len); 355 data += len; 356 357 return data; 358 } 359 EXPORT_SYMBOL_GPL(asn1_encode_octet_string); 360 361 /** 362 * asn1_encode_sequence() - wrap a byte stream in an ASN.1 SEQUENCE 363 * @data: pointer to encode at 364 * @end_data: end of data pointer, points one beyond last usable byte in @data 365 * @seq: data to be encoded as a sequence 366 * @len: length of the data to be encoded as a sequence 367 * 368 * Fill in a sequence. To encode in place, pass NULL for @seq and -1 369 * for @len; then call again once the length is known (still with NULL 370 * for @seq). In order to avoid having to keep both before and after 371 * pointers, the repeat expects to be called with @data pointing to 372 * where the first encode placed it. 373 */ 374 unsigned char * 375 asn1_encode_sequence(unsigned char *data, const unsigned char *end_data, 376 const unsigned char *seq, int len) 377 { 378 int data_len = end_data - data; 379 int ret; 380 381 if (!seq && WARN(len > 127, 382 "BUG: recode sequence is too big (>127)")) 383 return ERR_PTR(-EINVAL); 384 385 if (IS_ERR(data)) 386 return data; 387 388 if (!seq && len >= 0) { 389 /* 390 * we're recoding, so move back to the start of the 391 * sequence and install a dummy length because the 392 * real length should be NULL 393 */ 394 data -= 2; 395 data_len = 2; 396 } 397 398 if (data_len < 2) 399 return ERR_PTR(-EINVAL); 400 401 *(data++) = _tag(UNIV, CONS, SEQ); 402 data_len--; 403 404 ret = asn1_encode_length(&data, &data_len, len); 405 if (ret) 406 return ERR_PTR(ret); 407 408 if (!seq) 409 return data; 410 411 if (data_len < len) 412 return ERR_PTR(-EINVAL); 413 414 memcpy(data, seq, len); 415 data += len; 416 417 return data; 418 } 419 EXPORT_SYMBOL_GPL(asn1_encode_sequence); 420 421 /** 422 * asn1_encode_boolean() - encode a boolean value to ASN.1 423 * @data: pointer to encode at 424 * @end_data: end of data pointer, points one beyond last usable byte in @data 425 * @val: the boolean true/false value 426 */ 427 unsigned char * 428 asn1_encode_boolean(unsigned char *data, const unsigned char *end_data, 429 bool val) 430 { 431 int data_len = end_data - data; 432 433 if (IS_ERR(data)) 434 return data; 435 436 /* booleans are 3 bytes: tag, length == 1 and value == 0 or 1 */ 437 if (data_len < 3) 438 return ERR_PTR(-EINVAL); 439 440 *(data++) = _tag(UNIV, PRIM, BOOL); 441 data_len--; 442 443 asn1_encode_length(&data, &data_len, 1); 444 445 if (val) 446 *(data++) = 1; 447 else 448 *(data++) = 0; 449 450 return data; 451 } 452 EXPORT_SYMBOL_GPL(asn1_encode_boolean); 453 454 MODULE_LICENSE("GPL"); 455