xref: /freebsd/crypto/openssl/util/perl/OpenSSL/OID.pm (revision e0c4386e7e71d93b0edc0c8fa156263fc4a8b0b6)
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