1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
242d5ec27SDavid Howells /* Decoder for ASN.1 BER/DER/CER encoded bytestream
342d5ec27SDavid Howells *
442d5ec27SDavid Howells * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
542d5ec27SDavid Howells * Written by David Howells ([email protected])
642d5ec27SDavid Howells */
742d5ec27SDavid Howells
842d5ec27SDavid Howells #include <linux/export.h>
942d5ec27SDavid Howells #include <linux/kernel.h>
1042d5ec27SDavid Howells #include <linux/errno.h>
11ccab6058STudor Ambarus #include <linux/module.h>
1242d5ec27SDavid Howells #include <linux/asn1_decoder.h>
1342d5ec27SDavid Howells #include <linux/asn1_ber_bytecode.h>
1442d5ec27SDavid Howells
1542d5ec27SDavid Howells static const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
1642d5ec27SDavid Howells /* OPC TAG JMP ACT */
1742d5ec27SDavid Howells [ASN1_OP_MATCH] = 1 + 1,
1842d5ec27SDavid Howells [ASN1_OP_MATCH_OR_SKIP] = 1 + 1,
1942d5ec27SDavid Howells [ASN1_OP_MATCH_ACT] = 1 + 1 + 1,
2042d5ec27SDavid Howells [ASN1_OP_MATCH_ACT_OR_SKIP] = 1 + 1 + 1,
2142d5ec27SDavid Howells [ASN1_OP_MATCH_JUMP] = 1 + 1 + 1,
2242d5ec27SDavid Howells [ASN1_OP_MATCH_JUMP_OR_SKIP] = 1 + 1 + 1,
2342d5ec27SDavid Howells [ASN1_OP_MATCH_ANY] = 1,
24233ce79dSDavid Howells [ASN1_OP_MATCH_ANY_OR_SKIP] = 1,
2542d5ec27SDavid Howells [ASN1_OP_MATCH_ANY_ACT] = 1 + 1,
26233ce79dSDavid Howells [ASN1_OP_MATCH_ANY_ACT_OR_SKIP] = 1 + 1,
2742d5ec27SDavid Howells [ASN1_OP_COND_MATCH_OR_SKIP] = 1 + 1,
2842d5ec27SDavid Howells [ASN1_OP_COND_MATCH_ACT_OR_SKIP] = 1 + 1 + 1,
2942d5ec27SDavid Howells [ASN1_OP_COND_MATCH_JUMP_OR_SKIP] = 1 + 1 + 1,
3042d5ec27SDavid Howells [ASN1_OP_COND_MATCH_ANY] = 1,
31233ce79dSDavid Howells [ASN1_OP_COND_MATCH_ANY_OR_SKIP] = 1,
3242d5ec27SDavid Howells [ASN1_OP_COND_MATCH_ANY_ACT] = 1 + 1,
33233ce79dSDavid Howells [ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP] = 1 + 1,
3442d5ec27SDavid Howells [ASN1_OP_COND_FAIL] = 1,
3542d5ec27SDavid Howells [ASN1_OP_COMPLETE] = 1,
3642d5ec27SDavid Howells [ASN1_OP_ACT] = 1 + 1,
373f3af97dSDavid Howells [ASN1_OP_MAYBE_ACT] = 1 + 1,
3842d5ec27SDavid Howells [ASN1_OP_RETURN] = 1,
3942d5ec27SDavid Howells [ASN1_OP_END_SEQ] = 1,
4042d5ec27SDavid Howells [ASN1_OP_END_SEQ_OF] = 1 + 1,
4142d5ec27SDavid Howells [ASN1_OP_END_SET] = 1,
4242d5ec27SDavid Howells [ASN1_OP_END_SET_OF] = 1 + 1,
4342d5ec27SDavid Howells [ASN1_OP_END_SEQ_ACT] = 1 + 1,
4442d5ec27SDavid Howells [ASN1_OP_END_SEQ_OF_ACT] = 1 + 1 + 1,
4542d5ec27SDavid Howells [ASN1_OP_END_SET_ACT] = 1 + 1,
4642d5ec27SDavid Howells [ASN1_OP_END_SET_OF_ACT] = 1 + 1 + 1,
4742d5ec27SDavid Howells };
4842d5ec27SDavid Howells
4942d5ec27SDavid Howells /*
5042d5ec27SDavid Howells * Find the length of an indefinite length object
51dbadc176SDavid Howells * @data: The data buffer
52dbadc176SDavid Howells * @datalen: The end of the innermost containing element in the buffer
53dbadc176SDavid Howells * @_dp: The data parse cursor (updated before returning)
54dbadc176SDavid Howells * @_len: Where to return the size of the element.
55dbadc176SDavid Howells * @_errmsg: Where to return a pointer to an error message on error
5642d5ec27SDavid Howells */
asn1_find_indefinite_length(const unsigned char * data,size_t datalen,size_t * _dp,size_t * _len,const char ** _errmsg)5742d5ec27SDavid Howells static int asn1_find_indefinite_length(const unsigned char *data, size_t datalen,
58dbadc176SDavid Howells size_t *_dp, size_t *_len,
59dbadc176SDavid Howells const char **_errmsg)
6042d5ec27SDavid Howells {
6142d5ec27SDavid Howells unsigned char tag, tmp;
62dbadc176SDavid Howells size_t dp = *_dp, len, n;
6342d5ec27SDavid Howells int indef_level = 1;
6442d5ec27SDavid Howells
6542d5ec27SDavid Howells next_tag:
6642d5ec27SDavid Howells if (unlikely(datalen - dp < 2)) {
6742d5ec27SDavid Howells if (datalen == dp)
6842d5ec27SDavid Howells goto missing_eoc;
6942d5ec27SDavid Howells goto data_overrun_error;
7042d5ec27SDavid Howells }
7142d5ec27SDavid Howells
7242d5ec27SDavid Howells /* Extract a tag from the data */
7342d5ec27SDavid Howells tag = data[dp++];
7423c8a812SDavid Howells if (tag == ASN1_EOC) {
7542d5ec27SDavid Howells /* It appears to be an EOC. */
7642d5ec27SDavid Howells if (data[dp++] != 0)
7742d5ec27SDavid Howells goto invalid_eoc;
78dbadc176SDavid Howells if (--indef_level <= 0) {
79dbadc176SDavid Howells *_len = dp - *_dp;
80dbadc176SDavid Howells *_dp = dp;
81dbadc176SDavid Howells return 0;
82dbadc176SDavid Howells }
8342d5ec27SDavid Howells goto next_tag;
8442d5ec27SDavid Howells }
8542d5ec27SDavid Howells
8699cca91eSDavid Howells if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) {
8742d5ec27SDavid Howells do {
8842d5ec27SDavid Howells if (unlikely(datalen - dp < 2))
8942d5ec27SDavid Howells goto data_overrun_error;
9042d5ec27SDavid Howells tmp = data[dp++];
9142d5ec27SDavid Howells } while (tmp & 0x80);
9242d5ec27SDavid Howells }
9342d5ec27SDavid Howells
9442d5ec27SDavid Howells /* Extract the length */
9542d5ec27SDavid Howells len = data[dp++];
9623c8a812SDavid Howells if (len <= 0x7f)
9723c8a812SDavid Howells goto check_length;
9842d5ec27SDavid Howells
9999cca91eSDavid Howells if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
10042d5ec27SDavid Howells /* Indefinite length */
10142d5ec27SDavid Howells if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5))
10242d5ec27SDavid Howells goto indefinite_len_primitive;
10342d5ec27SDavid Howells indef_level++;
10442d5ec27SDavid Howells goto next_tag;
10542d5ec27SDavid Howells }
10642d5ec27SDavid Howells
10742d5ec27SDavid Howells n = len - 0x80;
10823c8a812SDavid Howells if (unlikely(n > sizeof(len) - 1))
10942d5ec27SDavid Howells goto length_too_long;
11042d5ec27SDavid Howells if (unlikely(n > datalen - dp))
11142d5ec27SDavid Howells goto data_overrun_error;
11223c8a812SDavid Howells len = 0;
11323c8a812SDavid Howells for (; n > 0; n--) {
11442d5ec27SDavid Howells len <<= 8;
11542d5ec27SDavid Howells len |= data[dp++];
11642d5ec27SDavid Howells }
11723c8a812SDavid Howells check_length:
11823c8a812SDavid Howells if (len > datalen - dp)
11923c8a812SDavid Howells goto data_overrun_error;
12042d5ec27SDavid Howells dp += len;
12142d5ec27SDavid Howells goto next_tag;
12242d5ec27SDavid Howells
12342d5ec27SDavid Howells length_too_long:
12442d5ec27SDavid Howells *_errmsg = "Unsupported length";
12542d5ec27SDavid Howells goto error;
12642d5ec27SDavid Howells indefinite_len_primitive:
12742d5ec27SDavid Howells *_errmsg = "Indefinite len primitive not permitted";
12842d5ec27SDavid Howells goto error;
12942d5ec27SDavid Howells invalid_eoc:
13042d5ec27SDavid Howells *_errmsg = "Invalid length EOC";
13142d5ec27SDavid Howells goto error;
13242d5ec27SDavid Howells data_overrun_error:
13342d5ec27SDavid Howells *_errmsg = "Data overrun error";
13442d5ec27SDavid Howells goto error;
13542d5ec27SDavid Howells missing_eoc:
13642d5ec27SDavid Howells *_errmsg = "Missing EOC in indefinite len cons";
13742d5ec27SDavid Howells error:
138dbadc176SDavid Howells *_dp = dp;
13942d5ec27SDavid Howells return -1;
14042d5ec27SDavid Howells }
14142d5ec27SDavid Howells
14242d5ec27SDavid Howells /**
14342d5ec27SDavid Howells * asn1_ber_decoder - Decoder BER/DER/CER ASN.1 according to pattern
14442d5ec27SDavid Howells * @decoder: The decoder definition (produced by asn1_compiler)
14542d5ec27SDavid Howells * @context: The caller's context (to be passed to the action functions)
14642d5ec27SDavid Howells * @data: The encoded data
147548bbff9SFabian Frederick * @datalen: The size of the encoded data
14842d5ec27SDavid Howells *
14942d5ec27SDavid Howells * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern
15042d5ec27SDavid Howells * produced by asn1_compiler. Action functions are called on marked tags to
15142d5ec27SDavid Howells * allow the caller to retrieve significant data.
15242d5ec27SDavid Howells *
15342d5ec27SDavid Howells * LIMITATIONS:
15442d5ec27SDavid Howells *
15542d5ec27SDavid Howells * To keep down the amount of stack used by this function, the following limits
15642d5ec27SDavid Howells * have been imposed:
15742d5ec27SDavid Howells *
15842d5ec27SDavid Howells * (1) This won't handle datalen > 65535 without increasing the size of the
15942d5ec27SDavid Howells * cons stack elements and length_too_long checking.
16042d5ec27SDavid Howells *
16142d5ec27SDavid Howells * (2) The stack of constructed types is 10 deep. If the depth of non-leaf
16242d5ec27SDavid Howells * constructed types exceeds this, the decode will fail.
16342d5ec27SDavid Howells *
16442d5ec27SDavid Howells * (3) The SET type (not the SET OF type) isn't really supported as tracking
16542d5ec27SDavid Howells * what members of the set have been seen is a pain.
16642d5ec27SDavid Howells */
asn1_ber_decoder(const struct asn1_decoder * decoder,void * context,const unsigned char * data,size_t datalen)16742d5ec27SDavid Howells int asn1_ber_decoder(const struct asn1_decoder *decoder,
16842d5ec27SDavid Howells void *context,
16942d5ec27SDavid Howells const unsigned char *data,
17042d5ec27SDavid Howells size_t datalen)
17142d5ec27SDavid Howells {
17242d5ec27SDavid Howells const unsigned char *machine = decoder->machine;
17342d5ec27SDavid Howells const asn1_action_t *actions = decoder->actions;
17442d5ec27SDavid Howells size_t machlen = decoder->machlen;
17542d5ec27SDavid Howells enum asn1_opcode op;
17642d5ec27SDavid Howells unsigned char tag = 0, csp = 0, jsp = 0, optag = 0, hdr = 0;
17742d5ec27SDavid Howells const char *errmsg;
17842d5ec27SDavid Howells size_t pc = 0, dp = 0, tdp = 0, len = 0;
17942d5ec27SDavid Howells int ret;
18042d5ec27SDavid Howells
18142d5ec27SDavid Howells unsigned char flags = 0;
18242d5ec27SDavid Howells #define FLAG_INDEFINITE_LENGTH 0x01
18342d5ec27SDavid Howells #define FLAG_MATCHED 0x02
1843f3af97dSDavid Howells #define FLAG_LAST_MATCHED 0x04 /* Last tag matched */
18542d5ec27SDavid Howells #define FLAG_CONS 0x20 /* Corresponds to CONS bit in the opcode tag
18642d5ec27SDavid Howells * - ie. whether or not we are going to parse
18742d5ec27SDavid Howells * a compound type.
18842d5ec27SDavid Howells */
18942d5ec27SDavid Howells
19042d5ec27SDavid Howells #define NR_CONS_STACK 10
19142d5ec27SDavid Howells unsigned short cons_dp_stack[NR_CONS_STACK];
19242d5ec27SDavid Howells unsigned short cons_datalen_stack[NR_CONS_STACK];
19342d5ec27SDavid Howells unsigned char cons_hdrlen_stack[NR_CONS_STACK];
19442d5ec27SDavid Howells #define NR_JUMP_STACK 10
19542d5ec27SDavid Howells unsigned char jump_stack[NR_JUMP_STACK];
19642d5ec27SDavid Howells
19742d5ec27SDavid Howells if (datalen > 65535)
19842d5ec27SDavid Howells return -EMSGSIZE;
19942d5ec27SDavid Howells
20042d5ec27SDavid Howells next_op:
20142d5ec27SDavid Howells pr_debug("next_op: pc=\e[32m%zu\e[m/%zu dp=\e[33m%zu\e[m/%zu C=%d J=%d\n",
20242d5ec27SDavid Howells pc, machlen, dp, datalen, csp, jsp);
20342d5ec27SDavid Howells if (unlikely(pc >= machlen))
20442d5ec27SDavid Howells goto machine_overrun_error;
20542d5ec27SDavid Howells op = machine[pc];
20642d5ec27SDavid Howells if (unlikely(pc + asn1_op_lengths[op] > machlen))
20742d5ec27SDavid Howells goto machine_overrun_error;
20842d5ec27SDavid Howells
20942d5ec27SDavid Howells /* If this command is meant to match a tag, then do that before
21042d5ec27SDavid Howells * evaluating the command.
21142d5ec27SDavid Howells */
21242d5ec27SDavid Howells if (op <= ASN1_OP__MATCHES_TAG) {
21342d5ec27SDavid Howells unsigned char tmp;
21442d5ec27SDavid Howells
21542d5ec27SDavid Howells /* Skip conditional matches if possible */
2160d62e9ddSDavid Howells if ((op & ASN1_OP_MATCH__COND && flags & FLAG_MATCHED) ||
2170d62e9ddSDavid Howells (op & ASN1_OP_MATCH__SKIP && dp == datalen)) {
2183f3af97dSDavid Howells flags &= ~FLAG_LAST_MATCHED;
21942d5ec27SDavid Howells pc += asn1_op_lengths[op];
22042d5ec27SDavid Howells goto next_op;
22142d5ec27SDavid Howells }
22242d5ec27SDavid Howells
22342d5ec27SDavid Howells flags = 0;
22442d5ec27SDavid Howells hdr = 2;
22542d5ec27SDavid Howells
22642d5ec27SDavid Howells /* Extract a tag from the data */
227624f5ab8SEric Biggers if (unlikely(datalen - dp < 2))
22842d5ec27SDavid Howells goto data_overrun_error;
22942d5ec27SDavid Howells tag = data[dp++];
23099cca91eSDavid Howells if (unlikely((tag & 0x1f) == ASN1_LONG_TAG))
23142d5ec27SDavid Howells goto long_tag_not_supported;
23242d5ec27SDavid Howells
23342d5ec27SDavid Howells if (op & ASN1_OP_MATCH__ANY) {
23442d5ec27SDavid Howells pr_debug("- any %02x\n", tag);
23542d5ec27SDavid Howells } else {
23642d5ec27SDavid Howells /* Extract the tag from the machine
23742d5ec27SDavid Howells * - Either CONS or PRIM are permitted in the data if
23842d5ec27SDavid Howells * CONS is not set in the op stream, otherwise CONS
23942d5ec27SDavid Howells * is mandatory.
24042d5ec27SDavid Howells */
24142d5ec27SDavid Howells optag = machine[pc + 1];
24242d5ec27SDavid Howells flags |= optag & FLAG_CONS;
24342d5ec27SDavid Howells
24442d5ec27SDavid Howells /* Determine whether the tag matched */
24542d5ec27SDavid Howells tmp = optag ^ tag;
24642d5ec27SDavid Howells tmp &= ~(optag & ASN1_CONS_BIT);
24742d5ec27SDavid Howells pr_debug("- match? %02x %02x %02x\n", tag, optag, tmp);
24842d5ec27SDavid Howells if (tmp != 0) {
24942d5ec27SDavid Howells /* All odd-numbered tags are MATCH_OR_SKIP. */
25042d5ec27SDavid Howells if (op & ASN1_OP_MATCH__SKIP) {
25142d5ec27SDavid Howells pc += asn1_op_lengths[op];
25242d5ec27SDavid Howells dp--;
25342d5ec27SDavid Howells goto next_op;
25442d5ec27SDavid Howells }
25542d5ec27SDavid Howells goto tag_mismatch;
25642d5ec27SDavid Howells }
25742d5ec27SDavid Howells }
25842d5ec27SDavid Howells flags |= FLAG_MATCHED;
25942d5ec27SDavid Howells
26042d5ec27SDavid Howells len = data[dp++];
26142d5ec27SDavid Howells if (len > 0x7f) {
26299cca91eSDavid Howells if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
26342d5ec27SDavid Howells /* Indefinite length */
26442d5ec27SDavid Howells if (unlikely(!(tag & ASN1_CONS_BIT)))
26542d5ec27SDavid Howells goto indefinite_len_primitive;
26642d5ec27SDavid Howells flags |= FLAG_INDEFINITE_LENGTH;
26742d5ec27SDavid Howells if (unlikely(2 > datalen - dp))
26842d5ec27SDavid Howells goto data_overrun_error;
26942d5ec27SDavid Howells } else {
27042d5ec27SDavid Howells int n = len - 0x80;
27142d5ec27SDavid Howells if (unlikely(n > 2))
27242d5ec27SDavid Howells goto length_too_long;
273624f5ab8SEric Biggers if (unlikely(n > datalen - dp))
27442d5ec27SDavid Howells goto data_overrun_error;
27542d5ec27SDavid Howells hdr += n;
27642d5ec27SDavid Howells for (len = 0; n > 0; n--) {
27742d5ec27SDavid Howells len <<= 8;
27842d5ec27SDavid Howells len |= data[dp++];
27942d5ec27SDavid Howells }
28042d5ec27SDavid Howells if (unlikely(len > datalen - dp))
28142d5ec27SDavid Howells goto data_overrun_error;
28242d5ec27SDavid Howells }
2832eb9eabfSEric Biggers } else {
2842eb9eabfSEric Biggers if (unlikely(len > datalen - dp))
2852eb9eabfSEric Biggers goto data_overrun_error;
28642d5ec27SDavid Howells }
28742d5ec27SDavid Howells
28842d5ec27SDavid Howells if (flags & FLAG_CONS) {
28942d5ec27SDavid Howells /* For expected compound forms, we stack the positions
29042d5ec27SDavid Howells * of the start and end of the data.
29142d5ec27SDavid Howells */
29242d5ec27SDavid Howells if (unlikely(csp >= NR_CONS_STACK))
29342d5ec27SDavid Howells goto cons_stack_overflow;
29442d5ec27SDavid Howells cons_dp_stack[csp] = dp;
29542d5ec27SDavid Howells cons_hdrlen_stack[csp] = hdr;
29642d5ec27SDavid Howells if (!(flags & FLAG_INDEFINITE_LENGTH)) {
29742d5ec27SDavid Howells cons_datalen_stack[csp] = datalen;
29842d5ec27SDavid Howells datalen = dp + len;
29942d5ec27SDavid Howells } else {
30042d5ec27SDavid Howells cons_datalen_stack[csp] = 0;
30142d5ec27SDavid Howells }
30242d5ec27SDavid Howells csp++;
30342d5ec27SDavid Howells }
30442d5ec27SDavid Howells
30542d5ec27SDavid Howells pr_debug("- TAG: %02x %zu%s\n",
30642d5ec27SDavid Howells tag, len, flags & FLAG_CONS ? " CONS" : "");
30742d5ec27SDavid Howells tdp = dp;
30842d5ec27SDavid Howells }
30942d5ec27SDavid Howells
31042d5ec27SDavid Howells /* Decide how to handle the operation */
31142d5ec27SDavid Howells switch (op) {
31242d5ec27SDavid Howells case ASN1_OP_MATCH:
31342d5ec27SDavid Howells case ASN1_OP_MATCH_OR_SKIP:
314e0058f3aSEric Biggers case ASN1_OP_MATCH_ACT:
315e0058f3aSEric Biggers case ASN1_OP_MATCH_ACT_OR_SKIP:
31642d5ec27SDavid Howells case ASN1_OP_MATCH_ANY:
317233ce79dSDavid Howells case ASN1_OP_MATCH_ANY_OR_SKIP:
318e0058f3aSEric Biggers case ASN1_OP_MATCH_ANY_ACT:
319e0058f3aSEric Biggers case ASN1_OP_MATCH_ANY_ACT_OR_SKIP:
32042d5ec27SDavid Howells case ASN1_OP_COND_MATCH_OR_SKIP:
321e0058f3aSEric Biggers case ASN1_OP_COND_MATCH_ACT_OR_SKIP:
32242d5ec27SDavid Howells case ASN1_OP_COND_MATCH_ANY:
323233ce79dSDavid Howells case ASN1_OP_COND_MATCH_ANY_OR_SKIP:
324e0058f3aSEric Biggers case ASN1_OP_COND_MATCH_ANY_ACT:
325e0058f3aSEric Biggers case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP:
326e0058f3aSEric Biggers
32742d5ec27SDavid Howells if (!(flags & FLAG_CONS)) {
32842d5ec27SDavid Howells if (flags & FLAG_INDEFINITE_LENGTH) {
329e0058f3aSEric Biggers size_t tmp = dp;
330e0058f3aSEric Biggers
331dbadc176SDavid Howells ret = asn1_find_indefinite_length(
332e0058f3aSEric Biggers data, datalen, &tmp, &len, &errmsg);
333dbadc176SDavid Howells if (ret < 0)
33442d5ec27SDavid Howells goto error;
33542d5ec27SDavid Howells }
33642d5ec27SDavid Howells pr_debug("- LEAF: %zu\n", len);
33742d5ec27SDavid Howells }
338e0058f3aSEric Biggers
339e0058f3aSEric Biggers if (op & ASN1_OP_MATCH__ACT) {
340e0058f3aSEric Biggers unsigned char act;
341e0058f3aSEric Biggers
342e0058f3aSEric Biggers if (op & ASN1_OP_MATCH__ANY)
343e0058f3aSEric Biggers act = machine[pc + 1];
344e0058f3aSEric Biggers else
345e0058f3aSEric Biggers act = machine[pc + 2];
346e0058f3aSEric Biggers ret = actions[act](context, hdr, tag, data + dp, len);
347e0058f3aSEric Biggers if (ret < 0)
348e0058f3aSEric Biggers return ret;
349e0058f3aSEric Biggers }
350e0058f3aSEric Biggers
351e0058f3aSEric Biggers if (!(flags & FLAG_CONS))
352e0058f3aSEric Biggers dp += len;
35342d5ec27SDavid Howells pc += asn1_op_lengths[op];
35442d5ec27SDavid Howells goto next_op;
35542d5ec27SDavid Howells
35642d5ec27SDavid Howells case ASN1_OP_MATCH_JUMP:
35742d5ec27SDavid Howells case ASN1_OP_MATCH_JUMP_OR_SKIP:
35842d5ec27SDavid Howells case ASN1_OP_COND_MATCH_JUMP_OR_SKIP:
35942d5ec27SDavid Howells pr_debug("- MATCH_JUMP\n");
36042d5ec27SDavid Howells if (unlikely(jsp == NR_JUMP_STACK))
36142d5ec27SDavid Howells goto jump_stack_overflow;
36242d5ec27SDavid Howells jump_stack[jsp++] = pc + asn1_op_lengths[op];
36342d5ec27SDavid Howells pc = machine[pc + 2];
36442d5ec27SDavid Howells goto next_op;
36542d5ec27SDavid Howells
36642d5ec27SDavid Howells case ASN1_OP_COND_FAIL:
36742d5ec27SDavid Howells if (unlikely(!(flags & FLAG_MATCHED)))
36842d5ec27SDavid Howells goto tag_mismatch;
36942d5ec27SDavid Howells pc += asn1_op_lengths[op];
37042d5ec27SDavid Howells goto next_op;
37142d5ec27SDavid Howells
37242d5ec27SDavid Howells case ASN1_OP_COMPLETE:
37342d5ec27SDavid Howells if (unlikely(jsp != 0 || csp != 0)) {
37442d5ec27SDavid Howells pr_err("ASN.1 decoder error: Stacks not empty at completion (%u, %u)\n",
37542d5ec27SDavid Howells jsp, csp);
37642d5ec27SDavid Howells return -EBADMSG;
37742d5ec27SDavid Howells }
37842d5ec27SDavid Howells return 0;
37942d5ec27SDavid Howells
38042d5ec27SDavid Howells case ASN1_OP_END_SET:
38142d5ec27SDavid Howells case ASN1_OP_END_SET_ACT:
38242d5ec27SDavid Howells if (unlikely(!(flags & FLAG_MATCHED)))
38342d5ec27SDavid Howells goto tag_mismatch;
3844c1ca831SNick Desaulniers fallthrough;
385afb33e40SGustavo A. R. Silva
38642d5ec27SDavid Howells case ASN1_OP_END_SEQ:
38742d5ec27SDavid Howells case ASN1_OP_END_SET_OF:
38842d5ec27SDavid Howells case ASN1_OP_END_SEQ_OF:
38942d5ec27SDavid Howells case ASN1_OP_END_SEQ_ACT:
39042d5ec27SDavid Howells case ASN1_OP_END_SET_OF_ACT:
39142d5ec27SDavid Howells case ASN1_OP_END_SEQ_OF_ACT:
39242d5ec27SDavid Howells if (unlikely(csp <= 0))
39342d5ec27SDavid Howells goto cons_stack_underflow;
39442d5ec27SDavid Howells csp--;
39542d5ec27SDavid Howells tdp = cons_dp_stack[csp];
39642d5ec27SDavid Howells hdr = cons_hdrlen_stack[csp];
39742d5ec27SDavid Howells len = datalen;
39842d5ec27SDavid Howells datalen = cons_datalen_stack[csp];
39942d5ec27SDavid Howells pr_debug("- end cons t=%zu dp=%zu l=%zu/%zu\n",
40042d5ec27SDavid Howells tdp, dp, len, datalen);
40142d5ec27SDavid Howells if (datalen == 0) {
40242d5ec27SDavid Howells /* Indefinite length - check for the EOC. */
40342d5ec27SDavid Howells datalen = len;
40442d5ec27SDavid Howells if (unlikely(datalen - dp < 2))
40542d5ec27SDavid Howells goto data_overrun_error;
40642d5ec27SDavid Howells if (data[dp++] != 0) {
40742d5ec27SDavid Howells if (op & ASN1_OP_END__OF) {
40842d5ec27SDavid Howells dp--;
40942d5ec27SDavid Howells csp++;
41042d5ec27SDavid Howells pc = machine[pc + 1];
41142d5ec27SDavid Howells pr_debug("- continue\n");
41242d5ec27SDavid Howells goto next_op;
41342d5ec27SDavid Howells }
41442d5ec27SDavid Howells goto missing_eoc;
41542d5ec27SDavid Howells }
41642d5ec27SDavid Howells if (data[dp++] != 0)
41742d5ec27SDavid Howells goto invalid_eoc;
41842d5ec27SDavid Howells len = dp - tdp - 2;
41942d5ec27SDavid Howells } else {
42042d5ec27SDavid Howells if (dp < len && (op & ASN1_OP_END__OF)) {
42142d5ec27SDavid Howells datalen = len;
42242d5ec27SDavid Howells csp++;
42342d5ec27SDavid Howells pc = machine[pc + 1];
42442d5ec27SDavid Howells pr_debug("- continue\n");
42542d5ec27SDavid Howells goto next_op;
42642d5ec27SDavid Howells }
42742d5ec27SDavid Howells if (dp != len)
42842d5ec27SDavid Howells goto cons_length_error;
42942d5ec27SDavid Howells len -= tdp;
43042d5ec27SDavid Howells pr_debug("- cons len l=%zu d=%zu\n", len, dp - tdp);
43142d5ec27SDavid Howells }
43242d5ec27SDavid Howells
43342d5ec27SDavid Howells if (op & ASN1_OP_END__ACT) {
43442d5ec27SDavid Howells unsigned char act;
43542d5ec27SDavid Howells if (op & ASN1_OP_END__OF)
43642d5ec27SDavid Howells act = machine[pc + 2];
43742d5ec27SDavid Howells else
43842d5ec27SDavid Howells act = machine[pc + 1];
43942d5ec27SDavid Howells ret = actions[act](context, hdr, 0, data + tdp, len);
44081a7be2cSEric Biggers if (ret < 0)
44181a7be2cSEric Biggers return ret;
44242d5ec27SDavid Howells }
44342d5ec27SDavid Howells pc += asn1_op_lengths[op];
44442d5ec27SDavid Howells goto next_op;
44542d5ec27SDavid Howells
4463f3af97dSDavid Howells case ASN1_OP_MAYBE_ACT:
4473f3af97dSDavid Howells if (!(flags & FLAG_LAST_MATCHED)) {
4483f3af97dSDavid Howells pc += asn1_op_lengths[op];
4493f3af97dSDavid Howells goto next_op;
4503f3af97dSDavid Howells }
4514c1ca831SNick Desaulniers fallthrough;
452afb33e40SGustavo A. R. Silva
45342d5ec27SDavid Howells case ASN1_OP_ACT:
45442d5ec27SDavid Howells ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len);
4553f3af97dSDavid Howells if (ret < 0)
4563f3af97dSDavid Howells return ret;
45742d5ec27SDavid Howells pc += asn1_op_lengths[op];
45842d5ec27SDavid Howells goto next_op;
45942d5ec27SDavid Howells
46042d5ec27SDavid Howells case ASN1_OP_RETURN:
46142d5ec27SDavid Howells if (unlikely(jsp <= 0))
46242d5ec27SDavid Howells goto jump_stack_underflow;
46342d5ec27SDavid Howells pc = jump_stack[--jsp];
4643f3af97dSDavid Howells flags |= FLAG_MATCHED | FLAG_LAST_MATCHED;
46542d5ec27SDavid Howells goto next_op;
46642d5ec27SDavid Howells
46742d5ec27SDavid Howells default:
46842d5ec27SDavid Howells break;
46942d5ec27SDavid Howells }
47042d5ec27SDavid Howells
47142d5ec27SDavid Howells /* Shouldn't reach here */
4723f3af97dSDavid Howells pr_err("ASN.1 decoder error: Found reserved opcode (%u) pc=%zu\n",
4733f3af97dSDavid Howells op, pc);
47442d5ec27SDavid Howells return -EBADMSG;
47542d5ec27SDavid Howells
47642d5ec27SDavid Howells data_overrun_error:
47742d5ec27SDavid Howells errmsg = "Data overrun error";
47842d5ec27SDavid Howells goto error;
47942d5ec27SDavid Howells machine_overrun_error:
48042d5ec27SDavid Howells errmsg = "Machine overrun error";
48142d5ec27SDavid Howells goto error;
48242d5ec27SDavid Howells jump_stack_underflow:
48342d5ec27SDavid Howells errmsg = "Jump stack underflow";
48442d5ec27SDavid Howells goto error;
48542d5ec27SDavid Howells jump_stack_overflow:
48642d5ec27SDavid Howells errmsg = "Jump stack overflow";
48742d5ec27SDavid Howells goto error;
48842d5ec27SDavid Howells cons_stack_underflow:
48942d5ec27SDavid Howells errmsg = "Cons stack underflow";
49042d5ec27SDavid Howells goto error;
49142d5ec27SDavid Howells cons_stack_overflow:
49242d5ec27SDavid Howells errmsg = "Cons stack overflow";
49342d5ec27SDavid Howells goto error;
49442d5ec27SDavid Howells cons_length_error:
49542d5ec27SDavid Howells errmsg = "Cons length error";
49642d5ec27SDavid Howells goto error;
49742d5ec27SDavid Howells missing_eoc:
49842d5ec27SDavid Howells errmsg = "Missing EOC in indefinite len cons";
49942d5ec27SDavid Howells goto error;
50042d5ec27SDavid Howells invalid_eoc:
50142d5ec27SDavid Howells errmsg = "Invalid length EOC";
50242d5ec27SDavid Howells goto error;
50342d5ec27SDavid Howells length_too_long:
50442d5ec27SDavid Howells errmsg = "Unsupported length";
50542d5ec27SDavid Howells goto error;
50642d5ec27SDavid Howells indefinite_len_primitive:
50742d5ec27SDavid Howells errmsg = "Indefinite len primitive not permitted";
50842d5ec27SDavid Howells goto error;
50942d5ec27SDavid Howells tag_mismatch:
51042d5ec27SDavid Howells errmsg = "Unexpected tag";
51142d5ec27SDavid Howells goto error;
51242d5ec27SDavid Howells long_tag_not_supported:
51342d5ec27SDavid Howells errmsg = "Long tag not supported";
51442d5ec27SDavid Howells error:
51542d5ec27SDavid Howells pr_debug("\nASN1: %s [m=%zu d=%zu ot=%02x t=%02x l=%zu]\n",
51642d5ec27SDavid Howells errmsg, pc, dp, optag, tag, len);
51742d5ec27SDavid Howells return -EBADMSG;
51842d5ec27SDavid Howells }
51942d5ec27SDavid Howells EXPORT_SYMBOL_GPL(asn1_ber_decoder);
520ccab6058STudor Ambarus
521*a5561c88SArnd Bergmann MODULE_DESCRIPTION("Decoder for ASN.1 BER/DER/CER encoded bytestream");
522ccab6058STudor Ambarus MODULE_LICENSE("GPL");
523