STORE: Add documentation on search criteria
[openssl.git] / test / recipes / 90-test_store.t
1 #! /usr/bin/env perl
2 # Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the OpenSSL license (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 use File::Spec;
10 use File::Copy;
11 use MIME::Base64;
12 use OpenSSL::Test qw(:DEFAULT srctop_file srctop_dir bldtop_file data_file);
13 use OpenSSL::Test::Utils;
14
15 my $test_name = "test_store";
16 setup($test_name);
17
18 my @noexist_files =
19     ( "test/blahdiblah.pem",
20       "test/blahdibleh.der" );
21 my @src_files =
22     ( "test/testx509.pem",
23       "test/testrsa.pem",
24       "test/testrsapub.pem",
25       "test/testcrl.pem",
26       "apps/server.pem" );
27 my @generated_files =
28     (
29      ### generated from the source files
30
31      "testx509.der",
32      "testrsa.der",
33      "testrsapub.der",
34      "testcrl.der",
35
36      ### generated locally
37
38      "rsa-key-pkcs1.pem", "rsa-key-pkcs1.der",
39      "rsa-key-pkcs1-aes128.pem",
40      "rsa-key-pkcs8.pem", "rsa-key-pkcs8.der",
41      "rsa-key-pkcs8-pbes1-sha1-3des.pem", "rsa-key-pkcs8-pbes1-sha1-3des.der",
42      "rsa-key-pkcs8-pbes2-sha1.pem", "rsa-key-pkcs8-pbes2-sha1.der",
43      "rsa-key-sha1-3des-sha1.p12", "rsa-key-sha1-3des-sha256.p12",
44      "rsa-key-aes256-cbc-sha256.p12",
45      "rsa-key-md5-des-sha1.p12",
46      "rsa-key-aes256-cbc-md5-des-sha256.p12",
47      "rsa-key-pkcs8-pbes2-sha256.pem", "rsa-key-pkcs8-pbes2-sha256.der",
48      "rsa-key-pkcs8-pbes1-md5-des.pem", "rsa-key-pkcs8-pbes1-md5-des.der",
49      "dsa-key-pkcs1.pem", "dsa-key-pkcs1.der",
50      "dsa-key-pkcs1-aes128.pem",
51      "dsa-key-pkcs8.pem", "dsa-key-pkcs8.der",
52      "dsa-key-pkcs8-pbes2-sha1.pem", "dsa-key-pkcs8-pbes2-sha1.der",
53      "dsa-key-aes256-cbc-sha256.p12",
54      "ec-key-pkcs1.pem", "ec-key-pkcs1.der",
55      "ec-key-pkcs1-aes128.pem",
56      "ec-key-pkcs8.pem", "ec-key-pkcs8.der",
57      "ec-key-pkcs8-pbes2-sha1.pem", "ec-key-pkcs8-pbes2-sha1.der",
58      "ec-key-aes256-cbc-sha256.p12",
59     );
60 my %generated_file_files =
61     $^O eq 'linux'
62     ? ( "test/testx509.pem" => "file:testx509.pem",
63         "test/testrsa.pem" => "file:testrsa.pem",
64         "test/testrsapub.pem" => "file:testrsapub.pem",
65         "test/testcrl.pem" => "file:testcrl.pem",
66         "apps/server.pem" => "file:server.pem" )
67     : ();
68 my @noexist_file_files =
69     ( "file:blahdiblah.pem",
70       "file:test/blahdibleh.der" );
71
72 my $n = (3 * scalar @noexist_files)
73     + (6 * scalar @src_files)
74     + (4 * scalar @generated_files)
75     + (scalar keys %generated_file_files)
76     + (scalar @noexist_file_files)
77     + 3
78     + 4;
79
80 plan tests => $n;
81
82 indir "store_$$" => sub {
83  SKIP:
84     {
85         skip "failed initialisation", $n unless init();
86
87         # test PEM_read_bio_PrivateKey
88         ok(run(app(["openssl", "rsa", "-in", "rsa-key-pkcs8-pbes2-sha256.pem",
89                     "-passin", "pass:password"])));
90
91         foreach (@noexist_files) {
92             my $file = srctop_file($_);
93
94             ok(!run(app(["openssl", "storeutl", $file])));
95             ok(!run(app(["openssl", "storeutl", to_abs_file($file)])));
96             {
97                 local $ENV{MSYS2_ARG_CONV_EXCL} = "file:";
98
99                 ok(!run(app(["openssl", "storeutl", to_abs_file_uri($file)])));
100             }
101         }
102         foreach (@src_files) {
103             my $file = srctop_file($_);
104
105             ok(run(app(["openssl", "storeutl", $file])));
106             ok(run(app(["openssl", "storeutl", to_abs_file($file)])));
107             {
108                 local $ENV{MSYS2_ARG_CONV_EXCL} = "file:";
109
110                 ok(run(app(["openssl", "storeutl", to_abs_file_uri($file)])));
111                 ok(run(app(["openssl", "storeutl",
112                             to_abs_file_uri($file, 0, "")])));
113                 ok(run(app(["openssl", "storeutl",
114                             to_abs_file_uri($file, 0, "localhost")])));
115                 ok(!run(app(["openssl", "storeutl",
116                              to_abs_file_uri($file, 0, "dummy")])));
117             }
118         }
119         foreach (@generated_files) {
120             ok(run(app(["openssl", "storeutl", "-passin", "pass:password",
121                         $_])));
122             ok(run(app(["openssl", "storeutl", "-passin", "pass:password",
123                         to_abs_file($_)])));
124
125             {
126                 local $ENV{MSYS2_ARG_CONV_EXCL} = "file:";
127
128                 ok(run(app(["openssl", "storeutl", "-passin", "pass:password",
129                             to_abs_file_uri($_)])));
130                 ok(!run(app(["openssl", "storeutl", "-passin", "pass:password",
131                              to_file_uri($_)])));
132             }
133         }
134         foreach (values %generated_file_files) {
135             local $ENV{MSYS2_ARG_CONV_EXCL} = "file:";
136
137             ok(run(app(["openssl", "storeutl", $_])));
138         }
139         foreach (@noexist_file_files) {
140             local $ENV{MSYS2_ARG_CONV_EXCL} = "file:";
141
142             ok(!run(app(["openssl", "storeutl", $_])));
143         }
144         {
145             my $dir = srctop_dir("test", "certs");
146
147             ok(run(app(["openssl", "storeutl", $dir])));
148             ok(run(app(["openssl", "storeutl", to_abs_file($dir, 1)])));
149             {
150                 local $ENV{MSYS2_ARG_CONV_EXCL} = "file:";
151
152                 ok(run(app(["openssl", "storeutl", to_abs_file_uri($dir, 1)])));
153             }
154         }
155
156         ok(run(app(['openssl', 'storeutl', '-certs',
157                     srctop_file('test', 'testx509.pem')])),
158            "Checking that -certs returns 1 object on a certificate file");
159         ok(run(app(['openssl', 'storeutl', '-certs',
160                      srctop_file('test', 'testcrl.pem')])),
161            "Checking that -certs returns 0 objects on a CRL file");
162
163         ok(run(app(['openssl', 'storeutl', '-crls',
164                      srctop_file('test', 'testx509.pem')])),
165            "Checking that -crls returns 0 objects on a certificate file");
166         ok(run(app(['openssl', 'storeutl', '-crls',
167                     srctop_file('test', 'testcrl.pem')])),
168            "Checking that -crls returns 1 object on a CRL file");
169     }
170 }, create => 1, cleanup => 1;
171
172 sub init {
173     return (
174             # rsa-key-pkcs1.pem
175             run(app(["openssl", "genrsa",
176                      "-out", "rsa-key-pkcs1.pem", "2432"]))
177             # dsa-key-pkcs1.pem
178             && run(app(["openssl", "dsaparam", "-genkey",
179                         "-out", "dsa-key-pkcs1.pem", "1024"]))
180             # ec-key-pkcs1.pem (one might think that 'genec' would be practical)
181             && run(app(["openssl", "ecparam", "-genkey", "-name", "prime256v1",
182                         "-out", "ec-key-pkcs1.pem"]))
183             # rsa-key-pkcs1-aes128.pem
184             && run(app(["openssl", "rsa", "-passout", "pass:password", "-aes128",
185                         "-in", "rsa-key-pkcs1.pem",
186                         "-out", "rsa-key-pkcs1-aes128.pem"]))
187             # dsa-key-pkcs1-aes128.pem
188             && run(app(["openssl", "dsa", "-passout", "pass:password", "-aes128",
189                         "-in", "dsa-key-pkcs1.pem",
190                         "-out", "dsa-key-pkcs1-aes128.pem"]))
191             # ec-key-pkcs1-aes128.pem
192             && run(app(["openssl", "ec", "-passout", "pass:password", "-aes128",
193                         "-in", "ec-key-pkcs1.pem",
194                         "-out", "ec-key-pkcs1-aes128.pem"]))
195             # *-key-pkcs8.pem
196             && runall(sub {
197                           my $dstfile = shift;
198                           (my $srcfile = $dstfile)
199                               =~ s/-key-pkcs8\.pem$/-key-pkcs1.pem/i;
200                           run(app(["openssl", "pkcs8", "-topk8", "-nocrypt",
201                                    "-in", $srcfile, "-out", $dstfile]));
202                       }, grep(/-key-pkcs8\.pem$/, @generated_files))
203             # *-key-pkcs8-pbes1-sha1-3des.pem
204             && runall(sub {
205                           my $dstfile = shift;
206                           (my $srcfile = $dstfile)
207                               =~ s/-key-pkcs8-pbes1-sha1-3des\.pem$
208                                   /-key-pkcs8.pem/ix;
209                           run(app(["openssl", "pkcs8", "-topk8",
210                                    "-passout", "pass:password",
211                                    "-v1", "pbeWithSHA1And3-KeyTripleDES-CBC",
212                                    "-in", $srcfile, "-out", $dstfile]));
213                       }, grep(/-key-pkcs8-pbes1-sha1-3des\.pem$/, @generated_files))
214             # *-key-pkcs8-pbes1-md5-des.pem
215             && runall(sub {
216                           my $dstfile = shift;
217                           (my $srcfile = $dstfile)
218                               =~ s/-key-pkcs8-pbes1-md5-des\.pem$
219                                   /-key-pkcs8.pem/ix;
220                           run(app(["openssl", "pkcs8", "-topk8",
221                                    "-passout", "pass:password",
222                                    "-v1", "pbeWithSHA1And3-KeyTripleDES-CBC",
223                                    "-in", $srcfile, "-out", $dstfile]));
224                       }, grep(/-key-pkcs8-pbes1-md5-des\.pem$/, @generated_files))
225             # *-key-pkcs8-pbes2-sha1.pem
226             && runall(sub {
227                           my $dstfile = shift;
228                           (my $srcfile = $dstfile)
229                               =~ s/-key-pkcs8-pbes2-sha1\.pem$
230                                   /-key-pkcs8.pem/ix;
231                           run(app(["openssl", "pkcs8", "-topk8",
232                                    "-passout", "pass:password",
233                                    "-v2", "aes256", "-v2prf", "hmacWithSHA1",
234                                    "-in", $srcfile, "-out", $dstfile]));
235                       }, grep(/-key-pkcs8-pbes2-sha1\.pem$/, @generated_files))
236             # *-key-pkcs8-pbes2-sha1.pem
237             && runall(sub {
238                           my $dstfile = shift;
239                           (my $srcfile = $dstfile)
240                               =~ s/-key-pkcs8-pbes2-sha256\.pem$
241                                   /-key-pkcs8.pem/ix;
242                           run(app(["openssl", "pkcs8", "-topk8",
243                                    "-passout", "pass:password",
244                                    "-v2", "aes256", "-v2prf", "hmacWithSHA256",
245                                    "-in", $srcfile, "-out", $dstfile]));
246                       }, grep(/-key-pkcs8-pbes2-sha256\.pem$/, @generated_files))
247             # *-cert.pem (intermediary for the .p12 inits)
248             && run(app(["openssl", "req", "-x509",
249                         "-config", data_file("ca.cnf"), "-nodes",
250                         "-out", "cacert.pem", "-keyout", "cakey.pem"]))
251             && runall(sub {
252                           my $srckey = shift;
253                           (my $dstfile = $srckey) =~ s|-key-pkcs8\.|-cert.|;
254                           (my $csr = $dstfile) =~ s|\.pem|.csr|;
255
256                           (run(app(["openssl", "req", "-new",
257                                     "-config", data_file("user.cnf"),
258                                     "-key", $srckey, "-out", $csr]))
259                            &&
260                            run(app(["openssl", "x509", "-days", "3650",
261                                     "-CA", "cacert.pem",
262                                     "-CAkey", "cakey.pem",
263                                     "-set_serial", time(), "-req",
264                                     "-in", $csr, "-out", $dstfile])));
265                       }, grep(/-key-pkcs8\.pem$/, @generated_files))
266             # *.p12
267             && runall(sub {
268                           my $dstfile = shift;
269                           my ($type, $certpbe_index, $keypbe_index,
270                               $macalg_index) =
271                               $dstfile =~ m{^(.*)-key-(?|
272                                                 # cert and key PBE are same
273                                                 ()             #
274                                                 ([^-]*-[^-]*)- # key & cert PBE
275                                                 ([^-]*)        # MACalg
276                                             |
277                                                 # cert and key PBE are not same
278                                                 ([^-]*-[^-]*)- # cert PBE
279                                                 ([^-]*-[^-]*)- # key PBE
280                                                 ([^-]*)        # MACalg
281                                             )\.}x;
282                           if (!$certpbe_index) {
283                               $certpbe_index = $keypbe_index;
284                           }
285                           my $srckey = "$type-key-pkcs8.pem";
286                           my $srccert = "$type-cert.pem";
287                           my %pbes =
288                               (
289                                "sha1-3des" => "pbeWithSHA1And3-KeyTripleDES-CBC",
290                                "md5-des" => "pbeWithMD5AndDES-CBC",
291                                "aes256-cbc" => "AES-256-CBC",
292                               );
293                           my %macalgs =
294                               (
295                                "sha1" => "SHA1",
296                                "sha256" => "SHA256",
297                               );
298                           my $certpbe = $pbes{$certpbe_index};
299                           my $keypbe = $pbes{$keypbe_index};
300                           my $macalg = $macalgs{$macalg_index};
301                           if (!defined($certpbe) || !defined($keypbe)
302                               || !defined($macalg)) {
303                               print STDERR "Cert PBE for $pbe_index not defined\n"
304                                   unless defined $certpbe;
305                               print STDERR "Key PBE for $pbe_index not defined\n"
306                                   unless defined $keypbe;
307                               print STDERR "MACALG for $macalg_index not defined\n"
308                                   unless defined $macalg;
309                               print STDERR "(destination file was $dstfile)\n";
310                               return 0;
311                           }
312                           run(app(["openssl", "pkcs12", "-inkey", $srckey,
313                                    "-in", $srccert, "-passout", "pass:password",
314                                    "-export", "-macalg", $macalg,
315                                    "-certpbe", $certpbe, "-keypbe", $keypbe,
316                                    "-out", $dstfile]));
317                       }, grep(/\.p12/, @generated_files))
318             # *.der (the end all init)
319             && runall(sub {
320                           my $dstfile = shift;
321                           (my $srcfile = $dstfile) =~ s/\.der$/.pem/i;
322                           if (! -f $srcfile) {
323                               $srcfile = srctop_file("test", $srcfile);
324                           }
325                           my $infh;
326                           unless (open $infh, $srcfile) {
327                               return 0;
328                           }
329                           my $l;
330                           while (($l = <$infh>) !~ /^-----BEGIN\s/
331                                  || $l =~ /^-----BEGIN.*PARAMETERS-----/) {
332                           }
333                           my $b64 = "";
334                           while (($l = <$infh>) !~ /^-----END\s/) {
335                               $l =~ s|\R$||;
336                               $b64 .= $l unless $l =~ /:/;
337                           }
338                           close $infh;
339                           my $der = decode_base64($b64);
340                           unless (length($b64) / 4 * 3 - length($der) < 3) {
341                               print STDERR "Length error, ",length($b64),
342                                   " bytes of base64 became ",length($der),
343                                   " bytes of der? ($srcfile => $dstfile)\n";
344                               return 0;
345                           }
346                           my $outfh;
347                           unless (open $outfh, ">:raw", $dstfile) {
348                               return 0;
349                           }
350                           print $outfh $der;
351                           close $outfh;
352                           return 1;
353                       }, grep(/\.der$/, @generated_files))
354             && runall(sub {
355                           my $srcfile = shift;
356                           my $dstfile = $generated_file_files{$srcfile};
357
358                           unless (copy srctop_file($srcfile), $dstfile) {
359                               warn "$!\n";
360                               return 0;
361                           }
362                           return 1;
363                       }, keys %generated_file_files)
364            );
365 }
366
367 sub runall {
368     my ($function, @items) = @_;
369
370     foreach (@items) {
371         return 0 unless $function->($_);
372     }
373     return 1;
374 }
375
376 # According to RFC8089, a relative file: path is invalid.  We still produce
377 # them for testing purposes.
378 sub to_file_uri {
379     my ($file, $isdir, $authority) = @_;
380     my $vol;
381     my $dir;
382
383     die "to_file_uri: No file given\n" if !defined($file) || $file eq '';
384
385     ($vol, $dir, $file) = File::Spec->splitpath($file, $isdir // 0);
386
387     # Make sure we have a Unix style directory.
388     $dir = join('/', File::Spec->splitdir($dir));
389     # Canonicalise it (note: it seems to be only needed on Unix)
390     while (1) {
391         my $newdir = $dir;
392         $newdir =~ s|/[^/]*[^/\.]+[^/]*/\.\./|/|g;
393         last if $newdir eq $dir;
394         $dir = $newdir;
395     }
396     # Take care of the corner cases the loop can't handle, and that $dir
397     # ends with a / unless it's empty
398     $dir =~ s|/[^/]*[^/\.]+[^/]*/\.\.$|/|;
399     $dir =~ s|^[^/]*[^/\.]+[^/]*/\.\./|/|;
400     $dir =~ s|^[^/]*[^/\.]+[^/]*/\.\.$||;
401     if ($isdir // 0) {
402         $dir =~ s|/$|| if $dir ne '/';
403     } else {
404         $dir .= '/' if $dir ne '' && $dir !~ m|/$|;
405     }
406
407     # If the file system has separate volumes (at present, Windows and VMS)
408     # we need to handle them.  In URIs, they are invariably the first
409     # component of the path, which is always absolute.
410     # On VMS, user:[foo.bar] translates to /user/foo/bar
411     # On Windows, c:\Users\Foo translates to /c:/Users/Foo
412     if ($vol ne '') {
413         $vol =~ s|:||g if ($^O eq "VMS");
414         $dir = '/' . $dir if $dir ne '' && $dir !~ m|^/|;
415         $dir = '/' . $vol . $dir;
416     }
417     $file = $dir . $file;
418
419     return "file://$authority$file" if defined $authority;
420     return "file:$file";
421 }
422
423 sub to_abs_file {
424     my ($file) = @_;
425
426     return File::Spec->rel2abs($file);
427 }
428
429 sub to_abs_file_uri {
430     my ($file, $isdir, $authority) = @_;
431
432     die "to_abs_file_uri: No file given\n" if !defined($file) || $file eq '';
433     return to_file_uri(to_abs_file($file), $isdir, $authority);
434 }