18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* ASN.1 Object identifier (OID) registry
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/module.h>
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <linux/oid_registry.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/bug.h>
148c2ecf20Sopenharmony_ci#include <linux/asn1.h>
158c2ecf20Sopenharmony_ci#include "oid_registry_data.c"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OID Registry");
188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Red Hat, Inc.");
198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/**
228c2ecf20Sopenharmony_ci * look_up_OID - Find an OID registration for the specified data
238c2ecf20Sopenharmony_ci * @data: Binary representation of the OID
248c2ecf20Sopenharmony_ci * @datasize: Size of the binary representation
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_cienum OID look_up_OID(const void *data, size_t datasize)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	const unsigned char *octets = data;
298c2ecf20Sopenharmony_ci	enum OID oid;
308c2ecf20Sopenharmony_ci	unsigned char xhash;
318c2ecf20Sopenharmony_ci	unsigned i, j, k, hash;
328c2ecf20Sopenharmony_ci	size_t len;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* Hash the OID data */
358c2ecf20Sopenharmony_ci	hash = datasize - 1;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	for (i = 0; i < datasize; i++)
388c2ecf20Sopenharmony_ci		hash += octets[i] * 33;
398c2ecf20Sopenharmony_ci	hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash;
408c2ecf20Sopenharmony_ci	hash &= 0xff;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* Binary search the OID registry.  OIDs are stored in ascending order
438c2ecf20Sopenharmony_ci	 * of hash value then ascending order of size and then in ascending
448c2ecf20Sopenharmony_ci	 * order of reverse value.
458c2ecf20Sopenharmony_ci	 */
468c2ecf20Sopenharmony_ci	i = 0;
478c2ecf20Sopenharmony_ci	k = OID__NR;
488c2ecf20Sopenharmony_ci	while (i < k) {
498c2ecf20Sopenharmony_ci		j = (i + k) / 2;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci		xhash = oid_search_table[j].hash;
528c2ecf20Sopenharmony_ci		if (xhash > hash) {
538c2ecf20Sopenharmony_ci			k = j;
548c2ecf20Sopenharmony_ci			continue;
558c2ecf20Sopenharmony_ci		}
568c2ecf20Sopenharmony_ci		if (xhash < hash) {
578c2ecf20Sopenharmony_ci			i = j + 1;
588c2ecf20Sopenharmony_ci			continue;
598c2ecf20Sopenharmony_ci		}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci		oid = oid_search_table[j].oid;
628c2ecf20Sopenharmony_ci		len = oid_index[oid + 1] - oid_index[oid];
638c2ecf20Sopenharmony_ci		if (len > datasize) {
648c2ecf20Sopenharmony_ci			k = j;
658c2ecf20Sopenharmony_ci			continue;
668c2ecf20Sopenharmony_ci		}
678c2ecf20Sopenharmony_ci		if (len < datasize) {
688c2ecf20Sopenharmony_ci			i = j + 1;
698c2ecf20Sopenharmony_ci			continue;
708c2ecf20Sopenharmony_ci		}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		/* Variation is most likely to be at the tail end of the
738c2ecf20Sopenharmony_ci		 * OID, so do the comparison in reverse.
748c2ecf20Sopenharmony_ci		 */
758c2ecf20Sopenharmony_ci		while (len > 0) {
768c2ecf20Sopenharmony_ci			unsigned char a = oid_data[oid_index[oid] + --len];
778c2ecf20Sopenharmony_ci			unsigned char b = octets[len];
788c2ecf20Sopenharmony_ci			if (a > b) {
798c2ecf20Sopenharmony_ci				k = j;
808c2ecf20Sopenharmony_ci				goto next;
818c2ecf20Sopenharmony_ci			}
828c2ecf20Sopenharmony_ci			if (a < b) {
838c2ecf20Sopenharmony_ci				i = j + 1;
848c2ecf20Sopenharmony_ci				goto next;
858c2ecf20Sopenharmony_ci			}
868c2ecf20Sopenharmony_ci		}
878c2ecf20Sopenharmony_ci		return oid;
888c2ecf20Sopenharmony_ci	next:
898c2ecf20Sopenharmony_ci		;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return OID__NR;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(look_up_OID);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/**
978c2ecf20Sopenharmony_ci * parse_OID - Parse an OID from a bytestream
988c2ecf20Sopenharmony_ci * @data: Binary representation of the header + OID
998c2ecf20Sopenharmony_ci * @datasize: Size of the binary representation
1008c2ecf20Sopenharmony_ci * @oid: Pointer to oid to return result
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * Parse an OID from a bytestream that holds the OID in the format
1038c2ecf20Sopenharmony_ci * ASN1_OID | length | oid. The length indicator must equal to datasize - 2.
1048c2ecf20Sopenharmony_ci * -EBADMSG is returned if the bytestream is too short.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ciint parse_OID(const void *data, size_t datasize, enum OID *oid)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	const unsigned char *v = data;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* we need 2 bytes of header and at least 1 byte for oid */
1118c2ecf20Sopenharmony_ci	if (datasize < 3 || v[0] != ASN1_OID || v[1] != datasize - 2)
1128c2ecf20Sopenharmony_ci		return -EBADMSG;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	*oid = look_up_OID(data + 2, datasize - 2);
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(parse_OID);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/*
1208c2ecf20Sopenharmony_ci * sprint_OID - Print an Object Identifier into a buffer
1218c2ecf20Sopenharmony_ci * @data: The encoded OID to print
1228c2ecf20Sopenharmony_ci * @datasize: The size of the encoded OID
1238c2ecf20Sopenharmony_ci * @buffer: The buffer to render into
1248c2ecf20Sopenharmony_ci * @bufsize: The size of the buffer
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * The OID is rendered into the buffer in "a.b.c.d" format and the number of
1278c2ecf20Sopenharmony_ci * bytes is returned.  -EBADMSG is returned if the data could not be intepreted
1288c2ecf20Sopenharmony_ci * and -ENOBUFS if the buffer was too small.
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_ciint sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	const unsigned char *v = data, *end = v + datasize;
1338c2ecf20Sopenharmony_ci	unsigned long num;
1348c2ecf20Sopenharmony_ci	unsigned char n;
1358c2ecf20Sopenharmony_ci	size_t ret;
1368c2ecf20Sopenharmony_ci	int count;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (v >= end)
1398c2ecf20Sopenharmony_ci		goto bad;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	n = *v++;
1428c2ecf20Sopenharmony_ci	ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40);
1438c2ecf20Sopenharmony_ci	if (count >= bufsize)
1448c2ecf20Sopenharmony_ci		return -ENOBUFS;
1458c2ecf20Sopenharmony_ci	buffer += count;
1468c2ecf20Sopenharmony_ci	bufsize -= count;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	while (v < end) {
1498c2ecf20Sopenharmony_ci		num = 0;
1508c2ecf20Sopenharmony_ci		n = *v++;
1518c2ecf20Sopenharmony_ci		if (!(n & 0x80)) {
1528c2ecf20Sopenharmony_ci			num = n;
1538c2ecf20Sopenharmony_ci		} else {
1548c2ecf20Sopenharmony_ci			num = n & 0x7f;
1558c2ecf20Sopenharmony_ci			do {
1568c2ecf20Sopenharmony_ci				if (v >= end)
1578c2ecf20Sopenharmony_ci					goto bad;
1588c2ecf20Sopenharmony_ci				n = *v++;
1598c2ecf20Sopenharmony_ci				num <<= 7;
1608c2ecf20Sopenharmony_ci				num |= n & 0x7f;
1618c2ecf20Sopenharmony_ci			} while (n & 0x80);
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci		ret += count = snprintf(buffer, bufsize, ".%lu", num);
1648c2ecf20Sopenharmony_ci		if (count >= bufsize)
1658c2ecf20Sopenharmony_ci			return -ENOBUFS;
1668c2ecf20Sopenharmony_ci		buffer += count;
1678c2ecf20Sopenharmony_ci		bufsize -= count;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return ret;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cibad:
1738c2ecf20Sopenharmony_ci	snprintf(buffer, bufsize, "(bad)");
1748c2ecf20Sopenharmony_ci	return -EBADMSG;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sprint_oid);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * sprint_OID - Print an Object Identifier into a buffer
1808c2ecf20Sopenharmony_ci * @oid: The OID to print
1818c2ecf20Sopenharmony_ci * @buffer: The buffer to render into
1828c2ecf20Sopenharmony_ci * @bufsize: The size of the buffer
1838c2ecf20Sopenharmony_ci *
1848c2ecf20Sopenharmony_ci * The OID is rendered into the buffer in "a.b.c.d" format and the number of
1858c2ecf20Sopenharmony_ci * bytes is returned.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_ciint sprint_OID(enum OID oid, char *buffer, size_t bufsize)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int ret;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	BUG_ON(oid >= OID__NR);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	ret = sprint_oid(oid_data + oid_index[oid],
1948c2ecf20Sopenharmony_ci			 oid_index[oid + 1] - oid_index[oid],
1958c2ecf20Sopenharmony_ci			 buffer, bufsize);
1968c2ecf20Sopenharmony_ci	BUG_ON(ret == -EBADMSG);
1978c2ecf20Sopenharmony_ci	return ret;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sprint_OID);
200