Attached is an initial diff. This provides the basic needed functionality, attempts to conform to style, compiles and passes test on 0.9.8 (not functional), 1.0.2, and 1.1.0. It is a rather large patch. I am also attacking a test file.
There is still a bit more work needed to finish it up, but I'd like to get feedback before I start the fine polish.
#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use Socket;
use File::Spec;
use Symbol qw(gensym);
use Net::SSLeay;
use Config;
BEGIN {
plan skip_all => "openssl 1.0.2 required" unless Net::SSLeay::SSLeay >= 0x10002000;
}
plan tests => 10;
my $sock;
my $pid;
my $port = 40000+int(rand(9999));
my $ip = "\x7F\0\0\x01";
my $serv_params = sockaddr_in($port, $ip);
my $msg = 'ssleay-ocsp-responder-test';
my $intermediate_cert_pem = File::Spec->catfile('t', 'data', 'ocsptest_intermediate.crt.pem');
#my $intermediate_key_pem = File::Spec->catfile('t', 'data', 'ocsptest_intermediate.key.pem');
my $intermediate_ocsp_cert_pem = File::Spec->catfile('t', 'data', 'ocsptest_intermediate_ocsp.crt.pem');
my $intermediate_ocsp_key_pem = File::Spec->catfile('t', 'data', 'ocsptest_intermediate_ocsp.key.pem');
#my $server_cert_pem = File::Spec->catfile('t', 'data', 'ocsptest_server.crt.pem');
#my $server_key_pem = File::Spec->catfile('t', 'data', 'ocsptest_server.key.pem');
#my $root_cert_pem = File::Spec->catfile('t', 'data', 'ocsptest_root.crt.pem');
#my $root_key_pem = File::Spec->catfile('t', 'data', 'ocsptest_root.key.pem');
my $ca_index = File::Spec->catfile('t', 'data', 'ocsptest_index.txt');
my @results;
Net::SSLeay::initialize();
{
my $reqfile = File::Spec->catfile('t', 'data', 'ocsptest_req_nononce.der');
ok( $reqfile );
my $content = slurp( $reqfile );
ok( $content );
my $req = Net::SSLeay::d2i_OCSP_REQUEST( $content );
ok( $req );
my $index = Net::SSLeay::load_index( $ca_index, 0 );
ok ( $index );
my $intermediate_cert = load_cert( $intermediate_cert_pem ) || die;
ok( $intermediate_cert );
# my $intermediate_key = load_key( $intermediate_key_pem ) || die;
# ok( $intermediate_key );
my $ocsp_key = load_key( $intermediate_ocsp_key_pem ) || die;
ok( $ocsp_key );
my $ocsp_cert = load_cert( $intermediate_ocsp_cert_pem ) || die;
ok( $ocsp_cert );
# my $server_cert = load_cert( $server_cert_pem ) || die;
# ok( $server_cert );
my $resp = make_ocsp_response( $req, $index, $intermediate_cert, $ocsp_cert, $ocsp_key, "sha1", [ ], 0, 30, 3, 0 );
ok( $resp );
my $der = Net::SSLeay::i2d_OCSP_RESPONSE( $resp );
ok( $der );
open( FILE, ">", "resp.der" );
print FILE $der;
close( FILE );
}
push @results, [$? == 0, 'server exited with 0'];
END {
# Test::More->builder->current_test(3);
# ok( $_->[0], $_->[1] ) for (@results);
}
sub load_cert
{
my $filename = shift;
my $bio = Net::SSLeay::BIO_new_file( $filename, 'r' ) || die_now( "BIO_new_file ($!)" );
my $der = Net::SSLeay::PEM_read_bio_X509($bio) || die_now( "BIO_read_bio_X509 ($!)" );;
Net::SSLeay::BIO_free($bio);
return $der;
}
sub load_key
{
my $filename = shift;
my $bio = Net::SSLeay::BIO_new_file( $filename, 'r' ) || die_now( "BIO_new_file ($!)" );
my $der = Net::SSLeay::PEM_read_bio_PrivateKey($bio) || die_now( "BIO_read_bio_PrivateKey ($!)" );;
Net::SSLeay::BIO_free($bio);
return $der;
}
sub slurp
{
my $filename = shift;
local $/;
open( my $fh, '<', $filename );
my $text = <$fh>;
return $text;
}
sub make_ocsp_response
{
my $req = shift;
my $db = shift;
my $ca = shift;
my $rcert = shift;
my $rkey = shift;
my $md = shift || "sha1";
my $rother = shift;
my $flags = shift;
my $nmin = shift;
my $ndays = shift;
my $badsig = shift || 0;
my $resp;
my $i;
# Load the digest hash from openssl
my $rmd = Net::SSLeay::EVP_get_digestbyname( $md ) || die_now( "EVP_get_digestbyname ($!)" );
# get the count of certs to check from the request
my $id_count = Net::SSLeay::OCSP_request_onereq_count( $req );
if ( $id_count <= 0 )
{
$resp = Net::SSLeay::OCSP_response_create( Net::SSLeay::OCSP_RESPONSE_STATUS_MALFORMEDREQUEST(), 0 );
return $resp;
}
# BASICRESP is where we will build the respone. The signed version of it will
# constitute the final response
my $bs = Net::SSLeay::OCSP_BASICRESP_new();
# create the thisUpdate and nextUpdate fields
my $nextupd;
my $thisupd = Net::SSLeay::X509_gmtime_adj( 0, 0 );
if ( $ndays != -1 )
{
$nextupd = Net::SSLeay::X509_time_adj_ex( 0, $ndays, $nmin * 60, 0 );
}
# handle each cert in the request
for my $i ( 0 .. $id_count - 1 )
{
# ONEREQ is the 'Request' structure from the ASN.1 ( RFC 6960 )
my $one = Net::SSLeay::OCSP_request_onereq_get0( $req, $i );
# the CertID contains the hashAlgorithm, issuerNameHash, issuerKeyHash, and the serialNumber
# this three-tuple uniquely identifies the certificate
my $cid = Net::SSLeay::OCSP_onereq_get0_id( $one );
# $info is a hash of the CertID data
my $info = Net::SSLeay::OCSP_id_get0_info( $cid );
my $cert_id_md = Net::SSLeay::EVP_get_digestbyobj( $info->{hashAlgorithm} );
if ( ! $cert_id_md )
{
$resp = Net::SSLeay::OCSP_response_create( OCSP_RESPONSE_STATUS_INTERNALERROR(), 0 );
return $resp;
}
# extract the CertID from the issuing cert
my $ca_id = Net::SSLeay::OCSP_cert_to_id( $cert_id_md, 0, $ca );
# if the CertID from the request and the CertID from the issuer do not match
# respond saying as much.
if ( Net::SSLeay::OCSP_id_issuer_cmp( $ca_id, $cid ) )
{
Net::SSLeay::OCSP_basic_add1_status( $bs, $cid,
Net::SSLeay::V_OCSP_CERTSTATUS_UNKNOWN(),
0, 0, $thisupd, $nextupd);
next;
}
# Lookup the serial in the CA index
my $inf = Net::SSLeay::lookup_serial( $db, $info->{serialNumber} );
if ( ! $inf )
{
# Can't find it. Respond as unkonwn
Net::SSLeay::OCSP_basic_add1_status( $bs, $cid,
Net::SSLeay::V_OCSP_CERTSTATUS_UNKNOWN(),
0, 0, $thisupd, $nextupd);
}
# TODO: replace with constants DB_type and DB_TYPE_
elsif ( $inf->[0] eq "V" )
{
# Found it and it is good
Net::SSLeay::OCSP_basic_add1_status( $bs, $cid,
Net::SSLeay::V_OCSP_CERTSTATUS_GOOD(),
0, 0, $thisupd, $nextupd);
}
# TODO: replace with constants DB_type and DB_TYPE_REV
elsif( $inf->[0] eq "R" )
{
# Found it and it is revoked
Net::SSLeay::OCSP_basic_add1_status( $bs, $cid,
Net::SSLeay::V_OCSP_CERTSTATUS_UNKNOWN(),
0, 0, $thisupd, $nextupd);
my $arr = Net::SSLeay::unpack_revinfo( $inf->[2] );
Dump( $arr, 10 );
# unpack_revinfo(&revtm, &reason, &inst, &invtm, inf[DB_rev_date]);
my $single = Net::SSLeay::OCSP_basic_add1_status( $bs, $cid,
Net::SSLeay::V_OCSP_CERTSTATUS_REVOKED(),
$arr->[1], $arr->[0], $thisupd, $nextupd);
# TODO: Not 100% certain this is all working properly
if ( $arr->[3] )
{
Net::SSLeay::OCSP_SINGLERESP_add1_ext_i2d( $single, Net::SSLeay::NID_invalidity_date(),
$arr->[3], 0, 0);
}
elsif ( $arr->[2] )
{
Net::SSLeay::OCSP_SINGLERESP_add1_ext_i2d( $single,
Net::SSLeay::NID_hold_instruction_code(), $arr->[2],
0, 0);
}
# TODO: Free $arr
}
else
{
# An unexpected status. Respond and Unknown;
Net::SSLeay::OCSP_basic_add1_status( $bs, $cid,
Net::SSLeay::V_OCSP_CERTSTATUS_UNKNOWN(),
0, 0, $thisupd, $nextupd);
print "No status match\n";
}
# TODO: Oh, there are probably a bunch lf leaks right now.
Net::SSLeay::OCSP_CERTID_free( $ca_id );
}
# If the request had a nonce, copy it into the response
Net::SSLeay::OCSP_copy_nonce( $bs, $req );
# Sign the BASICRESP
# TODO: describe $rother
Net::SSLeay::OCSP_basic_sign( $bs, $rcert, $rkey, $rmd, $rother, $flags );
# Purposely corrupt the signature if asked to. This is in the openssl
# make_ocsp_response in apps/ocsp.c
if ( $badsig )
{
my $sig = Net::SSLeay::OCSP_resp_get0_signature( $bs );
Net:SSLeay::corrupt_signature( $sig );
}
# create the successful response from the signed BASICRESP
$resp = Net::SSLeay::OCSP_response_create( Net::SSLeay::OCSP_RESPONSE_STATUS_SUCCESSFUL(), $bs );
return $resp;
}
Message body is not shown because it is too large.