18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Decoder for ASN.1 BER/DER/CER encoded bytestream
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/export.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/asn1_decoder.h>
138c2ecf20Sopenharmony_ci#include <linux/asn1_ber_bytecode.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
168c2ecf20Sopenharmony_ci	/*					OPC TAG JMP ACT */
178c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH]				= 1 + 1,
188c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_OR_SKIP]			= 1 + 1,
198c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_ACT]			= 1 + 1     + 1,
208c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_ACT_OR_SKIP]		= 1 + 1     + 1,
218c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_JUMP]			= 1 + 1 + 1,
228c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_JUMP_OR_SKIP]		= 1 + 1 + 1,
238c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_ANY]			= 1,
248c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_ANY_OR_SKIP]		= 1,
258c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_ANY_ACT]			= 1         + 1,
268c2ecf20Sopenharmony_ci	[ASN1_OP_MATCH_ANY_ACT_OR_SKIP]		= 1         + 1,
278c2ecf20Sopenharmony_ci	[ASN1_OP_COND_MATCH_OR_SKIP]		= 1 + 1,
288c2ecf20Sopenharmony_ci	[ASN1_OP_COND_MATCH_ACT_OR_SKIP]	= 1 + 1     + 1,
298c2ecf20Sopenharmony_ci	[ASN1_OP_COND_MATCH_JUMP_OR_SKIP]	= 1 + 1 + 1,
308c2ecf20Sopenharmony_ci	[ASN1_OP_COND_MATCH_ANY]		= 1,
318c2ecf20Sopenharmony_ci	[ASN1_OP_COND_MATCH_ANY_OR_SKIP]	= 1,
328c2ecf20Sopenharmony_ci	[ASN1_OP_COND_MATCH_ANY_ACT]		= 1         + 1,
338c2ecf20Sopenharmony_ci	[ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP]	= 1         + 1,
348c2ecf20Sopenharmony_ci	[ASN1_OP_COND_FAIL]			= 1,
358c2ecf20Sopenharmony_ci	[ASN1_OP_COMPLETE]			= 1,
368c2ecf20Sopenharmony_ci	[ASN1_OP_ACT]				= 1         + 1,
378c2ecf20Sopenharmony_ci	[ASN1_OP_MAYBE_ACT]			= 1         + 1,
388c2ecf20Sopenharmony_ci	[ASN1_OP_RETURN]			= 1,
398c2ecf20Sopenharmony_ci	[ASN1_OP_END_SEQ]			= 1,
408c2ecf20Sopenharmony_ci	[ASN1_OP_END_SEQ_OF]			= 1     + 1,
418c2ecf20Sopenharmony_ci	[ASN1_OP_END_SET]			= 1,
428c2ecf20Sopenharmony_ci	[ASN1_OP_END_SET_OF]			= 1     + 1,
438c2ecf20Sopenharmony_ci	[ASN1_OP_END_SEQ_ACT]			= 1         + 1,
448c2ecf20Sopenharmony_ci	[ASN1_OP_END_SEQ_OF_ACT]		= 1     + 1 + 1,
458c2ecf20Sopenharmony_ci	[ASN1_OP_END_SET_ACT]			= 1         + 1,
468c2ecf20Sopenharmony_ci	[ASN1_OP_END_SET_OF_ACT]		= 1     + 1 + 1,
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci * Find the length of an indefinite length object
518c2ecf20Sopenharmony_ci * @data: The data buffer
528c2ecf20Sopenharmony_ci * @datalen: The end of the innermost containing element in the buffer
538c2ecf20Sopenharmony_ci * @_dp: The data parse cursor (updated before returning)
548c2ecf20Sopenharmony_ci * @_len: Where to return the size of the element.
558c2ecf20Sopenharmony_ci * @_errmsg: Where to return a pointer to an error message on error
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic int asn1_find_indefinite_length(const unsigned char *data, size_t datalen,
588c2ecf20Sopenharmony_ci				       size_t *_dp, size_t *_len,
598c2ecf20Sopenharmony_ci				       const char **_errmsg)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	unsigned char tag, tmp;
628c2ecf20Sopenharmony_ci	size_t dp = *_dp, len, n;
638c2ecf20Sopenharmony_ci	int indef_level = 1;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cinext_tag:
668c2ecf20Sopenharmony_ci	if (unlikely(datalen - dp < 2)) {
678c2ecf20Sopenharmony_ci		if (datalen == dp)
688c2ecf20Sopenharmony_ci			goto missing_eoc;
698c2ecf20Sopenharmony_ci		goto data_overrun_error;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* Extract a tag from the data */
738c2ecf20Sopenharmony_ci	tag = data[dp++];
748c2ecf20Sopenharmony_ci	if (tag == ASN1_EOC) {
758c2ecf20Sopenharmony_ci		/* It appears to be an EOC. */
768c2ecf20Sopenharmony_ci		if (data[dp++] != 0)
778c2ecf20Sopenharmony_ci			goto invalid_eoc;
788c2ecf20Sopenharmony_ci		if (--indef_level <= 0) {
798c2ecf20Sopenharmony_ci			*_len = dp - *_dp;
808c2ecf20Sopenharmony_ci			*_dp = dp;
818c2ecf20Sopenharmony_ci			return 0;
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci		goto next_tag;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) {
878c2ecf20Sopenharmony_ci		do {
888c2ecf20Sopenharmony_ci			if (unlikely(datalen - dp < 2))
898c2ecf20Sopenharmony_ci				goto data_overrun_error;
908c2ecf20Sopenharmony_ci			tmp = data[dp++];
918c2ecf20Sopenharmony_ci		} while (tmp & 0x80);
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Extract the length */
958c2ecf20Sopenharmony_ci	len = data[dp++];
968c2ecf20Sopenharmony_ci	if (len <= 0x7f)
978c2ecf20Sopenharmony_ci		goto check_length;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
1008c2ecf20Sopenharmony_ci		/* Indefinite length */
1018c2ecf20Sopenharmony_ci		if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5))
1028c2ecf20Sopenharmony_ci			goto indefinite_len_primitive;
1038c2ecf20Sopenharmony_ci		indef_level++;
1048c2ecf20Sopenharmony_ci		goto next_tag;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	n = len - 0x80;
1088c2ecf20Sopenharmony_ci	if (unlikely(n > sizeof(len) - 1))
1098c2ecf20Sopenharmony_ci		goto length_too_long;
1108c2ecf20Sopenharmony_ci	if (unlikely(n > datalen - dp))
1118c2ecf20Sopenharmony_ci		goto data_overrun_error;
1128c2ecf20Sopenharmony_ci	len = 0;
1138c2ecf20Sopenharmony_ci	for (; n > 0; n--) {
1148c2ecf20Sopenharmony_ci		len <<= 8;
1158c2ecf20Sopenharmony_ci		len |= data[dp++];
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_cicheck_length:
1188c2ecf20Sopenharmony_ci	if (len > datalen - dp)
1198c2ecf20Sopenharmony_ci		goto data_overrun_error;
1208c2ecf20Sopenharmony_ci	dp += len;
1218c2ecf20Sopenharmony_ci	goto next_tag;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cilength_too_long:
1248c2ecf20Sopenharmony_ci	*_errmsg = "Unsupported length";
1258c2ecf20Sopenharmony_ci	goto error;
1268c2ecf20Sopenharmony_ciindefinite_len_primitive:
1278c2ecf20Sopenharmony_ci	*_errmsg = "Indefinite len primitive not permitted";
1288c2ecf20Sopenharmony_ci	goto error;
1298c2ecf20Sopenharmony_ciinvalid_eoc:
1308c2ecf20Sopenharmony_ci	*_errmsg = "Invalid length EOC";
1318c2ecf20Sopenharmony_ci	goto error;
1328c2ecf20Sopenharmony_cidata_overrun_error:
1338c2ecf20Sopenharmony_ci	*_errmsg = "Data overrun error";
1348c2ecf20Sopenharmony_ci	goto error;
1358c2ecf20Sopenharmony_cimissing_eoc:
1368c2ecf20Sopenharmony_ci	*_errmsg = "Missing EOC in indefinite len cons";
1378c2ecf20Sopenharmony_cierror:
1388c2ecf20Sopenharmony_ci	*_dp = dp;
1398c2ecf20Sopenharmony_ci	return -1;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/**
1438c2ecf20Sopenharmony_ci * asn1_ber_decoder - Decoder BER/DER/CER ASN.1 according to pattern
1448c2ecf20Sopenharmony_ci * @decoder: The decoder definition (produced by asn1_compiler)
1458c2ecf20Sopenharmony_ci * @context: The caller's context (to be passed to the action functions)
1468c2ecf20Sopenharmony_ci * @data: The encoded data
1478c2ecf20Sopenharmony_ci * @datalen: The size of the encoded data
1488c2ecf20Sopenharmony_ci *
1498c2ecf20Sopenharmony_ci * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern
1508c2ecf20Sopenharmony_ci * produced by asn1_compiler.  Action functions are called on marked tags to
1518c2ecf20Sopenharmony_ci * allow the caller to retrieve significant data.
1528c2ecf20Sopenharmony_ci *
1538c2ecf20Sopenharmony_ci * LIMITATIONS:
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * To keep down the amount of stack used by this function, the following limits
1568c2ecf20Sopenharmony_ci * have been imposed:
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci *  (1) This won't handle datalen > 65535 without increasing the size of the
1598c2ecf20Sopenharmony_ci *	cons stack elements and length_too_long checking.
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci *  (2) The stack of constructed types is 10 deep.  If the depth of non-leaf
1628c2ecf20Sopenharmony_ci *	constructed types exceeds this, the decode will fail.
1638c2ecf20Sopenharmony_ci *
1648c2ecf20Sopenharmony_ci *  (3) The SET type (not the SET OF type) isn't really supported as tracking
1658c2ecf20Sopenharmony_ci *	what members of the set have been seen is a pain.
1668c2ecf20Sopenharmony_ci */
1678c2ecf20Sopenharmony_ciint asn1_ber_decoder(const struct asn1_decoder *decoder,
1688c2ecf20Sopenharmony_ci		     void *context,
1698c2ecf20Sopenharmony_ci		     const unsigned char *data,
1708c2ecf20Sopenharmony_ci		     size_t datalen)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	const unsigned char *machine = decoder->machine;
1738c2ecf20Sopenharmony_ci	const asn1_action_t *actions = decoder->actions;
1748c2ecf20Sopenharmony_ci	size_t machlen = decoder->machlen;
1758c2ecf20Sopenharmony_ci	enum asn1_opcode op;
1768c2ecf20Sopenharmony_ci	unsigned char tag = 0, csp = 0, jsp = 0, optag = 0, hdr = 0;
1778c2ecf20Sopenharmony_ci	const char *errmsg;
1788c2ecf20Sopenharmony_ci	size_t pc = 0, dp = 0, tdp = 0, len = 0;
1798c2ecf20Sopenharmony_ci	int ret;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	unsigned char flags = 0;
1828c2ecf20Sopenharmony_ci#define FLAG_INDEFINITE_LENGTH	0x01
1838c2ecf20Sopenharmony_ci#define FLAG_MATCHED		0x02
1848c2ecf20Sopenharmony_ci#define FLAG_LAST_MATCHED	0x04 /* Last tag matched */
1858c2ecf20Sopenharmony_ci#define FLAG_CONS		0x20 /* Corresponds to CONS bit in the opcode tag
1868c2ecf20Sopenharmony_ci				      * - ie. whether or not we are going to parse
1878c2ecf20Sopenharmony_ci				      *   a compound type.
1888c2ecf20Sopenharmony_ci				      */
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci#define NR_CONS_STACK 10
1918c2ecf20Sopenharmony_ci	unsigned short cons_dp_stack[NR_CONS_STACK];
1928c2ecf20Sopenharmony_ci	unsigned short cons_datalen_stack[NR_CONS_STACK];
1938c2ecf20Sopenharmony_ci	unsigned char cons_hdrlen_stack[NR_CONS_STACK];
1948c2ecf20Sopenharmony_ci#define NR_JUMP_STACK 10
1958c2ecf20Sopenharmony_ci	unsigned char jump_stack[NR_JUMP_STACK];
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (datalen > 65535)
1988c2ecf20Sopenharmony_ci		return -EMSGSIZE;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cinext_op:
2018c2ecf20Sopenharmony_ci	pr_debug("next_op: pc=\e[32m%zu\e[m/%zu dp=\e[33m%zu\e[m/%zu C=%d J=%d\n",
2028c2ecf20Sopenharmony_ci		 pc, machlen, dp, datalen, csp, jsp);
2038c2ecf20Sopenharmony_ci	if (unlikely(pc >= machlen))
2048c2ecf20Sopenharmony_ci		goto machine_overrun_error;
2058c2ecf20Sopenharmony_ci	op = machine[pc];
2068c2ecf20Sopenharmony_ci	if (unlikely(pc + asn1_op_lengths[op] > machlen))
2078c2ecf20Sopenharmony_ci		goto machine_overrun_error;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* If this command is meant to match a tag, then do that before
2108c2ecf20Sopenharmony_ci	 * evaluating the command.
2118c2ecf20Sopenharmony_ci	 */
2128c2ecf20Sopenharmony_ci	if (op <= ASN1_OP__MATCHES_TAG) {
2138c2ecf20Sopenharmony_ci		unsigned char tmp;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		/* Skip conditional matches if possible */
2168c2ecf20Sopenharmony_ci		if ((op & ASN1_OP_MATCH__COND && flags & FLAG_MATCHED) ||
2178c2ecf20Sopenharmony_ci		    (op & ASN1_OP_MATCH__SKIP && dp == datalen)) {
2188c2ecf20Sopenharmony_ci			flags &= ~FLAG_LAST_MATCHED;
2198c2ecf20Sopenharmony_ci			pc += asn1_op_lengths[op];
2208c2ecf20Sopenharmony_ci			goto next_op;
2218c2ecf20Sopenharmony_ci		}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		flags = 0;
2248c2ecf20Sopenharmony_ci		hdr = 2;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		/* Extract a tag from the data */
2278c2ecf20Sopenharmony_ci		if (unlikely(datalen - dp < 2))
2288c2ecf20Sopenharmony_ci			goto data_overrun_error;
2298c2ecf20Sopenharmony_ci		tag = data[dp++];
2308c2ecf20Sopenharmony_ci		if (unlikely((tag & 0x1f) == ASN1_LONG_TAG))
2318c2ecf20Sopenharmony_ci			goto long_tag_not_supported;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		if (op & ASN1_OP_MATCH__ANY) {
2348c2ecf20Sopenharmony_ci			pr_debug("- any %02x\n", tag);
2358c2ecf20Sopenharmony_ci		} else {
2368c2ecf20Sopenharmony_ci			/* Extract the tag from the machine
2378c2ecf20Sopenharmony_ci			 * - Either CONS or PRIM are permitted in the data if
2388c2ecf20Sopenharmony_ci			 *   CONS is not set in the op stream, otherwise CONS
2398c2ecf20Sopenharmony_ci			 *   is mandatory.
2408c2ecf20Sopenharmony_ci			 */
2418c2ecf20Sopenharmony_ci			optag = machine[pc + 1];
2428c2ecf20Sopenharmony_ci			flags |= optag & FLAG_CONS;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci			/* Determine whether the tag matched */
2458c2ecf20Sopenharmony_ci			tmp = optag ^ tag;
2468c2ecf20Sopenharmony_ci			tmp &= ~(optag & ASN1_CONS_BIT);
2478c2ecf20Sopenharmony_ci			pr_debug("- match? %02x %02x %02x\n", tag, optag, tmp);
2488c2ecf20Sopenharmony_ci			if (tmp != 0) {
2498c2ecf20Sopenharmony_ci				/* All odd-numbered tags are MATCH_OR_SKIP. */
2508c2ecf20Sopenharmony_ci				if (op & ASN1_OP_MATCH__SKIP) {
2518c2ecf20Sopenharmony_ci					pc += asn1_op_lengths[op];
2528c2ecf20Sopenharmony_ci					dp--;
2538c2ecf20Sopenharmony_ci					goto next_op;
2548c2ecf20Sopenharmony_ci				}
2558c2ecf20Sopenharmony_ci				goto tag_mismatch;
2568c2ecf20Sopenharmony_ci			}
2578c2ecf20Sopenharmony_ci		}
2588c2ecf20Sopenharmony_ci		flags |= FLAG_MATCHED;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		len = data[dp++];
2618c2ecf20Sopenharmony_ci		if (len > 0x7f) {
2628c2ecf20Sopenharmony_ci			if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
2638c2ecf20Sopenharmony_ci				/* Indefinite length */
2648c2ecf20Sopenharmony_ci				if (unlikely(!(tag & ASN1_CONS_BIT)))
2658c2ecf20Sopenharmony_ci					goto indefinite_len_primitive;
2668c2ecf20Sopenharmony_ci				flags |= FLAG_INDEFINITE_LENGTH;
2678c2ecf20Sopenharmony_ci				if (unlikely(2 > datalen - dp))
2688c2ecf20Sopenharmony_ci					goto data_overrun_error;
2698c2ecf20Sopenharmony_ci			} else {
2708c2ecf20Sopenharmony_ci				int n = len - 0x80;
2718c2ecf20Sopenharmony_ci				if (unlikely(n > 2))
2728c2ecf20Sopenharmony_ci					goto length_too_long;
2738c2ecf20Sopenharmony_ci				if (unlikely(n > datalen - dp))
2748c2ecf20Sopenharmony_ci					goto data_overrun_error;
2758c2ecf20Sopenharmony_ci				hdr += n;
2768c2ecf20Sopenharmony_ci				for (len = 0; n > 0; n--) {
2778c2ecf20Sopenharmony_ci					len <<= 8;
2788c2ecf20Sopenharmony_ci					len |= data[dp++];
2798c2ecf20Sopenharmony_ci				}
2808c2ecf20Sopenharmony_ci				if (unlikely(len > datalen - dp))
2818c2ecf20Sopenharmony_ci					goto data_overrun_error;
2828c2ecf20Sopenharmony_ci			}
2838c2ecf20Sopenharmony_ci		} else {
2848c2ecf20Sopenharmony_ci			if (unlikely(len > datalen - dp))
2858c2ecf20Sopenharmony_ci				goto data_overrun_error;
2868c2ecf20Sopenharmony_ci		}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		if (flags & FLAG_CONS) {
2898c2ecf20Sopenharmony_ci			/* For expected compound forms, we stack the positions
2908c2ecf20Sopenharmony_ci			 * of the start and end of the data.
2918c2ecf20Sopenharmony_ci			 */
2928c2ecf20Sopenharmony_ci			if (unlikely(csp >= NR_CONS_STACK))
2938c2ecf20Sopenharmony_ci				goto cons_stack_overflow;
2948c2ecf20Sopenharmony_ci			cons_dp_stack[csp] = dp;
2958c2ecf20Sopenharmony_ci			cons_hdrlen_stack[csp] = hdr;
2968c2ecf20Sopenharmony_ci			if (!(flags & FLAG_INDEFINITE_LENGTH)) {
2978c2ecf20Sopenharmony_ci				cons_datalen_stack[csp] = datalen;
2988c2ecf20Sopenharmony_ci				datalen = dp + len;
2998c2ecf20Sopenharmony_ci			} else {
3008c2ecf20Sopenharmony_ci				cons_datalen_stack[csp] = 0;
3018c2ecf20Sopenharmony_ci			}
3028c2ecf20Sopenharmony_ci			csp++;
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		pr_debug("- TAG: %02x %zu%s\n",
3068c2ecf20Sopenharmony_ci			 tag, len, flags & FLAG_CONS ? " CONS" : "");
3078c2ecf20Sopenharmony_ci		tdp = dp;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* Decide how to handle the operation */
3118c2ecf20Sopenharmony_ci	switch (op) {
3128c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH:
3138c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_OR_SKIP:
3148c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_ACT:
3158c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_ACT_OR_SKIP:
3168c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_ANY:
3178c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_ANY_OR_SKIP:
3188c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_ANY_ACT:
3198c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_ANY_ACT_OR_SKIP:
3208c2ecf20Sopenharmony_ci	case ASN1_OP_COND_MATCH_OR_SKIP:
3218c2ecf20Sopenharmony_ci	case ASN1_OP_COND_MATCH_ACT_OR_SKIP:
3228c2ecf20Sopenharmony_ci	case ASN1_OP_COND_MATCH_ANY:
3238c2ecf20Sopenharmony_ci	case ASN1_OP_COND_MATCH_ANY_OR_SKIP:
3248c2ecf20Sopenharmony_ci	case ASN1_OP_COND_MATCH_ANY_ACT:
3258c2ecf20Sopenharmony_ci	case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP:
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		if (!(flags & FLAG_CONS)) {
3288c2ecf20Sopenharmony_ci			if (flags & FLAG_INDEFINITE_LENGTH) {
3298c2ecf20Sopenharmony_ci				size_t tmp = dp;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci				ret = asn1_find_indefinite_length(
3328c2ecf20Sopenharmony_ci					data, datalen, &tmp, &len, &errmsg);
3338c2ecf20Sopenharmony_ci				if (ret < 0)
3348c2ecf20Sopenharmony_ci					goto error;
3358c2ecf20Sopenharmony_ci			}
3368c2ecf20Sopenharmony_ci			pr_debug("- LEAF: %zu\n", len);
3378c2ecf20Sopenharmony_ci		}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		if (op & ASN1_OP_MATCH__ACT) {
3408c2ecf20Sopenharmony_ci			unsigned char act;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci			if (op & ASN1_OP_MATCH__ANY)
3438c2ecf20Sopenharmony_ci				act = machine[pc + 1];
3448c2ecf20Sopenharmony_ci			else
3458c2ecf20Sopenharmony_ci				act = machine[pc + 2];
3468c2ecf20Sopenharmony_ci			ret = actions[act](context, hdr, tag, data + dp, len);
3478c2ecf20Sopenharmony_ci			if (ret < 0)
3488c2ecf20Sopenharmony_ci				return ret;
3498c2ecf20Sopenharmony_ci		}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		if (!(flags & FLAG_CONS))
3528c2ecf20Sopenharmony_ci			dp += len;
3538c2ecf20Sopenharmony_ci		pc += asn1_op_lengths[op];
3548c2ecf20Sopenharmony_ci		goto next_op;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_JUMP:
3578c2ecf20Sopenharmony_ci	case ASN1_OP_MATCH_JUMP_OR_SKIP:
3588c2ecf20Sopenharmony_ci	case ASN1_OP_COND_MATCH_JUMP_OR_SKIP:
3598c2ecf20Sopenharmony_ci		pr_debug("- MATCH_JUMP\n");
3608c2ecf20Sopenharmony_ci		if (unlikely(jsp == NR_JUMP_STACK))
3618c2ecf20Sopenharmony_ci			goto jump_stack_overflow;
3628c2ecf20Sopenharmony_ci		jump_stack[jsp++] = pc + asn1_op_lengths[op];
3638c2ecf20Sopenharmony_ci		pc = machine[pc + 2];
3648c2ecf20Sopenharmony_ci		goto next_op;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	case ASN1_OP_COND_FAIL:
3678c2ecf20Sopenharmony_ci		if (unlikely(!(flags & FLAG_MATCHED)))
3688c2ecf20Sopenharmony_ci			goto tag_mismatch;
3698c2ecf20Sopenharmony_ci		pc += asn1_op_lengths[op];
3708c2ecf20Sopenharmony_ci		goto next_op;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	case ASN1_OP_COMPLETE:
3738c2ecf20Sopenharmony_ci		if (unlikely(jsp != 0 || csp != 0)) {
3748c2ecf20Sopenharmony_ci			pr_err("ASN.1 decoder error: Stacks not empty at completion (%u, %u)\n",
3758c2ecf20Sopenharmony_ci			       jsp, csp);
3768c2ecf20Sopenharmony_ci			return -EBADMSG;
3778c2ecf20Sopenharmony_ci		}
3788c2ecf20Sopenharmony_ci		return 0;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	case ASN1_OP_END_SET:
3818c2ecf20Sopenharmony_ci	case ASN1_OP_END_SET_ACT:
3828c2ecf20Sopenharmony_ci		if (unlikely(!(flags & FLAG_MATCHED)))
3838c2ecf20Sopenharmony_ci			goto tag_mismatch;
3848c2ecf20Sopenharmony_ci		/* fall through */
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	case ASN1_OP_END_SEQ:
3878c2ecf20Sopenharmony_ci	case ASN1_OP_END_SET_OF:
3888c2ecf20Sopenharmony_ci	case ASN1_OP_END_SEQ_OF:
3898c2ecf20Sopenharmony_ci	case ASN1_OP_END_SEQ_ACT:
3908c2ecf20Sopenharmony_ci	case ASN1_OP_END_SET_OF_ACT:
3918c2ecf20Sopenharmony_ci	case ASN1_OP_END_SEQ_OF_ACT:
3928c2ecf20Sopenharmony_ci		if (unlikely(csp <= 0))
3938c2ecf20Sopenharmony_ci			goto cons_stack_underflow;
3948c2ecf20Sopenharmony_ci		csp--;
3958c2ecf20Sopenharmony_ci		tdp = cons_dp_stack[csp];
3968c2ecf20Sopenharmony_ci		hdr = cons_hdrlen_stack[csp];
3978c2ecf20Sopenharmony_ci		len = datalen;
3988c2ecf20Sopenharmony_ci		datalen = cons_datalen_stack[csp];
3998c2ecf20Sopenharmony_ci		pr_debug("- end cons t=%zu dp=%zu l=%zu/%zu\n",
4008c2ecf20Sopenharmony_ci			 tdp, dp, len, datalen);
4018c2ecf20Sopenharmony_ci		if (datalen == 0) {
4028c2ecf20Sopenharmony_ci			/* Indefinite length - check for the EOC. */
4038c2ecf20Sopenharmony_ci			datalen = len;
4048c2ecf20Sopenharmony_ci			if (unlikely(datalen - dp < 2))
4058c2ecf20Sopenharmony_ci				goto data_overrun_error;
4068c2ecf20Sopenharmony_ci			if (data[dp++] != 0) {
4078c2ecf20Sopenharmony_ci				if (op & ASN1_OP_END__OF) {
4088c2ecf20Sopenharmony_ci					dp--;
4098c2ecf20Sopenharmony_ci					csp++;
4108c2ecf20Sopenharmony_ci					pc = machine[pc + 1];
4118c2ecf20Sopenharmony_ci					pr_debug("- continue\n");
4128c2ecf20Sopenharmony_ci					goto next_op;
4138c2ecf20Sopenharmony_ci				}
4148c2ecf20Sopenharmony_ci				goto missing_eoc;
4158c2ecf20Sopenharmony_ci			}
4168c2ecf20Sopenharmony_ci			if (data[dp++] != 0)
4178c2ecf20Sopenharmony_ci				goto invalid_eoc;
4188c2ecf20Sopenharmony_ci			len = dp - tdp - 2;
4198c2ecf20Sopenharmony_ci		} else {
4208c2ecf20Sopenharmony_ci			if (dp < len && (op & ASN1_OP_END__OF)) {
4218c2ecf20Sopenharmony_ci				datalen = len;
4228c2ecf20Sopenharmony_ci				csp++;
4238c2ecf20Sopenharmony_ci				pc = machine[pc + 1];
4248c2ecf20Sopenharmony_ci				pr_debug("- continue\n");
4258c2ecf20Sopenharmony_ci				goto next_op;
4268c2ecf20Sopenharmony_ci			}
4278c2ecf20Sopenharmony_ci			if (dp != len)
4288c2ecf20Sopenharmony_ci				goto cons_length_error;
4298c2ecf20Sopenharmony_ci			len -= tdp;
4308c2ecf20Sopenharmony_ci			pr_debug("- cons len l=%zu d=%zu\n", len, dp - tdp);
4318c2ecf20Sopenharmony_ci		}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		if (op & ASN1_OP_END__ACT) {
4348c2ecf20Sopenharmony_ci			unsigned char act;
4358c2ecf20Sopenharmony_ci			if (op & ASN1_OP_END__OF)
4368c2ecf20Sopenharmony_ci				act = machine[pc + 2];
4378c2ecf20Sopenharmony_ci			else
4388c2ecf20Sopenharmony_ci				act = machine[pc + 1];
4398c2ecf20Sopenharmony_ci			ret = actions[act](context, hdr, 0, data + tdp, len);
4408c2ecf20Sopenharmony_ci			if (ret < 0)
4418c2ecf20Sopenharmony_ci				return ret;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci		pc += asn1_op_lengths[op];
4448c2ecf20Sopenharmony_ci		goto next_op;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	case ASN1_OP_MAYBE_ACT:
4478c2ecf20Sopenharmony_ci		if (!(flags & FLAG_LAST_MATCHED)) {
4488c2ecf20Sopenharmony_ci			pc += asn1_op_lengths[op];
4498c2ecf20Sopenharmony_ci			goto next_op;
4508c2ecf20Sopenharmony_ci		}
4518c2ecf20Sopenharmony_ci		/* fall through */
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	case ASN1_OP_ACT:
4548c2ecf20Sopenharmony_ci		ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len);
4558c2ecf20Sopenharmony_ci		if (ret < 0)
4568c2ecf20Sopenharmony_ci			return ret;
4578c2ecf20Sopenharmony_ci		pc += asn1_op_lengths[op];
4588c2ecf20Sopenharmony_ci		goto next_op;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	case ASN1_OP_RETURN:
4618c2ecf20Sopenharmony_ci		if (unlikely(jsp <= 0))
4628c2ecf20Sopenharmony_ci			goto jump_stack_underflow;
4638c2ecf20Sopenharmony_ci		pc = jump_stack[--jsp];
4648c2ecf20Sopenharmony_ci		flags |= FLAG_MATCHED | FLAG_LAST_MATCHED;
4658c2ecf20Sopenharmony_ci		goto next_op;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	default:
4688c2ecf20Sopenharmony_ci		break;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* Shouldn't reach here */
4728c2ecf20Sopenharmony_ci	pr_err("ASN.1 decoder error: Found reserved opcode (%u) pc=%zu\n",
4738c2ecf20Sopenharmony_ci	       op, pc);
4748c2ecf20Sopenharmony_ci	return -EBADMSG;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cidata_overrun_error:
4778c2ecf20Sopenharmony_ci	errmsg = "Data overrun error";
4788c2ecf20Sopenharmony_ci	goto error;
4798c2ecf20Sopenharmony_cimachine_overrun_error:
4808c2ecf20Sopenharmony_ci	errmsg = "Machine overrun error";
4818c2ecf20Sopenharmony_ci	goto error;
4828c2ecf20Sopenharmony_cijump_stack_underflow:
4838c2ecf20Sopenharmony_ci	errmsg = "Jump stack underflow";
4848c2ecf20Sopenharmony_ci	goto error;
4858c2ecf20Sopenharmony_cijump_stack_overflow:
4868c2ecf20Sopenharmony_ci	errmsg = "Jump stack overflow";
4878c2ecf20Sopenharmony_ci	goto error;
4888c2ecf20Sopenharmony_cicons_stack_underflow:
4898c2ecf20Sopenharmony_ci	errmsg = "Cons stack underflow";
4908c2ecf20Sopenharmony_ci	goto error;
4918c2ecf20Sopenharmony_cicons_stack_overflow:
4928c2ecf20Sopenharmony_ci	errmsg = "Cons stack overflow";
4938c2ecf20Sopenharmony_ci	goto error;
4948c2ecf20Sopenharmony_cicons_length_error:
4958c2ecf20Sopenharmony_ci	errmsg = "Cons length error";
4968c2ecf20Sopenharmony_ci	goto error;
4978c2ecf20Sopenharmony_cimissing_eoc:
4988c2ecf20Sopenharmony_ci	errmsg = "Missing EOC in indefinite len cons";
4998c2ecf20Sopenharmony_ci	goto error;
5008c2ecf20Sopenharmony_ciinvalid_eoc:
5018c2ecf20Sopenharmony_ci	errmsg = "Invalid length EOC";
5028c2ecf20Sopenharmony_ci	goto error;
5038c2ecf20Sopenharmony_cilength_too_long:
5048c2ecf20Sopenharmony_ci	errmsg = "Unsupported length";
5058c2ecf20Sopenharmony_ci	goto error;
5068c2ecf20Sopenharmony_ciindefinite_len_primitive:
5078c2ecf20Sopenharmony_ci	errmsg = "Indefinite len primitive not permitted";
5088c2ecf20Sopenharmony_ci	goto error;
5098c2ecf20Sopenharmony_citag_mismatch:
5108c2ecf20Sopenharmony_ci	errmsg = "Unexpected tag";
5118c2ecf20Sopenharmony_ci	goto error;
5128c2ecf20Sopenharmony_cilong_tag_not_supported:
5138c2ecf20Sopenharmony_ci	errmsg = "Long tag not supported";
5148c2ecf20Sopenharmony_cierror:
5158c2ecf20Sopenharmony_ci	pr_debug("\nASN1: %s [m=%zu d=%zu ot=%02x t=%02x l=%zu]\n",
5168c2ecf20Sopenharmony_ci		 errmsg, pc, dp, optag, tag, len);
5178c2ecf20Sopenharmony_ci	return -EBADMSG;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(asn1_ber_decoder);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
522