1*e0c4386eSCy Schubert# Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved. 2*e0c4386eSCy Schubert# 3*e0c4386eSCy Schubert# Licensed under the Apache License 2.0 (the "License"). You may not use 4*e0c4386eSCy Schubert# this file except in compliance with the License. You can obtain a copy 5*e0c4386eSCy Schubert# in the file LICENSE in the source distribution or at 6*e0c4386eSCy Schubert# https://www.openssl.org/source/license.html 7*e0c4386eSCy Schubert 8*e0c4386eSCy Schubert# Author note: this is originally RL::ASN1::OID, 9*e0c4386eSCy Schubert# repurposed by the author for OpenSSL use. 10*e0c4386eSCy Schubert 11*e0c4386eSCy Schubertpackage OpenSSL::OID; 12*e0c4386eSCy Schubert 13*e0c4386eSCy Schubertuse 5.10.0; 14*e0c4386eSCy Schubertuse strict; 15*e0c4386eSCy Schubertuse warnings; 16*e0c4386eSCy Schubertuse Carp; 17*e0c4386eSCy Schubert 18*e0c4386eSCy Schubertuse Exporter; 19*e0c4386eSCy Schubertuse vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 20*e0c4386eSCy Schubert@ISA = qw(Exporter); 21*e0c4386eSCy Schubert@EXPORT = qw(parse_oid encode_oid register_oid 22*e0c4386eSCy Schubert registered_oid_arcs registered_oid_leaves); 23*e0c4386eSCy Schubert@EXPORT_OK = qw(encode_oid_nums); 24*e0c4386eSCy Schubert 25*e0c4386eSCy Schubert# Unfortunately, the pairwise List::Util functionality came with perl 26*e0c4386eSCy Schubert# v5.19.3, and I want to target absolute compatibility with perl 5.10 27*e0c4386eSCy Schubert# and up. That means I have to implement quick pairwise functions here. 28*e0c4386eSCy Schubert 29*e0c4386eSCy Schubert#use List::Util; 30*e0c4386eSCy Schubertsub _pairs (@); 31*e0c4386eSCy Schubertsub _pairmap (&@); 32*e0c4386eSCy Schubert 33*e0c4386eSCy Schubert=head1 NAME 34*e0c4386eSCy Schubert 35*e0c4386eSCy SchubertOpenSSL::OID - an OBJECT IDENTIFIER parser / encoder 36*e0c4386eSCy Schubert 37*e0c4386eSCy Schubert=head1 VERSION 38*e0c4386eSCy Schubert 39*e0c4386eSCy SchubertVersion 0.1 40*e0c4386eSCy Schubert 41*e0c4386eSCy Schubert=cut 42*e0c4386eSCy Schubert 43*e0c4386eSCy Schubertour $VERSION = '0.1'; 44*e0c4386eSCy Schubert 45*e0c4386eSCy Schubert 46*e0c4386eSCy Schubert=head1 SYNOPSIS 47*e0c4386eSCy Schubert 48*e0c4386eSCy Schubert use OpenSSL::OID; 49*e0c4386eSCy Schubert 50*e0c4386eSCy Schubert # This gives the array ( 1 2 840 113549 1 1 ) 51*e0c4386eSCy Schubert my @nums = parse_oid('{ pkcs-1 1 }'); 52*e0c4386eSCy Schubert 53*e0c4386eSCy Schubert # This gives the array of DER encoded bytes for the OID, i.e. 54*e0c4386eSCy Schubert # ( 42, 134, 72, 134, 247, 13, 1, 1 ) 55*e0c4386eSCy Schubert my @bytes = encode_oid('{ pkcs-1 1 }'); 56*e0c4386eSCy Schubert 57*e0c4386eSCy Schubert # This registers a name with an OID. It's saved internally and 58*e0c4386eSCy Schubert # serves as repository of names for further parsing, such as 'pkcs-1' 59*e0c4386eSCy Schubert # in the strings used above. 60*e0c4386eSCy Schubert register_object('pkcs-1', '{ pkcs 1 }'); 61*e0c4386eSCy Schubert 62*e0c4386eSCy Schubert 63*e0c4386eSCy Schubert use OpenSSL::OID qw(:DEFAULT encode_oid_nums); 64*e0c4386eSCy Schubert 65*e0c4386eSCy Schubert # This does the same as encode_oid(), but takes the output of 66*e0c4386eSCy Schubert # parse_oid() as input. 67*e0c4386eSCy Schubert my @bytes = encode_oid_nums(@nums); 68*e0c4386eSCy Schubert 69*e0c4386eSCy Schubert=head1 EXPORT 70*e0c4386eSCy Schubert 71*e0c4386eSCy SchubertThe functions parse_oid and encode_oid are exported by default. 72*e0c4386eSCy SchubertThe function encode_oid_nums() can be exported explicitly. 73*e0c4386eSCy Schubert 74*e0c4386eSCy Schubert=cut 75*e0c4386eSCy Schubert 76*e0c4386eSCy Schubert######## REGEXPS 77*e0c4386eSCy Schubert 78*e0c4386eSCy Schubert# ASN.1 object identifiers come in two forms: 1) the bracketed form 79*e0c4386eSCy Schubert#(referred to as ObjectIdentifierValue in X.690), 2) the dotted form 80*e0c4386eSCy Schubert#(referred to as XMLObjIdentifierValue in X.690) 81*e0c4386eSCy Schubert# 82*e0c4386eSCy Schubert# examples of 1 (these are all the OID for rsaEncrypted): 83*e0c4386eSCy Schubert# 84*e0c4386eSCy Schubert# { iso (1) 2 840 11349 1 1 } 85*e0c4386eSCy Schubert# { pkcs 1 1 } 86*e0c4386eSCy Schubert# { pkcs1 1 } 87*e0c4386eSCy Schubert# 88*e0c4386eSCy Schubert# examples of 2: 89*e0c4386eSCy Schubert# 90*e0c4386eSCy Schubert# 1.2.840.113549.1.1 91*e0c4386eSCy Schubert# pkcs.1.1 92*e0c4386eSCy Schubert# pkcs1.1 93*e0c4386eSCy Schubert# 94*e0c4386eSCy Schubertmy $identifier_re = qr/[a-z](?:[-_A-Za-z0-9]*[A-Za-z0-9])?/; 95*e0c4386eSCy Schubert# The only difference between $objcomponent_re and $xmlobjcomponent_re is 96*e0c4386eSCy Schubert# the separator in the top branch. Each component is always parsed in two 97*e0c4386eSCy Schubert# groups, so we get a pair of values regardless. That's the reason for the 98*e0c4386eSCy Schubert# empty parentheses. 99*e0c4386eSCy Schubert# Because perl doesn't try to do an exhaustive try of every branch it rather 100*e0c4386eSCy Schubert# stops on the first that matches, we need to have them in order of longest 101*e0c4386eSCy Schubert# to shortest where there may be ambiguity. 102*e0c4386eSCy Schubertmy $objcomponent_re = qr/(?| 103*e0c4386eSCy Schubert (${identifier_re}) \s* \((\d+)\) 104*e0c4386eSCy Schubert | 105*e0c4386eSCy Schubert (${identifier_re}) () 106*e0c4386eSCy Schubert | 107*e0c4386eSCy Schubert ()(\d+) 108*e0c4386eSCy Schubert )/x; 109*e0c4386eSCy Schubertmy $xmlobjcomponent_re = qr/(?| 110*e0c4386eSCy Schubert (${identifier_re}) \. \((\d+)\) 111*e0c4386eSCy Schubert | 112*e0c4386eSCy Schubert (${identifier_re}) () 113*e0c4386eSCy Schubert | 114*e0c4386eSCy Schubert () (\d+) 115*e0c4386eSCy Schubert )/x; 116*e0c4386eSCy Schubert 117*e0c4386eSCy Schubertmy $obj_re = 118*e0c4386eSCy Schubert qr/(?: \{ \s* (?: ${objcomponent_re} \s+ )* ${objcomponent_re} \s* \} )/x; 119*e0c4386eSCy Schubertmy $xmlobj_re = 120*e0c4386eSCy Schubert qr/(?: (?: ${xmlobjcomponent_re} \. )* ${xmlobjcomponent_re} )/x; 121*e0c4386eSCy Schubert 122*e0c4386eSCy Schubert######## NAME TO OID REPOSITORY 123*e0c4386eSCy Schubert 124*e0c4386eSCy Schubert# Recorded OIDs, to support things like '{ pkcs1 1 }' 125*e0c4386eSCy Schubert# Do note that we don't currently support relative OIDs 126*e0c4386eSCy Schubert# 127*e0c4386eSCy Schubert# The key is the identifier. 128*e0c4386eSCy Schubert# 129*e0c4386eSCy Schubert# The value is a hash, composed of: 130*e0c4386eSCy Schubert# type => 'arc' | 'leaf' 131*e0c4386eSCy Schubert# nums => [ LIST ] 132*e0c4386eSCy Schubert# Note that the |type| always starts as a 'leaf', and may change to an 'arc' 133*e0c4386eSCy Schubert# on the fly, as new OIDs are parsed. 134*e0c4386eSCy Schubertmy %name2oid = (); 135*e0c4386eSCy Schubert 136*e0c4386eSCy Schubert######## 137*e0c4386eSCy Schubert 138*e0c4386eSCy Schubert=head1 SUBROUTINES/METHODS 139*e0c4386eSCy Schubert 140*e0c4386eSCy Schubert=over 4 141*e0c4386eSCy Schubert 142*e0c4386eSCy Schubert=item parse_oid() 143*e0c4386eSCy Schubert 144*e0c4386eSCy SchubertTBA 145*e0c4386eSCy Schubert 146*e0c4386eSCy Schubert=cut 147*e0c4386eSCy Schubert 148*e0c4386eSCy Schubertsub parse_oid { 149*e0c4386eSCy Schubert my $input = shift; 150*e0c4386eSCy Schubert 151*e0c4386eSCy Schubert croak "Invalid extra arguments" if (@_); 152*e0c4386eSCy Schubert 153*e0c4386eSCy Schubert # The components become a list of ( identifier, number ) pairs, 154*e0c4386eSCy Schubert # where they can also be the empty string if they are not present 155*e0c4386eSCy Schubert # in the input. 156*e0c4386eSCy Schubert my @components; 157*e0c4386eSCy Schubert if ($input =~ m/^\s*(${obj_re})\s*$/x) { 158*e0c4386eSCy Schubert my $oid = $1; 159*e0c4386eSCy Schubert @components = ( $oid =~ m/${objcomponent_re}\s*/g ); 160*e0c4386eSCy Schubert } elsif ($input =~ m/^\s*(${xmlobj_re})\s*$/) { 161*e0c4386eSCy Schubert my $oid = $1; 162*e0c4386eSCy Schubert @components = ( $oid =~ m/${xmlobjcomponent_re}\.?/g ); 163*e0c4386eSCy Schubert } 164*e0c4386eSCy Schubert 165*e0c4386eSCy Schubert croak "Invalid ASN.1 object '$input'" unless @components; 166*e0c4386eSCy Schubert die "Internal error when parsing '$input'" 167*e0c4386eSCy Schubert unless scalar(@components) % 2 == 0; 168*e0c4386eSCy Schubert 169*e0c4386eSCy Schubert # As we currently only support a name without number as first 170*e0c4386eSCy Schubert # component, the easiest is to have a direct look at it and 171*e0c4386eSCy Schubert # hack it. 172*e0c4386eSCy Schubert my @first = _pairmap { 173*e0c4386eSCy Schubert my ($a, $b) = @$_; 174*e0c4386eSCy Schubert return $b if $b ne ''; 175*e0c4386eSCy Schubert return @{$name2oid{$a}->{nums}} if $a ne '' && defined $name2oid{$a}; 176*e0c4386eSCy Schubert croak "Undefined identifier $a" if $a ne ''; 177*e0c4386eSCy Schubert croak "Empty OID element (how's that possible?)"; 178*e0c4386eSCy Schubert } ( @components[0..1] ); 179*e0c4386eSCy Schubert 180*e0c4386eSCy Schubert my @numbers = 181*e0c4386eSCy Schubert ( 182*e0c4386eSCy Schubert @first, 183*e0c4386eSCy Schubert _pairmap { 184*e0c4386eSCy Schubert my ($a, $b) = @$_; 185*e0c4386eSCy Schubert return $b if $b ne ''; 186*e0c4386eSCy Schubert croak "Unsupported relative OID $a" if $a ne ''; 187*e0c4386eSCy Schubert croak "Empty OID element (how's that possible?)"; 188*e0c4386eSCy Schubert } @components[2..$#components] 189*e0c4386eSCy Schubert ); 190*e0c4386eSCy Schubert 191*e0c4386eSCy Schubert # If the first component has an identifier and there are other 192*e0c4386eSCy Schubert # components following it, we change the type of that identifier 193*e0c4386eSCy Schubert # to 'arc'. 194*e0c4386eSCy Schubert if (scalar @components > 2 195*e0c4386eSCy Schubert && $components[0] ne '' 196*e0c4386eSCy Schubert && defined $name2oid{$components[0]}) { 197*e0c4386eSCy Schubert $name2oid{$components[0]}->{type} = 'arc'; 198*e0c4386eSCy Schubert } 199*e0c4386eSCy Schubert 200*e0c4386eSCy Schubert return @numbers; 201*e0c4386eSCy Schubert} 202*e0c4386eSCy Schubert 203*e0c4386eSCy Schubert=item encode_oid() 204*e0c4386eSCy Schubert 205*e0c4386eSCy Schubert=cut 206*e0c4386eSCy Schubert 207*e0c4386eSCy Schubert# Forward declaration 208*e0c4386eSCy Schubertsub encode_oid_nums; 209*e0c4386eSCy Schubertsub encode_oid { 210*e0c4386eSCy Schubert return encode_oid_nums parse_oid @_; 211*e0c4386eSCy Schubert} 212*e0c4386eSCy Schubert 213*e0c4386eSCy Schubert=item register_oid() 214*e0c4386eSCy Schubert 215*e0c4386eSCy Schubert=cut 216*e0c4386eSCy Schubert 217*e0c4386eSCy Schubertsub register_oid { 218*e0c4386eSCy Schubert my $name = shift; 219*e0c4386eSCy Schubert my @nums = parse_oid @_; 220*e0c4386eSCy Schubert 221*e0c4386eSCy Schubert if (defined $name2oid{$name}) { 222*e0c4386eSCy Schubert my $str1 = join(',', @nums); 223*e0c4386eSCy Schubert my $str2 = join(',', @{$name2oid{$name}->{nums}}); 224*e0c4386eSCy Schubert 225*e0c4386eSCy Schubert croak "Invalid redefinition of $name with different value" 226*e0c4386eSCy Schubert unless $str1 eq $str2; 227*e0c4386eSCy Schubert } else { 228*e0c4386eSCy Schubert $name2oid{$name} = { type => 'leaf', nums => [ @nums ] }; 229*e0c4386eSCy Schubert } 230*e0c4386eSCy Schubert} 231*e0c4386eSCy Schubert 232*e0c4386eSCy Schubert=item registered_oid_arcs() 233*e0c4386eSCy Schubert 234*e0c4386eSCy Schubert=item registered_oid_leaves() 235*e0c4386eSCy Schubert 236*e0c4386eSCy Schubert=cut 237*e0c4386eSCy Schubert 238*e0c4386eSCy Schubertsub _registered_oids { 239*e0c4386eSCy Schubert my $type = shift; 240*e0c4386eSCy Schubert 241*e0c4386eSCy Schubert return grep { $name2oid{$_}->{type} eq $type } keys %name2oid; 242*e0c4386eSCy Schubert} 243*e0c4386eSCy Schubert 244*e0c4386eSCy Schubertsub registered_oid_arcs { 245*e0c4386eSCy Schubert return _registered_oids( 'arc' ); 246*e0c4386eSCy Schubert} 247*e0c4386eSCy Schubert 248*e0c4386eSCy Schubertsub registered_oid_leaves { 249*e0c4386eSCy Schubert return _registered_oids( 'leaf' ); 250*e0c4386eSCy Schubert} 251*e0c4386eSCy Schubert 252*e0c4386eSCy Schubert=item encode_oid_nums() 253*e0c4386eSCy Schubert 254*e0c4386eSCy Schubert=cut 255*e0c4386eSCy Schubert 256*e0c4386eSCy Schubert# Internal helper. It takes a numeric OID component and generates the 257*e0c4386eSCy Schubert# DER encoding for it. 258*e0c4386eSCy Schubertsub _gen_oid_bytes { 259*e0c4386eSCy Schubert my $num = shift; 260*e0c4386eSCy Schubert my $cnt = 0; 261*e0c4386eSCy Schubert 262*e0c4386eSCy Schubert return ( $num ) if $num < 128; 263*e0c4386eSCy Schubert return ( ( map { $_ | 0x80 } _gen_oid_bytes($num >> 7) ), $num & 0x7f ); 264*e0c4386eSCy Schubert} 265*e0c4386eSCy Schubert 266*e0c4386eSCy Schubertsub encode_oid_nums { 267*e0c4386eSCy Schubert my @numbers = @_; 268*e0c4386eSCy Schubert 269*e0c4386eSCy Schubert croak 'Invalid OID values: ( ', join(', ', @numbers), ' )' 270*e0c4386eSCy Schubert if (scalar @numbers < 2 271*e0c4386eSCy Schubert || $numbers[0] < 0 || $numbers[0] > 2 272*e0c4386eSCy Schubert || $numbers[1] < 0 || $numbers[1] > 39); 273*e0c4386eSCy Schubert 274*e0c4386eSCy Schubert my $first = shift(@numbers) * 40 + shift(@numbers); 275*e0c4386eSCy Schubert @numbers = ( $first, map { _gen_oid_bytes($_) } @numbers ); 276*e0c4386eSCy Schubert 277*e0c4386eSCy Schubert return @numbers; 278*e0c4386eSCy Schubert} 279*e0c4386eSCy Schubert 280*e0c4386eSCy Schubert=back 281*e0c4386eSCy Schubert 282*e0c4386eSCy Schubert=head1 AUTHOR 283*e0c4386eSCy Schubert 284*e0c4386eSCy SchubertRichard levitte, C<< <richard at levitte.org> >> 285*e0c4386eSCy Schubert 286*e0c4386eSCy Schubert=cut 287*e0c4386eSCy Schubert 288*e0c4386eSCy Schubert######## Helpers 289*e0c4386eSCy Schubert 290*e0c4386eSCy Schubertsub _pairs (@) { 291*e0c4386eSCy Schubert croak "Odd number of arguments" if @_ & 1; 292*e0c4386eSCy Schubert 293*e0c4386eSCy Schubert my @pairlist = (); 294*e0c4386eSCy Schubert 295*e0c4386eSCy Schubert while (@_) { 296*e0c4386eSCy Schubert my $x = [ shift, shift ]; 297*e0c4386eSCy Schubert push @pairlist, $x; 298*e0c4386eSCy Schubert } 299*e0c4386eSCy Schubert return @pairlist; 300*e0c4386eSCy Schubert} 301*e0c4386eSCy Schubert 302*e0c4386eSCy Schubertsub _pairmap (&@) { 303*e0c4386eSCy Schubert my $block = shift; 304*e0c4386eSCy Schubert map { $block->($_) } _pairs @_; 305*e0c4386eSCy Schubert} 306*e0c4386eSCy Schubert 307*e0c4386eSCy Schubert1; # End of OpenSSL::OID 308