xref: /openssl/test/recipes/tconversion.pl (revision 7ed6de99)
1#! /usr/bin/env perl
2# Copyright 2015-2024 The OpenSSL Project Authors. All Rights Reserved.
3#
4# Licensed under the Apache License 2.0 (the "License").  You may not use
5# this file except in compliance with the License.  You can obtain a copy
6# in the file LICENSE in the source distribution or at
7# https://www.openssl.org/source/license.html
8
9
10use strict;
11use warnings;
12
13use File::Compare qw/compare_text/;
14use File::Copy;
15use OpenSSL::Test qw/:DEFAULT/;
16use Time::Piece;
17use POSIX qw(strftime);
18
19my %conversionforms = (
20    # Default conversion forms.  Other series may be added with
21    # specific test types as key.
22    "*"		=> [ "d", "p" ],
23    "msb"	=> [ "d", "p", "msblob" ],
24    "pvk"	=> [ "d", "p", "pvk" ],
25    );
26sub tconversion {
27    my %opts = @_;
28
29    die "Missing option -type" unless $opts{-type};
30    die "Missing option -in" unless $opts{-in};
31    my $testtype = $opts{-type};
32    my $t = $opts{-in};
33    my $prefix = $opts{-prefix} // $testtype;
34    my @conversionforms =
35	defined($conversionforms{$testtype}) ?
36	@{$conversionforms{$testtype}} :
37	@{$conversionforms{"*"}};
38    my @openssl_args;
39    if (defined $opts{-args}) {
40        @openssl_args = @{$opts{-args}} if ref $opts{-args} eq 'ARRAY';
41        @openssl_args = ($opts{-args}) if ref $opts{-args} eq '';
42    }
43    @openssl_args = ($testtype) unless @openssl_args;
44
45    my $n = scalar @conversionforms;
46    my $totaltests =
47	1			# for initializing
48	+ $n			# initial conversions from p to all forms (A)
49	+ $n*$n			# conversion from result of A to all forms (B)
50	+ 1			# comparing original test file to p form of A
51	+ $n*($n-1);		# comparing first conversion to each form in A with B
52    $totaltests-- if ($testtype eq "p7d"); # no comparison of original test file
53    $totaltests -= $n if ($testtype eq "pvk"); # no comparisons of the pvk form
54    plan tests => $totaltests;
55
56    my @cmd = ("openssl", @openssl_args);
57
58    my $init;
59    if (scalar @openssl_args > 0 && $openssl_args[0] eq "pkey") {
60	$init = ok(run(app([@cmd, "-in", $t, "-out", "$prefix-fff.p"])),
61		   'initializing');
62    } else {
63	$init = ok(copy($t, "$prefix-fff.p"), 'initializing');
64    }
65    if (!$init) {
66	diag("Trying to copy $t to $prefix-fff.p : $!");
67    }
68
69  SKIP: {
70      skip "Not initialized, skipping...", 22 unless $init;
71
72      foreach my $to (@conversionforms) {
73	  ok(run(app([@cmd,
74		      "-in", "$prefix-fff.p",
75		      "-inform", "p",
76		      "-out", "$prefix-f.$to",
77		      "-outform", $to])),
78	     "p -> $to");
79      }
80
81      foreach my $to (@conversionforms) {
82	  foreach my $from (@conversionforms) {
83	      ok(run(app([@cmd,
84			  "-in", "$prefix-f.$from",
85			  "-inform", $from,
86			  "-out", "$prefix-ff.$from$to",
87			  "-outform", $to])),
88		 "$from -> $to");
89	  }
90      }
91
92      if ($testtype ne "p7d") {
93	  is(cmp_text("$prefix-fff.p", "$prefix-f.p"), 0,
94	     'comparing orig to p');
95      }
96
97      foreach my $to (@conversionforms) {
98	  next if $to eq "d" or $to eq "pvk";
99	  foreach my $from (@conversionforms) {
100	      is(cmp_text("$prefix-f.$to", "$prefix-ff.$from$to"), 0,
101		 "comparing $to to $from$to");
102	  }
103      }
104    }
105}
106
107sub cmp_text {
108    return compare_text(@_, sub {
109        $_[0] =~ s/\R//g;
110        $_[1] =~ s/\R//g;
111        return $_[0] ne $_[1];
112    });
113}
114
115sub file_contains {
116    $_ = shift @_;
117    my $pattern = shift @_;
118    open(DATA, $_) or return 0;
119    $_= join('', <DATA>);
120    close(DATA);
121    s/\s+/ /g; # take multiple whitespace (including newline) as single space
122    return m/$pattern/ ? 1 : 0;
123}
124
125sub cert_contains {
126    my $cert = shift @_;
127    my $pattern = shift @_;
128    my $expected = shift @_;
129    my $name = shift @_;
130    my $out = "cert_contains.out";
131    run(app(["openssl", "x509", "-noout", "-text", "-in", $cert, "-out", $out]));
132    is(file_contains($out, $pattern), $expected, ($name ? "$name: " : "").
133       "$cert should ".($expected ? "" : "not ")."contain: \"$pattern\"");
134    # not unlinking $out
135}
136
137sub has_version {
138    my $cert = shift @_;
139    my $expect = shift @_;
140    cert_contains($cert, "Version: $expect", 1);
141}
142
143sub has_SKID {
144    my $cert = shift @_;
145    my $expect = shift @_;
146    cert_contains($cert, "Subject Key Identifier", $expect);
147}
148
149sub has_AKID {
150    my $cert = shift @_;
151    my $expect = shift @_;
152    cert_contains($cert, "Authority Key Identifier", $expect);
153}
154
155sub uniq (@) {
156    my %seen = ();
157    grep { not $seen{$_}++ } @_;
158}
159
160sub file_n_different_lines {
161    my $filename = shift @_;
162    open(DATA, $filename) or return 0;
163    chomp(my @lines = <DATA>);
164    close(DATA);
165    return scalar(uniq @lines);
166}
167
168sub cert_ext_has_n_different_lines {
169    my $cert = shift @_;
170    my $expected = shift @_;
171    my $exts = shift @_;
172    my $name = shift @_;
173    my $out = "cert_n_different_exts.out";
174    run(app(["openssl", "x509", "-noout", "-ext", $exts,
175             "-in", $cert, "-out", $out]));
176    is(file_n_different_lines($out), $expected, ($name ? "$name: " : "").
177       "$cert '$exts' output should contain $expected different lines");
178    # not unlinking $out
179}
180
181# extracts string value of certificate field from a -text formatted-output
182sub get_field {
183    my ($f, $field) = @_;
184    my $string = "";
185    open my $fh, $f or die;
186    while (my $line = <$fh>) {
187        if ($line =~ /$field:\s+(.*)/) {
188            $string = $1;
189        }
190    }
191    close $fh;
192    return $string;
193}
194
195sub get_issuer {
196    return get_field(@_, "Issuer");
197}
198
199sub get_not_before {
200    return get_field(@_, "Not Before");
201}
202
203# Date as yyyy-mm-dd
204sub get_not_before_date {
205    return Time::Piece->strptime(
206        get_not_before(@_),
207        "%b %d %T %Y %Z")->date;
208}
209
210sub get_not_after {
211    return get_field(@_, "Not After ");
212}
213
214# Date as yyyy-mm-dd
215sub get_not_after_date {
216    return Time::Piece->strptime(
217        get_not_after(@_),
218        "%b %d %T %Y %Z")->date;
219}
220
2211;
222