1#!/usr/bin/perl -w 2# SPDX-License-Identifier: GPL-2.0-or-later 3# 4# Build a static ASN.1 Object Identified (OID) registry 5# 6# Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. 7# Written by David Howells (dhowells@redhat.com) 8# 9 10use strict; 11use Cwd qw(abs_path); 12 13my @names = (); 14my @oids = (); 15 16if ($#ARGV != 1) { 17 print STDERR "Format: ", $0, " <in-h-file> <out-c-file>\n"; 18 exit(2); 19} 20 21my $abs_srctree = abs_path($ENV{'srctree'}); 22 23# 24# Open the file to read from 25# 26open IN_FILE, "<$ARGV[0]" || die; 27while (<IN_FILE>) { 28 chomp; 29 if (m!\s+OID_([a-zA-z][a-zA-Z0-9_]+),\s+/[*]\s+([012][.0-9]*)\s+[*]/!) { 30 push @names, $1; 31 push @oids, $2; 32 } 33} 34close IN_FILE || die; 35 36# 37# Open the files to write into 38# 39open C_FILE, ">$ARGV[1]" or die; 40print C_FILE "/*\n"; 41my $scriptname = $0; 42$scriptname =~ s#^\Q$abs_srctree/\E##; 43print C_FILE " * Automatically generated by ", $scriptname, ". Do not edit\n"; 44print C_FILE " */\n"; 45 46# 47# Split the data up into separate lists and also determine the lengths of the 48# encoded data arrays. 49# 50my @indices = (); 51my @lengths = (); 52my $total_length = 0; 53 54for (my $i = 0; $i <= $#names; $i++) { 55 my $name = $names[$i]; 56 my $oid = $oids[$i]; 57 58 my @components = split(/[.]/, $oid); 59 60 # Determine the encoded length of this OID 61 my $size = $#components; 62 for (my $loop = 2; $loop <= $#components; $loop++) { 63 $ENV{'BC_LINE_LENGTH'} = "0"; 64 my $c = `echo "ibase=10; obase=2; $components[$loop]" | bc`; 65 chomp($c); 66 67 # We will base128 encode the number 68 my $tmp = length($c) - 1; 69 $tmp = int($tmp / 7); 70 $size += $tmp; 71 } 72 push @lengths, $size; 73 push @indices, $total_length; 74 $total_length += $size; 75} 76 77# 78# Emit the look-up-by-OID index table 79# 80print C_FILE "\n"; 81if ($total_length <= 255) { 82 print C_FILE "static const unsigned char oid_index[OID__NR + 1] = {\n"; 83} else { 84 print C_FILE "static const unsigned short oid_index[OID__NR + 1] = {\n"; 85} 86for (my $i = 0; $i <= $#names; $i++) { 87 print C_FILE "\t[OID_", $names[$i], "] = ", $indices[$i], ",\n" 88} 89print C_FILE "\t[OID__NR] = ", $total_length, "\n"; 90print C_FILE "};\n"; 91 92# 93# Encode the OIDs 94# 95my @encoded_oids = (); 96 97for (my $i = 0; $i <= $#names; $i++) { 98 my @octets = (); 99 100 my @components = split(/[.]/, $oids[$i]); 101 102 push @octets, $components[0] * 40 + $components[1]; 103 104 for (my $loop = 2; $loop <= $#components; $loop++) { 105 # get the base 2 representation of the component 106 $ENV{'BC_LINE_LENGTH'} = "0"; 107 my $c = `echo "ibase=10; obase=2; $components[$loop]" | bc`; 108 chomp($c); 109 110 my $tmp = length($c) - 1; 111 $tmp = int($tmp / 7); 112 113 # zero pad upto length multiple of 7 114 $c = substr("0000000", 0, ($tmp + 1) * 7 - length($c)).$c; 115 116 # Base128 encode the number 117 for (my $j = 0; $j < $tmp; $j++) { 118 my $b = oct("0b".substr($c, $j * 7, 7)); 119 120 push @octets, $b | 0x80; 121 } 122 push @octets, oct("0b".substr($c, $tmp * 7, 7)); 123 } 124 125 push @encoded_oids, \@octets; 126} 127 128# 129# Create a hash value for each OID 130# 131my @hash_values = (); 132for (my $i = 0; $i <= $#names; $i++) { 133 my @octets = @{$encoded_oids[$i]}; 134 135 my $hash = $#octets; 136 foreach (@octets) { 137 $hash += $_ * 33; 138 } 139 140 $hash = ($hash >> 24) ^ ($hash >> 16) ^ ($hash >> 8) ^ ($hash); 141 142 push @hash_values, $hash & 0xff; 143} 144 145# 146# Emit the OID data 147# 148print C_FILE "\n"; 149print C_FILE "static const unsigned char oid_data[", $total_length, "] = {\n"; 150for (my $i = 0; $i <= $#names; $i++) { 151 my @octets = @{$encoded_oids[$i]}; 152 print C_FILE "\t"; 153 print C_FILE $_, ", " foreach (@octets); 154 print C_FILE "\t// ", $names[$i]; 155 print C_FILE "\n"; 156} 157print C_FILE "};\n"; 158 159# 160# Build the search index table (ordered by length then hash then content) 161# 162my @index_table = ( 0 .. $#names ); 163 164@index_table = sort { 165 my @octets_a = @{$encoded_oids[$a]}; 166 my @octets_b = @{$encoded_oids[$b]}; 167 168 return $hash_values[$a] <=> $hash_values[$b] 169 if ($hash_values[$a] != $hash_values[$b]); 170 return $#octets_a <=> $#octets_b 171 if ($#octets_a != $#octets_b); 172 for (my $i = $#octets_a; $i >= 0; $i--) { 173 return $octets_a[$i] <=> $octets_b[$i] 174 if ($octets_a[$i] != $octets_b[$i]); 175 } 176 return 0; 177 178} @index_table; 179 180# 181# Emit the search index and hash value table 182# 183print C_FILE "\n"; 184print C_FILE "static const struct {\n"; 185print C_FILE "\tunsigned char hash;\n"; 186if ($#names <= 255) { 187 print C_FILE "\tenum OID oid : 8;\n"; 188} else { 189 print C_FILE "\tenum OID oid : 16;\n"; 190} 191print C_FILE "} oid_search_table[OID__NR] = {\n"; 192for (my $i = 0; $i <= $#names; $i++) { 193 my @octets = @{$encoded_oids[$index_table[$i]]}; 194 printf(C_FILE "\t[%3u] = { %3u, OID_%-35s }, // ", 195 $i, 196 $hash_values[$index_table[$i]], 197 $names[$index_table[$i]]); 198 printf C_FILE "%02x", $_ foreach (@octets); 199 print C_FILE "\n"; 200} 201print C_FILE "};\n"; 202 203# 204# Emit the OID debugging name table 205# 206#print C_FILE "\n"; 207#print C_FILE "const char *const oid_name_table[OID__NR + 1] = {\n"; 208# 209#for (my $i = 0; $i <= $#names; $i++) { 210# print C_FILE "\t\"", $names[$i], "\",\n" 211#} 212#print C_FILE "\t\"Unknown-OID\"\n"; 213#print C_FILE "};\n"; 214 215# 216# Polish off 217# 218close C_FILE or die; 219