Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Simple encoder primitives for ASN.1 BER/DER/CER
0004  *
0005  * Copyright (C) 2019 James.Bottomley@HansenPartnership.com
0006  */
0007 
0008 #include <linux/asn1_encoder.h>
0009 #include <linux/bug.h>
0010 #include <linux/string.h>
0011 #include <linux/module.h>
0012 
0013 /**
0014  * asn1_encode_integer() - encode positive integer to ASN.1
0015  * @data:   pointer to the pointer to the data
0016  * @end_data:   end of data pointer, points one beyond last usable byte in @data
0017  * @integer:    integer to be encoded
0018  *
0019  * This is a simplified encoder: it only currently does
0020  * positive integers, but it should be simple enough to add the
0021  * negative case if a use comes along.
0022  */
0023 unsigned char *
0024 asn1_encode_integer(unsigned char *data, const unsigned char *end_data,
0025             s64 integer)
0026 {
0027     int data_len = end_data - data;
0028     unsigned char *d = &data[2];
0029     bool found = false;
0030     int i;
0031 
0032     if (WARN(integer < 0,
0033          "BUG: integer encode only supports positive integers"))
0034         return ERR_PTR(-EINVAL);
0035 
0036     if (IS_ERR(data))
0037         return data;
0038 
0039     /* need at least 3 bytes for tag, length and integer encoding */
0040     if (data_len < 3)
0041         return ERR_PTR(-EINVAL);
0042 
0043     /* remaining length where at d (the start of the integer encoding) */
0044     data_len -= 2;
0045 
0046     data[0] = _tag(UNIV, PRIM, INT);
0047     if (integer == 0) {
0048         *d++ = 0;
0049         goto out;
0050     }
0051 
0052     for (i = sizeof(integer); i > 0 ; i--) {
0053         int byte = integer >> (8 * (i - 1));
0054 
0055         if (!found && byte == 0)
0056             continue;
0057 
0058         /*
0059          * for a positive number the first byte must have bit
0060          * 7 clear in two's complement (otherwise it's a
0061          * negative number) so prepend a leading zero if
0062          * that's not the case
0063          */
0064         if (!found && (byte & 0x80)) {
0065             /*
0066              * no check needed here, we already know we
0067              * have len >= 1
0068              */
0069             *d++ = 0;
0070             data_len--;
0071         }
0072 
0073         found = true;
0074         if (data_len == 0)
0075             return ERR_PTR(-EINVAL);
0076 
0077         *d++ = byte;
0078         data_len--;
0079     }
0080 
0081  out:
0082     data[1] = d - data - 2;
0083 
0084     return d;
0085 }
0086 EXPORT_SYMBOL_GPL(asn1_encode_integer);
0087 
0088 /* calculate the base 128 digit values setting the top bit of the first octet */
0089 static int asn1_encode_oid_digit(unsigned char **_data, int *data_len, u32 oid)
0090 {
0091     unsigned char *data = *_data;
0092     int start = 7 + 7 + 7 + 7;
0093     int ret = 0;
0094 
0095     if (*data_len < 1)
0096         return -EINVAL;
0097 
0098     /* quick case */
0099     if (oid == 0) {
0100         *data++ = 0x80;
0101         (*data_len)--;
0102         goto out;
0103     }
0104 
0105     while (oid >> start == 0)
0106         start -= 7;
0107 
0108     while (start > 0 && *data_len > 0) {
0109         u8 byte;
0110 
0111         byte = oid >> start;
0112         oid = oid - (byte << start);
0113         start -= 7;
0114         byte |= 0x80;
0115         *data++ = byte;
0116         (*data_len)--;
0117     }
0118 
0119     if (*data_len > 0) {
0120         *data++ = oid;
0121         (*data_len)--;
0122     } else {
0123         ret = -EINVAL;
0124     }
0125 
0126  out:
0127     *_data = data;
0128     return ret;
0129 }
0130 
0131 /**
0132  * asn1_encode_oid() - encode an oid to ASN.1
0133  * @data:   position to begin encoding at
0134  * @end_data:   end of data pointer, points one beyond last usable byte in @data
0135  * @oid:    array of oids
0136  * @oid_len:    length of oid array
0137  *
0138  * this encodes an OID up to ASN.1 when presented as an array of OID values
0139  */
0140 unsigned char *
0141 asn1_encode_oid(unsigned char *data, const unsigned char *end_data,
0142         u32 oid[], int oid_len)
0143 {
0144     int data_len = end_data - data;
0145     unsigned char *d = data + 2;
0146     int i, ret;
0147 
0148     if (WARN(oid_len < 2, "OID must have at least two elements"))
0149         return ERR_PTR(-EINVAL);
0150 
0151     if (WARN(oid_len > 32, "OID is too large"))
0152         return ERR_PTR(-EINVAL);
0153 
0154     if (IS_ERR(data))
0155         return data;
0156 
0157 
0158     /* need at least 3 bytes for tag, length and OID encoding */
0159     if (data_len < 3)
0160         return ERR_PTR(-EINVAL);
0161 
0162     data[0] = _tag(UNIV, PRIM, OID);
0163     *d++ = oid[0] * 40 + oid[1];
0164 
0165     data_len -= 3;
0166 
0167     for (i = 2; i < oid_len; i++) {
0168         ret = asn1_encode_oid_digit(&d, &data_len, oid[i]);
0169         if (ret < 0)
0170             return ERR_PTR(ret);
0171     }
0172 
0173     data[1] = d - data - 2;
0174 
0175     return d;
0176 }
0177 EXPORT_SYMBOL_GPL(asn1_encode_oid);
0178 
0179 /**
0180  * asn1_encode_length() - encode a length to follow an ASN.1 tag
0181  * @data: pointer to encode at
0182  * @data_len: pointer to remaining length (adjusted by routine)
0183  * @len: length to encode
0184  *
0185  * This routine can encode lengths up to 65535 using the ASN.1 rules.
0186  * It will accept a negative length and place a zero length tag
0187  * instead (to keep the ASN.1 valid).  This convention allows other
0188  * encoder primitives to accept negative lengths as singalling the
0189  * sequence will be re-encoded when the length is known.
0190  */
0191 static int asn1_encode_length(unsigned char **data, int *data_len, int len)
0192 {
0193     if (*data_len < 1)
0194         return -EINVAL;
0195 
0196     if (len < 0) {
0197         *((*data)++) = 0;
0198         (*data_len)--;
0199         return 0;
0200     }
0201 
0202     if (len <= 0x7f) {
0203         *((*data)++) = len;
0204         (*data_len)--;
0205         return 0;
0206     }
0207 
0208     if (*data_len < 2)
0209         return -EINVAL;
0210 
0211     if (len <= 0xff) {
0212         *((*data)++) = 0x81;
0213         *((*data)++) = len & 0xff;
0214         *data_len -= 2;
0215         return 0;
0216     }
0217 
0218     if (*data_len < 3)
0219         return -EINVAL;
0220 
0221     if (len <= 0xffff) {
0222         *((*data)++) = 0x82;
0223         *((*data)++) = (len >> 8) & 0xff;
0224         *((*data)++) = len & 0xff;
0225         *data_len -= 3;
0226         return 0;
0227     }
0228 
0229     if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff"))
0230         return -EINVAL;
0231 
0232     if (*data_len < 4)
0233         return -EINVAL;
0234     *((*data)++) = 0x83;
0235     *((*data)++) = (len >> 16) & 0xff;
0236     *((*data)++) = (len >> 8) & 0xff;
0237     *((*data)++) = len & 0xff;
0238     *data_len -= 4;
0239 
0240     return 0;
0241 }
0242 
0243 /**
0244  * asn1_encode_tag() - add a tag for optional or explicit value
0245  * @data:   pointer to place tag at
0246  * @end_data:   end of data pointer, points one beyond last usable byte in @data
0247  * @tag:    tag to be placed
0248  * @string: the data to be tagged
0249  * @len:    the length of the data to be tagged
0250  *
0251  * Note this currently only handles short form tags < 31.
0252  *
0253  * Standard usage is to pass in a @tag, @string and @length and the
0254  * @string will be ASN.1 encoded with @tag and placed into @data.  If
0255  * the encoding would put data past @end_data then an error is
0256  * returned, otherwise a pointer to a position one beyond the encoding
0257  * is returned.
0258  *
0259  * To encode in place pass a NULL @string and -1 for @len and the
0260  * maximum allowable beginning and end of the data; all this will do
0261  * is add the current maximum length and update the data pointer to
0262  * the place where the tag contents should be placed is returned.  The
0263  * data should be copied in by the calling routine which should then
0264  * repeat the prior statement but now with the known length.  In order
0265  * to avoid having to keep both before and after pointers, the repeat
0266  * expects to be called with @data pointing to where the first encode
0267  * returned it and still NULL for @string but the real length in @len.
0268  */
0269 unsigned char *
0270 asn1_encode_tag(unsigned char *data, const unsigned char *end_data,
0271         u32 tag, const unsigned char *string, int len)
0272 {
0273     int data_len = end_data - data;
0274     int ret;
0275 
0276     if (WARN(tag > 30, "ASN.1 tag can't be > 30"))
0277         return ERR_PTR(-EINVAL);
0278 
0279     if (!string && WARN(len > 127,
0280                 "BUG: recode tag is too big (>127)"))
0281         return ERR_PTR(-EINVAL);
0282 
0283     if (IS_ERR(data))
0284         return data;
0285 
0286     if (!string && len > 0) {
0287         /*
0288          * we're recoding, so move back to the start of the
0289          * tag and install a dummy length because the real
0290          * data_len should be NULL
0291          */
0292         data -= 2;
0293         data_len = 2;
0294     }
0295 
0296     if (data_len < 2)
0297         return ERR_PTR(-EINVAL);
0298 
0299     *(data++) = _tagn(CONT, CONS, tag);
0300     data_len--;
0301     ret = asn1_encode_length(&data, &data_len, len);
0302     if (ret < 0)
0303         return ERR_PTR(ret);
0304 
0305     if (!string)
0306         return data;
0307 
0308     if (data_len < len)
0309         return ERR_PTR(-EINVAL);
0310 
0311     memcpy(data, string, len);
0312     data += len;
0313 
0314     return data;
0315 }
0316 EXPORT_SYMBOL_GPL(asn1_encode_tag);
0317 
0318 /**
0319  * asn1_encode_octet_string() - encode an ASN.1 OCTET STRING
0320  * @data:   pointer to encode at
0321  * @end_data:   end of data pointer, points one beyond last usable byte in @data
0322  * @string: string to be encoded
0323  * @len:    length of string
0324  *
0325  * Note ASN.1 octet strings may contain zeros, so the length is obligatory.
0326  */
0327 unsigned char *
0328 asn1_encode_octet_string(unsigned char *data,
0329              const unsigned char *end_data,
0330              const unsigned char *string, u32 len)
0331 {
0332     int data_len = end_data - data;
0333     int ret;
0334 
0335     if (IS_ERR(data))
0336         return data;
0337 
0338     /* need minimum of 2 bytes for tag and length of zero length string */
0339     if (data_len < 2)
0340         return ERR_PTR(-EINVAL);
0341 
0342     *(data++) = _tag(UNIV, PRIM, OTS);
0343     data_len--;
0344 
0345     ret = asn1_encode_length(&data, &data_len, len);
0346     if (ret)
0347         return ERR_PTR(ret);
0348 
0349     if (data_len < len)
0350         return ERR_PTR(-EINVAL);
0351 
0352     memcpy(data, string, len);
0353     data += len;
0354 
0355     return data;
0356 }
0357 EXPORT_SYMBOL_GPL(asn1_encode_octet_string);
0358 
0359 /**
0360  * asn1_encode_sequence() - wrap a byte stream in an ASN.1 SEQUENCE
0361  * @data:   pointer to encode at
0362  * @end_data:   end of data pointer, points one beyond last usable byte in @data
0363  * @seq:    data to be encoded as a sequence
0364  * @len:    length of the data to be encoded as a sequence
0365  *
0366  * Fill in a sequence.  To encode in place, pass NULL for @seq and -1
0367  * for @len; then call again once the length is known (still with NULL
0368  * for @seq). In order to avoid having to keep both before and after
0369  * pointers, the repeat expects to be called with @data pointing to
0370  * where the first encode placed it.
0371  */
0372 unsigned char *
0373 asn1_encode_sequence(unsigned char *data, const unsigned char *end_data,
0374              const unsigned char *seq, int len)
0375 {
0376     int data_len = end_data - data;
0377     int ret;
0378 
0379     if (!seq && WARN(len > 127,
0380              "BUG: recode sequence is too big (>127)"))
0381         return ERR_PTR(-EINVAL);
0382 
0383     if (IS_ERR(data))
0384         return data;
0385 
0386     if (!seq && len >= 0) {
0387         /*
0388          * we're recoding, so move back to the start of the
0389          * sequence and install a dummy length because the
0390          * real length should be NULL
0391          */
0392         data -= 2;
0393         data_len = 2;
0394     }
0395 
0396     if (data_len < 2)
0397         return ERR_PTR(-EINVAL);
0398 
0399     *(data++) = _tag(UNIV, CONS, SEQ);
0400     data_len--;
0401 
0402     ret = asn1_encode_length(&data, &data_len, len);
0403     if (ret)
0404         return ERR_PTR(ret);
0405 
0406     if (!seq)
0407         return data;
0408 
0409     if (data_len < len)
0410         return ERR_PTR(-EINVAL);
0411 
0412     memcpy(data, seq, len);
0413     data += len;
0414 
0415     return data;
0416 }
0417 EXPORT_SYMBOL_GPL(asn1_encode_sequence);
0418 
0419 /**
0420  * asn1_encode_boolean() - encode a boolean value to ASN.1
0421  * @data:   pointer to encode at
0422  * @end_data:   end of data pointer, points one beyond last usable byte in @data
0423  * @val:    the boolean true/false value
0424  */
0425 unsigned char *
0426 asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,
0427             bool val)
0428 {
0429     int data_len = end_data - data;
0430 
0431     if (IS_ERR(data))
0432         return data;
0433 
0434     /* booleans are 3 bytes: tag, length == 1 and value == 0 or 1 */
0435     if (data_len < 3)
0436         return ERR_PTR(-EINVAL);
0437 
0438     *(data++) = _tag(UNIV, PRIM, BOOL);
0439     data_len--;
0440 
0441     asn1_encode_length(&data, &data_len, 1);
0442 
0443     if (val)
0444         *(data++) = 1;
0445     else
0446         *(data++) = 0;
0447 
0448     return data;
0449 }
0450 EXPORT_SYMBOL_GPL(asn1_encode_boolean);
0451 
0452 MODULE_LICENSE("GPL");