forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
4.4 KiB
199 lines
4.4 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* ASN.1 Object identifier (OID) registry |
|
* |
|
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. |
|
* Written by David Howells ([email protected]) |
|
*/ |
|
|
|
#include <linux/module.h> |
|
#include <linux/export.h> |
|
#include <linux/oid_registry.h> |
|
#include <linux/kernel.h> |
|
#include <linux/errno.h> |
|
#include <linux/bug.h> |
|
#include <linux/asn1.h> |
|
#include "oid_registry_data.c" |
|
|
|
MODULE_DESCRIPTION("OID Registry"); |
|
MODULE_AUTHOR("Red Hat, Inc."); |
|
MODULE_LICENSE("GPL"); |
|
|
|
/** |
|
* look_up_OID - Find an OID registration for the specified data |
|
* @data: Binary representation of the OID |
|
* @datasize: Size of the binary representation |
|
*/ |
|
enum OID look_up_OID(const void *data, size_t datasize) |
|
{ |
|
const unsigned char *octets = data; |
|
enum OID oid; |
|
unsigned char xhash; |
|
unsigned i, j, k, hash; |
|
size_t len; |
|
|
|
/* Hash the OID data */ |
|
hash = datasize - 1; |
|
|
|
for (i = 0; i < datasize; i++) |
|
hash += octets[i] * 33; |
|
hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash; |
|
hash &= 0xff; |
|
|
|
/* Binary search the OID registry. OIDs are stored in ascending order |
|
* of hash value then ascending order of size and then in ascending |
|
* order of reverse value. |
|
*/ |
|
i = 0; |
|
k = OID__NR; |
|
while (i < k) { |
|
j = (i + k) / 2; |
|
|
|
xhash = oid_search_table[j].hash; |
|
if (xhash > hash) { |
|
k = j; |
|
continue; |
|
} |
|
if (xhash < hash) { |
|
i = j + 1; |
|
continue; |
|
} |
|
|
|
oid = oid_search_table[j].oid; |
|
len = oid_index[oid + 1] - oid_index[oid]; |
|
if (len > datasize) { |
|
k = j; |
|
continue; |
|
} |
|
if (len < datasize) { |
|
i = j + 1; |
|
continue; |
|
} |
|
|
|
/* Variation is most likely to be at the tail end of the |
|
* OID, so do the comparison in reverse. |
|
*/ |
|
while (len > 0) { |
|
unsigned char a = oid_data[oid_index[oid] + --len]; |
|
unsigned char b = octets[len]; |
|
if (a > b) { |
|
k = j; |
|
goto next; |
|
} |
|
if (a < b) { |
|
i = j + 1; |
|
goto next; |
|
} |
|
} |
|
return oid; |
|
next: |
|
; |
|
} |
|
|
|
return OID__NR; |
|
} |
|
EXPORT_SYMBOL_GPL(look_up_OID); |
|
|
|
/** |
|
* parse_OID - Parse an OID from a bytestream |
|
* @data: Binary representation of the header + OID |
|
* @datasize: Size of the binary representation |
|
* @oid: Pointer to oid to return result |
|
* |
|
* Parse an OID from a bytestream that holds the OID in the format |
|
* ASN1_OID | length | oid. The length indicator must equal to datasize - 2. |
|
* -EBADMSG is returned if the bytestream is too short. |
|
*/ |
|
int parse_OID(const void *data, size_t datasize, enum OID *oid) |
|
{ |
|
const unsigned char *v = data; |
|
|
|
/* we need 2 bytes of header and at least 1 byte for oid */ |
|
if (datasize < 3 || v[0] != ASN1_OID || v[1] != datasize - 2) |
|
return -EBADMSG; |
|
|
|
*oid = look_up_OID(data + 2, datasize - 2); |
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(parse_OID); |
|
|
|
/* |
|
* sprint_OID - Print an Object Identifier into a buffer |
|
* @data: The encoded OID to print |
|
* @datasize: The size of the encoded OID |
|
* @buffer: The buffer to render into |
|
* @bufsize: The size of the buffer |
|
* |
|
* The OID is rendered into the buffer in "a.b.c.d" format and the number of |
|
* bytes is returned. -EBADMSG is returned if the data could not be intepreted |
|
* and -ENOBUFS if the buffer was too small. |
|
*/ |
|
int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize) |
|
{ |
|
const unsigned char *v = data, *end = v + datasize; |
|
unsigned long num; |
|
unsigned char n; |
|
size_t ret; |
|
int count; |
|
|
|
if (v >= end) |
|
goto bad; |
|
|
|
n = *v++; |
|
ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40); |
|
if (count >= bufsize) |
|
return -ENOBUFS; |
|
buffer += count; |
|
bufsize -= count; |
|
|
|
while (v < end) { |
|
num = 0; |
|
n = *v++; |
|
if (!(n & 0x80)) { |
|
num = n; |
|
} else { |
|
num = n & 0x7f; |
|
do { |
|
if (v >= end) |
|
goto bad; |
|
n = *v++; |
|
num <<= 7; |
|
num |= n & 0x7f; |
|
} while (n & 0x80); |
|
} |
|
ret += count = snprintf(buffer, bufsize, ".%lu", num); |
|
if (count >= bufsize) |
|
return -ENOBUFS; |
|
buffer += count; |
|
bufsize -= count; |
|
} |
|
|
|
return ret; |
|
|
|
bad: |
|
snprintf(buffer, bufsize, "(bad)"); |
|
return -EBADMSG; |
|
} |
|
EXPORT_SYMBOL_GPL(sprint_oid); |
|
|
|
/** |
|
* sprint_OID - Print an Object Identifier into a buffer |
|
* @oid: The OID to print |
|
* @buffer: The buffer to render into |
|
* @bufsize: The size of the buffer |
|
* |
|
* The OID is rendered into the buffer in "a.b.c.d" format and the number of |
|
* bytes is returned. |
|
*/ |
|
int sprint_OID(enum OID oid, char *buffer, size_t bufsize) |
|
{ |
|
int ret; |
|
|
|
BUG_ON(oid >= OID__NR); |
|
|
|
ret = sprint_oid(oid_data + oid_index[oid], |
|
oid_index[oid + 1] - oid_index[oid], |
|
buffer, bufsize); |
|
BUG_ON(ret == -EBADMSG); |
|
return ret; |
|
} |
|
EXPORT_SYMBOL_GPL(sprint_OID);
|
|
|