162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* ASN.1 Object identifier (OID) registry
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/export.h>
1062306a36Sopenharmony_ci#include <linux/oid_registry.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/bug.h>
1462306a36Sopenharmony_ci#include <linux/asn1.h>
1562306a36Sopenharmony_ci#include "oid_registry_data.c"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciMODULE_DESCRIPTION("OID Registry");
1862306a36Sopenharmony_ciMODULE_AUTHOR("Red Hat, Inc.");
1962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/**
2262306a36Sopenharmony_ci * look_up_OID - Find an OID registration for the specified data
2362306a36Sopenharmony_ci * @data: Binary representation of the OID
2462306a36Sopenharmony_ci * @datasize: Size of the binary representation
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cienum OID look_up_OID(const void *data, size_t datasize)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	const unsigned char *octets = data;
2962306a36Sopenharmony_ci	enum OID oid;
3062306a36Sopenharmony_ci	unsigned char xhash;
3162306a36Sopenharmony_ci	unsigned i, j, k, hash;
3262306a36Sopenharmony_ci	size_t len;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* Hash the OID data */
3562306a36Sopenharmony_ci	hash = datasize - 1;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	for (i = 0; i < datasize; i++)
3862306a36Sopenharmony_ci		hash += octets[i] * 33;
3962306a36Sopenharmony_ci	hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash;
4062306a36Sopenharmony_ci	hash &= 0xff;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* Binary search the OID registry.  OIDs are stored in ascending order
4362306a36Sopenharmony_ci	 * of hash value then ascending order of size and then in ascending
4462306a36Sopenharmony_ci	 * order of reverse value.
4562306a36Sopenharmony_ci	 */
4662306a36Sopenharmony_ci	i = 0;
4762306a36Sopenharmony_ci	k = OID__NR;
4862306a36Sopenharmony_ci	while (i < k) {
4962306a36Sopenharmony_ci		j = (i + k) / 2;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci		xhash = oid_search_table[j].hash;
5262306a36Sopenharmony_ci		if (xhash > hash) {
5362306a36Sopenharmony_ci			k = j;
5462306a36Sopenharmony_ci			continue;
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci		if (xhash < hash) {
5762306a36Sopenharmony_ci			i = j + 1;
5862306a36Sopenharmony_ci			continue;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		oid = oid_search_table[j].oid;
6262306a36Sopenharmony_ci		len = oid_index[oid + 1] - oid_index[oid];
6362306a36Sopenharmony_ci		if (len > datasize) {
6462306a36Sopenharmony_ci			k = j;
6562306a36Sopenharmony_ci			continue;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci		if (len < datasize) {
6862306a36Sopenharmony_ci			i = j + 1;
6962306a36Sopenharmony_ci			continue;
7062306a36Sopenharmony_ci		}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		/* Variation is most likely to be at the tail end of the
7362306a36Sopenharmony_ci		 * OID, so do the comparison in reverse.
7462306a36Sopenharmony_ci		 */
7562306a36Sopenharmony_ci		while (len > 0) {
7662306a36Sopenharmony_ci			unsigned char a = oid_data[oid_index[oid] + --len];
7762306a36Sopenharmony_ci			unsigned char b = octets[len];
7862306a36Sopenharmony_ci			if (a > b) {
7962306a36Sopenharmony_ci				k = j;
8062306a36Sopenharmony_ci				goto next;
8162306a36Sopenharmony_ci			}
8262306a36Sopenharmony_ci			if (a < b) {
8362306a36Sopenharmony_ci				i = j + 1;
8462306a36Sopenharmony_ci				goto next;
8562306a36Sopenharmony_ci			}
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci		return oid;
8862306a36Sopenharmony_ci	next:
8962306a36Sopenharmony_ci		;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return OID__NR;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(look_up_OID);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/**
9762306a36Sopenharmony_ci * parse_OID - Parse an OID from a bytestream
9862306a36Sopenharmony_ci * @data: Binary representation of the header + OID
9962306a36Sopenharmony_ci * @datasize: Size of the binary representation
10062306a36Sopenharmony_ci * @oid: Pointer to oid to return result
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci * Parse an OID from a bytestream that holds the OID in the format
10362306a36Sopenharmony_ci * ASN1_OID | length | oid. The length indicator must equal to datasize - 2.
10462306a36Sopenharmony_ci * -EBADMSG is returned if the bytestream is too short.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_ciint parse_OID(const void *data, size_t datasize, enum OID *oid)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	const unsigned char *v = data;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* we need 2 bytes of header and at least 1 byte for oid */
11162306a36Sopenharmony_ci	if (datasize < 3 || v[0] != ASN1_OID || v[1] != datasize - 2)
11262306a36Sopenharmony_ci		return -EBADMSG;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	*oid = look_up_OID(data + 2, datasize - 2);
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(parse_OID);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/*
12062306a36Sopenharmony_ci * sprint_OID - Print an Object Identifier into a buffer
12162306a36Sopenharmony_ci * @data: The encoded OID to print
12262306a36Sopenharmony_ci * @datasize: The size of the encoded OID
12362306a36Sopenharmony_ci * @buffer: The buffer to render into
12462306a36Sopenharmony_ci * @bufsize: The size of the buffer
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * The OID is rendered into the buffer in "a.b.c.d" format and the number of
12762306a36Sopenharmony_ci * bytes is returned.  -EBADMSG is returned if the data could not be interpreted
12862306a36Sopenharmony_ci * and -ENOBUFS if the buffer was too small.
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_ciint sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	const unsigned char *v = data, *end = v + datasize;
13362306a36Sopenharmony_ci	unsigned long num;
13462306a36Sopenharmony_ci	unsigned char n;
13562306a36Sopenharmony_ci	size_t ret;
13662306a36Sopenharmony_ci	int count;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (v >= end)
13962306a36Sopenharmony_ci		goto bad;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	n = *v++;
14262306a36Sopenharmony_ci	ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40);
14362306a36Sopenharmony_ci	if (count >= bufsize)
14462306a36Sopenharmony_ci		return -ENOBUFS;
14562306a36Sopenharmony_ci	buffer += count;
14662306a36Sopenharmony_ci	bufsize -= count;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	while (v < end) {
14962306a36Sopenharmony_ci		n = *v++;
15062306a36Sopenharmony_ci		if (!(n & 0x80)) {
15162306a36Sopenharmony_ci			num = n;
15262306a36Sopenharmony_ci		} else {
15362306a36Sopenharmony_ci			num = n & 0x7f;
15462306a36Sopenharmony_ci			do {
15562306a36Sopenharmony_ci				if (v >= end)
15662306a36Sopenharmony_ci					goto bad;
15762306a36Sopenharmony_ci				n = *v++;
15862306a36Sopenharmony_ci				num <<= 7;
15962306a36Sopenharmony_ci				num |= n & 0x7f;
16062306a36Sopenharmony_ci			} while (n & 0x80);
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci		ret += count = snprintf(buffer, bufsize, ".%lu", num);
16362306a36Sopenharmony_ci		if (count >= bufsize)
16462306a36Sopenharmony_ci			return -ENOBUFS;
16562306a36Sopenharmony_ci		buffer += count;
16662306a36Sopenharmony_ci		bufsize -= count;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cibad:
17262306a36Sopenharmony_ci	snprintf(buffer, bufsize, "(bad)");
17362306a36Sopenharmony_ci	return -EBADMSG;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprint_oid);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/**
17862306a36Sopenharmony_ci * sprint_OID - Print an Object Identifier into a buffer
17962306a36Sopenharmony_ci * @oid: The OID to print
18062306a36Sopenharmony_ci * @buffer: The buffer to render into
18162306a36Sopenharmony_ci * @bufsize: The size of the buffer
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * The OID is rendered into the buffer in "a.b.c.d" format and the number of
18462306a36Sopenharmony_ci * bytes is returned.
18562306a36Sopenharmony_ci */
18662306a36Sopenharmony_ciint sprint_OID(enum OID oid, char *buffer, size_t bufsize)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	int ret;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	BUG_ON(oid >= OID__NR);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	ret = sprint_oid(oid_data + oid_index[oid],
19362306a36Sopenharmony_ci			 oid_index[oid + 1] - oid_index[oid],
19462306a36Sopenharmony_ci			 buffer, bufsize);
19562306a36Sopenharmony_ci	BUG_ON(ret == -EBADMSG);
19662306a36Sopenharmony_ci	return ret;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprint_OID);
199