NAME

Crypt::OpenSSL::CA::Test - Testing Crypt::OpenSSL::CA

SYNOPSIS

use Crypt::OpenSSL::CA::Test qw(:default %test_der_DNs);
use Test::Group;

my $utf8 = Crypt::OpenSSL::CA::Test->test_simple_utf8();

run_perl_ok(<<"SCRIPT");
use Crypt::OpenSSL::CA::Test;
warn "Hello world";
SCRIPT

skip_next_test "Devel::Leak needed" if cannot_check_SV_leaks;
test "leaky code" => sub {
   leaks_SVs_ok {
      # Do stuff
   }, -max => 6;
};

skip_next_test "Devel::Mallinfo needed" if cannot_check_bytes_leaks;
test "even leakier code" => sub {
   leaks_bytes_ok {
     # Do stuff
   }, -max => 51200;
};
use Crypt::OpenSSL::CA::Test qw(x509_decoder);

my $dn_as_tree = x509_decoder('Name')->decode($dn_der);

DESCRIPTION

This module provides some handy utility functions for testing Crypt::OpenSSL::CA. "leaks_bytes_ok" and "leaks_SVs_ok" are especially handy for testing XS or Inline::C stuff.

EXPORTED FUNCTIONS

All functions described in this section factor some useful test tactics and are exported by default. The "SAMPLE INPUTS" may also be exported upon request.

openssl_path

Returns the path to the openssl command-line tool, if it is known, or undef. Useful for skipping tests that depend on "run_thru_openssl" being able to run.

run_thru_openssl ($stdin_text, $arg1, $arg2, ...)

Runs the command openssl $arg1 $arg2 ..., feeding it $stdin_text on its standard input. In list context, returns a ($stdout_text, $stderr_text) pair. In scalar context, returns the text of the combined standard output and error streams. Throws an exception if the openssl command is unavailable (that is, "openssl_path" returns undef). Upon return $? will be set to the exit status of openssl.

dumpasn1_available ()>

Returns true iff the dumpasn1 command can be found in $ENV{PATH}.

run_dumpasn1 ($der)

Runs the dumpasn1 command (found in $ENV{PATH}) on $der and returns its output. Throws an exception if dumpasn1 fails for some reason. See also "dumpasn1_available".

run_perl ($scripttext)

Runs $scripttext in a sub-Perl interpreter, returning the text of its combined stdout and stderr as a single string. $? is set to the exit value of same.

run_perl_ok ($scripttext)

run_perl_ok ($scripttext, \$stdout)

run_perl_ok ($scripttext, \$stdout, $testname)

Like "run_perl" but simultaneously asserts (using Test::More) that the exit value is successful. The return value of the sub is the status of the assertion; the output of $scripttext (that is, the return value of the underlying call to run_perl) is transmitted to the caller by modifying in-place the scalar reference passed as the second argument, if any. Additionally the aforementioned output is passed to "diag" in Test::More if the script does exit with nonzero status.

run_perl_script ($scriptname)

run_perl_script_ok ($scriptname, \$stdout, $testname)

Like "run_perl" resp "run_perl_ok" except that the script is specified as a file name instead of Perl text.

_perl_cmdline ()

Computes (with cache) and returns the command line to invoke sub-Perls as on behalf of "run_perl" and "run_perl_script" while (more or less) preserving @INC.

Returns the name of the Perl binary and a list of -I command line switches that should be passed as part of an invocation of <perlfunc/system> or similar. The -I paths returned are exactly the elements in the current @INC that are not part of the Perl interpreter's compiled-in @INC.

errstack_empty_ok ()

Asserts that OpenSSL's error stack is empty, and clears it if not. To be run at the end of every test.

cannot_check_SV_leaks ()

Returns true iff Devel::Leak is unavailable.

cannot_check_bytes_leaks ()

Returns true iff Devel::Mallinfo is unavailable or does nothing on this platform (eg MacOS).

leaks_SVs_ok ($coderef, %named_arguments)

Executes $coderef and asserts (with Test::More) that it doesn't leak Perl SVs (checked using Devel::Leak). As a tester, you should arrange for $coderef to manipulate about 10 SVs; smaller leaks will not be detected (see -max below).

Available named arguments are:

-name => $testname

The name of the test, as in the second argument to "ok" in Test::Builder.

-max => $threshold

The minimum number of leaked SVs to look for. The default is 6. Setting this too low will trigger false positives, as Devel::Leak needs a couple of SVs of its own.

leaks_bytes_ok ($coderef)

leaks_bytes_ok ($coderef, $testname)

Executes $coderef and asserts (with Test::More) that it doesn't leak memory (checked using Devel::Mallinfo). As a tester, you should arrange for $coderef to manipulate about 100k of memory; smaller leaks will not be detected (see -max below).

Available named arguments are:

-name => $testname

The name of the test, as in the second argument to "ok" in Test::Builder.

-max => $threshold

The minimum number of leaked bytes to look for. The default is 51200. Setting this too low will trigger false positives, as Perl does some funky memory management eg in hash tables and that may cause jitter in the memory consumption as measured from malloc's point of view.

certificate_looks_ok ($pem_certificate)

certificate_looks_ok ($pem_certificate, $test_name)

Checks that a certificate passed as a PEM string looks OK to OpenSSL, meaning that the signature validates OK and OpenSSL is able to parse it.

certificate_chain_ok ($pem_certificate, \@certchain )

certificate_chain_ok ($pem_certificate, \@certchain , $test_name)

Checks that a certificate passed as a PEM string is validly signed by the certificate chain @certchain, which is a list of PEM strings passed as a reference.

certificate_chain_invalid_ok ($pem_certificate, \@certchain )

The converse of "certificate_chain_ok"; checks that $pem_certificate is not validly signed by @certchain. Note, however, that there is a case where both certificate_chain_ok and certificate_chain_invalid_ok both fail, and that is when @certchain doesn't contain any valid CA certificate.

x509_schema ()

Returns the ASN.1 schema for the whole X509 specification, as a string that Convert::ASN1 will grok.

x509_decoder ($name)

Returns the same as "find" in Convert::ASN1 would when called upon an object that would previously have "x509_schema" fed to him. The difference is that x509_decoder checks for errors and will therefore never return undef.

The returned object has a ->decode object that serves to validate the various pieces of DER produced by OpenSSL from within the tests.

SAMPLE INPUTS

Crypt::OpenSSL::CA::Test also provides a couple of constants and class methods to serve as inputs for tests. All such symbols are exportable, but not exported by default (see "SYNOPSIS") and they start with test_, so as to be clearly identified as sample data in the test code.

test_simple_utf8 ()

test_bmp_utf8 ()

Two constant functions that return test strings for testing the UTF-8 capabilities of Crypt::OpenSSL::CA. Both strings are encoded internally in UTF-8 in the sense of "is_utf8" in utf8. test_simple_utf8() contains only characters in the Latin1 range; test_bmp_utf8() contains only characters outside Latin1, but inside the Basic Multilingual Plane.

%test_der_DNs

Contains a set of DER-encoded DNs. The keys are the DNs in "RFC4514" in Crypt::OpenSSL::CA::Resources notation, and the values are strings of bytes. Available DN keys for now are CN=Zoinx,C=fr.

@test_DN_CAs

The DN used in all CA and self-signed certificates, namely "%test_self_signed_certs", "%test_rootca_certs" and friends. Set in the same order as the parameters to the new function in "Crypt::OpenSSL::CA::X509_NAME" in Crypt::OpenSSL::CA.

%test_reqs_SPKAC

Certificate signing requests (CSRs) in Netscape "SPKAC" in Crypt::OpenSSL::CA::AlphabetSoup format, as if generated by

openssl spkac -key test.key -challenge secret

but without the trailing newline, and with the leading SPKAC= removed.

%test_reqs_PKCS10

Certificate signing requests (CSRs) in standard PKCS#10 PEM format, as if generated by

openssl req -new -key test.key -batch

but without the trailing newline, and with the leading SPKAC= removed.

%test_keys_plaintext

An array of test private keys in PEM format. The values of this hash tables are strings in PEM format (that is, Base64-encoded DER with delimiters -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----, without encryption). The keys are well-known key handles that are re-used throughout the sample input hashes below:

rsa1024
rsa2048

RSA keys of size 1024 bits and 2048 bits respectively.

More RSA keys can be obtained using the command

openssl genrsa 1024

or similar (e.g. changing the key size)

%test_keys_password

The same private keys as in "%test_keys_plaintext", but protected with secret as the password. Keys are the same as in %test_keys_plaintext; values are encrypted using 3DES-CBC, as if by the command

openssl rsa -des3 -passout pass:secret -in test.key

%test_public_keys

Public keys obtained from the "%test_keys_plaintext" using the following openssl command:

openssl rsa -pubout -in test.key

%test_self_signed_certs

Self-signed certificates obtained from the "%test_keys_plaintext" as if using the following openssl command:

openssl req -x509 -new -key test.key -batch -days 10958 \
  -extensions usr_cert

where 10958 stands for a validity period of 30 years, so that these self-signed certificates seldom actually expire. Because the default configuration is used, the world-famous yet Belgian Internet Widgits Pty Ltd company is put in charge as issuer and subject of these certificates.

%test_rootca_certs

Self-signed certificates just like "%test_self_signed_certs", except that these certificates are signed using -extensions v3_ca in lieu of -extensions usr_cert, resulting in certificates that have the CA BasicConstraint set to true. Those certificates can therefore be used e.g. in the second argument to "certificate_chain_ok", unlike "%test_self_signed_certs" which, lacking a CA BasicConstraint, usually cannot be a non-leaf part of a valid certification chain as per RFC3280 section 6.1.4, item k.

%test_entity_certs

Certificates generated using openssl ca from "%test_rootca_certs", "%test_keys_plaintext" and the default OpenSSL configuration using the procedure described in "Building a toy CA:" in Crypt::OpenSSL::CA::Resources where the precise openssl commands used are

openssl req -new -batch -subj "/C=fr/O=Yoyodyne/CN=John Doe" \
  -key test.key | \
openssl ca -batch -days 10958 -in /dev/stdin

In particular this means that entries keyed off the same identifier in %test_entity_certs and %test_rootca_certs form a valid RFC3280 certification path: that is,

certificate_chain_ok($test_entity_certs{$id},
                     [ $test_rootca_certs{$id} ]);     # Works

holds for every valid $id. But conversely,

certificate_chain_ok($test_entity_certs{$id},
                     [ $test_self_signed_certs{$id} ]);     # NOT OK!

fails, due to the lack of a CA:TRUE BasicConstraint extension in %test_self_signed_certs.

Notice that in the sample inputs, CAs and end entities share the same set of private RSA keys "%test_keys_plaintext" which would not be the case in a real PKI deployment. However this is of little impact, if any, on the test coverage of Crypt::OpenSSL::CA as we never make use of the fact that all certificates for a given key length actually have the same private key.

INTERNAL METHODS

_tempdir

Returns a temporary directory e.g. for storing the ca-bundle.crt for "certificate_chain_ok".

_unique_number

As the name implies. Typically used to create unique filenames in "_tempdir".

TODO

Maybe "leaks_bytes_ok" and "leaks_SVs_ok" deserve a CPAN module of their own?

TEST SUITE

Fixture Tests

Running commands

Leak tests

Synopsis tests

Sample Input Validation