]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Add OCSP functionality.
authorSimon Josefsson <simon@josefsson.org>
Fri, 20 Jan 2012 12:41:19 +0000 (13:41 +0100)
committerSimon Josefsson <simon@josefsson.org>
Fri, 20 Jan 2012 12:41:19 +0000 (13:41 +0100)
28 files changed:
.gitignore
NEWS
configure.ac
doc/Makefile.am
doc/cha-bib.texi
doc/cha-cert-auth2.texi
doc/cha-functions.texi
doc/cha-gtls-examples.texi
doc/examples/Makefile.am
doc/examples/ex-ocsp-client.c [new file with mode: 0644]
doc/examples/ex-ocsp-verify.c [new file with mode: 0644]
doc/manpages/Makefile.am
doc/reference/gnutls-docs.sgml
lib/includes/Makefile.am
lib/includes/gnutls/ocsp.h [new file with mode: 0644]
lib/libgnutls.map
lib/pkix.asn
lib/pkix_asn1_tab.c
lib/x509/Makefile.am
lib/x509/ocsp.c [new file with mode: 0644]
lib/x509/ocsp_output.c [new file with mode: 0644]
m4/hooks.m4
src/Makefile.am
src/ocsptool-common.h [new file with mode: 0644]
src/ocsptool.c [new file with mode: 0644]
src/ocsptool.gaa [new file with mode: 0644]
tests/Makefile.am
tests/ocsp.c [new file with mode: 0644]

index f11eec16cdc17383ff48f175f524acc1b0d1cf20..094061481814c012e4367481e36664292a1f4122 100644 (file)
@@ -110,6 +110,7 @@ doc/latex/gnutls.lof
 doc/latex/gnutls.lot
 doc/manpages/Makefile
 doc/manpages/Makefile.in
+doc/ocsp-api.texi
 doc/pgp-api.texi
 doc/printlist
 doc/reference/Makefile
@@ -441,10 +442,14 @@ src/libcfg.la
 src/libcmd-certtool.la
 src/libcmd-cli-debug.la
 src/libcmd-cli.la
+src/libcmd-ocsp.la
 src/libcmd-p11tool.la
 src/libcmd-psk.la
 src/libcmd-serv.la
 src/libcmd-srp.la
+src/ocsptool
+src/ocsptool-gaa.c
+src/ocsptool-gaa.h
 src/p11tool
 src/psktool
 src/srptool
@@ -487,6 +492,7 @@ tests/moredn
 tests/mpi
 tests/netconf-psk
 tests/nul-in-x509-names
+tests/ocsp
 tests/openpgp-auth
 tests/openpgp-auth2
 tests/openpgp-certs/Makefile
@@ -541,6 +547,8 @@ tests/slow/keygen
 tests/slow/gendh
 doc/reference/*.bak
 doc/manpages/gnutls_*.3
+doc/examples/ex-ocsp-client
+doc/examples/ex-ocsp-verify
 doc/examples/ex-client-anon
 doc/examples/ex-client-dtls
 doc/examples/ex-client-x509
diff --git a/NEWS b/NEWS
index 76fa241f7a794800638d44a97d8da69548e1c7fe..fc428dcfcc31e58105d1b2fce075b95e30673d68 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,20 @@ See the end for copying conditions.
 
 * Version 3.0.12 (unreleased)
 
+** libgnutls: Added OCSP support.
+There is a new header file gnutls/ocsp.h and a set of new functions
+under the gnutls_ocsp namespace.  Currently the functionality provided
+is to parse and extract information from OCSP requests/responses, to
+generate OCSP requests and to verify OCSP responses.  See the manual
+for more information.  Run ./configure with --disable-ocsp to build
+GnuTLS without OCSP support.
+
+This work was sponsored by Smoothwall <http://smoothwall.net/>.
+
+** ocsptool: Added new command line tool.
+The tool can parse OCSP request/responses, generate OCSP requests and
+verify OCSP responses.  See the manual for more information.
+
 ** certtool: --outder option now works for private
 and public keys as well.
 
@@ -24,6 +38,42 @@ return a descriptive name of a DN OID.
 gnutls_pubkey_encrypt_data: Added
 gnutls_x509_dn_oid_name: Added
 gnutls_session_resumption_requested: Added
+gnutls/ocsp.h: Added new header file.
+gnutls_ocsp_print_formats_t: Added new type.
+gnutls_ocsp_resp_status_t: Added new type.
+gnutls_ocsp_cert_status_t: Added new type.
+gnutls_x509_crl_reason_t: Added new type.
+gnutls_ocsp_req_add_cert: Added.
+gnutls_ocsp_req_add_cert_id: Added.
+gnutls_ocsp_req_deinit: Added.
+gnutls_ocsp_req_export: Added.
+gnutls_ocsp_req_get_cert_id: Added.
+gnutls_ocsp_req_get_extension: Added.
+gnutls_ocsp_req_get_nonce: Added.
+gnutls_ocsp_req_get_version: Added.
+gnutls_ocsp_req_import: Added.
+gnutls_ocsp_req_init: Added.
+gnutls_ocsp_req_print: Added.
+gnutls_ocsp_req_randomize_nonce: Added.
+gnutls_ocsp_req_set_extension: Added.
+gnutls_ocsp_req_set_nonce: Added.
+gnutls_ocsp_resp_deinit: Added.
+gnutls_ocsp_resp_export: Added.
+gnutls_ocsp_resp_get_certs: Added.
+gnutls_ocsp_resp_get_extension: Added.
+gnutls_ocsp_resp_get_nonce: Added.
+gnutls_ocsp_resp_get_produced: Added.
+gnutls_ocsp_resp_get_responder: Added.
+gnutls_ocsp_resp_get_response: Added.
+gnutls_ocsp_resp_get_signature: Added.
+gnutls_ocsp_resp_get_signature_algorithm: Added.
+gnutls_ocsp_resp_get_single: Added.
+gnutls_ocsp_resp_get_status: Added.
+gnutls_ocsp_resp_get_version: Added.
+gnutls_ocsp_resp_import: Added.
+gnutls_ocsp_resp_init: Added.
+gnutls_ocsp_resp_print: Added.
+gnutls_ocsp_resp_verify: Added.
 
 * Version 3.0.11 (released 2012-01-06)
 
index dbfb7796fca11331e69b0026eb87dd4ad21550ff..b3d4a40707a89612c1f2ddbc15f61599072e6a11 100644 (file)
@@ -529,4 +529,5 @@ AC_MSG_NOTICE([summary of build options:
   Crypto library:   $cryptolib
   PKCS#11 support:  $with_p11_kit
   crywrap app:      $libidn
+  OCSP support:     $ac_enable_ocsp
 ])
index 4fc27c4f431ad0503ee8fe4813e1ddeb02064edb..0d05b8808fc03d0e93c50f6420dd25bd2a0e4e53 100644 (file)
@@ -88,13 +88,13 @@ MAINTAINERCLEANFILES =
 
 # Generated texinfos.
 
-gnutls_TEXINFOS += gnutls-api.texi \
-       x509-api.texi pgp-api.texi pkcs12-api.texi pkcs11-api.texi \
-       abstract-api.texi compat-api.texi dtls-api.texi crypto-api.texi
+gnutls_TEXINFOS += gnutls-api.texi x509-api.texi pgp-api.texi          \
+       pkcs12-api.texi pkcs11-api.texi abstract-api.texi               \
+       compat-api.texi dtls-api.texi crypto-api.texi ocsp-api.texi
 
-MAINTAINERCLEANFILES += gnutls-api.texi \
-       x509-api.texi pgp-api.texi pkcs12-api.texi pkcs11-api.texi \
-       abstract-api.texi compat-api.texi dtls-api.texi crypto-api.texi
+MAINTAINERCLEANFILES += gnutls-api.texi x509-api.texi pgp-api.texi     \
+       pkcs12-api.texi pkcs11-api.texi abstract-api.texi               \
+       compat-api.texi dtls-api.texi crypto-api.texi ocsp-api.texi
 
 gnutls-api.texi: $(top_srcdir)/lib/includes/gnutls/gnutls.h.in
        echo "" > $@-tmp
@@ -196,6 +196,17 @@ crypto-api.texi: $(top_srcdir)/lib/includes/gnutls/crypto.h
        done
        mv -f $@-tmp $@
 
+ocsp-api.texi: $(top_srcdir)/lib/includes/gnutls/ocsp.h
+       echo "" > $@-tmp
+       for i in `$(top_srcdir)/doc/scripts/getfuncs.pl <$(top_srcdir)/lib/includes/gnutls/ocsp.h|sort|uniq`; do \
+               echo -n "Creating documentation for file $$i... " && \
+               $(srcdir)/scripts/gdoc -texinfo \
+                       -function $$i \
+                       $(top_srcdir)/lib/*.c $(top_srcdir)/lib/*/*.c >> $@-tmp 2>/dev/null && \
+               echo "ok"; \
+       done
+       mv -f $@-tmp $@
+
 # Generated texinfos.
 # for some reason it does not work when cross compiling
 if !WINDOWS
index 40848dbdea246a48c25548eabbf832bf3853c273..c88d9f6ac5671f217035265c827df6b331ae122a 100644 (file)
@@ -181,4 +181,16 @@ John Wiley \& Sons, Inc., 2001.
 M. Mathis, J. Heffner, "Packetization Layer Path MTU Discovery", March 2007,
 available from @url{http://www.ietf.org/rfc/rfc4821.txt}.
 
+@item @anchor{RFC2560}[RFC2560]
+M. Myers et al, "X.509 Internet Public Key Infrastructure Online
+Certificate Status Protocol - OCSP", June 1999, Available from
+@url{http://www.ietf.org/rfc/rfc2560.txt}.
+
+@item @anchor{RIVESTCRL}[RIVESTCRL]
+R. L. Rivest, "Can We Eliminate Certificate Revocation Lists?",
+Proceedings of Financial Cryptography '98; Springer Lecture Notes in
+Computer Science No. 1465 (Rafael Hirschfeld, ed.), February 1998),
+pages 178--183, available from
+@url{http://people.csail.mit.edu/rivest/Rivest-CanWeEliminateCertificateRevocationLists.pdf}.
+
 @end table
index 01ac7b5936008039ebce0daaedbb42acc3c1ed42..27caf4930f5b3f29d1fb13986c9c2f0c7dd4f5f3 100644 (file)
@@ -10,8 +10,10 @@ structures, etc., are discussed in this chapter.
 @menu
 * PKCS 10 certificate requests::
 * PKIX certificate revocation lists::
+* OCSP certificate status checking::
 * Managing encrypted keys::
 * The certtool application::
+* The ocsptool application::
 * Smart cards and HSMs::
 * Abstract key types::
 @end menu
@@ -116,6 +118,177 @@ CRL number extension and the authority key identifier.
 
 @showfuncB{gnutls_x509_crl_set_number,gnutls_x509_crl_set_authority_key_id}
 
+@node OCSP certificate status checking
+@section @acronym{OCSP} certificate status checking
+@cindex certificate status
+@cindex Online Certificate Status Protocol
+@cindex OCSP
+
+Certificates may be revoked before their expiration time has been
+reached.  There are several reasons for revoking certificates, but a
+typical situation is when the private key associated with a
+certificate has been compromised.  Traditionally, Certificate
+Revocation Lists (CRLs) have been used by application to implement
+revocation checking, however several disadvantages with CRLs have been
+identified, see for example @xcite{RIVESTCRL}.
+
+The Online Certificate Status Protocol (@acronym{OCSP}) is a widely
+implemented protocol to perform certificate (revocation) status
+checking.  @xcite{RFC2560}.  An application that wish to verify the
+identity of a peer will verify the certificate against a set of
+trusted certificates and then check whether the certificate is listed
+in a CRL and/or perform an OCSP check for the certificate.
+
+Before performing the OCSP query, the application will need to figure
+out the address of the OCSP server.  The OCSP server address can be
+provided by the local user in manual configuration.  The address can
+also be provided in the certificate that is being checked.  There is
+an extension field called the Authority Information Access (AIA) which
+has an access method called @code{id-ad-ocsp} that holds the location
+of the OCSP responder.  There is a function for extracting this
+information from a certificate.
+
+@showfuncA{gnutls_x509_crt_get_authority_info_access}
+
+There are several functions in GnuTLS for creating and manipulating
+OCSP requests and responses.  The general idea is that a client
+application create an OCSP request object, store some information
+about the certificate to check in the request, and then export the
+request in DER format.  The request will then need to be sent to the
+OCSP responder, which needs to be done by the application (GnuTLS does
+not send and receive OCSP packets).  Normally an OCSP response is
+received that the application will need to import into an OCSP
+response object.  The digital signature in the OCSP response needs to
+be verified against a set of trust anchors before the information in
+the response can be trusted.
+
+The ASN.1 structure of OCSP requests are briefly as follows.  It is
+useful to review the structures to get an understanding of which
+fields are modified by GnuTLS functions.
+
+@example
+OCSPRequest     ::=     SEQUENCE @{
+    tbsRequest                  TBSRequest,
+    optionalSignature   [0]     EXPLICIT Signature OPTIONAL @}
+
+TBSRequest      ::=     SEQUENCE @{
+    version             [0]     EXPLICIT Version DEFAULT v1,
+    requestorName       [1]     EXPLICIT GeneralName OPTIONAL,
+    requestList                 SEQUENCE OF Request,
+    requestExtensions   [2]     EXPLICIT Extensions OPTIONAL @}
+
+Request         ::=     SEQUENCE @{
+    reqCert                     CertID,
+    singleRequestExtensions     [0] EXPLICIT Extensions OPTIONAL @}
+
+CertID          ::=     SEQUENCE @{
+    hashAlgorithm       AlgorithmIdentifier,
+    issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
+    issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
+    serialNumber        CertificateSerialNumber @}
+@end example
+
+The basic functions to initialize, import, export and deallocate OCSP
+requests are the following.
+
+@showfuncE{gnutls_ocsp_req_init,gnutls_ocsp_req_deinit,gnutls_ocsp_req_import,gnutls_ocsp_req_export,gnutls_ocsp_req_print}
+
+There are two interfaces for setting the identity of a certificate in
+a OCSP request, the first being a low-level function when you have the
+issuer name hash, issuer key hash, and certificate serial number in
+binary form.  The second is usually more useful if you have the
+certificate (and its issuer) in a @code{gnutls_x509_crt_t} type.
+There is also a function to extract this information from an OCSP
+request.
+
+@showfuncC{gnutls_ocsp_req_add_cert_id,gnutls_ocsp_req_add_cert,gnutls_ocsp_req_get_cert_id}
+
+Each OCSP request may contain a number of extensions.  Extensions are
+identified by an Object Identifier (OID) and an opaque data buffer
+whose syntax and semantics is implied by the OID.
+
+@showfuncB{gnutls_ocsp_req_get_extension,gnutls_ocsp_req_set_extension}
+
+A common OCSP Request extension is the nonce extension (OID
+1.3.6.1.5.5.7.48.1.2), which is used to avoid replay attacks of
+earlier recorded OCSP responses.  The nonce extension carries a value
+that is intended to be sufficiently random and unique so that an
+attacker will not be able to give a stale response for the same nonce.
+
+@showfuncC{gnutls_ocsp_req_get_nonce,gnutls_ocsp_req_set_nonce,gnutls_ocsp_req_randomize_nonce}
+
+The OCSP response structures is a bit more complex than the request.
+The important ASN.1 structure is as follows.  In practice, all OCSP
+responses contain a Basic OCSP response sub-structure.
+
+@example
+OCSPResponse ::= SEQUENCE @{
+   responseStatus         OCSPResponseStatus,
+   responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL @}
+
+OCSPResponseStatus ::= ENUMERATED @{
+    successful            (0),  --Response has valid confirmations
+    malformedRequest      (1),  --Illegal confirmation request
+    internalError         (2),  --Internal error in issuer
+    tryLater              (3),  --Try again later
+                                --(4) is not used
+    sigRequired           (5),  --Must sign the request
+    unauthorized          (6)   --Request unauthorized @}
+
+ResponseBytes ::=       SEQUENCE @{
+    responseType   OBJECT IDENTIFIER,
+    response       OCTET STRING @}
+
+id-pkix-ocsp-basic     OBJECT IDENTIFIER ::= @{ id-pkix-ocsp 1 @}
+
+BasicOCSPResponse       ::= SEQUENCE @{
+   tbsResponseData      ResponseData,
+   signatureAlgorithm   AlgorithmIdentifier,
+   signature            BIT STRING,
+   certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL @}
+
+ResponseData ::= SEQUENCE @{
+   version              [0] EXPLICIT Version DEFAULT v1,
+   responderID              ResponderID,
+   producedAt               GeneralizedTime,
+   responses                SEQUENCE OF SingleResponse,
+   responseExtensions   [1] EXPLICIT Extensions OPTIONAL @}
+
+ResponderID ::= CHOICE @{
+   byName               [1] Name,
+   byKey                [2] KeyHash @}
+
+KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
+(excluding the tag and length fields)
+
+SingleResponse ::= SEQUENCE @{
+   certID                       CertID,
+   certStatus                   CertStatus,
+   thisUpdate                   GeneralizedTime,
+   nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+   singleExtensions   [1]       EXPLICIT Extensions OPTIONAL @}
+
+CertStatus ::= CHOICE @{
+    good        [0]     IMPLICIT NULL,
+    revoked     [1]     IMPLICIT RevokedInfo,
+    unknown     [2]     IMPLICIT UnknownInfo @}
+
+RevokedInfo ::= SEQUENCE @{
+    revocationTime              GeneralizedTime,
+    revocationReason    [0]     EXPLICIT CRLReason OPTIONAL @}
+@end example
+
+We provide basic functions for initialization, importing, exporting
+and deallocating OCSP responses.  The Basic OCSP Response structure is
+automatically parsed when an OCSP Response is imported.
+
+@showfuncE{gnutls_ocsp_resp_init,gnutls_ocsp_resp_deinit,gnutls_ocsp_resp_import,gnutls_ocsp_resp_export,gnutls_ocsp_resp_print}
+
+The OCSP response needs to be verified against some set of trust
+anchors before it can be relied upon.
+
+@showfuncB{gnutls_ocsp_resp_verify,gnutls_ocsp_resp_verify_direct}
+
 @node Managing encrypted keys
 @section Managing encrypted keys
 @cindex Encrypted keys
@@ -519,6 +692,195 @@ signing_key
 @end example
 
 
+@node The ocsptool application
+@section The ocsptool application
+@cindex ocsptool
+
+This is a program that can parse and print information about
+@acronym{OCSP} requests/responses, generate requests and verify
+responses.
+
+@example
+Ocsptool help
+Usage : ocsptool [options]
+     -e, --verify-response    Verify response.
+     -i, --request-info       Print information on a OCSP request.
+     -j, --response-info      Print information on a OCSP response.
+     -q, --generate-request   Generate a OCSP request.
+     --no-nonce               don't add nonce to OCSP request.
+     --load-issuer FILE       read issuer certificate from FILE.
+     --load-cert FILE         read certificate to check from FILE.
+     --load-trust FILE        read trust anchors from FILE.
+     --inder                  Use DER format for input certificates.
+     -Q, --load-request FILE
+                              read DER encoded OCSP request from
+                              FILE.
+     -S, --load-response FILE
+                              read DER encoded OCSP response from
+                              FILE.
+     --outfile FILE           Output file.
+     --infile FILE            Input file.
+     -V, --verbose            More verbose output.
+     -d, --debug integer      Enable debugging
+     -v, --version            prints the program's version number
+     -h, --help               shows this help text
+@end example
+
+@subheading Print information about an OCSP request
+
+To parse an OCSP request and print information about the content, the
+@code{-i} or @code{--request-info} parameter may be used as follows.
+The @code{-Q} parameter specify the name of the file containing the
+OCSP request, and it should contain the OCSP request in binary DER
+format.
+
+@smallexample
+$ ocsptool -i -Q ocsp-request.der
+@end smallexample
+
+The input file may also be sent to standard input like this:
+
+@smallexample
+$ cat ocsp-request.der | ocsptool --request-info
+@end smallexample
+
+@subheading Print information about an OCSP response
+
+Similar to parsing OCSP requests, OCSP responses can be parsed using
+the @code{-j} or @code{--response-info} as follows.
+
+@smallexample
+$ ocsptool -j -Q ocsp-response.der
+$ cat ocsp-response.der | ocsptool --response-info
+@end smallexample
+
+@subheading Generate an OCSP request
+
+The @code{-q} or @code{--generate-request} parameters are used to
+generate an OCSP request.  By default the OCSP request is written to
+standard output in binary DER format, but can be stored in a file
+using @code{--outfile}.  To generate an OCSP request the issuer of the
+certificate to check needs to be specified with @code{--load-issuer}
+and the certificate to check with @code{--load-cert}.  By default PEM
+format is used for these files, although @code{--inder} can be used to
+specify that the input files are in DER format.
+
+@smallexample
+$ ocsptool -q --load-issuer issuer.pem --load-cert client.pem --outfile ocsp-request.der
+@end smallexample
+
+When generating OCSP requests, the tool will add an OCSP extension
+containing a nonce.  This behaviour can be disabled by specifying
+@code{--no-nonce}.
+
+@subheading Verify signature in OCSP response
+
+To verify the signature in an OCSP response the @code{-e} or
+@code{--verify-response} parameter is used.  The tool will read an
+OCSP response in DER format from standard input, or from the file
+specified by @code{--load-response}.  The OCSP response is verified
+against a set of trust anchors, which are specified using
+@code{--load-trust}.  The trust anchors are concatenated certificates
+in PEM format.  The certificate that signed the OCSP response needs to
+be in the set of trust anchors, or the issuer of the signer
+certificate needs to be in the set of trust anchors and the OCSP
+Extended Key Usage bit has to be asserted in the signer certificate.
+
+@smallexample
+$ ocsptool -e --load-trust issuer.pem --load-response ocsp-response.der
+@end smallexample
+
+The tool will print status of verification.
+
+@subheading Verify signature in OCSP response against given certificate
+
+It is possible to override the normal trust logic if you know that a
+certain certificate is supposed to have signed the OCSP response, and
+you want to use it to check the signature.  This is achieved using
+@code{--load-signer} instead of @code{--load-trust}.  This will load
+one certificate and it will be used to verify the signature in the
+OCSP response.  It will not check the Extended Key Usage bit.
+
+@smallexample
+$ ocsptool -e --load-signer ocsp-signer.pem --load-response ocsp-response.der
+@end smallexample
+
+This approach is normally only relevant in two situations.  The first
+is when the OCSP response does not contain a copy of the signer
+certificate, so the @code{--load-trust} code would fail.  The second
+is if you want to avoid the indirect mode where the OCSP response
+signer certificate is signed by a trust anchor.
+
+@subheading Real-world example
+
+Here is an example of how to generate an OCSP request for a
+certificate and to verify the response.  For illustration we'll use
+the @code{blog.josefsson.org} host, which (as of writing) uses a
+certificate from CACert.  First we'll use @code{gnutls-cli} to get a
+copy of the server certificate chain.  The server is not required to
+send this information, but this particular one is configured to do so.
+
+@smallexample
+$ echo | gnutls-cli -p 443 blog.josefsson.org --print-cert > chain.pem
+@end smallexample
+
+Use a text editor on @code{chain.pem} to create three files for each
+separate certificates, called @code{cert.pem} for the first
+certificate for the domain itself, secondly @code{issuer.pem} for the
+intermediate certificate and @code{root.pem} for the final root
+certificate.
+
+The domain certificate normally contains a pointer to where the OCSP
+responder is located, in the Authority Information Access Information
+extension.  For example, from @code{certtool -i < cert.pem} there is
+this information:
+
+@smallexample
+               Authority Information Access Information (not critical):
+                       Access Method: 1.3.6.1.5.5.7.48.1 (id-ad-ocsp)
+                       Access Location URI: http://ocsp.CAcert.org/
+@end smallexample
+
+This means the CA support OCSP queries over HTTP.  We are now ready to
+create a OCSP request for the certificate.
+
+@smallexample
+$ ocsptool --generate-request --load-issuer issuer.pem  --load-cert cert.pem --outfile ocsp-request.der
+@end smallexample
+
+The request is sent base64 encoded via HTTP to the address indicated
+by the id-ad-ocsp extension, as follows.
+
+@smallexample
+$ wget -O ocsp-response.der http://ocsp.CAcert.org/$(base64 -w0 ocsp-request.der)
+@end smallexample
+
+The OCSP response is now in the file @code{ocsp-response.der} and you
+can view it using @code{ocsptool -j < ocsp-response.der}.  To verify
+the signature you need to load the issuer as the trust anchor.
+
+@smallexample
+$ ocsptool --verify-response --load-trust issuer.pem --load-response ocsp-response.der
+Verifying OCSP Response: Success.
+$
+@end smallexample
+
+This particular OCSP responder includes its signer certificate in the
+OCSP respnose, so you may extract it and use it together with
+@code{--load-signer} for verifying the signature directly against the
+certificate.
+
+@smallexample
+$ ocsptool -j < ocsp-response.der > signer.pem
+$ ocsptool --verify-response --load-signer signer.pem --load-response ocsp-response.der
+Verifying OCSP Response: Success.
+$
+@end smallexample
+
+You may experiment passing different certificates to
+@code{--load-trust} and @code{--load-signer} to find common error
+conditions for OCSP response verification failures.
+
 @node Smart cards and HSMs
 @section Smart cards and HSMs
 @cindex PKCS #11 tokens
index 1835fcf97d291776c933eb8f67766fac3a9452ae..aca17e2e33bc4c558029f45eef1b43e2026aff37 100644 (file)
@@ -6,6 +6,7 @@
 * Core TLS API::
 * Datagram TLS API::
 * X509 certificate API::
+* OCSP API::
 * OpenPGP API::
 * PKCS 12 API::
 * PKCS 11 API::
@@ -39,6 +40,15 @@ Their prototypes lie in @file{gnutls/x509.h}.
 
 @include x509-api.texi
 
+@node OCSP API
+@section @acronym{OCSP} API
+@cindex OCSP Functions
+
+The following functions are for @acronym{OCSP} certificate status
+checking.  Their prototypes lie in @file{gnutls/ocsp.h}.
+
+@include ocsp-api.texi
+
 @node OpenPGP API
 @section @acronym{OpenPGP} API
 @cindex OpenPGP API
index 36ec42539b2d3ae5fae9f6bf379018b56f26b374..c08f9cd4ac0c9f740d6729f41158bdd8c5445009 100644 (file)
@@ -11,6 +11,7 @@ no error checking.
 @menu
 * Client examples::
 * Server examples::
+* OCSP examples::
 * Miscellaneous examples::
 @end menu
 
@@ -209,6 +210,28 @@ used to serve the example client for anonymous authentication.
 
 @verbatiminclude examples/ex-serv-anon.c
 
+@node OCSP examples
+@section OCSP examples
+
+@menu
+* Generate OCSP request::
+* Verify OCSP response::
+@end menu
+
+@node Generate OCSP request
+@subsection Generate @acronym{OCSP} request
+
+A small tool to generate OCSP requests.
+
+@verbatiminclude examples/ex-ocsp-client.c
+
+@node Verify OCSP response
+@subsection Verify @acronym{OCSP} response
+
+A small tool to verify OCSP responses.
+
+@verbatiminclude examples/ex-ocsp-verify.c
+
 @node DTLS echo server with X.509 authentication
 @subsection DTLS echo server with @acronym{X.509} authentication
 
index 85875568f00ed13c66d6af59df9aa79a9378a6a9..7115e4b5fd632fde12f7ba19cd1fe1b7f849ebaf 100644 (file)
@@ -76,6 +76,10 @@ if ENABLE_SRP
 noinst_PROGRAMS += ex-client-srp ex-serv-srp
 endif
 
+if ENABLE_OCSP
+noinst_PROGRAMS += ex-ocsp-client ex-ocsp-verify
+endif
+
 noinst_LTLIBRARIES = libexamples.la
 
 libexamples_la_SOURCES = examples.h ex-alert.c ex-pkcs12.c \
diff --git a/doc/examples/ex-ocsp-client.c b/doc/examples/ex-ocsp-client.c
new file mode 100644 (file)
index 0000000..79088de
--- /dev/null
@@ -0,0 +1,193 @@
+/* This example code is placed in the public domain. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/ocsp.h>
+
+#include "read-file.h"
+
+/* This program will read a file (argv[1]) containing a certificate in
+   PEM format and print the "CA issuers" and "OCSP address" extensions
+   for the certificate.  If another file is given (argv[2]) it holds
+   the issuer certificate for the first certificate.  Then the tool
+   will generate an OCSP request for the certificate and write it to
+   the file "ocsp-req.der". */
+
+int
+main (int argc, char *argv[])
+{
+  int rc;
+  gnutls_x509_crt_t cert = NULL, issuer = NULL;
+  gnutls_datum_t certdata, issuerdata, tmp;
+  size_t s;
+  unsigned int seq;
+  gnutls_ocsp_req_t ocspreq = NULL;
+  FILE *fh;
+
+  rc = gnutls_global_init ();
+  if (rc < 0)
+    goto done;
+
+  /* Read certificate and print AIA info. */
+
+  rc = gnutls_x509_crt_init (&cert);
+  if (rc < 0)
+    goto done;
+
+  certdata.data = read_binary_file (argv[1], &s);
+  if (certdata.data == NULL)
+    {
+      printf ("cannot read certificate\n");
+      goto done;
+    }
+  certdata.size = s;
+
+  rc = gnutls_x509_crt_import (cert, &certdata, GNUTLS_X509_FMT_PEM);
+  free (certdata.data);
+  if (rc < 0)
+    goto done;
+
+  rc = gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &tmp);
+  if (rc < 0)
+    goto done;
+
+  printf ("cert: %.*s\n", tmp.size, tmp.data);
+
+  gnutls_free (tmp.data); tmp.data = NULL;
+
+  for (seq = 0; ; seq++)
+    {
+      rc = gnutls_x509_crt_get_authority_info_access (cert, seq,
+                                                     GNUTLS_IA_CAISSUERS_URI,
+                                                     &tmp, NULL);
+      if (rc == GNUTLS_E_UNKNOWN_ALGORITHM)
+       continue;
+      if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       break;
+      if (rc < 0)
+       goto done;
+
+      printf ("CA issuers URI: %.*s\n", tmp.size, tmp.data);
+      gnutls_free (tmp.data);
+      break;
+    }
+
+  if (!tmp.data)
+    printf ("No CA issuers URI found\n");
+
+  for (seq = 0; ; seq++)
+    {
+      rc = gnutls_x509_crt_get_authority_info_access (cert, seq,
+                                                     GNUTLS_IA_OCSP_URI,
+                                                     &tmp, NULL);
+      if (rc == GNUTLS_E_UNKNOWN_ALGORITHM)
+       continue;
+      if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       break;
+      if (rc < 0)
+       goto done;
+
+      printf ("OCSP URI: %.*s\n", tmp.size, tmp.data);
+      gnutls_free (tmp.data);
+      break;
+    }
+
+  if (!tmp.data)
+    printf ("No OCSP URI URI found\n");
+
+  if (argc < 3)
+    {
+      printf ("Done...\n");
+      goto done;
+    }
+
+  /* Read issuer cert and print brief info about it. */
+
+  rc = gnutls_x509_crt_init (&issuer);
+  if (rc < 0)
+    goto done;
+
+  issuerdata.data = read_binary_file (argv[2], &s);
+  if (issuerdata.data == NULL)
+    {
+      printf ("cannot read issuer\n");
+      goto done;
+    }
+  issuerdata.size = s;
+
+  rc = gnutls_x509_crt_import (issuer, &issuerdata, GNUTLS_X509_FMT_PEM);
+  free (issuerdata.data);
+  if (rc < 0)
+    goto done;
+
+  rc = gnutls_x509_crt_print (issuer, GNUTLS_CRT_PRINT_ONELINE, &tmp);
+  if (rc < 0)
+    goto done;
+
+  printf ("issuer: %.*s\n", tmp.size, tmp.data);
+
+  gnutls_free (tmp.data);
+
+  /* Generate OCSP request and write it. */
+
+  rc = gnutls_ocsp_req_init (&ocspreq);
+  if (rc < 0)
+    goto done;
+
+  rc = gnutls_ocsp_req_add_cert (ocspreq, GNUTLS_DIG_SHA1, issuer, cert);
+  if (rc < 0)
+    goto done;
+
+  rc = gnutls_ocsp_req_print (ocspreq, GNUTLS_OCSP_PRINT_FULL, &tmp);
+  if (rc < 0)
+    goto done;
+
+  printf ("ocsp request: %.*s\n", tmp.size, tmp.data);
+
+  gnutls_free (tmp.data);
+
+  fh = fopen ("ocsp-req.der", "w");
+  if (fh == NULL)
+    goto done;
+
+  rc = gnutls_ocsp_req_export (ocspreq, &tmp);
+  if (rc < 0)
+    goto done;
+
+  s = fwrite (tmp.data, 1, tmp.size, fh);
+
+  gnutls_free (tmp.data);
+
+  if (s != tmp.size)
+    {
+      perror ("fwrite");
+      fclose (fh);
+      goto done;
+    }
+
+  rc = fclose (fh);
+  if (rc != 0)
+    {
+      perror ("fclose");
+      rc = 0;
+      goto done;
+    }
+
+  rc = 0;
+
+ done:
+  if (rc != 0)
+    printf ("error (%d): %s\n", rc, gnutls_strerror (rc));
+  gnutls_ocsp_req_deinit (ocspreq);
+  gnutls_x509_crt_deinit (cert);
+  gnutls_x509_crt_deinit (issuer);
+  gnutls_global_deinit ();
+
+  return rc == 0 ? 0 : 1;
+}
diff --git a/doc/examples/ex-ocsp-verify.c b/doc/examples/ex-ocsp-verify.c
new file mode 100644 (file)
index 0000000..37a2122
--- /dev/null
@@ -0,0 +1,153 @@
+/* This example code is placed in the public domain. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/ocsp.h>
+
+#include "read-file.h"
+
+/* This program will read a file (argv[1]) containing an OCSP response
+   and another file (argv[2]) containing a set of OCSP trust anchors.
+   The tool will try to verify the OCSP response against the set of
+   trust anchors.  It will also parse the OCSP response and check that
+   the X.509 certificate is still valid, or print why it is not valid
+   (according to the OCSP response).  */
+
+int
+main (int argc, char *argv[])
+{
+  int rc;
+  gnutls_datum_t tmp;
+  gnutls_ocsp_resp_t ocspresp = NULL;
+  gnutls_x509_trust_list_t trustlist = NULL;
+  gnutls_x509_crt_t *trustcerts = NULL;
+  unsigned int ntrustcerts = 0;
+  unsigned verify, i;
+  size_t s;
+
+  rc = gnutls_global_init ();
+  if (rc < 0)
+    goto done;
+
+  /* Read OCSP response and print it. */
+
+  rc = gnutls_ocsp_resp_init (&ocspresp);
+  if (rc < 0)
+    goto done;
+
+  tmp.data = read_binary_file (argv[1], &s);
+  if (tmp.data == NULL)
+    {
+      printf ("cannot read OCSP response\n");
+      goto done;
+    }
+  tmp.size = s;
+
+  rc = gnutls_ocsp_resp_import (ocspresp, &tmp);
+  free (tmp.data);
+  if (rc < 0)
+    goto done;
+
+  rc = gnutls_ocsp_resp_print (ocspresp, GNUTLS_OCSP_PRINT_FULL, &tmp);
+  if (rc < 0)
+    goto done;
+
+  printf ("ocsp response: %.*s\n", tmp.size, tmp.data);
+
+  gnutls_free (tmp.data);
+
+  if (argc < 3)
+    {
+      printf ("Done...\n");
+      goto done;
+    }
+
+  /* Read X.509 trustlist. */
+
+  rc = gnutls_x509_trust_list_init (&trustlist, 0);
+  if (rc < 0)
+    goto done;
+
+  tmp.data = read_binary_file (argv[2], &s);
+  if (tmp.data == NULL)
+    {
+      printf ("cannot read OCSP response\n");
+      goto done;
+    }
+  tmp.size = s;
+
+  rc = gnutls_x509_crt_list_import2 (&trustcerts, &ntrustcerts, &tmp,
+                                    GNUTLS_X509_FMT_PEM, 0);
+  free (tmp.data);
+  if (rc < 0)
+    goto done;
+
+  for (i = 0; i < ntrustcerts; i++)
+    {
+      gnutls_datum_t out;
+
+      rc = gnutls_x509_crt_print (trustcerts[i],
+                                 GNUTLS_CRT_PRINT_ONELINE, &out);
+      if (rc < 0)
+       goto done;
+
+      printf ("Trust anchor %d: %.*s\n", i, out.size, out.data);
+      gnutls_free (out.data);
+    }
+
+  rc = gnutls_x509_trust_list_add_cas (trustlist, trustcerts, ntrustcerts, 0);
+  gnutls_free (trustcerts);
+  if (rc < 0)
+    goto done;
+
+  printf ("Loaded %d trust anchors\n", ntrustcerts);
+
+  /* Verify it */
+
+  rc = gnutls_ocsp_resp_verify (ocspresp, trustlist, &verify, 0);
+  if (rc < 0)
+    goto done;
+
+  if (verify == 0)
+    printf ("Verification success!\n");
+  else
+    printf ("Verification error!\n");
+
+  if (verify & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND)
+    printf ("Signer cert not found\n");
+
+  if (verify & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR)
+    printf ("Signer cert keyusage error\n");
+
+  if (verify & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
+    printf ("Signer cert is not trusted\n");
+
+  if (verify & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM)
+    printf ("Insecure algorithm\n");
+
+  if (verify & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
+    printf ("Signature failure\n");
+
+  if (verify & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED)
+    printf ("Signer cert not yet activated\n");
+
+  if (verify & GNUTLS_OCSP_VERIFY_CERT_EXPIRED)
+    printf ("Signer cert expired\n");
+
+  rc = 0;
+
+ done:
+  if (rc != 0)
+    printf ("error (%d): %s\n", rc, gnutls_strerror (rc));
+  gnutls_x509_trust_list_deinit (trustlist, 1);
+  gnutls_ocsp_resp_deinit (ocspresp);
+  gnutls_global_deinit ();
+
+  return rc == 0 ? 0 : 1;
+}
index fc49dab4b74af588bb9ea0cf6a0272045df285fa..5057a076dae8e9a71a3604bef59062673a5049f6 100644 (file)
@@ -359,6 +359,38 @@ APIMANS += gnutls_certificate_set_params_function.3
 APIMANS += gnutls_anon_set_params_function.3
 APIMANS += gnutls_psk_set_params_function.3
 APIMANS += gnutls_hex2bin.3
+APIMANS += gnutls_ocsp_req_init.3
+APIMANS += gnutls_ocsp_req_deinit.3
+APIMANS += gnutls_ocsp_req_import.3
+APIMANS += gnutls_ocsp_req_export.3
+APIMANS += gnutls_ocsp_req_print.3
+APIMANS += gnutls_ocsp_req_get_version.3
+APIMANS += gnutls_ocsp_req_get_cert_id.3
+APIMANS += gnutls_ocsp_req_add_cert_id.3
+APIMANS += gnutls_ocsp_req_add_cert.3
+APIMANS += gnutls_ocsp_req_get_extension.3
+APIMANS += gnutls_ocsp_req_set_extension.3
+APIMANS += gnutls_ocsp_req_get_nonce.3
+APIMANS += gnutls_ocsp_req_set_nonce.3
+APIMANS += gnutls_ocsp_req_randomize_nonce.3
+APIMANS += gnutls_ocsp_resp_init.3
+APIMANS += gnutls_ocsp_resp_deinit.3
+APIMANS += gnutls_ocsp_resp_import.3
+APIMANS += gnutls_ocsp_resp_export.3
+APIMANS += gnutls_ocsp_resp_print.3
+APIMANS += gnutls_ocsp_resp_get_status.3
+APIMANS += gnutls_ocsp_resp_get_response.3
+APIMANS += gnutls_ocsp_resp_get_version.3
+APIMANS += gnutls_ocsp_resp_get_responder.3
+APIMANS += gnutls_ocsp_resp_get_produced.3
+APIMANS += gnutls_ocsp_resp_get_single.3
+APIMANS += gnutls_ocsp_resp_get_extension.3
+APIMANS += gnutls_ocsp_resp_get_nonce.3
+APIMANS += gnutls_ocsp_resp_get_signature_algorithm.3
+APIMANS += gnutls_ocsp_resp_get_signature.3
+APIMANS += gnutls_ocsp_resp_get_certs.3
+APIMANS += gnutls_ocsp_resp_verify_direct.3
+APIMANS += gnutls_ocsp_resp_verify.3
 APIMANS += gnutls_openpgp_crt_init.3
 APIMANS += gnutls_openpgp_crt_deinit.3
 APIMANS += gnutls_openpgp_crt_import.3
@@ -746,6 +778,20 @@ doit:
                echo -n "."; \
        done
        @echo ""
+       @echo -n "Creating man pages for ocsp.h..." && \
+       for i in `$(top_srcdir)/doc/scripts/getfuncs.pl <$(top_srcdir)/lib/includes/gnutls/ocsp.h`; do \
+               $(top_srcdir)/doc/scripts/gdoc -man \
+                       -module $(PACKAGE) -sourceversion $(VERSION) \
+                       -bugsto $(PACKAGE_BUGREPORT) \
+                       -pkg-name "$(PACKAGE_NAME)" \
+                       -include "gnutls/ocsp.h" \
+                       -seeinfo $(PACKAGE) -verbatimcopying \
+                       -copyright "2012 Free Software Foundation" \
+                       -function $$i \
+                       $(top_srcdir)/lib/*.c $(top_srcdir)/lib/*/*.c > $$i.3 2>/dev/null && \
+               echo -n "."; \
+       done
+       @echo ""
        @echo -n "Creating man pages for abstract.h..." && \
        for i in `$(top_srcdir)/doc/scripts/getfuncs.pl <$(top_srcdir)/lib/includes/gnutls/abstract.h`; do \
                $(top_srcdir)/doc/scripts/gdoc -man \
index 63df17cb16478f4723a36575d2bb3d56836c1856..88f7fbc42f9914669b329ee01bae20fbeaeeef0e 100644 (file)
@@ -37,6 +37,7 @@
     <xi:include href="xml/crypto.xml"/>
     <xi:include href="xml/pkcs11.xml"/>
     <xi:include href="xml/pkcs12.xml"/>
+    <xi:include href="xml/ocsp.xml"/>
 
   </chapter>
   <index id="api-index-full">
index 5da8bda6dd1fc29e25ecdb96369623a2b5f11d05..31650c1c3e1cb50b31a8d32adb82b850c3e7e2e8 100644 (file)
@@ -20,7 +20,7 @@
 
 nobase_include_HEADERS = gnutls/x509.h gnutls/pkcs12.h gnutls/compat.h \
        gnutls/openpgp.h gnutls/crypto.h gnutls/pkcs11.h                \
-       gnutls/abstract.h gnutls/dtls.h
+       gnutls/abstract.h gnutls/dtls.h gnutls/ocsp.h
 
 if ENABLE_CXX
 nobase_include_HEADERS += gnutls/gnutlsxx.h
diff --git a/lib/includes/gnutls/ocsp.h b/lib/includes/gnutls/ocsp.h
new file mode 100644 (file)
index 0000000..f748d75
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* Online Certificate Status Protocol - RFC 2560
+ */
+
+#ifndef GNUTLS_OCSP_H
+#define GNUTLS_OCSP_H
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define GNUTLS_OCSP_NONCE "1.3.6.1.5.5.7.48.1.2"
+
+  /**
+   * gnutls_ocsp_print_formats_t:
+   * @GNUTLS_OCSP_PRINT_FULL: Full information about OCSP request/response.
+   *
+   * Enumeration of different OCSP printing variants.
+   */
+  typedef enum gnutls_ocsp_print_formats_t
+    {
+      GNUTLS_OCSP_PRINT_FULL = 0,
+    } gnutls_ocsp_print_formats_t;
+
+  /**
+   * gnutls_ocsp_resp_status_t:
+   * @GNUTLS_OCSP_RESP_SUCCESSFUL: Response has valid confirmations.
+   * @GNUTLS_OCSP_RESP_MALFORMEDREQUEST: Illegal confirmation request
+   * @GNUTLS_OCSP_RESP_INTERNALERROR: Internal error in issuer
+   * @GNUTLS_OCSP_RESP_TRYLATER: Try again later
+   * @GNUTLS_OCSP_RESP_SIGREQUIRED: Must sign the request
+   * @GNUTLS_OCSP_RESP_UNAUTHORIZED: Request unauthorized
+   *
+   * Enumeration of different OCSP response status codes.
+   */
+  typedef enum gnutls_ocsp_resp_status_t
+    {
+      GNUTLS_OCSP_RESP_SUCCESSFUL = 0,
+      GNUTLS_OCSP_RESP_MALFORMEDREQUEST = 1,
+      GNUTLS_OCSP_RESP_INTERNALERROR = 2,
+      GNUTLS_OCSP_RESP_TRYLATER = 3,
+      GNUTLS_OCSP_RESP_SIGREQUIRED = 5,
+      GNUTLS_OCSP_RESP_UNAUTHORIZED = 6
+    } gnutls_ocsp_resp_status_t;
+
+  /**
+   * gnutls_ocsp_cert_status_t:
+   * @GNUTLS_OCSP_CERT_GOOD: Positive response to status inquiry.
+   * @GNUTLS_OCSP_CERT_REVOKED: Certificate has been revoked.
+   * @GNUTLS_OCSP_CERT_UNKNOWN: The responder doesn't know about the
+   *   certificate.
+   *
+   * Enumeration of different OCSP response certificate status codes.
+   */
+  typedef enum gnutls_ocsp_cert_status_t
+    {
+      GNUTLS_OCSP_CERT_GOOD = 0,
+      GNUTLS_OCSP_CERT_REVOKED = 1,
+      GNUTLS_OCSP_CERT_UNKNOWN = 2
+    } gnutls_ocsp_cert_status_t;
+
+  /**
+   * gnutls_x509_crl_reason_t:
+   * @GNUTLS_X509_CRLREASON_UNSPECIFIED: Unspecified reason.
+   * @GNUTLS_X509_CRLREASON_KEYCOMPROMISE: Private key compromised.
+   * @GNUTLS_X509_CRLREASON_CACOMPROMISE: CA compromised.
+   * @GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: Affiliation has changed.
+   * @GNUTLS_X509_CRLREASON_SUPERSEDED: Certificate superseded.
+   * @GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: Operation has ceased.
+   * @GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: Certificate is on hold.
+   * @GNUTLS_X509_CRLREASON_REMOVEFROMCRL: Will be removed from delta CRL.
+   * @GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: Privilege withdrawn.
+   * @GNUTLS_X509_CRLREASON_AACOMPROMISE: AA compromised.
+   *
+   * Enumeration of different reason codes.  Note that this
+   * corresponds to the CRLReason ASN.1 enumeration type, and not the
+   * ReasonFlags ASN.1 bit string.
+   */
+  typedef enum gnutls_x509_crl_reason_t
+    {
+      GNUTLS_X509_CRLREASON_UNSPECIFIED = 0,
+      GNUTLS_X509_CRLREASON_KEYCOMPROMISE = 1,
+      GNUTLS_X509_CRLREASON_CACOMPROMISE = 2,
+      GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED = 3,
+      GNUTLS_X509_CRLREASON_SUPERSEDED = 4,
+      GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION = 5,
+      GNUTLS_X509_CRLREASON_CERTIFICATEHOLD = 6,
+      /* -- value 7 is not used */
+      GNUTLS_X509_CRLREASON_REMOVEFROMCRL = 8,
+      GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN = 9,
+      GNUTLS_X509_CRLREASON_AACOMPROMISE = 10
+    } gnutls_x509_crl_reason_t;
+
+  /**
+   * gnutls_ocsp_verify_reason_t:
+   * @GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND: Signer cert not found.
+   * @GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR: Signer keyusage bits incorrect.
+   * @GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER: Signer is not trusted.
+   * @GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM: Signature using insecure algorithm.
+   * @GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE: Signature mismatch.
+   * @GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED: Signer cert is not yet activated.
+   * @GNUTLS_OCSP_VERIFY_CERT_EXPIRED: Signer cert has expired.
+   *
+   * Enumeration of OCSP verify status codes, used by
+   * gnutls_ocsp_resp_verify() and gnutls_ocsp_resp_verify_direct().
+   */
+  typedef enum gnutls_ocsp_verify_reason_t
+    {
+      GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND = 1,
+      GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR = 2,
+      GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER = 4,
+      GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM = 8,
+      GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE = 16,
+      GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED = 32,
+      GNUTLS_OCSP_VERIFY_CERT_EXPIRED = 64
+    } gnutls_ocsp_verify_reason_t;
+
+  struct gnutls_ocsp_req_int;
+  typedef struct gnutls_ocsp_req_int *gnutls_ocsp_req_t;
+
+  int gnutls_ocsp_req_init (gnutls_ocsp_req_t * req);
+  void gnutls_ocsp_req_deinit (gnutls_ocsp_req_t req);
+
+  int gnutls_ocsp_req_import (gnutls_ocsp_req_t req,
+                             const gnutls_datum_t * data);
+  int gnutls_ocsp_req_export (gnutls_ocsp_req_t req, gnutls_datum_t * data);
+  int gnutls_ocsp_req_print (gnutls_ocsp_req_t req,
+                            gnutls_ocsp_print_formats_t format,
+                            gnutls_datum_t * out);
+
+  int gnutls_ocsp_req_get_version (gnutls_ocsp_req_t req);
+
+  int gnutls_ocsp_req_get_cert_id (gnutls_ocsp_req_t req,
+                                  unsigned indx,
+                                  gnutls_digest_algorithm_t *digest,
+                                  gnutls_datum_t *issuer_name_hash,
+                                  gnutls_datum_t *issuer_key_hash,
+                                  gnutls_datum_t *serial_number);
+  int gnutls_ocsp_req_add_cert_id (gnutls_ocsp_req_t req,
+                                  gnutls_digest_algorithm_t digest,
+                                  const gnutls_datum_t *issuer_name_hash,
+                                  const gnutls_datum_t *issuer_key_hash,
+                                  const gnutls_datum_t *serial_number);
+  int gnutls_ocsp_req_add_cert (gnutls_ocsp_req_t req,
+                               gnutls_digest_algorithm_t digest,
+                               gnutls_x509_crt_t issuer,
+                               gnutls_x509_crt_t cert);
+
+  int gnutls_ocsp_req_get_extension (gnutls_ocsp_req_t req,
+                                    unsigned indx,
+                                    gnutls_datum_t *oid,
+                                    unsigned int *critical,
+                                    gnutls_datum_t *data);
+  int gnutls_ocsp_req_set_extension (gnutls_ocsp_req_t req,
+                                    const char *oid,
+                                    unsigned int critical,
+                                    const gnutls_datum_t *data);
+
+  int gnutls_ocsp_req_get_nonce (gnutls_ocsp_req_t req,
+                                unsigned int *critical,
+                                gnutls_datum_t *nonce);
+  int gnutls_ocsp_req_set_nonce (gnutls_ocsp_req_t req,
+                                unsigned int critical,
+                                const gnutls_datum_t *nonce);
+  int gnutls_ocsp_req_randomize_nonce (gnutls_ocsp_req_t req);
+
+  struct gnutls_ocsp_resp_int;
+  typedef struct gnutls_ocsp_resp_int *gnutls_ocsp_resp_t;
+
+  int gnutls_ocsp_resp_init (gnutls_ocsp_resp_t * resp);
+  void gnutls_ocsp_resp_deinit (gnutls_ocsp_resp_t resp);
+
+  int gnutls_ocsp_resp_import (gnutls_ocsp_resp_t resp,
+                              const gnutls_datum_t * data);
+  int gnutls_ocsp_resp_export (gnutls_ocsp_resp_t resp,
+                              gnutls_datum_t * data);
+  int gnutls_ocsp_resp_print (gnutls_ocsp_resp_t resp,
+                             gnutls_ocsp_print_formats_t format,
+                             gnutls_datum_t * out);
+
+  int gnutls_ocsp_resp_get_status (gnutls_ocsp_resp_t resp);
+  int gnutls_ocsp_resp_get_response (gnutls_ocsp_resp_t resp,
+                                    gnutls_datum_t *response_type_oid,
+                                    gnutls_datum_t *response);
+
+  int gnutls_ocsp_resp_get_version (gnutls_ocsp_resp_t resp);
+  int gnutls_ocsp_resp_get_responder (gnutls_ocsp_resp_t resp,
+                                     gnutls_datum_t *dn);
+  time_t gnutls_ocsp_resp_get_produced (gnutls_ocsp_resp_t resp);
+  int gnutls_ocsp_resp_get_single (gnutls_ocsp_resp_t resp,
+                                  unsigned indx,
+                                  gnutls_digest_algorithm_t *digest,
+                                  gnutls_datum_t *issuer_name_hash,
+                                  gnutls_datum_t *issuer_key_hash,
+                                  gnutls_datum_t *serial_number,
+                                  int *cert_status,
+                                  time_t *this_update,
+                                  time_t *next_update,
+                                  time_t *revocation_time,
+                                  int *revocation_reason);
+  int gnutls_ocsp_resp_get_extension (gnutls_ocsp_resp_t resp,
+                                     unsigned indx,
+                                     gnutls_datum_t *oid,
+                                     unsigned int *critical,
+                                     gnutls_datum_t *data);
+  int gnutls_ocsp_resp_get_nonce (gnutls_ocsp_resp_t resp,
+                                 unsigned int *critical,
+                                 gnutls_datum_t *nonce);
+  int gnutls_ocsp_resp_get_signature_algorithm (gnutls_ocsp_resp_t resp);
+  int gnutls_ocsp_resp_get_signature (gnutls_ocsp_resp_t resp,
+                                     gnutls_datum_t *sig);
+  int gnutls_ocsp_resp_get_certs (gnutls_ocsp_resp_t resp,
+                                 gnutls_x509_crt_t ** certs,
+                                 size_t *ncerts);
+
+  int gnutls_ocsp_resp_verify_direct (gnutls_ocsp_resp_t resp,
+                                     gnutls_x509_crt_t signercert,
+                                     unsigned *verify,
+                                     int flags);
+  int gnutls_ocsp_resp_verify (gnutls_ocsp_resp_t resp,
+                              gnutls_x509_trust_list_t trustlist,
+                              unsigned *verify,
+                              int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GNUTLS_OCSP_H */
index fe9d258a94a27ba5f7bd988a8b89717d9a10b630..0c68c71212c0406b57efcab91718c7c5d97fd892 100644 (file)
@@ -718,6 +718,38 @@ GNUTLS_3_0_0 {
        gnutls_pubkey_import_ecc_raw2;
        gnutls_record_get_discarded;
        gnutls_x509_crt_get_authority_info_access;
+       gnutls_ocsp_req_add_cert;
+       gnutls_ocsp_req_add_cert_id;
+       gnutls_ocsp_req_deinit;
+       gnutls_ocsp_req_export;
+       gnutls_ocsp_req_get_cert_id;
+       gnutls_ocsp_req_get_extension;
+       gnutls_ocsp_req_get_nonce;
+       gnutls_ocsp_req_get_version;
+       gnutls_ocsp_req_import;
+       gnutls_ocsp_req_init;
+       gnutls_ocsp_req_print;
+       gnutls_ocsp_req_randomize_nonce;
+       gnutls_ocsp_req_set_extension;
+       gnutls_ocsp_req_set_nonce;
+       gnutls_ocsp_resp_deinit;
+       gnutls_ocsp_resp_export;
+       gnutls_ocsp_resp_get_certs;
+       gnutls_ocsp_resp_get_extension;
+       gnutls_ocsp_resp_get_nonce;
+       gnutls_ocsp_resp_get_produced;
+       gnutls_ocsp_resp_get_responder;
+       gnutls_ocsp_resp_get_response;
+       gnutls_ocsp_resp_get_signature;
+       gnutls_ocsp_resp_get_signature_algorithm;
+       gnutls_ocsp_resp_get_single;
+       gnutls_ocsp_resp_get_status;
+       gnutls_ocsp_resp_get_version;
+       gnutls_ocsp_resp_import;
+       gnutls_ocsp_resp_init;
+       gnutls_ocsp_resp_print;
+       gnutls_ocsp_resp_verify;
+       gnutls_ocsp_resp_verify_direct;
        gnutls_privkey_import_ext;
        gnutls_certificate_set_key;
        gnutls_srp_3072_group_generator;
index ef1937c92597f456deecd49a5197c85af1ffd427..e0121c6d3268c47dcd676a190157fa8dd71f6cbc 100644 (file)
@@ -572,4 +572,113 @@ ProxyPolicy ::= SEQUENCE {
   policyLanguage   OBJECT IDENTIFIER,
   policy           OCTET STRING OPTIONAL }
 
+-- rfc2560
+
+OCSPRequest     ::=     SEQUENCE {
+    tbsRequest                  TBSRequest,
+    optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
+
+TBSRequest      ::=     SEQUENCE {
+    version             [0] EXPLICIT Version DEFAULT v1,
+    requestorName       [1] EXPLICIT GeneralName OPTIONAL,
+    requestList             SEQUENCE OF Request,
+    requestExtensions   [2] EXPLICIT Extensions OPTIONAL }
+
+Signature       ::=     SEQUENCE {
+    signatureAlgorithm   AlgorithmIdentifier,
+    signature            BIT STRING,
+    certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+
+-- comment out, already used above, fortunately with same values and names
+-- Version  ::=  INTEGER  {  v1(0) }
+
+Request ::=     SEQUENCE {
+    reqCert                    CertID,
+    singleRequestExtensions    [0] EXPLICIT Extensions OPTIONAL }
+
+CertID ::= SEQUENCE {
+    hashAlgorithm            AlgorithmIdentifier,
+    issuerNameHash     OCTET STRING, -- Hash of Issuer's DN
+    issuerKeyHash      OCTET STRING, -- Hash of Issuers public key
+    serialNumber       CertificateSerialNumber }
+
+OCSPResponse ::= SEQUENCE {
+   responseStatus         OCSPResponseStatus,
+   responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+
+OCSPResponseStatus ::= ENUMERATED {
+    successful            (0),      --Response has valid confirmations
+    malformedRequest      (1),      --Illegal confirmation request
+    internalError         (2),      --Internal error in issuer
+    tryLater              (3),      --Try again later
+                                    --(4) is not used
+    sigRequired           (5),      --Must sign the request
+    unauthorized          (6)       --Request unauthorized
+}
+
+ResponseBytes ::=       SEQUENCE {
+    responseType   OBJECT IDENTIFIER,
+    response       OCTET STRING }
+
+BasicOCSPResponse       ::= SEQUENCE {
+   tbsResponseData      ResponseData,
+   signatureAlgorithm   AlgorithmIdentifier,
+   signature            BIT STRING,
+   certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+
+ResponseData ::= SEQUENCE {
+   version              [0] EXPLICIT Version DEFAULT v1,
+   responderID              ResponderID,
+   producedAt               GeneralizedTime,
+   responses                SEQUENCE OF SingleResponse,
+   responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+
+ResponderID ::= CHOICE {
+-- Changed to work with the libtasn1 parser.
+   byName   [1] EXPLICIT RDNSequence, --Name,
+   byKey    [2] KeyHash }
+
+KeyHash ::= OCTET STRING --SHA-1 hash of responder's public key
+                         --(excluding the tag and length fields)
+
+SingleResponse ::= SEQUENCE {
+   certID                       CertID,
+   certStatus                   CertStatus,
+   thisUpdate                   GeneralizedTime,
+   nextUpdate           [0]     EXPLICIT GeneralizedTime OPTIONAL,
+   singleExtensions     [1]     EXPLICIT Extensions OPTIONAL }
+
+CertStatus ::= CHOICE {
+    good                [0]     IMPLICIT NULL,
+    revoked             [1]     IMPLICIT RevokedInfo,
+    unknown             [2]     IMPLICIT UnknownInfo }
+
+RevokedInfo ::= SEQUENCE {
+    revocationTime              GeneralizedTime,
+    revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+
+UnknownInfo ::= NULL -- this can be replaced with an enumeration
+
+ArchiveCutoff ::= GeneralizedTime
+
+AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER
+
+ServiceLocator ::= SEQUENCE {
+    issuer    Name,
+    locator   AuthorityInfoAccessSyntax }
+
+-- rfc5280
+
+CRLReason ::= ENUMERATED {
+     unspecified             (0),
+     keyCompromise           (1),
+     cACompromise            (2),
+     affiliationChanged      (3),
+     superseded              (4),
+     cessationOfOperation    (5),
+     certificateHold         (6),
+     removeFromCRL           (8),
+     privilegeWithdrawn      (9),
+     aACompromise           (10) }
+
 END
index c42b155b4ad6c9d71a8c464b41478c23bf3496f2..159bba5fcd159ab87bd2c25a12a6207fdb35615c 100644 (file)
@@ -407,8 +407,111 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "pCPathLenConstraint", 1611153411, NULL },
   { "0", 10, "MAX"},
   { "proxyPolicy", 2, "ProxyPolicy"},
-  { "ProxyPolicy", 536870917, NULL },
+  { "ProxyPolicy", 1610612741, NULL },
   { "policyLanguage", 1073741836, NULL },
   { "policy", 16391, NULL },
+  { "OCSPRequest", 1610612741, NULL },
+  { "tbsRequest", 1073741826, "TBSRequest"},
+  { "optionalSignature", 536895490, "Signature"},
+  { NULL, 2056, "0"},
+  { "TBSRequest", 1610612741, NULL },
+  { "version", 1610653698, "Version"},
+  { NULL, 1073741833, "v1"},
+  { NULL, 2056, "0"},
+  { "requestorName", 1610637314, "GeneralName"},
+  { NULL, 2056, "1"},
+  { "requestList", 1610612747, NULL },
+  { NULL, 2, "Request"},
+  { "requestExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "2"},
+  { "Signature", 1610612741, NULL },
+  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signature", 1073741830, NULL },
+  { "certs", 536895499, NULL },
+  { NULL, 1073743880, "0"},
+  { NULL, 2, "Certificate"},
+  { "Request", 1610612741, NULL },
+  { "reqCert", 1073741826, "CertID"},
+  { "singleRequestExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "0"},
+  { "CertID", 1610612741, NULL },
+  { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "issuerNameHash", 1073741831, NULL },
+  { "issuerKeyHash", 1073741831, NULL },
+  { "serialNumber", 2, "CertificateSerialNumber"},
+  { "OCSPResponse", 1610612741, NULL },
+  { "responseStatus", 1073741826, "OCSPResponseStatus"},
+  { "responseBytes", 536895490, "ResponseBytes"},
+  { NULL, 2056, "0"},
+  { "OCSPResponseStatus", 1610874901, NULL },
+  { "successful", 1073741825, "0"},
+  { "malformedRequest", 1073741825, "1"},
+  { "internalError", 1073741825, "2"},
+  { "tryLater", 1073741825, "3"},
+  { "sigRequired", 1073741825, "5"},
+  { "unauthorized", 1, "6"},
+  { "ResponseBytes", 1610612741, NULL },
+  { "responseType", 1073741836, NULL },
+  { "response", 7, NULL },
+  { "BasicOCSPResponse", 1610612741, NULL },
+  { "tbsResponseData", 1073741826, "ResponseData"},
+  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signature", 1073741830, NULL },
+  { "certs", 536895499, NULL },
+  { NULL, 1073743880, "0"},
+  { NULL, 2, "Certificate"},
+  { "ResponseData", 1610612741, NULL },
+  { "version", 1610653698, "Version"},
+  { NULL, 1073741833, "v1"},
+  { NULL, 2056, "0"},
+  { "responderID", 1073741826, "ResponderID"},
+  { "producedAt", 1082130449, NULL },
+  { "responses", 1610612747, NULL },
+  { NULL, 2, "SingleResponse"},
+  { "responseExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "1"},
+  { "ResponderID", 1610612754, NULL },
+  { "byName", 1610620930, "RDNSequence"},
+  { NULL, 2056, "1"},
+  { "byKey", 536879106, "KeyHash"},
+  { NULL, 4104, "2"},
+  { "KeyHash", 1073741831, NULL },
+  { "SingleResponse", 1610612741, NULL },
+  { "certID", 1073741826, "CertID"},
+  { "certStatus", 1073741826, "CertStatus"},
+  { "thisUpdate", 1082130449, NULL },
+  { "nextUpdate", 1619025937, NULL },
+  { NULL, 2056, "0"},
+  { "singleExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "1"},
+  { "CertStatus", 1610612754, NULL },
+  { "good", 1610620948, NULL },
+  { NULL, 4104, "0"},
+  { "revoked", 1610620930, "RevokedInfo"},
+  { NULL, 4104, "1"},
+  { "unknown", 536879106, "UnknownInfo"},
+  { NULL, 4104, "2"},
+  { "RevokedInfo", 1610612741, NULL },
+  { "revocationTime", 1082130449, NULL },
+  { "revocationReason", 536895490, "CRLReason"},
+  { NULL, 2056, "0"},
+  { "UnknownInfo", 1073741844, NULL },
+  { "ArchiveCutoff", 1082130449, NULL },
+  { "AcceptableResponses", 1610612747, NULL },
+  { NULL, 12, NULL },
+  { "ServiceLocator", 1610612741, NULL },
+  { "issuer", 1073741826, "Name"},
+  { "locator", 2, "AuthorityInfoAccessSyntax"},
+  { "CRLReason", 537133077, NULL },
+  { "unspecified", 1073741825, "0"},
+  { "keyCompromise", 1073741825, "1"},
+  { "cACompromise", 1073741825, "2"},
+  { "affiliationChanged", 1073741825, "3"},
+  { "superseded", 1073741825, "4"},
+  { "cessationOfOperation", 1073741825, "5"},
+  { "certificateHold", 1073741825, "6"},
+  { "removeFromCRL", 1073741825, "8"},
+  { "privilegeWithdrawn", 1073741825, "9"},
+  { "aACompromise", 1, "10"},
   { NULL, 0, NULL }
 };
index 9386a4e630afb74e05555ce3407cbccec8b39ce1..d16dfc7246e6d988814a35ace71f61867829c890 100644 (file)
@@ -57,3 +57,7 @@ libgnutls_x509_la_SOURCES =   \
        x509_write.c            \
        verify-high.c           \
        verify-high.h
+
+if ENABLE_OCSP
+libgnutls_x509_la_SOURCES += ocsp.c ocsp_output.c
+endif
diff --git a/lib/x509/ocsp.c b/lib/x509/ocsp.c
new file mode 100644 (file)
index 0000000..ae23168
--- /dev/null
@@ -0,0 +1,2160 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ * Author: Simon Josefsson
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* Online Certificate Status Protocol - RFC 2560
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_global.h>
+#include <gnutls_errors.h>
+#include <libtasn1.h>
+#include <gnutls_pk.h>
+#include "common.h"
+#include "hash.h"
+#include "verify-high.h"
+
+#include <gnutls/ocsp.h>
+
+typedef struct gnutls_ocsp_req_int
+{
+  ASN1_TYPE req;
+} gnutls_ocsp_req_int;
+
+typedef struct gnutls_ocsp_resp_int
+{
+  ASN1_TYPE resp;
+  gnutls_datum_t response_type_oid;
+  ASN1_TYPE basicresp;
+} gnutls_ocsp_resp_int;
+
+#define MAX_TIME 64
+
+/**
+ * gnutls_ocsp_req_init:
+ * @req: The structure to be initialized
+ *
+ * This function will initialize an OCSP request structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_req_init (gnutls_ocsp_req_t * req)
+{
+  gnutls_ocsp_req_t tmp = gnutls_calloc (1, sizeof (gnutls_ocsp_req_int));
+  int ret;
+
+  if (!tmp)
+    return GNUTLS_E_MEMORY_ERROR;
+
+  ret = asn1_create_element (_gnutls_get_pkix (), "PKIX1.OCSPRequest",
+                            &tmp->req);
+  if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      gnutls_free (tmp);
+      return _gnutls_asn2err (ret);
+    }
+
+  *req = tmp;
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_deinit:
+ * @req: The structure to be deinitialized
+ *
+ * This function will deinitialize a OCSP request structure.
+ **/
+void
+gnutls_ocsp_req_deinit (gnutls_ocsp_req_t req)
+{
+  if (!req)
+    return;
+
+  if (req->req)
+    asn1_delete_structure (&req->req);
+
+  req->req = NULL;
+
+  gnutls_free (req);
+}
+
+/**
+ * gnutls_ocsp_resp_init:
+ * @resp: The structure to be initialized
+ *
+ * This function will initialize an OCSP response structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_resp_init (gnutls_ocsp_resp_t * resp)
+{
+  gnutls_ocsp_resp_t tmp = gnutls_calloc (1, sizeof (gnutls_ocsp_resp_int));
+  int ret;
+
+  if (!tmp)
+    return GNUTLS_E_MEMORY_ERROR;
+
+  ret = asn1_create_element (_gnutls_get_pkix (),
+                               "PKIX1.OCSPResponse", &tmp->resp);
+  if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      gnutls_free (tmp);
+      return _gnutls_asn2err (ret);
+    }
+
+  ret = asn1_create_element (_gnutls_get_pkix (),
+                            "PKIX1.BasicOCSPResponse", &tmp->basicresp);
+  if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      asn1_delete_structure (&tmp->resp);
+      gnutls_free (tmp);
+      return _gnutls_asn2err (ret);
+    }
+
+  *resp = tmp;
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_deinit:
+ * @resp: The structure to be deinitialized
+ *
+ * This function will deinitialize a OCSP response structure.
+ **/
+void
+gnutls_ocsp_resp_deinit (gnutls_ocsp_resp_t resp)
+{
+  if (!resp)
+    return;
+
+  if (resp->resp)
+    asn1_delete_structure (&resp->resp);
+  gnutls_free (resp->response_type_oid.data);
+  if (resp->basicresp)
+    asn1_delete_structure (&resp->basicresp);
+
+  resp->resp = NULL;
+  resp->response_type_oid.data = NULL;
+  resp->basicresp = NULL;
+
+  gnutls_free (resp);
+}
+
+/**
+ * gnutls_ocsp_req_import:
+ * @req: The structure to store the parsed request.
+ * @data: DER encoded OCSP request.
+ *
+ * This function will convert the given DER encoded OCSP request to
+ * the native #gnutls_ocsp_req_t format. The output will be stored in
+ * @req.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_req_import (gnutls_ocsp_req_t req,
+                        const gnutls_datum_t * data)
+{
+  int ret = 0;
+
+  if (req == NULL || data == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  if (req->req)
+    {
+      /* Any earlier asn1_der_decoding will modify the ASN.1
+         structure, so we need to replace it with a fresh
+         structure. */
+      asn1_delete_structure (&req->req);
+
+      ret = asn1_create_element (_gnutls_get_pkix (),
+                                    "PKIX1.OCSPRequest", &req->req);
+      if (ret != ASN1_SUCCESS)
+        {
+          gnutls_assert ();
+          return _gnutls_asn2err (ret);
+        }
+    }
+
+  ret = asn1_der_decoding (&req->req, data->data, data->size, NULL);
+  if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_import:
+ * @resp: The structure to store the parsed response.
+ * @data: DER encoded OCSP response.
+ *
+ * This function will convert the given DER encoded OCSP response to
+ * the native #gnutls_ocsp_resp_t format.  It also decodes the Basic
+ * OCSP Response part, if any.  The output will be stored in @resp.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_resp_import (gnutls_ocsp_resp_t resp,
+                        const gnutls_datum_t * data)
+{
+  int ret = 0;
+
+  if (resp == NULL || data == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  if (resp->resp)
+    {
+      /* Any earlier asn1_der_decoding will modify the ASN.1
+         structure, so we need to replace it with a fresh
+         structure. */
+      asn1_delete_structure (&resp->resp);
+
+      ret = asn1_create_element (_gnutls_get_pkix (),
+                                "PKIX1.OCSPResponse", &resp->resp);
+      if (ret != ASN1_SUCCESS)
+        {
+          gnutls_assert ();
+          return _gnutls_asn2err (ret);
+        }
+    }
+
+  ret = asn1_der_decoding (&resp->resp, data->data, data->size, NULL);
+  if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+
+  if (gnutls_ocsp_resp_get_status (resp) != GNUTLS_OCSP_RESP_SUCCESSFUL)
+    return GNUTLS_E_SUCCESS;
+
+  ret = _gnutls_x509_read_value (resp->resp, "responseBytes.responseType",
+                                &resp->response_type_oid, 0);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1"
+
+  if (resp->response_type_oid.size == sizeof (OCSP_BASIC)
+      && memcmp (resp->response_type_oid.data, OCSP_BASIC,
+                resp->response_type_oid.size) == 0)
+    {
+      gnutls_datum_t d;
+
+      if (resp->basicresp)
+       {
+         asn1_delete_structure (&resp->basicresp);
+
+         ret = asn1_create_element (_gnutls_get_pkix (),
+                                    "PKIX1.BasicOCSPResponse", &resp->basicresp);
+         if (ret != ASN1_SUCCESS)
+           {
+             gnutls_assert ();
+             return _gnutls_asn2err (ret);
+           }
+       }
+
+      ret = _gnutls_x509_read_value (resp->resp, "responseBytes.response",
+                                    &d, 0);
+      if (ret < 0)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+
+      ret = asn1_der_decoding (&resp->basicresp, d.data, d.size, NULL);
+      gnutls_free (d.data);
+      if (ret != ASN1_SUCCESS)
+       {
+         gnutls_assert ();
+         return _gnutls_asn2err (ret);
+       }
+    }
+  else
+    resp->basicresp = NULL;
+
+  return GNUTLS_E_SUCCESS;
+}
+
+static int
+export (ASN1_TYPE node, const char *name, gnutls_datum_t * data)
+{
+  int ret;
+  int len = 0;
+
+  ret = asn1_der_coding (node, name, NULL, &len, NULL);
+  if (ret != ASN1_MEM_ERROR)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+  data->size = len;
+  data->data = gnutls_malloc (len);
+  if (data->data == NULL)
+    return GNUTLS_E_MEMORY_ERROR;
+  ret = asn1_der_coding (node, name, data->data, &len, NULL);
+  if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_export:
+ * @req: Holds the OCSP request
+ * @data: newly allocate buffer holding DER encoded OCSP request
+ *
+ * This function will export the OCSP request to DER format.
+ *
+ * Returns: In case of failure a negative error code will be
+ *   returned, and 0 on success.
+ **/
+int
+gnutls_ocsp_req_export (gnutls_ocsp_req_t req, gnutls_datum_t * data)
+{
+  int ret;
+
+  if (req == NULL || data == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  /* XXX remove when we support these fields */
+  asn1_write_value (req->req, "tbsRequest.requestorName", NULL, 0);
+  asn1_write_value (req->req, "optionalSignature", NULL, 0);
+
+  /* prune extension field if we don't have any extension */
+  ret = gnutls_ocsp_req_get_extension (req, 0, NULL, NULL, NULL);
+  if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+    asn1_write_value (req->req, "tbsRequest.requestExtensions", NULL, 0);
+
+  return export (req->req, "", data);
+}
+
+/**
+ * gnutls_ocsp_resp_export:
+ * @resp: Holds the OCSP response
+ * @data: newly allocate buffer holding DER encoded OCSP response
+ *
+ * This function will export the OCSP response to DER format.
+ *
+ * Returns: In case of failure a negative error code will be
+ *   returned, and 0 on success.
+ **/
+int
+gnutls_ocsp_resp_export (gnutls_ocsp_resp_t resp, gnutls_datum_t * data)
+{
+  if (resp == NULL || data == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  return export (resp->resp, "", data);
+}
+
+/**
+ * gnutls_ocsp_req_get_version:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ *
+ * This function will return the version of the OCSP request.
+ * Typically this is always 1 indicating version 1.
+ *
+ * Returns: version of OCSP request, or a negative error code on error.
+ **/
+int
+gnutls_ocsp_req_get_version (gnutls_ocsp_req_t req)
+{
+  opaque version[8];
+  int len, ret;
+
+  if (req == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  len = sizeof (version);
+  ret = asn1_read_value (req->req, "tbsRequest.version", version, &len);
+  if (ret != ASN1_SUCCESS)
+    {
+      if (ret == ASN1_ELEMENT_NOT_FOUND)
+        return 1;               /* the DEFAULT version */
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+
+  return (int) version[0] + 1;
+}
+
+/**
+ * gnutls_ocsp_req_get_cert_id:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @digest: output variable with #gnutls_digest_algorithm_t hash algorithm
+ * @issuer_name_hash: output buffer with hash of issuer's DN
+ * @issuer_key_hash: output buffer with hash of issuer's public key
+ * @serial_number: output buffer with serial number of certificate to check
+ *
+ * This function will return the certificate information of the
+ * @indx'ed request in the OCSP request.  The information returned
+ * corresponds to the CertID structure:
+ *
+ * <informalexample><programlisting>
+ *    CertID          ::=     SEQUENCE {
+ *        hashAlgorithm       AlgorithmIdentifier,
+ *        issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
+ *        issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
+ *        serialNumber        CertificateSerialNumber }
+ * </programlisting></informalexample>
+ *
+ * Each of the pointers to output variables may be NULL to indicate
+ * that the caller is not interested in that value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.  If you have reached the last
+ *   CertID available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ *   returned.
+ **/
+int
+gnutls_ocsp_req_get_cert_id (gnutls_ocsp_req_t req,
+                            unsigned indx,
+                            gnutls_digest_algorithm_t *digest,
+                            gnutls_datum_t *issuer_name_hash,
+                            gnutls_datum_t *issuer_key_hash,
+                            gnutls_datum_t *serial_number)
+{
+  gnutls_datum_t sa;
+  char name[ASN1_MAX_NAME_SIZE];
+  int ret;
+
+  if (req == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  snprintf (name, sizeof (name),
+           "tbsRequest.requestList.?%u.reqCert.hashAlgorithm.algorithm",
+           indx + 1);
+  ret = _gnutls_x509_read_value (req->req, name, &sa, 0);
+  if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+    return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+  else if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = _gnutls_x509_oid2digest_algorithm (sa.data);
+  _gnutls_free_datum (&sa);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  if (digest)
+    *digest = ret;
+
+  if (issuer_name_hash)
+    {
+      snprintf (name, sizeof (name),
+               "tbsRequest.requestList.?%u.reqCert.issuerNameHash", indx + 1);
+      ret = _gnutls_x509_read_value (req->req, name, issuer_name_hash, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+    }
+
+  if (issuer_key_hash)
+    {
+      snprintf (name, sizeof (name),
+               "tbsRequest.requestList.?%u.reqCert.issuerKeyHash", indx + 1);
+      ret = _gnutls_x509_read_value (req->req, name, issuer_key_hash, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         if (issuer_name_hash)
+           gnutls_free (issuer_name_hash->data);
+         return ret;
+       }
+    }
+
+  if (serial_number)
+    {
+      snprintf (name, sizeof (name),
+               "tbsRequest.requestList.?%u.reqCert.serialNumber", indx + 1);
+      ret = _gnutls_x509_read_value (req->req, name, serial_number, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         if (issuer_name_hash)
+           gnutls_free (issuer_name_hash->data);
+         if (issuer_key_hash)
+           gnutls_free (issuer_key_hash->data);
+         return ret;
+       }
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_add_cert_id:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ * @digest: hash algorithm, a #gnutls_digest_algorithm_t value
+ * @issuer_name_hash: hash of issuer's DN
+ * @issuer_key_hash: hash of issuer's public key
+ * @serial_number: serial number of certificate to check
+ *
+ * This function will add another request to the OCSP request for a
+ * particular certificate having the issuer name hash of
+ * @issuer_name_hash and issuer key hash of @issuer_key_hash (both
+ * hashed using @digest) and serial number @serial_number.
+ *
+ * The information needed corresponds to the CertID structure:
+ *
+ * <informalexample><programlisting>
+ *    CertID          ::=     SEQUENCE {
+ *        hashAlgorithm       AlgorithmIdentifier,
+ *        issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
+ *        issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
+ *        serialNumber        CertificateSerialNumber }
+ * </programlisting></informalexample>
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_add_cert_id (gnutls_ocsp_req_t req,
+                            gnutls_digest_algorithm_t digest,
+                            const gnutls_datum_t *issuer_name_hash,
+                            const gnutls_datum_t *issuer_key_hash,
+                            const gnutls_datum_t *serial_number)
+{
+  int result;
+  const char *oid;
+
+  if (req == NULL || issuer_name_hash == NULL
+      || issuer_key_hash == NULL || serial_number == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  oid = _gnutls_x509_digest_to_oid (digest);
+  if (oid == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  result = asn1_write_value (req->req, "tbsRequest.requestList", "NEW", 1);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (result);
+    }
+
+  result = asn1_write_value
+    (req->req, "tbsRequest.requestList.?LAST.reqCert.hashAlgorithm.algorithm",
+     oid, 1);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (result);
+    }
+
+  /* XXX we don't support any algorithm with parameters */
+  result = asn1_write_value
+    (req->req, "tbsRequest.requestList.?LAST.reqCert.hashAlgorithm.parameters",
+     ASN1_NULL, ASN1_NULL_SIZE);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (result);
+    }
+
+  result = asn1_write_value
+    (req->req, "tbsRequest.requestList.?LAST.reqCert.issuerNameHash",
+     issuer_name_hash->data, issuer_name_hash->size);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (result);
+    }
+
+  result = asn1_write_value
+    (req->req, "tbsRequest.requestList.?LAST.reqCert.issuerKeyHash",
+     issuer_key_hash->data, issuer_key_hash->size);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (result);
+    }
+
+  result = asn1_write_value
+    (req->req, "tbsRequest.requestList.?LAST.reqCert.serialNumber",
+     serial_number->data, serial_number->size);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (result);
+    }
+
+  /* XXX add separate function that can add extensions too */
+  result = asn1_write_value
+    (req->req, "tbsRequest.requestList.?LAST.singleRequestExtensions",
+     NULL, 0);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (result);
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_add_cert:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ * @digest: hash algorithm, a #gnutls_digest_algorithm_t value
+ * @issuer: issuer of @subject certificate
+ * @cert: certificate to request status for
+ *
+ * This function will add another request to the OCSP request for a
+ * particular certificate.  The issuer name hash, issuer key hash, and
+ * serial number fields is populated as follows.  The issuer name and
+ * the serial number is taken from @cert.  The issuer key is taken
+ * from @issuer.  The hashed values will be hashed using the @digest
+ * algorithm, normally %GNUTLS_DIG_SHA1.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_add_cert (gnutls_ocsp_req_t req,
+                         gnutls_digest_algorithm_t digest,
+                         gnutls_x509_crt_t issuer,
+                         gnutls_x509_crt_t cert)
+{
+  int ret;
+  gnutls_datum_t sn, tmp, inh, ikh;
+  char inh_buf[MAX_HASH_SIZE];
+  char ikh_buf[MAX_HASH_SIZE];
+  size_t inhlen = MAX_HASH_SIZE;
+  size_t ikhlen = MAX_HASH_SIZE;
+
+  if (req == NULL || issuer == NULL || cert == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  ret = _gnutls_x509_der_encode (cert->cert,
+                                "tbsCertificate.issuer.rdnSequence",
+                                &tmp, 0);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = gnutls_fingerprint (digest, &tmp, inh_buf, &inhlen);
+  gnutls_free (tmp.data);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+  inh.size = inhlen;
+  inh.data = inh_buf;
+
+  ret = _gnutls_x509_read_value
+    (issuer->cert, "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey",
+     &tmp, 2);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = gnutls_fingerprint (digest, &tmp, ikh_buf, &ikhlen);
+  gnutls_free (tmp.data);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+  ikh.size = ikhlen;
+  ikh.data = ikh_buf;
+
+  ret = _gnutls_x509_read_value (cert->cert, "tbsCertificate.serialNumber",
+                                &sn, 0);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = gnutls_ocsp_req_add_cert_id (req, digest, &inh, &ikh, &sn);
+  gnutls_free (sn.data);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_get_extension:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @oid: will hold newly allocated buffer with OID of extension, may be NULL
+ * @critical: output variable with critical flag, may be NULL.
+ * @data: will hold newly allocated buffer with extension data, may be NULL
+ *
+ * This function will return all information about the requested
+ * extension in the OCSP request.  The information returned is the
+ * OID, the critical flag, and the data itself.  The extension OID
+ * will be stored as a string.  Any of @oid, @critical, and @data may
+ * be NULL which means that the caller is not interested in getting
+ * that information back.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @oid->data and @data->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.  If you have reached the last
+ *   extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
+ *   be returned.
+ **/
+int
+gnutls_ocsp_req_get_extension (gnutls_ocsp_req_t req,
+                              unsigned indx,
+                              gnutls_datum_t *oid,
+                              unsigned int *critical,
+                              gnutls_datum_t *data)
+{
+  int ret;
+  char str_critical[10];
+  char name[ASN1_MAX_NAME_SIZE];
+  int len;
+
+  if (!req)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  snprintf (name, sizeof (name), "tbsRequest.requestExtensions.?%u.critical",
+           indx + 1);
+  len = sizeof (str_critical);
+  ret = asn1_read_value (req->req, name, str_critical, &len);
+  if (ret == ASN1_ELEMENT_NOT_FOUND)
+    return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+  else if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+
+  if (critical)
+    {
+      if (str_critical[0] == 'T')
+       *critical = 1;
+      else
+       *critical = 0;
+    }
+
+  if (oid)
+    {
+      snprintf (name, sizeof (name),
+               "tbsRequest.requestExtensions.?%u.extnID", indx + 1);
+      ret = _gnutls_x509_read_value (req->req, name, oid, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+    }
+
+  if (data)
+    {
+      snprintf (name, sizeof (name),
+               "tbsRequest.requestExtensions.?%u.extnValue", indx + 1);
+      ret = _gnutls_x509_read_value (req->req, name, data, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         if (oid)
+           gnutls_free (oid->data);
+         return ret;
+       }
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_set_extension:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ * @oid: buffer with OID of extension as a string.
+ * @critical: critical flag, normally false.
+ * @data: the extension data
+ *
+ * This function will add an extension to the OCSP request.  Calling
+ * this function multiple times for the same OID will overwrite values
+ * from earlier calls.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_set_extension (gnutls_ocsp_req_t req,
+                              const char *oid,
+                              unsigned int critical,
+                              const gnutls_datum_t *data)
+{
+  if (req == NULL || oid == NULL || data == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  return set_extension (req->req, "tbsRequest.requestExtensions", oid,
+                       data, critical);
+}
+
+/**
+ * gnutls_ocsp_req_get_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ * @critical: whether nonce extension is marked critical, or NULL
+ * @nonce: will hold newly allocated buffer with nonce data
+ *
+ * This function will return the OCSP request nonce extension data.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @nonce->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_get_nonce (gnutls_ocsp_req_t req,
+                          unsigned int *critical,
+                          gnutls_datum_t *nonce)
+{
+  int ret;
+  size_t l = 0;
+  gnutls_datum_t tmp;
+
+  if (req == NULL || nonce == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  ret = get_extension (req->req, "tbsRequest.requestExtensions",
+                      GNUTLS_OCSP_NONCE, 0,
+                      &tmp, critical);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = _gnutls_x509_decode_octet_string (NULL, tmp.data, (size_t) tmp.size,
+                                         NULL, &l);
+  if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      gnutls_assert ();
+      gnutls_free (tmp.data);
+      return ret;
+    }
+
+  nonce->data = gnutls_malloc (l);
+  if (nonce->data == NULL)
+    {
+      gnutls_assert ();
+      gnutls_free (tmp.data);
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  ret = _gnutls_x509_decode_octet_string (NULL, tmp.data, (size_t) tmp.size,
+                                         nonce->data, &l);
+  gnutls_free (tmp.data);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+  nonce->size = l;
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_set_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ * @critical: critical flag, normally false.
+ * @nonce: the nonce data
+ *
+ * This function will add an nonce extension to the OCSP request.
+ * Calling this function multiple times will overwrite values from
+ * earlier calls.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_set_nonce (gnutls_ocsp_req_t req,
+                          unsigned int critical,
+                          const gnutls_datum_t *nonce)
+{
+  int ret;
+  gnutls_datum_t dernonce;
+  unsigned char temp[SIZEOF_UNSIGNED_LONG_INT + 1];
+  int len;
+
+  if (req == NULL || nonce == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  asn1_length_der (nonce->size, temp, &len);
+
+  dernonce.size = 1 + len + nonce->size;
+  dernonce.data = gnutls_malloc (dernonce.size);
+  if (dernonce.data == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  dernonce.data[0] = '\x04';
+  memcpy (dernonce.data + 1, temp, len);
+  memcpy (dernonce.data + 1 + len, nonce->data, nonce->size);
+
+  ret =  set_extension (req->req, "tbsRequest.requestExtensions",
+                       GNUTLS_OCSP_NONCE, &dernonce, critical);
+  gnutls_free (dernonce.data);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  return ret;
+}
+
+/**
+ * gnutls_ocsp_req_randomize_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t structure
+ *
+ * This function will add or update an nonce extension to the OCSP
+ * request with a newly generated random value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_randomize_nonce (gnutls_ocsp_req_t req)
+{
+  int ret;
+  char rndbuf[23];
+  gnutls_datum_t nonce = { rndbuf, sizeof (rndbuf) };
+
+  if (req == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  ret = gnutls_rnd (GNUTLS_RND_NONCE, rndbuf, sizeof (rndbuf));
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = gnutls_ocsp_req_set_nonce (req, 0, &nonce);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_status:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ *
+ * This function will return the status of a OCSP response, an
+ * #gnutls_ocsp_resp_status_t enumeration.
+ *
+ * Returns: status of OCSP request as a #gnutls_ocsp_resp_status_t, or
+ *   a negative error code on error.
+ **/
+int
+gnutls_ocsp_resp_get_status (gnutls_ocsp_resp_t resp)
+{
+  opaque str[1];
+  int len, ret;
+
+  if (resp == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  len = sizeof (str);
+  ret = asn1_read_value (resp->resp, "responseStatus", str, &len);
+  if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+
+  switch (str[0])
+    {
+    case GNUTLS_OCSP_RESP_SUCCESSFUL:
+    case GNUTLS_OCSP_RESP_MALFORMEDREQUEST:
+    case GNUTLS_OCSP_RESP_INTERNALERROR:
+    case GNUTLS_OCSP_RESP_TRYLATER:
+    case GNUTLS_OCSP_RESP_SIGREQUIRED:
+    case GNUTLS_OCSP_RESP_UNAUTHORIZED:
+      break;
+    default:
+      return GNUTLS_E_UNEXPECTED_PACKET;
+    }
+
+  return (int) str[0];
+}
+
+/**
+ * gnutls_ocsp_resp_get_response:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @response_type_oid: newly allocated output buffer with response type OID
+ * @response: newly allocated output buffer with DER encoded response
+ *
+ * This function will extract the response type OID in and the
+ * response data from an OCSP response.  Normally the
+ * @response_type_oid is always "1.3.6.1.5.5.7.48.1.1" which means the
+ * @response should be decoded as a Basic OCSP Response, but
+ * technically other response types could be used.
+ *
+ * This function is typically only useful when you want to extract the
+ * response type OID of an response for diagnostic purposes.
+ * Otherwise gnutls_ocsp_resp_import() will decode the basic OCSP
+ * response part and the caller need not worry about that aspect.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_response (gnutls_ocsp_resp_t resp,
+                              gnutls_datum_t *response_type_oid,
+                              gnutls_datum_t *response)
+{
+  int ret;
+
+  if (resp == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  if (response_type_oid != NULL)
+    {
+      ret = _gnutls_x509_read_value (resp->resp, "responseBytes.responseType",
+                                    response_type_oid, 0);
+      if (ret < 0)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+    }
+
+  if (response != NULL)
+    {
+      ret = _gnutls_x509_read_value (resp->resp, "responseBytes.response",
+                                    response, 0);
+      if (ret < 0)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_version:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ *
+ * This function will return the version of the Basic OCSP Response.
+ * Typically this is always 1 indicating version 1.
+ *
+ * Returns: version of Basic OCSP response, or a negative error code
+ *   on error.
+ **/
+int
+gnutls_ocsp_resp_get_version (gnutls_ocsp_resp_t resp)
+{
+  opaque version[8];
+  int len, ret;
+
+  if (resp == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  len = sizeof (version);
+  ret = asn1_read_value (resp->resp, "tbsResponseData.version", version, &len);
+  if (ret != ASN1_SUCCESS)
+    {
+      if (ret == ASN1_ELEMENT_NOT_FOUND)
+        return 1;               /* the DEFAULT version */
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+
+  return (int) version[0] + 1;
+}
+
+/**
+ * gnutls_ocsp_resp_get_responder:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @dn: newly allocated buffer with name
+ *
+ * This function will extract the name of the Basic OCSP Response in
+ * the provided buffer. The name will be in the form
+ * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string
+ * will be ASCII or UTF-8 encoded, depending on the certificate data.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @dn->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.
+ **/
+int
+gnutls_ocsp_resp_get_responder (gnutls_ocsp_resp_t resp,
+                               gnutls_datum_t *dn)
+{
+  int ret;
+  size_t l = 0;
+
+  if (resp == NULL || dn == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  ret =  _gnutls_x509_parse_dn
+    (resp->basicresp, "tbsResponseData.responderID.byName",
+     NULL, &l);
+  if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  dn->data = gnutls_malloc (l);
+  if (dn->data == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  ret =  _gnutls_x509_parse_dn
+    (resp->basicresp, "tbsResponseData.responderID.byName",
+     dn->data, &l);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  dn->size = l;
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_produced:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ *
+ * This function will return the time when the OCSP response was
+ * signed.
+ *
+ * Returns: signing time, or (time_t)-1 on error.
+ **/
+time_t
+gnutls_ocsp_resp_get_produced (gnutls_ocsp_resp_t resp)
+{
+  char ttime[MAX_TIME];
+  int len, ret;
+  time_t c_time;
+
+  if (resp == NULL || resp->basicresp == NULL)
+    {
+      gnutls_assert ();
+      return (time_t) (-1);
+    }
+
+  len = sizeof (ttime) - 1;
+  ret = asn1_read_value (resp->basicresp, "tbsResponseData.producedAt",
+                        ttime, &len);
+  if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return (time_t) (-1);
+    }
+
+  c_time = _gnutls_x509_generalTime2gtime (ttime);
+
+  return c_time;
+}
+
+/**
+ * gnutls_ocsp_resp_get_single:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @digest: output variable with #gnutls_digest_algorithm_t hash algorithm
+ * @issuer_name_hash: output buffer with hash of issuer's DN
+ * @issuer_key_hash: output buffer with hash of issuer's public key
+ * @serial_number: output buffer with serial number of certificate to check
+ * @cert_status: a certificate status, a #gnutls_ocsp_cert_status_t enum.
+ * @this_update: time at which the status is known to be correct.
+ * @next_update: when newer information will be available, or (time_t)-1 if unspecified
+ * @revocation_time: when @cert_status is %GNUTLS_OCSP_CERT_REVOKED, holds time of revocation.
+ * @revocation_reason: revocation reason, a #gnutls_x509_crl_reason_t enum.
+ *
+ * This function will return the certificate information of the
+ * @indx'ed response in the Basic OCSP Response @resp.  The
+ * information returned corresponds to the SingleResponse structure
+ * except the final singleExtensions, reproduced here for illustration:
+ *
+ * <informalexample><programlisting>
+ * SingleResponse ::= SEQUENCE {
+ *    certID                       CertID,
+ *    certStatus                   CertStatus,
+ *    thisUpdate                   GeneralizedTime,
+ *    nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+ *    singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
+ *
+ *    CertID          ::=     SEQUENCE {
+ *        hashAlgorithm       AlgorithmIdentifier,
+ *        issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
+ *        issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
+ *        serialNumber        CertificateSerialNumber }
+ *
+ * CertStatus ::= CHOICE {
+ *     good                [0]     IMPLICIT NULL,
+ *     revoked             [1]     IMPLICIT RevokedInfo,
+ *     unknown             [2]     IMPLICIT UnknownInfo }
+ * 
+ * RevokedInfo ::= SEQUENCE {
+ *     revocationTime              GeneralizedTime,
+ *     revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+ * </programlisting></informalexample>
+ *
+ * Each of the pointers to output variables may be NULL to indicate
+ * that the caller is not interested in that value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.  If you have reached the last
+ *   CertID available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ *   returned.
+ **/
+int
+gnutls_ocsp_resp_get_single (gnutls_ocsp_resp_t resp,
+                            unsigned indx,
+                            gnutls_digest_algorithm_t *digest,
+                            gnutls_datum_t *issuer_name_hash,
+                            gnutls_datum_t *issuer_key_hash,
+                            gnutls_datum_t *serial_number,
+                            int *cert_status,
+                            time_t *this_update,
+                            time_t *next_update,
+                            time_t *revocation_time,
+                            int *revocation_reason)
+{
+  gnutls_datum_t sa;
+  char name[ASN1_MAX_NAME_SIZE];
+  int ret;
+
+  snprintf (name, sizeof (name),
+           "tbsResponseData.responses.?%u.certID.hashAlgorithm.algorithm",
+           indx + 1);
+  ret = _gnutls_x509_read_value (resp->basicresp, name, &sa, 0);
+  if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+    return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+  else if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = _gnutls_x509_oid2digest_algorithm (sa.data);
+  _gnutls_free_datum (&sa);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  if (digest)
+    *digest = ret;
+
+  if (issuer_name_hash)
+    {
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responses.?%u.certID.issuerNameHash",
+               indx + 1);
+      ret = _gnutls_x509_read_value (resp->basicresp, name,
+                                    issuer_name_hash, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+    }
+
+  if (issuer_key_hash)
+    {
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responses.?%u.certID.issuerKeyHash",
+               indx + 1);
+      ret = _gnutls_x509_read_value (resp->basicresp, name,
+                                    issuer_key_hash, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         if (issuer_name_hash)
+           gnutls_free (issuer_name_hash->data);
+         return ret;
+       }
+    }
+
+  if (serial_number)
+    {
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responses.?%u.certID.serialNumber",
+               indx + 1);
+      ret = _gnutls_x509_read_value (resp->basicresp, name,
+                                    serial_number, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         if (issuer_name_hash)
+           gnutls_free (issuer_name_hash->data);
+         if (issuer_key_hash)
+           gnutls_free (issuer_key_hash->data);
+         return ret;
+       }
+    }
+
+  if (cert_status)
+    {
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responses.?%u.certStatus",
+               indx + 1);
+      ret = _gnutls_x509_read_value (resp->basicresp, name, &sa, 0);
+      if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+       return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+      else if (ret < 0)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+      if (sa.size == 5 && memcmp (sa.data, "good", sa.size) == 0)
+       *cert_status = GNUTLS_OCSP_CERT_GOOD;
+      else if (sa.size == 8 && memcmp (sa.data, "revoked", sa.size) == 0)
+       *cert_status = GNUTLS_OCSP_CERT_REVOKED;
+      else if (sa.size == 8 && memcmp (sa.data, "unknown", sa.size) == 0)
+       *cert_status = GNUTLS_OCSP_CERT_UNKNOWN;
+      else
+       {
+         gnutls_assert ();
+         gnutls_free (sa.data);
+         return GNUTLS_E_ASN1_DER_ERROR;
+       }
+      gnutls_free (sa.data);
+    }
+
+  if (this_update)
+    {
+      char ttime[MAX_TIME];
+      int len;
+
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responses.?%u.thisUpdate",
+               indx + 1);
+      len = sizeof (ttime) - 1;
+      ret = asn1_read_value (resp->basicresp, name, ttime, &len);
+      if (ret != ASN1_SUCCESS)
+       {
+         gnutls_assert ();
+         *this_update = (time_t) (-1);
+       }
+      else
+       *this_update = _gnutls_x509_generalTime2gtime (ttime);
+    }
+
+  if (next_update)
+    {
+      char ttime[MAX_TIME];
+      int len;
+
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responses.?%u.nextUpdate",
+               indx + 1);
+      len = sizeof (ttime) - 1;
+      ret = asn1_read_value (resp->basicresp, name, ttime, &len);
+      if (ret != ASN1_SUCCESS)
+       {
+         gnutls_assert ();
+         *next_update = (time_t) (-1);
+       }
+      else
+       *next_update = _gnutls_x509_generalTime2gtime (ttime);
+    }
+
+  if (revocation_time)
+    {
+      char ttime[MAX_TIME];
+      int len;
+
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responses.?%u.certStatus."
+               "revoked.revocationTime",
+               indx + 1);
+      len = sizeof (ttime) - 1;
+      ret = asn1_read_value (resp->basicresp, name, ttime, &len);
+      if (ret != ASN1_SUCCESS)
+       {
+         gnutls_assert ();
+         *revocation_time = (time_t) (-1);
+       }
+      else
+       *revocation_time = _gnutls_x509_generalTime2gtime (ttime);
+    }
+
+  /* revocation_reason */
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_extension:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @oid: will hold newly allocated buffer with OID of extension, may be NULL
+ * @critical: output variable with critical flag, may be NULL.
+ * @data: will hold newly allocated buffer with extension data, may be NULL
+ *
+ * This function will return all information about the requested
+ * extension in the OCSP response.  The information returned is the
+ * OID, the critical flag, and the data itself.  The extension OID
+ * will be stored as a string.  Any of @oid, @critical, and @data may
+ * be NULL which means that the caller is not interested in getting
+ * that information back.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @oid->data and @data->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.  If you have reached the last
+ *   extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
+ *   be returned.
+ **/
+int
+gnutls_ocsp_resp_get_extension (gnutls_ocsp_resp_t resp,
+                               unsigned indx,
+                               gnutls_datum_t *oid,
+                               unsigned int *critical,
+                               gnutls_datum_t *data)
+{
+  int ret;
+  char str_critical[10];
+  char name[ASN1_MAX_NAME_SIZE];
+  int len;
+
+  if (!resp)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  snprintf (name, sizeof (name),
+           "tbsResponseData.responseExtensions.?%u.critical",
+           indx + 1);
+  len = sizeof (str_critical);
+  ret = asn1_read_value (resp->basicresp, name, str_critical, &len);
+  if (ret == ASN1_ELEMENT_NOT_FOUND)
+    return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+  else if (ret != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
+    }
+
+  if (critical)
+    {
+      if (str_critical[0] == 'T')
+       *critical = 1;
+      else
+       *critical = 0;
+    }
+
+  if (oid)
+    {
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responseExtensions.?%u.extnID", indx + 1);
+      ret = _gnutls_x509_read_value (resp->basicresp, name, oid, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+    }
+
+  if (data)
+    {
+      snprintf (name, sizeof (name),
+               "tbsResponseData.responseExtensions.?%u.extnValue", indx + 1);
+      ret = _gnutls_x509_read_value (resp->basicresp, name, data, 0);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         if (oid)
+           gnutls_free (oid->data);
+         return ret;
+       }
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_nonce:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @critical: whether nonce extension is marked critical
+ * @nonce: will hold newly allocated buffer with nonce data
+ *
+ * This function will return the Basic OCSP Response nonce extension
+ * data.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @nonce->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error code is returned.
+ **/
+int
+gnutls_ocsp_resp_get_nonce (gnutls_ocsp_resp_t resp,
+                           unsigned int *critical,
+                           gnutls_datum_t *nonce)
+{
+  int ret;
+  size_t l = 0;
+  gnutls_datum_t tmp;
+
+  ret = get_extension (resp->basicresp, "tbsResponseData.responseExtensions",
+                      GNUTLS_OCSP_NONCE, 0,
+                      &tmp, critical);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = _gnutls_x509_decode_octet_string (NULL, tmp.data, (size_t) tmp.size,
+                                         NULL, &l);
+  if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      gnutls_assert ();
+      gnutls_free (tmp.data);
+      return ret;
+    }
+
+  nonce->data = gnutls_malloc (l);
+  if (nonce->data == NULL)
+    {
+      gnutls_assert ();
+      gnutls_free (tmp.data);
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  ret = _gnutls_x509_decode_octet_string (NULL, tmp.data, (size_t) tmp.size,
+                                         nonce->data, &l);
+  gnutls_free (tmp.data);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+  nonce->size = l;
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_signature_algorithm:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ *
+ * This function will return a value of the #gnutls_sign_algorithm_t
+ * enumeration that is the signature algorithm that has been used to
+ * sign the OCSP response.
+ *
+ * Returns: a #gnutls_sign_algorithm_t value, or a negative error code
+ *   on error.
+ **/
+int
+gnutls_ocsp_resp_get_signature_algorithm (gnutls_ocsp_resp_t resp)
+{
+  int ret;
+  gnutls_datum_t sa;
+
+  ret = _gnutls_x509_read_value (resp->basicresp,
+                                "signatureAlgorithm.algorithm", &sa, 0);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ret = _gnutls_x509_oid2sign_algorithm (sa.data);
+
+  _gnutls_free_datum (&sa);
+
+  return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_signature:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @sig: newly allocated output buffer with signature data
+ *
+ * This function will extract the signature field of a OCSP response.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_signature (gnutls_ocsp_resp_t resp,
+                               gnutls_datum_t *sig)
+{
+  int ret;
+
+  if (resp == NULL || sig == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  ret = _gnutls_x509_read_value (resp->basicresp, "signature", sig, 2);
+  if (ret != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_certs:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @certs: newly allocated array with #gnutls_x509_crt_t certificates
+ * @ncerts: output variable with number of allocated certs.
+ *
+ * This function will extract the X.509 certificates found in the
+ * Basic OCSP Response.  The @certs output variable will hold a newly
+ * allocated zero-terminated array with X.509 certificates.
+ *
+ * Every certificate in the array needs to be de-allocated with
+ * gnutls_x509_crt_deinit() and the array itself must be freed using
+ * gnutls_free().
+ *
+ * Both the @certs and @ncerts variables may be NULL.  Then the
+ * function will work as normal but will not return the NULL:d
+ * information.  This can be used to get the number of certificates
+ * only, or to just get the certificate array without its size.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_certs (gnutls_ocsp_resp_t resp,
+                           gnutls_x509_crt_t ** certs,
+                           size_t *ncerts)
+{
+  int ret;
+  size_t ctr = 0, i;
+  gnutls_x509_crt_t *tmpcerts = NULL, *tmpcerts2;
+  gnutls_datum_t c = { NULL, 0 };
+
+  if (resp == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  tmpcerts = gnutls_malloc (sizeof (*tmpcerts));
+  if (tmpcerts == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  for (;;)
+    {
+      char name[ASN1_MAX_NAME_SIZE];
+
+      snprintf (name, sizeof (name), "certs.?%lu", ctr + 1);
+      ret = _gnutls_x509_der_encode (resp->basicresp, name, &c, 0);
+      if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+       break;
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         goto error;
+       }
+
+      tmpcerts2 = gnutls_realloc (tmpcerts, (ctr + 2) * sizeof (*tmpcerts));
+      if (tmpcerts2 == NULL)
+       {
+         gnutls_assert ();
+         ret = GNUTLS_E_MEMORY_ERROR;
+         goto error;
+       }
+      tmpcerts = tmpcerts2;
+
+      ret = gnutls_x509_crt_init (&tmpcerts[ctr]);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         goto error;
+       }
+      ctr++;
+
+      ret = gnutls_x509_crt_import (tmpcerts[ctr - 1], &c,
+                                   GNUTLS_X509_FMT_DER);
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         goto error;
+       }
+
+      gnutls_free (c.data);
+      c.data = NULL;
+    }
+
+  tmpcerts[ctr] = NULL;
+
+  if (ncerts)
+    *ncerts = ctr;
+  if (certs)
+    *certs = tmpcerts;
+  else
+    {
+      /* clean up memory */
+      ret = GNUTLS_E_SUCCESS;
+      goto error;
+    }
+
+  return GNUTLS_E_SUCCESS;
+
+ error:
+  gnutls_free (c.data);
+  for (i = 0; i < ctr; i++)
+    gnutls_x509_crt_deinit (tmpcerts[i]);
+  gnutls_free (tmpcerts);
+  return ret;
+}
+
+/* Search the OCSP response for a certificate matching the responderId
+   mentioned in the OCSP response. */
+static gnutls_x509_crt_t
+find_signercert (gnutls_ocsp_resp_t resp)
+{
+  int rc;
+  gnutls_x509_crt_t * certs;
+  size_t ncerts = 0, i;
+  gnutls_datum_t riddn;
+  gnutls_x509_crt_t signercert = NULL;
+
+  rc = gnutls_ocsp_resp_get_responder (resp, &riddn);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return NULL;
+    }
+
+  rc = gnutls_ocsp_resp_get_certs (resp, &certs, &ncerts);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      gnutls_free (riddn.data);
+      return NULL;
+    }
+
+  for (i = 0; i < ncerts; i++)
+    {
+      char *crtdn;
+      size_t crtdnsize = 0;
+      int cmpok;
+
+      rc = gnutls_x509_crt_get_dn (certs[i], NULL, &crtdnsize);
+      if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
+       {
+         gnutls_assert ();
+         goto quit;
+       }
+
+      crtdn = gnutls_malloc (crtdnsize);
+      if (crtdn == NULL)
+       {
+         gnutls_assert ();
+         goto quit;
+       }
+
+      rc = gnutls_x509_crt_get_dn (certs[i], crtdn, &crtdnsize);
+      if (rc != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         gnutls_free (crtdn);
+         goto quit;
+       }
+
+      cmpok = (crtdnsize == riddn.size)
+       && memcmp (riddn.data, crtdn, crtdnsize);
+
+      gnutls_free (crtdn);
+
+      if (cmpok == 0)
+       {
+         signercert = certs[i];
+         goto quit;
+       }
+    }
+
+  gnutls_assert ();
+  signercert = NULL;
+
+ quit:
+  gnutls_free (riddn.data);
+  for (i = 0; i < ncerts; i++)
+    if (certs[i] != signercert)
+      gnutls_x509_crt_deinit (certs[i]);
+  gnutls_free (certs);
+  return signercert;
+}
+
+/**
+ * gnutls_ocsp_resp_verify_direct:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @signercert: certificate believed to have signed the response
+ * @verify: output variable with verification status, an #gnutls_ocsp_cert_status_t
+ * @flags: verification flags, 0 for now.
+ *
+ * Verify signature of the Basic OCSP Response against the public key
+ * in the @signercert certificate.
+ *
+ * The output @verify variable will hold verification status codes
+ * (e.g., %GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND,
+ * %GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) which are only valid if the
+ * function returned %GNUTLS_E_SUCCESS.
+ *
+ * Note that the function returns %GNUTLS_E_SUCCESS even when
+ * verification failed.  The caller must always inspect the @verify
+ * variable to find out the verification status.
+ *
+ * The @flags variable should be 0 for now.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_resp_verify_direct (gnutls_ocsp_resp_t resp,
+                               gnutls_x509_crt_t signercert,
+                               unsigned *verify,
+                               int flags)
+{
+  gnutls_datum_t sig = { NULL };
+  gnutls_datum_t data = { NULL };
+  gnutls_pubkey_t pubkey = NULL;
+  int sigalg;
+  int rc;
+
+  if (resp == NULL || signercert == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  sigalg = gnutls_ocsp_resp_get_signature_algorithm (resp);
+
+  rc = export (resp->basicresp, "tbsResponseData", &data);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      goto done;
+    }
+
+  rc = gnutls_pubkey_init (&pubkey);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      goto done;
+    }
+
+  rc = gnutls_pubkey_import_x509 (pubkey, signercert, 0);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      goto done;
+    }
+
+  rc = gnutls_ocsp_resp_get_signature (resp, &sig);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      goto done;
+    }
+
+  rc = gnutls_pubkey_verify_data2 (pubkey, sigalg, 0, &data, &sig);
+  if (rc == GNUTLS_E_PK_SIG_VERIFY_FAILED)
+    {
+      gnutls_assert ();
+      *verify = GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE;
+    }
+  else if (rc < 0)
+    {
+      gnutls_assert ();
+      goto done;
+    }
+  else
+    *verify = 0;
+
+  rc = GNUTLS_E_SUCCESS;
+
+ done:
+  gnutls_free (data.data);
+  gnutls_free (sig.data);
+  gnutls_pubkey_deinit (pubkey);
+
+  return rc;
+}
+
+/**
+ * gnutls_ocsp_resp_verify:
+ * @resp: should contain a #gnutls_ocsp_resp_t structure
+ * @trustlist: trust anchors as a #gnutls_x509_trust_list_t structure
+ * @verify: output variable with verification status, an #gnutls_ocsp_cert_status_t
+ * @flags: verification flags, 0 for now.
+ *
+ * Verify signature of the Basic OCSP Response against the public key
+ * in the certificate of a trusted signer.  The @trustlist should be
+ * populated with trust anchors.  The function will extract the signer
+ * certificate from the Basic OCSP Response and will verify it against
+ * the @trustlist.  A trusted signer is a certificate that is either
+ * in @trustlist, or it is signed directly by a certificate in
+ * @trustlist and has the id-ad-ocspSigning Extended Key Usage bit
+ * set.
+ *
+ * The output @verify variable will hold verification status codes
+ * (e.g., %GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND,
+ * %GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) which are only valid if the
+ * function returned %GNUTLS_E_SUCCESS.
+ *
+ * Note that the function returns %GNUTLS_E_SUCCESS even when
+ * verification failed.  The caller must always inspect the @verify
+ * variable to find out the verification status.
+ *
+ * The @flags variable should be 0 for now.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_resp_verify (gnutls_ocsp_resp_t resp,
+                        gnutls_x509_trust_list_t trustlist,
+                        unsigned *verify,
+                        int flags)
+{
+  gnutls_x509_crt_t signercert = NULL;
+  int rc;
+
+  /* Algorithm:
+     1. Find signer cert.
+        1a. Search in OCSP response Certificate field for responderID.
+        1b. Verify that signer cert is trusted.
+        2a. It is in trustlist?
+        2b. It has OCSP key usage and directly signed by a CA in trustlist?
+     3. Verify signature of Basic Response using public key from signer cert.
+  */
+
+  signercert = find_signercert (resp);
+  if (!signercert)
+    {
+      /* XXX Search in trustlist for certificate matching
+        responderId as well? */
+      gnutls_assert ();
+      *verify = GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND;
+      rc = GNUTLS_E_SUCCESS;
+      goto done;
+    }
+
+  /* Either the signer is directly trusted (i.e., in trustlist) or it
+     is directly signed by something in trustlist and has proper OCSP
+     extkeyusage. */
+  rc = _gnutls_trustlist_inlist (trustlist, signercert);
+  if (rc < 0)
+    {
+      gnutls_assert ();
+      goto done;
+    }
+  if (rc == 1)
+    {
+      /* not in trustlist, need to verify signature and bits */
+      gnutls_x509_crt_t issuer;
+      unsigned vtmp;
+      char oidtmp[sizeof (GNUTLS_KP_OCSP_SIGNING)];
+      size_t oidsize;
+      int indx;
+
+      gnutls_assert ();
+
+      rc = gnutls_x509_trust_list_get_issuer (trustlist, signercert,
+                                             &issuer, 0);
+      if (rc != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         *verify = GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER;
+         rc = GNUTLS_E_SUCCESS;
+         goto done;
+       }
+
+      rc = gnutls_x509_crt_verify (signercert, &issuer, 1, 0, &vtmp);
+      if (rc != GNUTLS_E_SUCCESS)
+       {
+         gnutls_assert ();
+         goto done;
+       }
+
+      if (vtmp != 0)
+       {
+         gnutls_assert ();
+         if (vtmp & GNUTLS_CERT_INSECURE_ALGORITHM)
+           *verify = GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM;
+         else if (vtmp & GNUTLS_CERT_NOT_ACTIVATED)
+           *verify = GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED;
+         else if (vtmp & GNUTLS_CERT_EXPIRED)
+           *verify = GNUTLS_OCSP_VERIFY_CERT_EXPIRED;
+         else
+           *verify = GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER;
+         rc = GNUTLS_E_SUCCESS;
+         goto done;
+       }
+
+      for (indx = 0; ; indx++)
+       {
+         oidsize = sizeof (oidtmp);
+         rc = gnutls_x509_crt_get_key_purpose_oid (signercert, indx,
+                                                   oidtmp, &oidsize,
+                                                   NULL);
+         if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+           {
+             gnutls_assert ();
+             *verify = GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR;
+             rc = GNUTLS_E_SUCCESS;
+             goto done;
+           }
+         else if (rc == GNUTLS_E_SHORT_MEMORY_BUFFER)
+           {
+             gnutls_assert ();
+             continue;
+           }
+         else if (rc != GNUTLS_E_SUCCESS)
+           {
+             gnutls_assert ();
+             goto done;
+           }
+
+         if (memcmp (oidtmp, GNUTLS_KP_OCSP_SIGNING, oidsize) != 0)
+           {
+             gnutls_assert ();
+             continue;
+           }
+
+         break;
+       }
+    }
+
+  rc = gnutls_ocsp_resp_verify_direct (resp, signercert, verify, flags);
+
+ done:
+  gnutls_x509_crt_deinit (signercert);
+
+  return rc;
+}
diff --git a/lib/x509/ocsp_output.c b/lib/x509/ocsp_output.c
new file mode 100644 (file)
index 0000000..719ce89
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ * Author: Simon Josefsson
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* Online Certificate Status Protocol - RFC 2560
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_global.h>
+#include <gnutls_errors.h>
+#include <libtasn1.h>
+#include <gnutls_pk.h>
+#include "algorithms.h"
+
+#include <gnutls/ocsp.h>
+
+/* I18n of error codes. */
+#include "gettext.h"
+#define _(String) dgettext (PACKAGE, String)
+
+#define addf _gnutls_buffer_append_printf
+#define adds _gnutls_buffer_append_str
+
+static void
+print_req (gnutls_buffer_st * str, gnutls_ocsp_req_t req)
+{
+  int ret;
+  unsigned indx;
+
+  /* Version. */
+  {
+    int version = gnutls_ocsp_req_get_version (req);
+    if (version < 0)
+      addf (str, "error: get_version: %s\n", gnutls_strerror (version));
+    else
+      addf (str, _("\tVersion: %d\n"), version);
+  }
+
+  /* XXX requestorName */
+
+  /* requestList */
+  addf (str, "\tRequest List:\n");
+  for (indx = 0; ; indx++)
+    {
+      gnutls_digest_algorithm_t digest;
+      gnutls_datum_t in, ik, sn;
+
+      ret = gnutls_ocsp_req_get_cert_id (req, indx, &digest, &in, &ik, &sn);
+      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       break;
+      addf (str, "\t\tCertificate ID:\n");
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         addf (str, "error: get_cert_id: %s\n",
+               gnutls_strerror (ret));
+         continue;
+       }
+      addf (str, "\t\t\tHash Algorithm: %s\n",
+           _gnutls_digest_get_name (digest));
+
+      adds (str, "\t\t\tIssuer Name Hash: ");
+      _gnutls_buffer_hexprint (str, in.data, in.size);
+      adds (str, "\n");
+
+      adds (str, "\t\t\tIssuer Key Hash: ");
+      _gnutls_buffer_hexprint (str, ik.data, ik.size);
+      adds (str, "\n");
+
+      adds (str, "\t\t\tSerial Number: ");
+      _gnutls_buffer_hexprint (str, sn.data, sn.size);
+      adds (str, "\n");
+
+      gnutls_free (in.data);
+      gnutls_free (ik.data);
+      gnutls_free (sn.data);
+
+      /* XXX singleRequestExtensions */
+    }
+
+  for (indx = 0; ; indx++)
+    {
+      gnutls_datum_t oid;
+      unsigned int critical;
+      gnutls_datum_t data;
+
+      ret = gnutls_ocsp_req_get_extension (req, indx, &oid, &critical, &data);
+      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       break;
+      else if (ret != GNUTLS_E_SUCCESS)
+       {
+         addf (str, "error: get_extension: %s\n",
+               gnutls_strerror (ret));
+         continue;
+       }
+      if (indx == 0)
+       adds (str, "\tExtensions:\n");
+
+      if (memcmp (oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0)
+       {
+         gnutls_datum_t nonce;
+         unsigned int critical;
+
+         ret = gnutls_ocsp_req_get_nonce (req, &critical, &nonce);
+         if (ret != GNUTLS_E_SUCCESS)
+           {
+             addf (str, "error: get_nonce: %s\n",
+                   gnutls_strerror (ret));
+           }
+         else
+           {
+             addf (str, "\t\tNonce%s: ", critical ? " (critical)" : "");
+             _gnutls_buffer_hexprint (str, nonce.data, nonce.size);
+             adds (str, "\n");
+             gnutls_free (nonce.data);
+           }
+       }
+      else
+       {
+         addf (str, "\t\tUnknown extension %s (%s):\n", oid.data,
+               critical ? "critical" : "not critical");
+
+         addf (str, _("\t\t\tASCII: "));
+         _gnutls_buffer_asciiprint (str, data.data, data.size);
+         addf (str, "\n");
+
+         addf (str, _("\t\t\tHexdump: "));
+         _gnutls_buffer_hexprint (str, data.data, data.size);
+         adds (str, "\n");
+       }
+
+      gnutls_free (oid.data);
+      gnutls_free (data.data);
+    }
+
+  /* XXX Signature */
+}
+
+/**
+ * gnutls_ocsp_req_print:
+ * @req: The structure to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with (0) terminated string.
+ *
+ * This function will pretty print a OCSP request, suitable for
+ * display to a human.
+ *
+ * If the format is %GNUTLS_PRINT_FULL then all fields of the request
+ * will be output, on multiple lines.
+ *
+ * The output @out->data needs to be deallocate using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_req_print (gnutls_ocsp_req_t req,
+                       gnutls_ocsp_print_formats_t format,
+                       gnutls_datum_t * out)
+{
+  gnutls_buffer_st str;
+  int rc;
+
+  if (format != GNUTLS_OCSP_PRINT_FULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  _gnutls_buffer_init (&str);
+
+  _gnutls_buffer_append_str (&str, _("OCSP Request Information:\n"));
+
+  print_req (&str, req);
+
+  _gnutls_buffer_append_data (&str, "\0", 1);
+
+  rc = _gnutls_buffer_to_datum (&str, out);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return rc;
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
+
+static void
+print_resp (gnutls_buffer_st * str, gnutls_ocsp_resp_t resp)
+{
+  int ret;
+  unsigned indx;
+
+  ret = gnutls_ocsp_resp_get_status (resp);
+  if (ret < 0)
+    {
+      addf (str, "error: ocsp_resp_get_status: %s\n",
+           gnutls_strerror (ret));
+      return;
+    }
+
+  adds (str, "\tResponse Status: ");
+  switch (ret)
+    {
+    case GNUTLS_OCSP_RESP_SUCCESSFUL:
+      adds (str, "Successful\n");
+      break;
+
+    case GNUTLS_OCSP_RESP_MALFORMEDREQUEST:
+      adds (str, "malformedRequest\n");
+      return;
+
+    case GNUTLS_OCSP_RESP_INTERNALERROR:
+      adds (str, "internalError\n");
+      return;
+
+    case GNUTLS_OCSP_RESP_TRYLATER:
+      adds (str, "tryLater\n");
+      return;
+
+    case GNUTLS_OCSP_RESP_SIGREQUIRED:
+      adds (str, "sigRequired\n");
+      return;
+
+    case GNUTLS_OCSP_RESP_UNAUTHORIZED:
+      adds (str, "unauthorized\n");
+      return;
+
+    default:
+      adds (str, "unknown\n");
+      return;
+    }
+
+  {
+    gnutls_datum_t oid;
+
+    ret = gnutls_ocsp_resp_get_response (resp, &oid, NULL);
+    if (ret < 0)
+      {
+       addf (str, "error: get_response: %s\n", gnutls_strerror (ret));
+       return;
+      }
+
+    adds (str, "\tResponse Type: ");
+#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1"
+
+    if (oid.size == sizeof (OCSP_BASIC)
+       && memcmp (oid.data, OCSP_BASIC, oid.size) == 0)
+      {
+       adds (str, "Basic OCSP Response\n");
+       gnutls_free (oid.data);
+      }
+    else
+      {
+       addf (str, "Unknown response type (%.*s)\n", oid.size, oid.data);
+       gnutls_free (oid.data);
+       return;
+      }
+  }
+
+  /* Version. */
+  {
+    int version = gnutls_ocsp_resp_get_version (resp);
+    if (version < 0)
+      addf (str, "error: get_version: %s\n", gnutls_strerror (version));
+    else
+      addf (str, _("\tVersion: %d\n"), version);
+  }
+
+  /* responderID */
+  {
+    gnutls_datum_t dn;
+
+    /* XXX byKey */
+
+    ret = gnutls_ocsp_resp_get_responder (resp, &dn);
+    if (ret < 0)
+      addf (str, "error: get_dn: %s\n", gnutls_strerror (ret));
+    else
+      {
+       addf (str, _("\tResponder ID: %.*s\n"), dn.size, dn.data);
+       gnutls_free (dn.data);
+      }
+  }
+
+  {
+    char s[42];
+    size_t max = sizeof (s);
+    struct tm t;
+    time_t tim = gnutls_ocsp_resp_get_produced (resp);
+
+    if (tim == (time_t) -1)
+      addf (str, "error: ocsp_resp_get_produced\n");
+    else if (gmtime_r (&tim, &t) == NULL)
+      addf (str, "error: gmtime_r (%ld)\n", (unsigned long) tim);
+    else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
+      addf (str, "error: strftime (%ld)\n", (unsigned long) tim);
+    else
+      addf (str, _("\tProduced At: %s\n"), s);
+  }
+
+  addf (str, "\tResponses:\n");
+  for (indx = 0; ; indx++)
+    {
+      gnutls_digest_algorithm_t digest;
+      gnutls_datum_t in, ik, sn;
+      int cert_status;
+      time_t this_update;
+      time_t next_update;
+      time_t revocation_time;
+      int revocation_reason;
+
+      ret = gnutls_ocsp_resp_get_single (resp,
+                                        indx,
+                                        &digest, &in, &ik, &sn,
+                                        &cert_status,
+                                        &this_update,
+                                        &next_update,
+                                        &revocation_time,
+                                        &revocation_reason);
+      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       break;
+      addf (str, "\t\tCertificate ID:\n");
+      if (ret != GNUTLS_E_SUCCESS)
+       {
+         addf (str, "error: get_singleresponse: %s\n",
+               gnutls_strerror (ret));
+         continue;
+       }
+      addf (str, "\t\t\tHash Algorithm: %s\n",
+           _gnutls_digest_get_name (digest));
+
+      adds (str, "\t\t\tIssuer Name Hash: ");
+      _gnutls_buffer_hexprint (str, in.data, in.size);
+      adds (str, "\n");
+
+      adds (str, "\t\t\tIssuer Key Hash: ");
+      _gnutls_buffer_hexprint (str, ik.data, ik.size);
+      adds (str, "\n");
+
+      adds (str, "\t\t\tSerial Number: ");
+      _gnutls_buffer_hexprint (str, sn.data, sn.size);
+      adds (str, "\n");
+
+      gnutls_free (in.data);
+      gnutls_free (ik.data);
+      gnutls_free (sn.data);
+
+      {
+       const char *p = NULL;
+
+       switch (cert_status)
+         {
+         case GNUTLS_OCSP_CERT_GOOD:
+           p = "good";
+           break;
+
+         case GNUTLS_OCSP_CERT_REVOKED:
+           p = "revoked";
+           break;
+
+         case GNUTLS_OCSP_CERT_UNKNOWN:
+           p = "unknown";
+           break;
+
+         default:
+           addf (str, "\t\tCertificate Status: unexpected value %d\n",
+                 cert_status);
+           break;
+         }
+
+       if (p)
+         addf (str, "\t\tCertificate Status: %s\n", p);
+      }
+
+      /* XXX revocation reason */
+
+      if (cert_status == GNUTLS_OCSP_CERT_REVOKED)
+       {
+         char s[42];
+         size_t max = sizeof (s);
+         struct tm t;
+
+         if (revocation_time == (time_t) -1)
+           addf (str, "error: revocation_time\n");
+         else if (gmtime_r (&revocation_time, &t) == NULL)
+           addf (str, "error: gmtime_r (%ld)\n",
+                 (unsigned long) revocation_time);
+         else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
+           addf (str, "error: strftime (%ld)\n",
+                 (unsigned long) revocation_time);
+         else
+           addf (str, _("\t\tRevocation time: %s\n"), s);
+      }
+
+      {
+       char s[42];
+       size_t max = sizeof (s);
+       struct tm t;
+
+       if (this_update == (time_t) -1)
+         addf (str, "error: this_update\n");
+       else if (gmtime_r (&this_update, &t) == NULL)
+         addf (str, "error: gmtime_r (%ld)\n", (unsigned long) this_update);
+       else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
+         addf (str, "error: strftime (%ld)\n", (unsigned long) this_update);
+       else
+         addf (str, _("\t\tThis Update: %s\n"), s);
+      }
+
+      {
+       char s[42];
+       size_t max = sizeof (s);
+       struct tm t;
+
+       if (next_update == (time_t) -1)
+         addf (str, "error: next_update\n");
+       else if (gmtime_r (&next_update, &t) == NULL)
+         addf (str, "error: gmtime_r (%ld)\n", (unsigned long) next_update);
+       else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
+         addf (str, "error: strftime (%ld)\n", (unsigned long) next_update);
+       else
+         addf (str, _("\t\tNext Update: %s\n"), s);
+      }
+
+      /* XXX singleRequestExtensions */
+    }
+
+  adds (str, "\tExtensions:\n");
+  for (indx = 0; ; indx++)
+    {
+      gnutls_datum_t oid;
+      unsigned int critical;
+      gnutls_datum_t data;
+
+      ret = gnutls_ocsp_resp_get_extension (resp, indx, &oid, &critical, &data);
+      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       break;
+      else if (ret != GNUTLS_E_SUCCESS)
+       {
+         addf (str, "error: get_extension: %s\n",
+               gnutls_strerror (ret));
+         continue;
+       }
+
+      if (memcmp (oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0)
+       {
+         gnutls_datum_t nonce;
+         unsigned int critical;
+
+         ret = gnutls_ocsp_resp_get_nonce (resp, &critical, &nonce);
+         if (ret != GNUTLS_E_SUCCESS)
+           {
+             addf (str, "error: get_nonce: %s\n",
+                   gnutls_strerror (ret));
+           }
+         else
+           {
+             addf (str, "\t\tNonce%s: ", critical ? " (critical)" : "");
+             _gnutls_buffer_hexprint (str, nonce.data, nonce.size);
+             adds (str, "\n");
+             gnutls_free (nonce.data);
+           }
+       }
+      else
+       {
+         addf (str, "\t\tUnknown extension %s (%s):\n", oid.data,
+               critical ? "critical" : "not critical");
+
+         addf (str, _("\t\t\tASCII: "));
+         _gnutls_buffer_asciiprint (str, data.data, data.size);
+         addf (str, "\n");
+
+         addf (str, _("\t\t\tHexdump: "));
+         _gnutls_buffer_hexprint (str, data.data, data.size);
+         adds (str, "\n");
+       }
+
+      gnutls_free (oid.data);
+      gnutls_free (data.data);
+    }
+
+  /* Signature. */
+  {
+    gnutls_datum_t sig;
+
+    ret = gnutls_ocsp_resp_get_signature_algorithm (resp);
+    if (ret < 0)
+      addf (str, "retor: get_signature_algorithm: %s\n",
+           gnutls_strerror (ret));
+    else
+      {
+       const char *name = gnutls_sign_algorithm_get_name (ret);
+       if (name == NULL)
+         name = _("unknown");
+       addf (str, _("\tSignature Algorithm: %s\n"), name);
+      }
+    if (ret == GNUTLS_SIGN_RSA_MD5 || ret == GNUTLS_SIGN_RSA_MD2)
+      {
+       adds (str, _("warning: signed using a broken signature "
+                    "algorithm that can be forged.\n"));
+      }
+
+    ret = gnutls_ocsp_resp_get_signature (resp, &sig);
+    if (ret < 0)
+      addf (str, "error: get_signature: %s\n", gnutls_strerror (ret));
+    else
+      {
+       adds (str, _("\tSignature:\n"));
+       _gnutls_buffer_hexdump (str, sig.data, sig.size, "\t\t");
+
+       gnutls_free (sig.data);
+      }
+  }
+
+  /* certs */
+  {
+    gnutls_x509_crt_t *certs;
+    size_t ncerts, i;
+    gnutls_datum_t out;
+
+    ret = gnutls_ocsp_resp_get_certs (resp, &certs, &ncerts);
+    if (ret < 0)
+      addf (str, "error: get_certs: %s\n", gnutls_strerror (ret));
+    else
+      {
+       for (i = 0; i < ncerts; i++)
+         {
+           size_t s = 0;
+
+           ret = gnutls_x509_crt_print (certs[i], GNUTLS_CRT_PRINT_FULL,
+                                        &out);
+           if (ret < 0)
+             addf (str, "error: crt_print: %s\n", gnutls_strerror (ret));
+           else
+             {
+               addf (str, "%.*s", out.size, out.data);
+               gnutls_free (out.data);
+             }
+
+           ret = gnutls_x509_crt_export (certs[i], GNUTLS_X509_FMT_PEM,
+                                         NULL, &s);
+           if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+             addf (str, "error: crt_export: %s\n", gnutls_strerror (ret));
+           else
+             {
+               out.data = gnutls_malloc (s);
+               if (out.data == NULL)
+                 addf (str, "error: malloc: %s\n",
+                       gnutls_strerror (GNUTLS_E_MEMORY_ERROR));
+               else
+                 {
+                   ret = gnutls_x509_crt_export (certs[i], GNUTLS_X509_FMT_PEM,
+                                                 out.data, &s);
+                   out.size = s;
+                   addf (str, "%.*s", out.size, out.data);
+                   gnutls_free (out.data);
+                 }
+             }
+
+           gnutls_x509_crt_deinit (certs[i]);
+         }
+       gnutls_free (certs);
+      }
+  }
+}
+
+/**
+ * gnutls_ocsp_resp_print:
+ * @resp: The structure to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with (0) terminated string.
+ *
+ * This function will pretty print a OCSP response, suitable for
+ * display to a human.
+ *
+ * If the format is %GNUTLS_PRINT_FULL then all fields of the response
+ * will be output, on multiple lines.
+ *
+ * The output @out->data needs to be deallocate using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ **/
+int
+gnutls_ocsp_resp_print (gnutls_ocsp_resp_t resp,
+                       gnutls_ocsp_print_formats_t format,
+                       gnutls_datum_t * out)
+{
+  gnutls_buffer_st str;
+  int rc;
+
+  if (format != GNUTLS_OCSP_PRINT_FULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  _gnutls_buffer_init (&str);
+
+  _gnutls_buffer_append_str (&str, _("OCSP Response Information:\n"));
+
+  print_resp (&str, resp);
+
+  _gnutls_buffer_append_data (&str, "\0", 1);
+
+  rc = _gnutls_buffer_to_datum (&str, out);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      gnutls_assert ();
+      return rc;
+    }
+
+  return GNUTLS_E_SUCCESS;
+}
index 8d80e2a183d936cf9d1fab30273349cbf227f668..d74d92c29984b2f56ce2d1063107ea30c7bf560b 100644 (file)
@@ -214,6 +214,21 @@ fi
     AC_DEFINE([ENABLE_CRYPTODEV], 1, [Enable cryptodev support])
   fi
 
+  AC_MSG_CHECKING([whether to disable OCSP support])
+  AC_ARG_ENABLE(ocsp,
+    AS_HELP_STRING([--disable-ocsp],
+                   [disable OCSP support]),
+    ac_enable_ocsp=no)
+  if test x$ac_enable_ocsp != xno; then
+   ac_enable_ocsp=yes
+   AC_MSG_RESULT(no)
+   AC_DEFINE([ENABLE_OCSP], 1, [enable OCSP support])
+  else
+   ac_full=0
+   AC_MSG_RESULT(yes)
+  fi
+  AM_CONDITIONAL(ENABLE_OCSP, test "$ac_enable_ocsp" != "no")
+
   # For storing integers in pointers without warnings
   # http://developer.gnome.org/doc/API/2.0/glib/glib-Type-Conversion-Macros.html#desc
   AC_CHECK_SIZEOF(void *)
index 888a1119dc5eb1d9ab9ef702d16cb9e0980e4504..a23b4b81fcda7bc0324dc09fbbb5e0e924f85707 100644 (file)
@@ -39,6 +39,9 @@ endif
 if ENABLE_SRP
 bin_PROGRAMS += srptool
 endif
+if ENABLE_OCSP
+bin_PROGRAMS += ocsptool
+endif
 
 if ENABLE_PKCS11
 bin_PROGRAMS += p11tool
@@ -70,6 +73,14 @@ libcmd_srp_la_CFLAGS =
 libcmd_srp_la_SOURCES = srptool.gaa srptool-gaa.h srptool-gaa.c
 endif
 
+if ENABLE_OCSP
+ocsptool_SOURCES = ocsptool.c ocsptool-common.h
+ocsptool_LDADD = ../lib/libgnutls.la libcmd-ocsp.la ../gl/libgnu.la
+noinst_LTLIBRARIES += libcmd-ocsp.la
+libcmd_ocsp_la_CFLAGS =
+libcmd_ocsp_la_SOURCES = ocsptool.gaa ocsptool-gaa.h ocsptool-gaa.c
+endif
+
 psktool_SOURCES = psk.c
 psktool_LDADD = ../lib/libgnutls.la libcmd-psk.la ../gl/libgnu.la
 noinst_LTLIBRARIES += libcmd-psk.la
@@ -148,6 +159,8 @@ psk-gaa.c: $(srcdir)/psk.gaa
        -$(GAA) $< -o psk-gaa.c -i psk-gaa.h
 srptool-gaa.c: $(srcdir)/srptool.gaa
        -$(GAA) $< -o srptool-gaa.c -i srptool-gaa.h
+ocsptool-gaa.c: $(srcdir)/ocsptool.gaa
+       -$(GAA) $< -o ocsptool-gaa.c -i ocsptool-gaa.h
 if ENABLE_PKCS11
 p11tool-gaa.c: $(srcdir)/p11tool.gaa
        -$(GAA) $< -o p11tool-gaa.c -i p11tool-gaa.h
diff --git a/src/ocsptool-common.h b/src/ocsptool-common.h
new file mode 100644 (file)
index 0000000..729d1eb
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef OCSPTOOL_COMMON_H
+#define OCSPTOOL_COMMON_H
+
+enum
+  {
+    ACTION_NONE,
+    ACTION_REQ_INFO,
+    ACTION_RESP_INFO,
+    ACTION_VERIFY_RESP,
+    ACTION_GEN_REQ
+  };
+
+extern void ocsptool_version (void);
+
+#endif
diff --git a/src/ocsptool.c b/src/ocsptool.c
new file mode 100644 (file)
index 0000000..dde1028
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/ocsp.h>
+#include <gnutls/x509.h>
+#include <gnutls/crypto.h>
+
+/* Gnulib portability files. */
+#include <error.h>
+#include <progname.h>
+#include <version-etc.h>
+#include <read-file.h>
+
+#include <ocsptool-common.h>
+#include <ocsptool-gaa.h>
+
+gaainfo info;
+FILE *outfile;
+FILE *infile;
+
+static void
+tls_log_func (int level, const char *str)
+{
+  fprintf (stderr, "|<%d>| %s", level, str);
+}
+
+static void
+request_info (void)
+{
+  gnutls_ocsp_req_t req;
+  int ret;
+  gnutls_datum_t dat;
+  size_t size;
+
+  ret = gnutls_ocsp_req_init (&req);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "ocsp_req_init: %s", gnutls_strerror (ret));
+
+  if (info.req)
+    dat.data = read_binary_file (info.req, &size);
+  else
+    dat.data = fread_file (infile, &size);
+  if (dat.data == NULL)
+    error (EXIT_FAILURE, errno, "reading request");
+  dat.size = size;
+
+  ret = gnutls_ocsp_req_import (req, &dat);
+  free (dat.data);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "importing request: %s", gnutls_strerror (ret));
+
+  ret = gnutls_ocsp_req_print (req, GNUTLS_OCSP_PRINT_FULL, &dat);
+  if (ret != 0)
+    error (EXIT_FAILURE, 0, "ocsp_req_print: %s", gnutls_strerror (ret));
+
+  printf ("%.*s", dat.size, dat.data);
+  gnutls_free (dat.data);
+
+  gnutls_ocsp_req_deinit (req);
+}
+
+static void
+response_info (void)
+{
+  gnutls_ocsp_resp_t resp;
+  int ret;
+  gnutls_datum_t dat;
+  size_t size;
+
+  ret = gnutls_ocsp_resp_init (&resp);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "ocsp_resp_init: %s", gnutls_strerror (ret));
+
+  if (info.resp)
+    dat.data = read_binary_file (info.resp, &size);
+  else
+    dat.data = fread_file (infile, &size);
+  if (dat.data == NULL)
+    error (EXIT_FAILURE, errno, "reading response");
+  dat.size = size;
+
+  ret = gnutls_ocsp_resp_import (resp, &dat);
+  free (dat.data);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "importing response: %s", gnutls_strerror (ret));
+
+  ret = gnutls_ocsp_resp_print (resp, GNUTLS_OCSP_PRINT_FULL, &dat);
+  if (ret != 0)
+    error (EXIT_FAILURE, 0, "ocsp_resp_print: %s", gnutls_strerror (ret));
+
+  printf ("%.*s", dat.size, dat.data);
+  gnutls_free (dat.data);
+
+  gnutls_ocsp_resp_deinit (resp);
+}
+
+static gnutls_x509_crt_t
+load_issuer (void)
+{
+  gnutls_x509_crt_t crt;
+  int ret;
+  gnutls_datum_t dat;
+  size_t size;
+
+  if (info.issuer == NULL)
+    error (EXIT_FAILURE, 0, "missing --load-issuer");
+
+  ret = gnutls_x509_crt_init (&crt);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "crt_init: %s", gnutls_strerror (ret));
+
+  dat.data = read_binary_file (info.issuer, &size);
+  dat.size = size;
+
+  if (!dat.data)
+    error (EXIT_FAILURE, errno, "reading --load-issuer: %s", info.issuer);
+
+  ret = gnutls_x509_crt_import (crt, &dat, info.inder);
+  free (dat.data);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "importing --load-issuer: %s: %s",
+           info.issuer, gnutls_strerror (ret));
+
+  return crt;
+}
+
+static gnutls_x509_crt_t
+load_cert (void)
+{
+  gnutls_x509_crt_t crt;
+  int ret;
+  gnutls_datum_t dat;
+  size_t size;
+
+  if (info.cert == NULL)
+    error (EXIT_FAILURE, 0, "missing --load-cert");
+
+  ret = gnutls_x509_crt_init (&crt);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "crt_init: %s", gnutls_strerror (ret));
+
+  dat.data = read_binary_file (info.cert, &size);
+  dat.size = size;
+
+  if (!dat.data)
+    error (EXIT_FAILURE, errno, "reading --load-cert: %s", info.cert);
+
+  ret = gnutls_x509_crt_import (crt, &dat, info.inder);
+  free (dat.data);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "importing --load-cert: %s: %s",
+           info.cert, gnutls_strerror (ret));
+
+  return crt;
+}
+
+static void
+generate_request (void)
+{
+  gnutls_ocsp_req_t req;
+  int ret;
+  gnutls_datum_t dat;
+  gnutls_x509_crt_t issuer, cert;
+
+  ret = gnutls_ocsp_req_init (&req);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "ocsp_req_init: %s", gnutls_strerror (ret));
+
+  issuer = load_issuer ();
+  cert = load_cert ();
+
+  ret = gnutls_ocsp_req_add_cert (req, GNUTLS_DIG_SHA1,
+                                 issuer, cert);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "ocsp_req_add_cert: %s", gnutls_strerror (ret));
+
+  gnutls_x509_crt_deinit (cert);
+  gnutls_x509_crt_deinit (issuer);
+
+  if (!info.nononce)
+    {
+      char noncebuf[23];
+      gnutls_datum_t nonce = { noncebuf, sizeof (noncebuf) };
+
+      ret = gnutls_rnd (GNUTLS_RND_RANDOM, nonce.data, nonce.size);
+      if (ret < 0)
+       error (EXIT_FAILURE, 0, "gnutls_rnd: %s", gnutls_strerror (ret));
+
+      ret = gnutls_ocsp_req_set_nonce (req, 0, &nonce);
+      if (ret < 0)
+       error (EXIT_FAILURE, 0, "ocsp_req_set_nonce: %s",
+              gnutls_strerror (ret));
+    }
+
+  ret = gnutls_ocsp_req_export (req, &dat);
+  if (ret != 0)
+    error (EXIT_FAILURE, 0, "ocsp_req_export: %s", gnutls_strerror (ret));
+
+  fwrite (dat.data, 1, dat.size, outfile);
+
+  gnutls_free (dat.data);
+
+  gnutls_ocsp_req_deinit (req);
+}
+
+static void
+print_verify_res (unsigned int output)
+{
+  int comma = 0;
+
+  if (output)
+    {
+      printf ("Failure");
+      comma = 1;
+    }
+  else
+    {
+      printf ("Success");
+      comma = 1;
+    }
+
+  if (output & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND)
+    {
+      if (comma)
+        printf (", ");
+      printf ("Signer cert not found");
+      comma = 1;
+    }
+
+  if (output & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR)
+    {
+      if (comma)
+        printf (", ");
+      printf ("Signer cert keyusage error");
+      comma = 1;
+    }
+
+  if (output & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
+    {
+      if (comma)
+        printf (", ");
+      printf ("Signer cert is not trusted");
+      comma = 1;
+    }
+
+  if (output & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM)
+    {
+      if (comma)
+        printf (", ");
+      printf ("Insecure algorithm");
+      comma = 1;
+    }
+
+  if (output & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
+    {
+      if (comma)
+        printf (", ");
+      printf ("Signature failure");
+      comma = 1;
+    }
+
+  if (output & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED)
+    {
+      if (comma)
+        printf (", ");
+      printf ("Signer cert not yet activated");
+      comma = 1;
+    }
+
+  if (output & GNUTLS_OCSP_VERIFY_CERT_EXPIRED)
+    {
+      if (comma)
+        printf (", ");
+      printf ("Signer cert expired");
+      comma = 1;
+    }
+}
+
+static void
+verify_response (void)
+{
+  gnutls_ocsp_resp_t resp;
+  int ret;
+  gnutls_datum_t dat;
+  size_t size;
+  gnutls_x509_crt_t *x509_ca_list = NULL;
+  unsigned int x509_ncas = 0;
+  gnutls_x509_trust_list_t list;
+  gnutls_x509_crt_t signer;
+  unsigned verify;
+
+  ret = gnutls_ocsp_resp_init (&resp);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "ocsp_resp_init: %s", gnutls_strerror (ret));
+
+  if (info.resp)
+    dat.data = read_binary_file (info.resp, &size);
+  else
+    dat.data = fread_file (infile, &size);
+  if (dat.data == NULL)
+    error (EXIT_FAILURE, errno, "reading response");
+  dat.size = size;
+
+  ret = gnutls_ocsp_resp_import (resp, &dat);
+  free (dat.data);
+  if (ret < 0)
+    error (EXIT_FAILURE, 0, "importing response: %s", gnutls_strerror (ret));
+
+  if (info.trust && info.signer)
+    error (EXIT_FAILURE, 0, "cannot mix --load-trust and --load-signer");
+  else if (info.signer == NULL)
+    {
+      dat.data = read_binary_file (info.trust, &size);
+      if (dat.data == NULL)
+       error (EXIT_FAILURE, errno, "reading --load-trust: %s", info.trust);
+      dat.size = size;
+
+      ret = gnutls_x509_trust_list_init (&list, 0);
+      if (ret < 0)
+       error (EXIT_FAILURE, 0, "gnutls_x509_trust_list_init: %s",
+              gnutls_strerror (ret));
+
+      ret = gnutls_x509_crt_list_import2 (&x509_ca_list, &x509_ncas, &dat,
+                                         GNUTLS_X509_FMT_PEM, 0);
+      if (ret < 0 || x509_ncas < 1)
+       error (EXIT_FAILURE, 0, "error parsing CAs: %s",
+              gnutls_strerror (ret));
+
+      if (info.verbose)
+       {
+         unsigned int i;
+         for (i = 0; i < x509_ncas; i++)
+           {
+             gnutls_datum_t out;
+
+             ret = gnutls_x509_crt_print (x509_ca_list[i],
+                                          GNUTLS_CRT_PRINT_ONELINE, &out);
+             if (ret < 0)
+               error (EXIT_FAILURE, 0, "gnutls_x509_crt_print: %s",
+                      gnutls_strerror (ret));
+
+             printf ("Trust anchor %d: %.*s\n", i, out.size, out.data);
+             gnutls_free (out.data);
+           }
+       }
+
+      ret = gnutls_x509_trust_list_add_cas (list, x509_ca_list, x509_ncas, 0);
+      if (ret < 0)
+       error (EXIT_FAILURE, 0, "gnutls_x509_trust_add_cas: %s",
+              gnutls_strerror (ret));
+
+      if (info.verbose)
+       fprintf (stdout, "Loaded %d trust anchors\n", x509_ncas);
+
+      ret = gnutls_ocsp_resp_verify (resp, list, &verify, 0);
+      if (ret < 0)
+       error (EXIT_FAILURE, 0, "gnutls_ocsp_resp_verify: %s",
+              gnutls_strerror (ret));
+    }
+  else if (info.trust == NULL)
+    {
+      ret = gnutls_x509_crt_init (&signer);
+      if (ret < 0)
+       error (EXIT_FAILURE, 0, "crt_init: %s", gnutls_strerror (ret));
+
+      dat.data = read_binary_file (info.signer, &size);
+      if (dat.data == NULL)
+       error (EXIT_FAILURE, errno, "reading --load-signer: %s", info.signer);
+      dat.size = size;
+
+      ret = gnutls_x509_crt_import (signer, &dat, info.inder);
+      free (dat.data);
+      if (ret < 0)
+       error (EXIT_FAILURE, 0, "importing --load-signer: %s: %s",
+              info.signer, gnutls_strerror (ret));
+
+      if (info.verbose)
+       {
+         gnutls_datum_t out;
+
+         ret = gnutls_x509_crt_print (signer, GNUTLS_CRT_PRINT_ONELINE, &out);
+         if (ret < 0)
+           error (EXIT_FAILURE, 0, "gnutls_x509_crt_print: %s",
+                  gnutls_strerror (ret));
+
+         printf ("Signer: %.*s\n", out.size, out.data);
+         gnutls_free (out.data);
+       }
+
+      ret = gnutls_ocsp_resp_verify_direct (resp, signer, &verify, 0);
+      if (ret < 0)
+       error (EXIT_FAILURE, 0, "gnutls_ocsp_resp_verify_direct: %s",
+              gnutls_strerror (ret));
+    }
+  else
+    error (EXIT_FAILURE, 0, "missing --load-trust or --load-signer");
+
+  printf ("Verifying OCSP Response: ");
+  print_verify_res (verify);
+  printf (".\n");
+
+  gnutls_ocsp_resp_deinit (resp);
+}
+
+int
+main (int argc, char **argv)
+{
+  int ret;
+
+  set_program_name (argv[0]);
+
+  if ((ret = gnutls_global_init ()) < 0)
+    error (EXIT_FAILURE, 0, "global_init: %s", gnutls_strerror (ret));
+
+  if (gaa (argc, argv, &info) != -1)
+    {
+      fprintf (stderr, "Try `%s --help' for more information.\n",
+               program_name);
+      exit (EXIT_FAILURE);
+    }
+
+  gnutls_global_set_log_function (tls_log_func);
+  gnutls_global_set_log_level (info.debug);
+
+  if (info.outfile)
+    {
+      outfile = fopen (info.outfile, "wb");
+      if (outfile == NULL)
+        error (EXIT_FAILURE, errno, "%s", info.outfile);
+    }
+  else
+    outfile = stdout;
+
+  if (info.infile)
+    {
+      infile = fopen (info.infile, "rb");
+      if (infile == NULL)
+        error (EXIT_FAILURE, errno, "%s", info.infile);
+    }
+  else
+    infile = stdin;
+
+  if (info.inder)
+    info.inder = GNUTLS_X509_FMT_DER;
+  else
+    info.inder = GNUTLS_X509_FMT_PEM;
+
+  switch (info.action)
+    {
+    case ACTION_REQ_INFO:
+      request_info ();
+      break;
+
+    case ACTION_RESP_INFO:
+      response_info ();
+      break;
+
+    case ACTION_GEN_REQ:
+      generate_request ();
+      break;
+
+    case ACTION_VERIFY_RESP:
+      verify_response ();
+      break;
+
+    default:
+      gaa_help();
+    }
+
+  return 0;
+}
+
+void
+ocsptool_version (void)
+{
+  const char *p = PACKAGE_NAME;
+  if (strcmp (gnutls_check_version (NULL), PACKAGE_VERSION) != 0)
+    p = PACKAGE_STRING;
+  version_etc (stdout, "ocsptool", p, gnutls_check_version (NULL),
+               "Simon Josefsson", (char *) NULL);
+}
diff --git a/src/ocsptool.gaa b/src/ocsptool.gaa
new file mode 100644 (file)
index 0000000..1f0195d
--- /dev/null
@@ -0,0 +1,76 @@
+#{
+
+/* C declarations */
+
+#include <config.h>
+#ifdef _WIN32
+# include <io.h>
+#endif
+
+#include "ocsptool-common.h"
+
+#}
+
+helpnode "Ocsptool help\nUsage : ocsptool [options]"
+
+#int action;
+
+option (e, verify-response) { $action = ACTION_VERIFY_RESP; } "Verify response."
+
+option (i, request-info) { $action = ACTION_REQ_INFO; } "Print information on a OCSP request."
+
+option (j, response-info) { $action = ACTION_RESP_INFO; } "Print information on a OCSP response."
+
+option (q, generate-request) { $action = ACTION_GEN_REQ; } "Generate a OCSP request."
+
+#int nononce;
+option (no-nonce) { $nononce = 1 } "don't add nonce to OCSP request."
+
+#char *issuer;
+option (load-issuer) STR "FILE" { $issuer = $1 } "read issuer certificate from FILE."
+
+#char *cert;
+option (load-cert) STR "FILE" { $cert = $1 } "read certificate to check from FILE."
+
+#char *trust;
+option (load-trust) STR "FILE" { $trust = $1 } "read OCSP trust anchors from FILE."
+
+#char *signer;
+option (load-signer) STR "FILE" { $signer = $1 } "read OCSP response signer from FILE."
+
+#int inder;
+option (inder) { $inder=1 } "Use DER format for input certificates."
+
+#char *req;
+option (Q, load-request) STR "FILE" { $req = $1 } "read DER encoded OCSP request from FILE."
+
+#char *resp;
+option (S, load-response) STR "FILE" { $resp = $1 } "read DER encoded OCSP response from FILE."
+
+#char *outfile;
+option (outfile) STR "FILE" { $outfile = $1 } "Output file."
+
+#char *infile;
+option (infile) STR "FILE" { $infile = $1 } "Input file."
+
+#int verbose;
+option (V, verbose) { $verbose = 1 } "More verbose output."
+
+#int debug;
+option (d, debug) INT "integer" { $debug = $1 } "Enable debugging"
+
+option (v, version) { ocsptool_version(); exit(0); } "prints the program's version number"
+option (h, help) { gaa_help(); exit(0); } "shows this help text"
+
+init { $action = ACTION_NONE;
+       $nononce = 0;
+       $inder = 0;
+       $issuer = NULL;
+       $cert = NULL;
+       $trust = NULL;
+       $req = NULL;
+       $resp = NULL;
+       $outfile = NULL;
+       $infile = NULL;
+       $verbose = 0;
+       $debug = 0; }
index f7ab496d31c8456c9adb8aa11e7191b96153d9c0..70f894638c6af1b3960d9c238c758e3c89074939 100644 (file)
@@ -68,6 +68,10 @@ ctests = mini-deflate simple gc set_pkcs12_cred certder certuniqueid \
         mini-x509-rehandshake rng-fork mini-eagain-dtls        \
         x509cert x509cert-tl infoaccess rsa-encrypt-decrypt
 
+if ENABLE_OCSP
+ctests += ocsp
+endif
+
 if ENABLE_OPENSSL
 ctests +=  openssl
 openssl_LDADD = $(LDADD) ../extra/libgnutls-openssl.la
diff --git a/tests/ocsp.c b/tests/ocsp.c
new file mode 100644 (file)
index 0000000..7f4235a
--- /dev/null
@@ -0,0 +1,1480 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/ocsp.h>
+#include <gnutls/x509.h>
+
+#include "utils.h"
+
+/* sample request */
+
+static void
+tls_log_func (int level, const char *str)
+{
+  fprintf (stderr, "|<%d>| %s", level, str);
+}
+
+#define REQ1 "\x30\x67\x30\x65\x30\x3e\x30\x3c\x30\x3a\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14\x13\x9d\xa0\x9e\xf4\x32\xab\x8f\xe2\x89\x56\x67\xfa\xd0\xd4\xe3\x35\x86\x71\xb9\x04\x14\x5d\xa7\xdd\x70\x06\x51\x32\x7e\xe7\xb6\x6d\xb3\xb5\xe5\xe0\x60\xea\x2e\x4d\xef\x02\x01\x1d\xa2\x23\x30\x21\x30\x1f\x06\x09\x2b\x06\x01\x05\x05\x07\x30\x01\x02\x04\x12\x04\x10\x35\xc5\xe3\x50\xc3\xcf\x04\x33\xcc\x9e\x06\x3a\x9a\x18\x80\xcc"
+
+static const gnutls_datum_t req1 =
+  { (unsigned char *) REQ1, sizeof (REQ1) - 1 };
+
+#define REQ1INFO                                                       \
+  "OCSP Request Information:\n"                                                \
+  "    Version: 1\n"                                                   \
+  "    Request List:\n"                                                \
+  "            Certificate ID:\n"                                      \
+  "                    Hash Algorithm: SHA1\n"                         \
+  "                    Issuer Name Hash: 139da09ef432ab8fe2895667fad0d4e3358671b9\n" \
+  "                    Issuer Key Hash: 5da7dd700651327ee7b66db3b5e5e060ea2e4def\n" \
+  "                    Serial Number: 1d\n"                            \
+  "    Extensions:\n"                                                  \
+  "            Nonce: 35c5e350c3cf0433cc9e063a9a1880cc\n"
+
+#define REQ1NONCE "\x04\x10\x35\xc5\xe3\x50\xc3\xcf\x04\x33\xcc\x9e\x06\x3a\x9a\x18\x80\xcc"
+
+#define REQ1INH "\x13\x9d\xa0\x9e\xf4\x32\xab\x8f\xe2\x89\x56\x67\xfa\xd0\xd4\xe3\x35\x86\x71\xb9"
+#define REQ1IKH "\x5d\xa7\xdd\x70\x06\x51\x32\x7e\xe7\xb6\x6d\xb3\xb5\xe5\xe0\x60\xea\x2e\x4d\xef"
+#define REQ1SN "\x1d"
+
+/* sample response */
+
+#define RESP1 "\x30\x03\x0a\x01\x01"
+
+static const gnutls_datum_t resp1 =
+  { (unsigned char*) RESP1, sizeof (RESP1) - 1 };
+
+#define RESP1INFO                                                      \
+  "OCSP Response Information:\n"                                       \
+  "    Response Status: malformedRequest\n"
+
+#define RESP2 "\x30\x82\x06\x8C\x0A\x01\x00\xA0\x82\x06\x85\x30\x82\x06\x81\x06\x09\x2B\x06\x01\x05\x05\x07\x30\x01\x01\x04\x82\x06\x72\x30\x82\x06\x6E\x30\x82\x01\x07\xA1\x69\x30\x67\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x48\x31\x19\x30\x17\x06\x03\x55\x04\x0A\x13\x10\x4C\x69\x6E\x75\x78\x20\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x31\x1F\x30\x1D\x06\x03\x55\x04\x0B\x13\x16\x4F\x43\x53\x50\x20\x53\x69\x67\x6E\x69\x6E\x67\x20\x41\x75\x74\x68\x6F\x72\x69\x74\x79\x31\x1C\x30\x1A\x06\x03\x55\x04\x03\x13\x13\x6F\x63\x73\x70\x2E\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x6F\x72\x67\x18\x0F\x32\x30\x31\x31\x30\x39\x32\x37\x30\x39\x35\x34\x32\x38\x5A\x30\x64\x30\x62\x30\x3A\x30\x09\x06\x05\x2B\x0E\x03\x02\x1A\x05\x00\x04\x14\x13\x9D\xA0\x9E\xF4\x32\xAB\x8F\xE2\x89\x56\x67\xFA\xD0\xD4\xE3\x35\x86\x71\xB9\x04\x14\x5D\xA7\xDD\x70\x06\x51\x32\x7E\xE7\xB6\x6D\xB3\xB5\xE5\xE0\x60\xEA\x2E\x4D\xEF\x02\x01\x1D\x80\x00\x18\x0F\x32\x30\x31\x31\x30\x39\x32\x37\x30\x39\x35\x34\x32\x38\x5A\xA0\x11\x18\x0F\x32\x30\x31\x31\x30\x39\x32\x37\x30\x39\x35\x39\x32\x38\x5A\xA1\x23\x30\x21\x30\x1F\x06\x09\x2B\x06\x01\x05\x05\x07\x30\x01\x02\x04\x12\x04\x10\x16\x89\x7D\x91\x3A\xB5\x25\xA4\x45\xFE\xC9\xFD\xC2\xE5\x08\xA4\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x4E\xAD\x6B\x2B\xF7\xF2\xBF\xA9\x23\x1E\x3A\x0B\x06\xDB\x55\x53\x2B\x64\x54\x11\x32\xBF\x60\xF7\x4F\xE0\x8E\x9B\xA0\xA2\x4C\x79\xC3\x2A\xE0\x43\xF7\x40\x1A\xDC\xB9\xB4\x25\xEF\x48\x01\x97\x8C\xF5\x1E\xDB\xD1\x30\x37\x73\x69\xD6\xA7\x7A\x2D\x8E\xDE\x5C\xAA\xEA\x39\xB9\x52\xAA\x25\x1E\x74\x7D\xF9\x78\x95\x8A\x92\x1F\x98\x21\xF4\x60\x7F\xD3\x28\xEE\x47\x9C\xBF\xE2\x5D\xF6\x3F\x68\x0A\xD6\xFF\x08\xC1\xDC\x95\x1E\x29\xD7\x3E\x85\xD5\x65\xA4\x4B\xC0\xAF\xC3\x78\xAB\x06\x98\x88\x19\x8A\x64\xA6\x83\x91\x87\x13\xDB\x17\xCC\x46\xBD\xAB\x4E\xC7\x16\xD1\xF8\x35\xFD\x27\xC8\xF6\x6B\xEB\x37\xB8\x08\x6F\xE2\x6F\xB4\x7E\xD5\x68\xDB\x7F\x5D\x5E\x36\x38\xF2\x77\x59\x13\xE7\x3E\x4D\x67\x5F\xDB\xA2\xF5\x5D\x7C\xBF\xBD\xB5\x37\x33\x51\x36\x63\xF8\x21\x1E\xFC\x73\x8F\x32\x69\xBB\x97\xA7\xBD\xF1\xB6\xE0\x40\x09\x68\xEA\xD5\x93\xB8\xBB\x39\x8D\xA8\x16\x1B\xBF\x04\x7A\xBC\x18\x43\x01\xE9\x3C\x19\x5C\x4D\x4B\x98\xD8\x23\x37\x39\xA4\xC4\xDD\xED\x9C\xEC\x37\xAB\x66\x44\x9B\xE7\x5B\x5D\x32\xA2\xDB\xA6\x0B\x3B\x8C\xE1\xF5\xDB\xCB\x7D\x58\xA0\x82\x04\x4B\x30\x82\x04\x47\x30\x82\x04\x43\x30\x82\x03\x2B\xA0\x03\x02\x01\x02\x02\x01\x1E\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0B\x05\x00\x30\x45\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x48\x31\x19\x30\x17\x06\x03\x55\x04\x0A\x13\x10\x4C\x69\x6E\x75\x78\x20\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x31\x1B\x30\x19\x06\x03\x55\x04\x03\x13\x12\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x20\x52\x6F\x6F\x74\x20\x43\x41\x30\x1E\x17\x0D\x30\x39\x31\x31\x32\x34\x31\x32\x35\x31\x35\x33\x5A\x17\x0D\x31\x34\x31\x31\x32\x33\x31\x32\x35\x31\x35\x33\x5A\x30\x67\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x48\x31\x19\x30\x17\x06\x03\x55\x04\x0A\x13\x10\x4C\x69\x6E\x75\x78\x20\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x31\x1F\x30\x1D\x06\x03\x55\x04\x0B\x13\x16\x4F\x43\x53\x50\x20\x53\x69\x67\x6E\x69\x6E\x67\x20\x41\x75\x74\x68\x6F\x72\x69\x74\x79\x31\x1C\x30\x1A\x06\x03\x55\x04\x03\x13\x13\x6F\x63\x73\x70\x2E\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x6F\x72\x67\x30\x82\x01\x22\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00\x03\x82\x01\x0F\x00\x30\x82\x01\x0A\x02\x82\x01\x01\x00\xBC\x05\x3E\x4B\xBE\xC6\xB1\x33\x48\x0E\xC3\xD4\x0C\xEF\x83\x0B\xBD\xBC\x57\x5F\x14\xEF\xF5\x6D\x0B\xFF\xFA\x01\x9C\xFA\x21\x6D\x5C\xAE\x79\x29\x74\xFE\xBD\xAB\x70\x87\x98\x6B\x48\x35\x79\xE3\xE0\xC1\x14\x41\x1F\x0A\xF7\xE7\xA3\xA6\xDA\x6B\xFF\xCD\x74\xE9\x95\x00\x38\xAA\xD6\x3A\x60\xC6\x64\xA1\xE6\x02\x39\x58\x4E\xFD\xF2\x78\x08\x63\xB6\xD7\x7A\x96\x79\x62\x18\x39\xEE\x27\x8D\x3B\xA2\x3D\x48\x88\xDB\x43\xD6\x6A\x77\x20\x6A\x27\x39\x50\xE0\x02\x50\x19\xF2\x7A\xCF\x78\x23\x99\x01\xD4\xE5\xB1\xD1\x31\xE6\x6B\x84\xAF\xD0\x77\x41\x46\x85\xB0\x3B\xE6\x6A\x00\x0F\x3B\x7E\x95\x7F\x59\xA8\x22\xE8\x49\x49\x05\xC8\xCB\x6C\xEE\x47\xA7\x2D\xC9\x74\x5B\xEB\x8C\xD5\x99\xC2\xE2\x70\xDB\xEA\x87\x43\x84\x0E\x4F\x83\x1C\xA6\xEB\x1F\x22\x38\x17\x69\x9B\x72\x12\x95\x48\x71\xB2\x7B\x92\x73\x52\xAB\xE3\x1A\xA5\xD3\xF4\x44\x14\xBA\xC3\x35\xDA\x91\x6C\x7D\xB4\xC2\x00\x07\xD8\x0A\x51\xF1\x0D\x4C\xD9\x7A\xD1\x99\xE6\xA8\x8D\x0A\x80\xA8\x91\xDD\x8A\xA2\x6B\xF6\xDB\xB0\x3E\xC9\x71\xA9\xE0\x39\xC3\xA3\x58\x0D\x87\xD0\xB2\xA7\x9C\xB7\x69\x02\x03\x01\x00\x01\xA3\x82\x01\x1A\x30\x82\x01\x16\x30\x09\x06\x03\x55\x1D\x13\x04\x02\x30\x00\x30\x0B\x06\x03\x55\x1D\x0F\x04\x04\x03\x02\x03\xA8\x30\x1D\x06\x03\x55\x1D\x0E\x04\x16\x04\x14\x34\x91\x6E\x91\x32\xBF\x35\x25\x43\xCC\x28\x74\xEF\x82\xC2\x57\x92\x79\x13\x73\x30\x6D\x06\x03\x55\x1D\x23\x04\x66\x30\x64\x80\x14\x5D\xA7\xDD\x70\x06\x51\x32\x7E\xE7\xB6\x6D\xB3\xB5\xE5\xE0\x60\xEA\x2E\x4D\xEF\xA1\x49\xA4\x47\x30\x45\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x48\x31\x19\x30\x17\x06\x03\x55\x04\x0A\x13\x10\x4C\x69\x6E\x75\x78\x20\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x31\x1B\x30\x19\x06\x03\x55\x04\x03\x13\x12\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x20\x52\x6F\x6F\x74\x20\x43\x41\x82\x01\x00\x30\x1E\x06\x03\x55\x1D\x11\x04\x17\x30\x15\x82\x13\x6F\x63\x73\x70\x2E\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x6F\x72\x67\x30\x13\x06\x03\x55\x1D\x25\x04\x0C\x30\x0A\x06\x08\x2B\x06\x01\x05\x05\x07\x03\x09\x30\x39\x06\x03\x55\x1D\x1F\x04\x32\x30\x30\x30\x2E\xA0\x2C\xA0\x2A\x86\x28\x68\x74\x74\x70\x3A\x2F\x2F\x63\x72\x6C\x2E\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x6F\x72\x67\x2F\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x63\x72\x6C\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0B\x05\x00\x03\x82\x01\x01\x00\x6D\x78\xD7\x66\x90\xA6\xEB\xDD\xB5\x09\x48\xA4\xDA\x27\xFA\xAC\xB1\xBC\x8F\x8C\xBE\xCC\x8C\x09\xA2\x40\x0D\x6C\x4A\xAE\x72\x22\x1E\xC8\xAF\x6D\xF1\x12\xAF\xD7\x40\x51\x79\xD4\xDD\xB2\x0C\xDB\x97\x84\xB6\x24\xD5\xF5\xA8\xBB\xC0\x4B\xF9\x7F\x71\xF7\xB0\x65\x42\x4A\x7D\xFE\x76\x7E\x05\xD2\x46\xB8\x7D\xB3\x39\x4C\x5C\xB1\xFA\xB9\xEE\x3B\x70\x33\x39\x57\x1A\xB9\x95\x51\x33\x00\x25\x1B\x4C\xAA\xB4\xA7\x55\xAF\x63\x6D\x6F\x88\x17\x6A\x7F\xB0\x97\xDE\x49\x14\x6A\x27\x6A\xB0\x42\x80\xD6\xA6\x9B\xEF\x04\x5E\x11\x7D\xD5\x8E\x54\x20\xA2\x76\xD4\x66\x58\xAC\x9C\x12\xD3\xF5\xCA\x54\x98\xCA\x21\xEC\xC1\x55\xA1\x2F\x68\x0B\x5D\x04\x50\xD2\x5E\x70\x25\xD8\x13\xD9\x44\x51\x0E\x8A\x42\x08\x18\x84\xE6\x61\xCE\x5A\x7D\x7B\x81\x35\x90\xC3\xD4\x9D\x19\xB6\x37\xEE\x8F\x63\x5C\xDA\xD8\xF0\x64\x60\x39\xEB\x9B\x1C\x54\x66\x75\x76\xB5\x0A\x58\xB9\x3F\x91\xE1\x21\x9C\xA0\x50\x15\x97\xB6\x7E\x41\xBC\xD0\xC4\x21\x4C\xF5\xD7\xF0\x13\xF8\x77\xE9\x74\xC4\x8A\x0E\x20\x17\x32\xAE\x38\xC2\xA5\xA8\x62\x85\x17\xB1\xA2\xD3\x22\x9F\x95\xB7\xA3\x4C"
+
+#define RESP2INFO                              \
+  "OCSP Response Information:\n"               \
+  "    Response Status: Successful\n"          \
+  "    Response Type: Basic OCSP Response\n"   \
+  "    Version: 1\n" \
+  "    Responder ID: C=CH,O=Linux strongSwan,OU=OCSP Signing Authority,CN=ocsp.strongswan.org\n" \
+  "    Produced At: Tue Sep 27 09:54:28 UTC 2011\n" \
+  "    Responses:\n" \
+  "            Certificate ID:\n" \
+  "                    Hash Algorithm: SHA1\n" \
+  "                    Issuer Name Hash: 139da09ef432ab8fe2895667fad0d4e3358671b9\n" \
+  "                    Issuer Key Hash: 5da7dd700651327ee7b66db3b5e5e060ea2e4def\n" \
+  "                    Serial Number: 1d\n" \
+  "            Certificate Status: good\n" \
+  "            This Update: Tue Sep 27 09:54:28 UTC 2011\n" \
+  "            Next Update: Tue Sep 27 09:59:28 UTC 2011\n" \
+  "    Extensions:\n" \
+  "            Nonce: 16897d913ab525a445fec9fdc2e508a4\n" \
+  "    Signature Algorithm: RSA-SHA1\n" \
+  "    Signature:\n" \
+  "            4e:ad:6b:2b:f7:f2:bf:a9:23:1e:3a:0b:06:db:55:53\n" \
+  "            2b:64:54:11:32:bf:60:f7:4f:e0:8e:9b:a0:a2:4c:79\n" \
+  "            c3:2a:e0:43:f7:40:1a:dc:b9:b4:25:ef:48:01:97:8c\n" \
+  "            f5:1e:db:d1:30:37:73:69:d6:a7:7a:2d:8e:de:5c:aa\n" \
+  "            ea:39:b9:52:aa:25:1e:74:7d:f9:78:95:8a:92:1f:98\n" \
+  "            21:f4:60:7f:d3:28:ee:47:9c:bf:e2:5d:f6:3f:68:0a\n" \
+  "            d6:ff:08:c1:dc:95:1e:29:d7:3e:85:d5:65:a4:4b:c0\n" \
+  "            af:c3:78:ab:06:98:88:19:8a:64:a6:83:91:87:13:db\n" \
+  "            17:cc:46:bd:ab:4e:c7:16:d1:f8:35:fd:27:c8:f6:6b\n" \
+  "            eb:37:b8:08:6f:e2:6f:b4:7e:d5:68:db:7f:5d:5e:36\n" \
+  "            38:f2:77:59:13:e7:3e:4d:67:5f:db:a2:f5:5d:7c:bf\n" \
+  "            bd:b5:37:33:51:36:63:f8:21:1e:fc:73:8f:32:69:bb\n" \
+  "            97:a7:bd:f1:b6:e0:40:09:68:ea:d5:93:b8:bb:39:8d\n" \
+  "            a8:16:1b:bf:04:7a:bc:18:43:01:e9:3c:19:5c:4d:4b\n" \
+  "            98:d8:23:37:39:a4:c4:dd:ed:9c:ec:37:ab:66:44:9b\n" \
+  "            e7:5b:5d:32:a2:db:a6:0b:3b:8c:e1:f5:db:cb:7d:58\n"
+  /* cut */
+
+static const gnutls_datum_t resp2 =
+  { (unsigned char*) RESP2, sizeof (RESP2) - 1 };
+
+static unsigned char issuer_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIIDuDCCAqCgAwIBAgIBADANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ\n"
+  "MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS\n"
+  "b290IENBMB4XDTA0MDkxMDEwMDExOFoXDTE5MDkwNzEwMDExOFowRTELMAkGA1UE\n"
+  "BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xGzAZBgNVBAMTEnN0cm9u\n"
+  "Z1N3YW4gUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL/y\n"
+  "X2LqPVZuWLPIeknK86xhz6ljd3NNhC2z+P1uoCP3sBMuZiZQEjFzhnKcbXxCeo2f\n"
+  "FnvhOOjrrisSuVkzuu82oxXD3fIkzuS7m9V4E10EZzgmKWIf+WuNRfbgAuUINmLc\n"
+  "4YGAXBQLPyzpP4Ou48hhz/YQo58Bics6PHy5v34qCVROIXDvqhj91P8g+pS+F21/\n"
+  "7P+CH2jRcVIEHZtG8M/PweTPQ95dPzpYd2Ov6SZ/U7EWmbMmT8VcUYn1aChxFmy5\n"
+  "gweVBWlkH6MP+1DeE0/tL5c87xo5KCeGK8Tdqpe7sBRC4pPEEHDQciTUvkeuJ1Pr\n"
+  "K+1LwdqRxo7HgMRiDw8CAwEAAaOBsjCBrzASBgNVHRMBAf8ECDAGAQH/AgEBMAsG\n"
+  "A1UdDwQEAwIBBjAdBgNVHQ4EFgQUXafdcAZRMn7ntm2zteXgYOouTe8wbQYDVR0j\n"
+  "BGYwZIAUXafdcAZRMn7ntm2zteXgYOouTe+hSaRHMEUxCzAJBgNVBAYTAkNIMRkw\n"
+  "FwYDVQQKExBMaW51eCBzdHJvbmdTd2FuMRswGQYDVQQDExJzdHJvbmdTd2FuIFJv\n"
+  "b3QgQ0GCAQAwDQYJKoZIhvcNAQELBQADggEBACOSmqEBtBLR9aV3UyCI8gmzR5in\n"
+  "Lte9aUXXS+qis6F2h2Stf4sN+Nl6Gj7REC6SpfEH4wWdwiUL5J0CJhyoOjQuDl3n\n"
+  "1Dw3dE4/zqMZdyDKEYTU75TmvusNJBdGsLkrf7EATAjoi/nrTOYPPhSUZvPp/D+Y\n"
+  "vORJ9Ej51GXlK1nwEB5iA8+tDYniNQn6BD1MEgIejzK+fbiy7braZB1kqhoEr2Si\n"
+  "7luBSnU912sw494E88a2EWbmMvg2TVHPNzCpVkpNk7kifCiwmw9VldkqYy9y/lCa\n"
+  "Epyp7lTfKw7cbD04Vk8QJW782L6Csuxkl346b17wmOqn8AZips3tFsuAY3w=\n"
+  "-----END CERTIFICATE-----\n";
+const gnutls_datum_t issuer_data = { issuer_pem, sizeof (issuer_pem) };
+
+static unsigned char subject_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIIEIjCCAwqgAwIBAgIBHTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ\n"
+  "MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS\n"
+  "b290IENBMB4XDTA5MDgyNzEwNDQ1MVoXDTE0MDgyNjEwNDQ1MVowWjELMAkGA1UE\n"
+  "BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xETAPBgNVBAsTCFJlc2Vh\n"
+  "cmNoMR0wGwYDVQQDFBRjYXJvbEBzdHJvbmdzd2FuLm9yZzCCASIwDQYJKoZIhvcN\n"
+  "AQEBBQADggEPADCCAQoCggEBANBdWU+BF7x4lyo+xHnr4UAOU89yQQuT5vdPoXzx\n"
+  "6kRPsjYAuuktgXR+SaLkQHw/YRgDPSKj5nzmmlOQf/rWRr+8O2q+C92aUICmkNvZ\n"
+  "Gamo5w2WlOMZ6T5dk2Hv+QM6xT/GzWyVr1dMYu/7tywD1Bw7aW/HqkRESDu6q95V\n"
+  "Wu+Lzg6XlxCNEez0YsZrN/fC6BL2qzKAqMBbIHFW8OOnh+nEY4IF5AzkZnFrw12G\n"
+  "I72Z882pw97lyKwZhSz/GMQFBJx+rnNdw5P1IJwTlG5PUdoDCte/Mcr1iiA+zOov\n"
+  "x55x1GoGxduoXWU5egrf1MtalRf9Pc8Xr4q3WEKTAmsZrVECAwEAAaOCAQYwggEC\n"
+  "MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgOoMB0GA1UdDgQWBBQfoamI2WSMtaCiVGQ5\n"
+  "tPI9dF1ufDBtBgNVHSMEZjBkgBRdp91wBlEyfue2bbO15eBg6i5N76FJpEcwRTEL\n"
+  "MAkGA1UEBhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xGzAZBgNVBAMT\n"
+  "EnN0cm9uZ1N3YW4gUm9vdCBDQYIBADAfBgNVHREEGDAWgRRjYXJvbEBzdHJvbmdz\n"
+  "d2FuLm9yZzA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4u\n"
+  "b3JnL3N0cm9uZ3N3YW4uY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQC8pqX3KrSzKeul\n"
+  "GdzydAV4hGwYB3WiB02oJ2nh5MJBu7J0Kn4IVkvLUHSSZhSRxx55tQZfdYqtXVS7\n"
+  "ZuyG+6rV7sb595SIRwfkLAdjbvv0yZIl4xx8j50K3yMR+9aXW1NSGPEkb8BjBUMr\n"
+  "F2kjGTOqomo8OIzyI369z9kJrtEhnS37nHcdpewZC1wHcWfJ6wd9wxmz2dVXmgVQ\n"
+  "L2BjXd/BcpLFaIC4h7jMXQ5FURjnU7K9xSa4T8PpR6FrQhOcIYBXAp94GiM8JqmK\n"
+  "ZBGUpeP+3cy4i3DV18Kyr64Q4XZlzhZClNE43sgMqiX88dc3znpDzT7T51j+d+9k\n"
+  "Rf5Z0GOR\n"
+  "-----END CERTIFICATE-----\n";
+const gnutls_datum_t subject_data = { subject_pem, sizeof (subject_pem) };
+
+/* For testing verify functions. */
+
+#define BLOG_RESP "\x30\x82\x06\xF8\x0A\x01\x00\xA0\x82\x06\xF1\x30\x82\x06\xED\x06\x09\x2B\x06\x01\x05\x05\x07\x30\x01\x01\x04\x82\x06\xDE\x30\x82\x06\xDA\x30\x82\x01\x25\xA1\x7E\x30\x7C\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x41\x55\x31\x0C\x30\x0A\x06\x03\x55\x04\x08\x13\x03\x4E\x53\x57\x31\x0F\x30\x0D\x06\x03\x55\x04\x07\x13\x06\x53\x79\x64\x6E\x65\x79\x31\x14\x30\x12\x06\x03\x55\x04\x0A\x13\x0B\x43\x41\x63\x65\x72\x74\x20\x49\x6E\x63\x2E\x31\x1E\x30\x1C\x06\x03\x55\x04\x0B\x13\x15\x53\x65\x72\x76\x65\x72\x20\x41\x64\x6D\x69\x6E\x69\x73\x74\x72\x61\x74\x69\x6F\x6E\x31\x18\x30\x16\x06\x03\x55\x04\x03\x13\x0F\x6F\x63\x73\x70\x2E\x63\x61\x63\x65\x72\x74\x2E\x6F\x72\x67\x18\x0F\x32\x30\x31\x32\x30\x31\x31\x33\x30\x38\x35\x30\x34\x32\x5A\x30\x66\x30\x64\x30\x3C\x30\x09\x06\x05\x2B\x0E\x03\x02\x1A\x05\x00\x04\x14\xF2\x2A\x62\x16\x93\xA6\xDA\x5A\xD0\xB9\x8D\x3A\x13\x5E\x35\xD1\xEB\x18\x36\x61\x04\x14\x75\xA8\x71\x60\x4C\x88\x13\xF0\x78\xD9\x89\x77\xB5\x6D\xC5\x89\xDF\xBC\xB1\x7A\x02\x03\x00\xBC\xE0\x80\x00\x18\x0F\x32\x30\x31\x32\x30\x31\x31\x33\x30\x37\x32\x30\x34\x39\x5A\xA0\x11\x18\x0F\x32\x30\x31\x32\x30\x31\x31\x35\x30\x38\x35\x30\x34\x32\x5A\xA1\x2A\x30\x28\x30\x26\x06\x09\x2B\x06\x01\x05\x05\x07\x30\x01\x02\x04\x19\x04\x17\x73\x69\xD2\xC5\x6F\xC7\x7E\x2E\xB0\x2F\xCC\xC3\xE2\x80\xD6\x2A\xCE\xD3\xDE\x8F\x27\x1B\xB2\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x3E\x50\x9D\xE9\xA2\xE0\xCA\x33\x88\x9B\x28\x7E\xE7\xA4\xAF\xDA\xBB\x75\x2D\xD9\x66\xA6\xD5\xFA\x17\x56\xC0\x3B\xDD\x74\xB6\x7E\x42\x2C\x28\xD0\x73\x91\x54\x69\xFA\xCF\xD8\xC7\x74\x1C\x5D\xBC\x8E\xCD\xE3\x0E\xD5\x3F\x80\x71\x9C\x95\x53\xC4\xD1\x95\x63\x5D\x72\xCE\xCC\x77\x9D\x7C\xAD\x47\x3F\x34\xDA\x90\x80\xC5\x15\xE1\x2B\xEE\x98\x57\xA3\xA7\x9F\xA2\xC3\xF5\x5E\xF7\x13\x26\x52\xDA\x09\x38\x5B\x18\x91\x07\x38\xCF\x09\xDA\x08\xED\x80\x4F\x26\x3A\xB9\xBE\xF6\xED\x65\x3F\xB1\x3A\x6D\xA3\x87\x22\xA3\x2A\xA5\x99\xCC\x06\xF3\x5A\xD5\x34\xFB\x9E\x32\x28\xC3\x3E\xF4\xAF\x33\x02\xCF\x6A\x74\x73\x17\x24\x17\x41\x0D\x7E\x86\x79\x83\x34\xE8\x82\x0A\x0D\x21\xED\xCB\x3B\xB7\x31\x64\xC9\xB6\x1E\xC7\x0C\x75\xCE\xBA\xB7\xDC\xB2\x67\x96\x2B\xAD\xBF\x86\x22\x81\x54\x66\xBA\x68\x89\xD7\x7E\x35\x60\x93\xEC\x6B\xD8\x59\x23\xA0\xD0\x95\x55\x8F\x93\x52\x48\x4E\x48\xCB\x92\xE9\x67\x71\x60\x07\xC9\xA3\x3B\xAC\xD1\xEA\x5B\x71\xDB\xC1\x94\x79\x85\x55\x8C\x03\x61\x9E\xC7\xD6\x32\x40\xFA\xDD\xF6\xC9\xF8\xE0\xFF\x4D\xAC\x54\xED\x61\xFE\xB2\xA0\x82\x04\x99\x30\x82\x04\x95\x30\x82\x04\x91\x30\x82\x02\x79\xA0\x03\x02\x01\x02\x02\x03\x00\xDC\xA6\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x30\x54\x31\x14\x30\x12\x06\x03\x55\x04\x0A\x13\x0B\x43\x41\x63\x65\x72\x74\x20\x49\x6E\x63\x2E\x31\x1E\x30\x1C\x06\x03\x55\x04\x0B\x13\x15\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x43\x41\x63\x65\x72\x74\x2E\x6F\x72\x67\x31\x1C\x30\x1A\x06\x03\x55\x04\x03\x13\x13\x43\x41\x63\x65\x72\x74\x20\x43\x6C\x61\x73\x73\x20\x33\x20\x52\x6F\x6F\x74\x30\x1E\x17\x0D\x31\x31\x30\x38\x32\x33\x30\x30\x30\x38\x33\x37\x5A\x17\x0D\x31\x33\x30\x38\x32\x32\x30\x30\x30\x38\x33\x37\x5A\x30\x7C\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x41\x55\x31\x0C\x30\x0A\x06\x03\x55\x04\x08\x13\x03\x4E\x53\x57\x31\x0F\x30\x0D\x06\x03\x55\x04\x07\x13\x06\x53\x79\x64\x6E\x65\x79\x31\x14\x30\x12\x06\x03\x55\x04\x0A\x13\x0B\x43\x41\x63\x65\x72\x74\x20\x49\x6E\x63\x2E\x31\x1E\x30\x1C\x06\x03\x55\x04\x0B\x13\x15\x53\x65\x72\x76\x65\x72\x20\x41\x64\x6D\x69\x6E\x69\x73\x74\x72\x61\x74\x69\x6F\x6E\x31\x18\x30\x16\x06\x03\x55\x04\x03\x13\x0F\x6F\x63\x73\x70\x2E\x63\x61\x63\x65\x72\x74\x2E\x6F\x72\x67\x30\x82\x01\x22\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00\x03\x82\x01\x0F\x00\x30\x82\x01\x0A\x02\x82\x01\x01\x00\x9C\xC6\xD4\x6F\xE4\x23\xC7\xC3\x70\x4B\x75\x1F\xE4\xFC\xAE\xF6\x62\xC4\x60\xA1\xD6\xCF\xF9\x47\x40\x38\xD9\xAF\x06\xF5\xB3\x87\x09\xBA\x07\xC8\x7A\x3B\xE3\x3A\xE2\xC1\x6B\xDB\x0E\x9B\x7B\xB4\x98\x04\x40\x88\xC8\xE4\x20\x34\x9D\x5F\x94\xAE\x0C\xA0\x05\xA1\x74\x10\x3F\x1F\x93\x6D\xC5\xA0\xCE\x29\xB0\x2A\x03\x6E\xED\x3B\xD1\x9A\x7A\xF7\x0F\xA7\xB7\x39\xD7\xC3\xB4\xDE\x15\x67\x94\xF2\xEF\xB0\xDD\x5F\xE3\xC9\xD8\xD2\x34\x0E\x5D\x44\xDF\xBF\x99\xD8\x5E\x60\xF4\x39\x24\x8A\xFD\x5D\xC8\x46\x8D\x0A\xB1\x60\x7A\x4F\xD5\x27\x30\x60\x9E\x13\x06\xF8\x3A\xAA\xB3\xBB\x33\x34\x6F\x84\x81\x7E\x5C\xCC\x12\x89\xF2\xFE\x6E\x93\x83\xFA\x8B\xEE\xAB\x36\x4C\xB6\x40\xA9\xEE\xFB\xF8\x16\x5A\x55\xD1\x64\x0D\x49\xDA\x04\xDE\xD1\xC8\xCA\xEE\x5F\x24\xB1\x79\x78\xB3\x9A\x88\x13\xDD\x68\x51\x39\xE9\x68\x31\xAF\xD7\xF8\x4D\x35\x6D\x60\x58\x04\x42\xBB\x55\x92\x18\xF6\x98\x01\xA5\x74\x3B\xBC\x36\xDB\x20\x68\x18\xB8\x85\xD4\x8B\x6D\x30\x87\x4D\xD6\x33\x2D\x7A\x54\x36\x1D\x57\x42\x14\x5C\x7A\x62\x74\xD5\x1E\x2B\xD5\xBF\x04\xF3\xFF\xEC\x03\xC1\x02\x03\x01\x00\x01\xA3\x44\x30\x42\x30\x0C\x06\x03\x55\x1D\x13\x01\x01\xFF\x04\x02\x30\x00\x30\x27\x06\x03\x55\x1D\x25\x04\x20\x30\x1E\x06\x08\x2B\x06\x01\x05\x05\x07\x03\x02\x06\x08\x2B\x06\x01\x05\x05\x07\x03\x01\x06\x08\x2B\x06\x01\x05\x05\x07\x03\x09\x30\x09\x06\x03\x55\x1D\x11\x04\x02\x30\x00\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x03\x82\x02\x01\x00\x50\xDD\x63\xB7\x1A\x6F\x91\x4C\xE8\x7F\x82\x1A\x27\x04\x81\x05\xBB\xA6\x69\xAC\x41\x7B\x62\xFC\x4B\x08\xDC\x60\xCF\xB2\x5A\xF1\xB4\xB5\x27\x69\x6B\x12\xE4\x07\xC8\x16\xCE\x3B\x42\xCC\x02\x90\x66\x0E\x79\xB8\x6C\x4B\x90\x00\xC5\x66\x64\x92\x2B\x2B\x48\x0E\x84\xC2\x6D\xBF\xA5\xDE\x16\xE3\xBD\x19\xF5\x5C\x93\xA1\x86\x7F\xD9\x89\x78\x6A\x3F\x83\xF0\xAA\xF8\xEA\x1D\xA4\x13\xF7\x2A\x15\x4C\x51\x9C\xC4\xB0\xBE\x58\x66\xCF\x4C\x6C\x3D\x31\xE5\xF9\x54\x21\xCD\xA1\x30\x01\x6A\xB3\x1A\x48\x85\x34\x93\xB8\xF9\x15\x19\x48\x34\x8D\x73\xE7\x03\x50\xAF\xDE\x50\xC7\x62\xAF\x25\x22\x2B\xF6\xE8\x37\x2E\xE4\x71\xA9\x5C\x26\xEA\x79\xCB\x04\x29\x73\x6B\x8F\xDF\x1F\x5C\x41\x52\xC0\x36\xAA\xD7\x7D\x8E\x44\x54\x98\x06\x4C\x63\xA6\x0B\x01\x94\x5D\x0C\x5C\xD4\xCF\xCB\x0B\x7B\x2D\x56\xCC\xBF\x97\x7F\x15\x24\x1D\xBA\xEA\xB7\x97\xB0\x32\xAD\xFC\xEA\x6D\x94\x39\x7A\xE3\x25\x54\xFC\x4A\xF5\x3D\xBD\x2E\xD5\x31\x07\x49\x24\xCC\x92\x69\x0E\x79\xB9\xDF\xDB\x36\xBF\x04\x44\x15\xD0\x46\x99\x8C\xD2\x4C\x94\x38\x0E\x10\x64\x13\xAB\xD9\x1B\x54\x02\x31\x56\x20\xEE\x69\x95\xDF\x39\xBB\xE9\xA7\x6D\xC3\x23\x86\x0B\xD6\x34\x40\x37\xC3\xD4\x41\xA8\x2E\x71\x1D\x6E\x5B\xD7\xC5\x9F\x2A\xE6\x02\x80\xAE\x0A\x28\x69\x63\x4B\x89\x2E\xBD\x4F\x42\x58\xFB\x86\x9A\xA2\x18\xDC\xC6\x32\xC1\x46\xBA\x28\xD2\x8B\xCE\x56\x63\x04\x80\x51\x51\x39\x00\x3B\x00\xB9\x5F\x67\xFA\x90\x1E\xDA\x76\xB5\x31\xA5\xBD\x11\xD2\x5F\xDA\x5D\xD5\xF7\xEE\xAB\xC0\x62\x74\x60\x47\x32\x42\xFD\xB2\x2E\x04\x3A\x2E\xF2\xC8\xB3\x41\xA3\xBD\xFE\x94\x5F\xEF\x6E\xD7\x92\x7C\x1D\x04\xF0\xC6\x53\x8E\x46\xDC\x30\x3A\x35\x5F\x1A\x4B\xEA\x3B\x00\x8B\x97\xB5\xB9\xCE\x71\x6E\x5C\xD5\xA0\x0B\xB1\x33\x08\x89\x61\x23\xCF\x97\x9F\x8F\x9A\x50\xB5\xEC\xCE\x40\x8D\x82\x95\x8B\x79\x26\x66\xF3\xF4\x70\xD8\xEE\x58\xDD\x75\x29\xD5\x6A\x91\x51\x7A\x17\xBC\x4F\xD4\xA3\x45\x7B\x84\xE7\xBE\x69\x53\xC1\xE2\x5C\xC8\x45\xA0\x3A\xEC\xDF\x8A\x1E\xC1\x18\x84\x8B\x7A\x4E\x4E\x9E\x3A\x26\xFE\x5D\x22\xD4\xC5\x14\xBE\xEE\x06\xEB\x05\x4A\x66\xC9\xA4\xB3\x68\x04\xB0\x5D\x25\x54\xB3\x05\xED\x41\xF0\x65\x69\x6D\xA5\x4E\xB7\x97\xD8\xD8\xF5"
+
+static const gnutls_datum_t blog_resp =
+  { (unsigned char*) BLOG_RESP, sizeof (BLOG_RESP) - 1 };
+
+static unsigned char blog_cert_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIIE8DCCAtigAwIBAgIDALzgMA0GCSqGSIb3DQEBBQUAMFQxFDASBgNVBAoTC0NB\n"
+  "Y2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5vcmcxHDAaBgNV\n"
+  "BAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwHhcNMTAxMTE2MjI1MjMzWhcNMTIxMTE1\n"
+  "MjI1MjMzWjAdMRswGQYDVQQDExJibG9nLmpvc2Vmc3Nvbi5vcmcwggEiMA0GCSqG\n"
+  "SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBKA6bm/Kip0i00vU+BOmUF2MBDTwps41c\n"
+  "xKN5bDn7usWZj8loi6BHRPE2WzCVPnPRD1FJXBc4rXL8zZWrCRe1b4A+l8NjPN2o\n"
+  "uUgJvYLXYQ2hXkvxlPBQPKNOudaOAVsahpyxk6g6Z3mskOfqPhxvjutHvMC4fOsJ\n"
+  "1+FstMzvg5SpDd4uYM9m0UK8pbEUSuwW+fxyWqhciSi7kJtdrD6bwx3ub3t9GFkM\n"
+  "9uTzImIslTq19w8AHQsTICNnmNwfUGF5XMUIuxun0HlFt2KUP5G3Qg9Cd18wZFql\n"
+  "RQJvLA3nbVFtmN3M3yKXnGSsEn38ZJvC+UxFuSfYJN9UwgoG6gwhAgMBAAGjggEA\n"
+  "MIH9MAwGA1UdEwEB/wQCMAAwNAYDVR0lBC0wKwYIKwYBBQUHAwIGCCsGAQUFBwMB\n"
+  "BglghkgBhvhCBAEGCisGAQQBgjcKAwMwCwYDVR0PBAQDAgWgMDMGCCsGAQUFBwEB\n"
+  "BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuY2FjZXJ0Lm9yZy8wdQYDVR0R\n"
+  "BG4wbIISYmxvZy5qb3NlZnNzb24ub3JnoCAGCCsGAQUFBwgFoBQMEmJsb2cuam9z\n"
+  "ZWZzc29uLm9yZ4ISYmxvZy5qb3NlZnNzb24ub3JnoCAGCCsGAQUFBwgFoBQMEmJs\n"
+  "b2cuam9zZWZzc29uLm9yZzANBgkqhkiG9w0BAQUFAAOCAgEACQX0KziT81G0XJ4C\n"
+  "SlVumGN0KcVPDjtiUYskMpUvyLF951Q4Uuih0Aa9c0LynyZq8yqr6sW5OTmnRfSU\n"
+  "DuUK5IH+IPq5PU7qteQSIy+63yjMQ+1wye1zfCWI+MyaS54AOn6uZObsr4grq41i\n"
+  "sTwnX8OF/z15dQBjDR18WoehsnbuMz3Ld7+w5UcVWRGDzTyZ7JrYisEywQ7TXcoK\n"
+  "1IlhD1TqwFucH7lIr4mPWNjL7Nw0sw11HN0Syt9H3upcq6lqyEI0ygfNZ9cdxvmX\n"
+  "WqOBxxLc6G/87G4nGW4jw3WrCX7LqSmChlR3SbEC1UhWpaQMQ+mOU5+vXon7blRV\n"
+  "zGJ/1wK8mKu3fKw9rm5TQ1xfJuRABbzsD3BrrUaHlREQQ+i6SCPVFGer6oeAaxyv\n"
+  "so0NCbmBQkcpmUUl0COIR/Lh/YT78PjIEfxaUnUlaZXvCbKPKP2cM8LY7ltEaTgJ\n"
+  "4W6sZi3QNFySzd4sz7J/YhY/jGjqku7TfpN/GOheW8AzKTBlm3WLps1YXys4TKrB\n"
+  "0RStfaPfRJI1PeSlrWl6+kQu/5O8WA8NK0JZ/0Jc4d5LNrtUXo4VU9XCthrxLkgL\n"
+  "3XWgZKFrqJd1UeJJ7OvkRYfI1c5i4oAP5ksuF0SHTpqnXE8K39kUnUx3B+ItJlZP\n"
+  "VXTFhXRc06QwYqYXuYSAmj7/GJk=\n"
+  "-----END CERTIFICATE-----\n";
+const gnutls_datum_t blog_cert_data = { blog_cert_pem,
+                                       sizeof (blog_cert_pem) };
+
+static unsigned char blog_issuer_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIIHWTCCBUGgAwIBAgIDCkGKMA0GCSqGSIb3DQEBCwUAMHkxEDAOBgNVBAoTB1Jv\n"
+  "b3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEiMCAGA1UEAxMZ\n"
+  "Q0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYSc3VwcG9y\n"
+  "dEBjYWNlcnQub3JnMB4XDTExMDUyMzE3NDgwMloXDTIxMDUyMDE3NDgwMlowVDEU\n"
+  "MBIGA1UEChMLQ0FjZXJ0IEluYy4xHjAcBgNVBAsTFWh0dHA6Ly93d3cuQ0FjZXJ0\n"
+  "Lm9yZzEcMBoGA1UEAxMTQ0FjZXJ0IENsYXNzIDMgUm9vdDCCAiIwDQYJKoZIhvcN\n"
+  "AQEBBQADggIPADCCAgoCggIBAKtJNRFIfNImflOUz0Op3SjXQiqL84d4GVh8D57a\n"
+  "iX3h++tykA10oZZkq5+gJJlz2uJVdscXe/UErEa4w75/ZI0QbCTzYZzA8pD6Ueb1\n"
+  "aQFjww9W4kpCz+JEjCUoqMV5CX1GuYrz6fM0KQhF5Byfy5QEHIGoFLOYZcRD7E6C\n"
+  "jQnRvapbjZLQ7N6QxX8KwuPr5jFaXnQ+lzNZ6MMDPWAzv/fRb0fEze5ig1JuLgia\n"
+  "pNkVGJGmhZJHsK5I6223IeyFGmhyNav/8BBdwPSUp2rVO5J+TJAFfpPBLIukjmJ0\n"
+  "FXFuC3ED6q8VOJrU0gVyb4z5K+taciX5OUbjchs+BMNkJyIQKopPWKcDrb60LhPt\n"
+  "XapI19V91Cp7XPpGBFDkzA5CW4zt2/LP/JaT4NsRNlRiNDiPDGCbO5dWOK3z0luL\n"
+  "oFvqTpa4fNfVoIZwQNORKbeiPK31jLvPGpKK5DR7wNhsX+kKwsOnIJpa3yxdUly6\n"
+  "R9Wb7yQocDggL9V/KcCyQQNokszgnMyXS0XvOhAKq3A6mJVwrTWx6oUrpByAITGp\n"
+  "rmB6gCZIALgBwJNjVSKRPFbnr9s6JfOPMVTqJouBWfmh0VMRxXudA/Z0EeBtsSw/\n"
+  "LIaRmXGapneLNGDRFLQsrJ2vjBDTn8Rq+G8T/HNZ92ZCdB6K4/jc0m+YnMtHmJVA\n"
+  "BfvpAgMBAAGjggINMIICCTAdBgNVHQ4EFgQUdahxYEyIE/B42Yl3tW3Fid+8sXow\n"
+  "gaMGA1UdIwSBmzCBmIAUFrUyG9TH8+DmjvO90rA67rI5GNGhfaR7MHkxEDAOBgNV\n"
+  "BAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEiMCAG\n"
+  "A1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYS\n"
+  "c3VwcG9ydEBjYWNlcnQub3JnggEAMA8GA1UdEwEB/wQFMAMBAf8wXQYIKwYBBQUH\n"
+  "AQEEUTBPMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5DQWNlcnQub3JnLzAoBggr\n"
+  "BgEFBQcwAoYcaHR0cDovL3d3dy5DQWNlcnQub3JnL2NhLmNydDBKBgNVHSAEQzBB\n"
+  "MD8GCCsGAQQBgZBKMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuQ0FjZXJ0Lm9y\n"
+  "Zy9pbmRleC5waHA/aWQ9MTAwNAYJYIZIAYb4QgEIBCcWJWh0dHA6Ly93d3cuQ0Fj\n"
+  "ZXJ0Lm9yZy9pbmRleC5waHA/aWQ9MTAwUAYJYIZIAYb4QgENBEMWQVRvIGdldCB5\n"
+  "b3VyIG93biBjZXJ0aWZpY2F0ZSBmb3IgRlJFRSwgZ28gdG8gaHR0cDovL3d3dy5D\n"
+  "QWNlcnQub3JnMA0GCSqGSIb3DQEBCwUAA4ICAQApKIWuRKm5r6R5E/CooyuXYPNc\n"
+  "7uMvwfbiZqARrjY3OnYVBFPqQvX56sAV2KaC2eRhrnILKVyQQ+hBsuF32wITRHhH\n"
+  "Va9Y/MyY9kW50SD42CEH/m2qc9SzxgfpCYXMO/K2viwcJdVxjDm1Luq+GIG6sJO4\n"
+  "D+Pm1yaMMVpyA4RS5qb1MyJFCsgLDYq4Nm+QCaGrvdfVTi5xotSu+qdUK+s1jVq3\n"
+  "VIgv7nSf7UgWyg1I0JTTrKSi9iTfkuO960NAkW4cGI5WtIIS86mTn9S8nK2cde5a\n"
+  "lxuV53QtHA+wLJef+6kzOXrnAzqSjiL2jA3k2X4Ndhj3AfnvlpaiVXPAPHG0HRpW\n"
+  "Q7fDCo1y/OIQCQtBzoyUoPkD/XFzS4pXM+WOdH4VAQDmzEoc53+VGS3FpQyLu7Xt\n"
+  "hbNc09+4ufLKxw0BFKxwWMWMjTPUnWajGlCVI/xI4AZDEtnNp4Y5LzZyo4AQ5OHz\n"
+  "0ctbGsDkgJp8E3MGT9ujayQKurMcvEp4u+XjdTilSKeiHq921F73OIZWWonO1sOn\n"
+  "ebJSoMbxhbQljPI/lrMQ2Y1sVzufb4Y6GIIiNsiwkTjbKqGTqoQ/9SdlrnPVyNXT\n"
+  "d+pLncdBu8fA46A/5H2kjXPmEkvfoXNzczqA6NXLji/L6hOn1kGLrPo8idck9U60\n"
+  "4GGSt/M3mMS+lqO3ig==\n"
+  "-----END CERTIFICATE-----\n";
+const gnutls_datum_t blog_issuer_data = { blog_issuer_pem,
+                                         sizeof (blog_issuer_pem) };
+
+static unsigned char blog_signer_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIIEkTCCAnmgAwIBAgIDANymMA0GCSqGSIb3DQEBBQUAMFQxFDASBgNVBAoTC0NB\n"
+  "Y2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5vcmcxHDAaBgNV\n"
+  "BAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwHhcNMTEwODIzMDAwODM3WhcNMTMwODIy\n"
+  "MDAwODM3WjB8MQswCQYDVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZT\n"
+  "eWRuZXkxFDASBgNVBAoTC0NBY2VydCBJbmMuMR4wHAYDVQQLExVTZXJ2ZXIgQWRt\n"
+  "aW5pc3RyYXRpb24xGDAWBgNVBAMTD29jc3AuY2FjZXJ0Lm9yZzCCASIwDQYJKoZI\n"
+  "hvcNAQEBBQADggEPADCCAQoCggEBAJzG1G/kI8fDcEt1H+T8rvZixGCh1s/5R0A4\n"
+  "2a8G9bOHCboHyHo74zriwWvbDpt7tJgEQIjI5CA0nV+UrgygBaF0ED8fk23FoM4p\n"
+  "sCoDbu070Zp69w+ntznXw7TeFWeU8u+w3V/jydjSNA5dRN+/mdheYPQ5JIr9XchG\n"
+  "jQqxYHpP1ScwYJ4TBvg6qrO7MzRvhIF+XMwSifL+bpOD+ovuqzZMtkCp7vv4FlpV\n"
+  "0WQNSdoE3tHIyu5fJLF5eLOaiBPdaFE56Wgxr9f4TTVtYFgEQrtVkhj2mAGldDu8\n"
+  "NtsgaBi4hdSLbTCHTdYzLXpUNh1XQhRcemJ01R4r1b8E8//sA8ECAwEAAaNEMEIw\n"
+  "DAYDVR0TAQH/BAIwADAnBgNVHSUEIDAeBggrBgEFBQcDAgYIKwYBBQUHAwEGCCsG\n"
+  "AQUFBwMJMAkGA1UdEQQCMAAwDQYJKoZIhvcNAQEFBQADggIBAFDdY7cab5FM6H+C\n"
+  "GicEgQW7pmmsQXti/EsI3GDPslrxtLUnaWsS5AfIFs47QswCkGYOebhsS5AAxWZk\n"
+  "kisrSA6Ewm2/pd4W470Z9VyToYZ/2Yl4aj+D8Kr46h2kE/cqFUxRnMSwvlhmz0xs\n"
+  "PTHl+VQhzaEwAWqzGkiFNJO4+RUZSDSNc+cDUK/eUMdiryUiK/boNy7kcalcJup5\n"
+  "ywQpc2uP3x9cQVLANqrXfY5EVJgGTGOmCwGUXQxc1M/LC3stVsy/l38VJB266reX\n"
+  "sDKt/OptlDl64yVU/Er1Pb0u1TEHSSTMkmkOebnf2za/BEQV0EaZjNJMlDgOEGQT\n"
+  "q9kbVAIxViDuaZXfObvpp23DI4YL1jRAN8PUQagucR1uW9fFnyrmAoCuCihpY0uJ\n"
+  "Lr1PQlj7hpqiGNzGMsFGuijSi85WYwSAUVE5ADsAuV9n+pAe2na1MaW9EdJf2l3V\n"
+  "9+6rwGJ0YEcyQv2yLgQ6LvLIs0Gjvf6UX+9u15J8HQTwxlOORtwwOjVfGkvqOwCL\n"
+  "l7W5znFuXNWgC7EzCIlhI8+Xn4+aULXszkCNgpWLeSZm8/Rw2O5Y3XUp1WqRUXoX\n"
+  "vE/Uo0V7hOe+aVPB4lzIRaA67N+KHsEYhIt6Tk6eOib+XSLUxRS+7gbrBUpmyaSz\n"
+  "aASwXSVUswXtQfBlaW2lTreX2Nj1\n"
+  "-----END CERTIFICATE-----\n";
+const gnutls_datum_t blog_signer_data = { blog_signer_pem,
+                                         sizeof (blog_signer_pem) };
+
+static void
+ocsp_invalid_calls (void)
+{
+  gnutls_ocsp_req_t req;
+  gnutls_ocsp_resp_t resp;
+  gnutls_datum_t dat;
+  char c = 42;
+  void *p = &c;
+  int rc;
+
+  rc = gnutls_ocsp_req_init (&req);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      fail ("gnutls_ocsp_req_init alloc\n");
+      exit (1);
+    }
+  rc = gnutls_ocsp_resp_init (&resp);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      fail ("gnutls_ocsp_resp_init alloc\n");
+      exit (1);
+    }
+
+  gnutls_ocsp_req_deinit (NULL);
+  gnutls_ocsp_resp_deinit (NULL);
+
+  rc = gnutls_ocsp_req_import (NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_import NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_import (NULL, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_import NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_import (req, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_import NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_import (NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_import NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_import (NULL, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_import NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_import (resp, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_import NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_export (NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_export NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_export (NULL, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_export NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_export (req, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_export NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_export (NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_export NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_export (NULL, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_export NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_export (resp, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_export NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_get_version (NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_get_version NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_get_cert_id (NULL, 0, NULL, NULL, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_get_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_get_cert_id (req, 0, NULL, NULL, NULL, NULL);
+  if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+    {
+      fail ("gnutls_ocsp_req_get_cert_id empty\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (NULL, 0, NULL, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (req, 0, NULL, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (req, GNUTLS_DIG_SHA1, NULL, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (req, GNUTLS_DIG_SHA1, p, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (req, GNUTLS_DIG_SHA1, NULL, p, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (req, GNUTLS_DIG_SHA1, NULL, NULL, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (req, GNUTLS_DIG_SHA1, p, p, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (req, GNUTLS_DIG_SHA1, p, NULL, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert_id (req, GNUTLS_DIG_SHA1, NULL, p, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert (NULL, 0, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert (req, 0, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+
+  rc = gnutls_ocsp_req_add_cert (req, 0, p, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_add_cert (req, 0, NULL, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_add_cert_id NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_get_extension (NULL, 0, NULL, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_get_extension NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_get_extension (req, 0, NULL, NULL, NULL);
+  if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+    {
+      fail ("gnutls_ocsp_req_get_extension NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_get_extension (req, 0, p, p, p);
+  if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+    {
+      fail ("gnutls_ocsp_req_get_extension NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_set_extension (NULL, NULL, 0, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_set_extension NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_set_extension (req, NULL, 0, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_set_extension NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_set_extension (req, p, 0, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_set_extension NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_set_extension (req, NULL, 0, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_set_extension NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_get_nonce (NULL, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_get_nonce NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_get_nonce (NULL, NULL, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_get_nonce NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_set_nonce (NULL, 0, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_set_nonce NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_set_nonce (req, 0, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_set_nonce NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_req_randomize_nonce (NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_req_randomize_nonce NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_status (NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_get_status NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_status (resp);
+  if (rc != GNUTLS_E_ASN1_VALUE_NOT_FOUND)
+    {
+      fail ("gnutls_ocsp_resp_get_status %d\n", rc);
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_response (NULL, NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_get_response NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_response (NULL, p, p);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_get_response NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_response (resp, NULL, NULL);
+  if (rc != GNUTLS_E_SUCCESS)
+    {
+      fail ("gnutls_ocsp_resp_get_response %d\n", rc);
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_version (NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_get_version NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_version (resp);
+  if (rc != 1)
+    {
+      fail ("gnutls_ocsp_resp_get_version ret %d\n", rc);
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_responder (NULL, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_get_responder NULL\n");
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_responder (resp, NULL);
+  if (rc != GNUTLS_E_INVALID_REQUEST)
+    {
+      fail ("gnutls_ocsp_resp_get_responder 2nd %d\n", rc);
+      exit (1);
+    }
+
+  rc = gnutls_ocsp_resp_get_responder (resp, &dat);
+  if (rc != GNUTLS_E_SUCCESS || dat.size != 0)
+    {
+      fail ("gnutls_ocsp_resp_get_responder %d\n", rc);
+      exit (1);
+    }
+
+  gnutls_free (dat.data);
+
+  gnutls_ocsp_req_deinit (req);
+  gnutls_ocsp_resp_deinit (resp);
+}
+
+/* import a request, query some fields and print and export it */
+static void
+req_parse (void)
+{
+  gnutls_ocsp_req_t req;
+  int ret;
+  gnutls_datum_t d;
+
+  /* init request */
+
+  ret = gnutls_ocsp_req_init (&req);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_init\n");
+      exit (1);
+    }
+
+  /* import ocsp request */
+
+  ret = gnutls_ocsp_req_import (req, &req1);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_import %d\n", ret);
+      exit (1);
+    }
+
+  /* simple version query */
+
+  ret = gnutls_ocsp_req_get_version (req);
+  if (ret != 1)
+    {
+      fail ("gnutls_ocsp_req_get_version %d\n", ret);
+      exit (1);
+    }
+
+  /* check nonce */
+  {
+    gnutls_datum_t expect =
+      { (unsigned char*) REQ1NONCE + 2, sizeof (REQ1NONCE) - 3 };
+    gnutls_datum_t got;
+    unsigned int critical;
+
+    ret = gnutls_ocsp_req_get_nonce (req, &critical, &got);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_get_nonce %d\n", ret);
+       exit (1);
+      }
+
+    if (critical != 0)
+      {
+       fail ("unexpected critical %d\n", critical);
+       exit (1);
+      }
+
+    if (expect.size != got.size ||
+       memcmp (expect.data, got.data, got.size) != 0)
+      {
+       fail ("ocsp request nonce memcmp failed\n");
+       exit (1);
+      }
+
+    gnutls_free (got.data);
+  }
+
+  /* print request */
+
+  ret = gnutls_ocsp_req_print (req, GNUTLS_OCSP_PRINT_FULL, &d);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_print\n");
+      exit (1);
+    }
+
+  if (strlen (REQ1INFO) != d.size - 1 ||
+      memcmp (REQ1INFO, d.data, strlen (REQ1INFO)) != 0)
+    {
+      printf ("expected (len %ld):\n%s\ngot (len %d):\n%.*s\n",
+             strlen (REQ1INFO), REQ1INFO, (int) d.size - 1,
+             (int) d.size, d.data);
+      fail ("ocsp request print failed\n");
+      exit (1);
+    }
+  gnutls_free (d.data);
+
+  /* test export */
+  ret = gnutls_ocsp_req_export (req, &d);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_export %d\n", ret);
+      exit (1);
+    }
+
+  /* compare against earlier imported bytes */
+
+  if (req1.size != d.size ||
+      memcmp (req1.data, d.data, d.size) != 0)
+    {
+      fail ("ocsp request export memcmp failed\n");
+      exit (1);
+    }
+  gnutls_free (d.data);
+
+  /* test setting nonce */
+  {
+    gnutls_datum_t n1 = { (unsigned char *) "foo", 3 };
+    gnutls_datum_t n2 = { (unsigned char *) "foobar", 6 };
+    gnutls_datum_t got;
+    unsigned critical;
+
+    ret = gnutls_ocsp_req_set_nonce (req, 0, &n1);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_set_nonce %d\n", ret);
+       exit (1);
+      }
+
+    ret = gnutls_ocsp_req_get_nonce (req, &critical, &got);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_get_nonce %d\n", ret);
+       exit (1);
+      }
+
+    if (critical != 0)
+      {
+       fail ("unexpected critical %d\n", critical);
+       exit (1);
+      }
+
+    if (n1.size != got.size ||
+       memcmp (n1.data, got.data, got.size) != 0)
+      {
+       fail ("ocsp request parse nonce memcmp failed\n");
+       exit (1);
+      }
+
+    gnutls_free (got.data);
+
+    /* set another time */
+
+    ret = gnutls_ocsp_req_set_nonce (req, 1, &n2);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_set_nonce %d\n", ret);
+       exit (1);
+      }
+
+    ret = gnutls_ocsp_req_get_nonce (req, &critical, &got);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_get_nonce %d\n", ret);
+       exit (1);
+      }
+
+    if (critical != 1)
+      {
+       fail ("unexpected critical %d\n", critical);
+       exit (1);
+      }
+
+    if (n2.size != got.size ||
+       memcmp (n2.data, got.data, got.size) != 0)
+      {
+       fail ("ocsp request parse2 nonce memcmp failed\n");
+       exit (1);
+      }
+
+    gnutls_free (got.data);
+
+    /* randomize nonce */
+
+    ret = gnutls_ocsp_req_randomize_nonce (req);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_randomize_nonce %d\n", ret);
+       exit (1);
+      }
+
+    ret = gnutls_ocsp_req_get_nonce (req, &critical, &n1);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_get_nonce %d\n", ret);
+       exit (1);
+      }
+
+    if (critical != 0)
+      {
+       fail ("unexpected random critical %d\n", critical);
+       exit (1);
+      }
+
+    ret = gnutls_ocsp_req_randomize_nonce (req);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_randomize_nonce %d\n", ret);
+       exit (1);
+      }
+
+    ret = gnutls_ocsp_req_get_nonce (req, &critical, &n2);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_get_nonce %d\n", ret);
+       exit (1);
+      }
+
+    if (critical != 0)
+      {
+       fail ("unexpected random critical %d\n", critical);
+       exit (1);
+      }
+
+    if (n2.size == got.size && memcmp (n1.data, n2.data, n1.size) == 0)
+      {
+       fail ("ocsp request random nonce memcmp failed\n");
+       exit (1);
+      }
+
+    gnutls_free (n1.data);
+    gnutls_free (n2.data);
+  }
+
+  /* cleanup */
+
+  gnutls_ocsp_req_deinit (req);
+}
+
+/* check that creating a request (using low-level add_cert_id) ends up
+   with same DER as above. */
+static void
+req_addcert_id (void)
+{
+  gnutls_ocsp_req_t req;
+  int ret;
+  gnutls_datum_t d;
+
+  /* init request */
+
+  ret = gnutls_ocsp_req_init (&req);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_init\n");
+      exit (1);
+    }
+
+  /* add ocsp request nonce */
+
+  {
+    gnutls_datum_t nonce =
+      { (unsigned char*) REQ1NONCE, sizeof (REQ1NONCE) - 1 };
+
+    ret = gnutls_ocsp_req_set_extension (req, "1.3.6.1.5.5.7.48.1.2",
+                                        0, &nonce);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_set_extension %d\n", ret);
+       exit (1);
+      }
+  }
+
+  /* add cert_id */
+  {
+    gnutls_datum_t issuer_name_hash =
+      { (unsigned char*) REQ1INH, sizeof (REQ1INH) - 1 };
+    gnutls_datum_t issuer_key_hash =
+      { (unsigned char*) REQ1IKH, sizeof (REQ1IKH) - 1 };
+    gnutls_datum_t serial_number =
+      { (unsigned char*) REQ1SN, sizeof (REQ1SN) - 1 };
+
+    ret = gnutls_ocsp_req_add_cert_id (req, GNUTLS_DIG_SHA1,
+                                     &issuer_name_hash,
+                                     &issuer_key_hash,
+                                     &serial_number);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_add_cert_id %d\n", ret);
+       exit (1);
+      }
+  }
+
+  /* print request */
+
+  ret = gnutls_ocsp_req_print (req, GNUTLS_OCSP_PRINT_FULL, &d);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_print\n");
+      exit (1);
+    }
+
+  if (strlen (REQ1INFO) != d.size - 1 ||
+      memcmp (REQ1INFO, d.data, strlen (REQ1INFO)) != 0)
+    {
+      printf ("expected (len %ld):\n%s\ngot (len %d):\n%.*s\n",
+             strlen (REQ1INFO), REQ1INFO, (int) d.size - 1,
+             (int) d.size, d.data);
+      fail ("ocsp request print failed\n");
+      exit (1);
+    }
+  gnutls_free (d.data);
+
+  /* test export */
+  ret = gnutls_ocsp_req_export (req, &d);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_export %d\n", ret);
+      exit (1);
+    }
+
+  /* compare against earlier imported bytes */
+
+  if (req1.size != d.size ||
+      memcmp (req1.data, d.data, d.size) != 0)
+    {
+      fail ("ocsp request export memcmp failed\n");
+      exit (1);
+    }
+  gnutls_free (d.data);
+
+  /* cleanup */
+
+  gnutls_ocsp_req_deinit (req);
+}
+
+/* check that creating a request (using high-level add_cert) ends up
+   with same DER as above. */
+static void
+req_addcert (void)
+{
+  gnutls_ocsp_req_t req;
+  int ret;
+  gnutls_datum_t d;
+
+  /* init request */
+
+  ret = gnutls_ocsp_req_init (&req);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_init\n");
+      exit (1);
+    }
+
+  /* add ocsp request nonce */
+
+  {
+    gnutls_datum_t nonce =
+      { (unsigned char*) REQ1NONCE, sizeof (REQ1NONCE) - 1 };
+
+    ret = gnutls_ocsp_req_set_extension (req, "1.3.6.1.5.5.7.48.1.2",
+                                        0, &nonce);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_req_set_extension %d\n", ret);
+       exit (1);
+      }
+  }
+
+  /* add cert_id */
+  {
+    gnutls_x509_crt_t issuer = NULL, subject = NULL;
+
+    ret = gnutls_x509_crt_init (&issuer);
+    if (ret < 0)
+      {
+       fail ("gnutls_x509_crt_init (issuer) %d\n", ret);
+       exit (1);
+      }
+
+    ret = gnutls_x509_crt_init (&subject);
+    if (ret < 0)
+      {
+       fail ("gnutls_x509_crt_init (subject) %d\n", ret);
+       exit (1);
+      }
+
+    ret = gnutls_x509_crt_import (issuer, &issuer_data, GNUTLS_X509_FMT_PEM);
+    if (ret < 0)
+      {
+       fail ("gnutls_x509_crt_import (issuer) %d\n", ret);
+       exit (1);
+      }
+
+    ret = gnutls_x509_crt_import (subject, &subject_data, GNUTLS_X509_FMT_PEM);
+    if (ret < 0)
+      {
+       fail ("gnutls_x509_crt_import (subject) %d\n", ret);
+       exit (1);
+      }
+
+    ret = gnutls_ocsp_req_add_cert (req, GNUTLS_DIG_SHA1,
+                                   issuer, subject);
+    if (ret != 0)
+      {
+       fail ("gnutls_ocsp_add_cert %d\n", ret);
+       exit (1);
+      }
+
+    gnutls_x509_crt_deinit (subject);
+    gnutls_x509_crt_deinit (issuer);
+  }
+
+  /* print request */
+
+  ret = gnutls_ocsp_req_print (req, GNUTLS_OCSP_PRINT_FULL, &d);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_print\n");
+      exit (1);
+    }
+
+  if (strlen (REQ1INFO) != d.size - 1 ||
+      memcmp (REQ1INFO, d.data, strlen (REQ1INFO)) != 0)
+    {
+      printf ("expected (len %ld):\n%s\ngot (len %d):\n%.*s\n",
+             strlen (REQ1INFO), REQ1INFO, (int) d.size - 1,
+             (int) d.size, d.data);
+      fail ("ocsp request print failed\n");
+      exit (1);
+    }
+  gnutls_free (d.data);
+
+  /* test export */
+  ret = gnutls_ocsp_req_export (req, &d);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_req_export %d\n", ret);
+      exit (1);
+    }
+
+  /* compare against earlier imported bytes */
+
+  if (req1.size != d.size ||
+      memcmp (req1.data, d.data, d.size) != 0)
+    {
+      fail ("ocsp request export memcmp failed\n");
+      exit (1);
+    }
+  gnutls_free (d.data);
+
+  /* cleanup */
+
+  gnutls_ocsp_req_deinit (req);
+}
+
+static void
+resp_import (void)
+{
+  gnutls_ocsp_resp_t resp;
+  int ret;
+  gnutls_datum_t d;
+
+  /* init response */
+
+  ret = gnutls_ocsp_resp_init (&resp);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_resp_init\n");
+      exit (1);
+    }
+
+  /* import ocsp response */
+
+  ret = gnutls_ocsp_resp_import (resp, &resp1);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_resp_import %d\n", ret);
+      exit (1);
+    }
+
+  /* print response */
+
+  ret = gnutls_ocsp_resp_print (resp, GNUTLS_OCSP_PRINT_FULL, &d);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_resp_print\n");
+      exit (1);
+    }
+
+  if (strlen (RESP1INFO) != d.size - 1 ||
+      memcmp (RESP1INFO, d.data, strlen (RESP1INFO)) != 0)
+    {
+      printf ("expected (len %ld):\n%s\ngot (len %d):\n%.*s\n",
+             strlen (RESP1INFO), RESP1INFO, (int) d.size - 1,
+             (int) d.size, d.data);
+      fail ("ocsp response print failed\n");
+      exit (1);
+    }
+  gnutls_free (d.data);
+
+  /* import ocsp response */
+
+  ret = gnutls_ocsp_resp_import (resp, &resp2);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_resp_import %d\n", ret);
+      exit (1);
+    }
+
+  /* print response */
+
+  ret = gnutls_ocsp_resp_print (resp, GNUTLS_OCSP_PRINT_FULL, &d);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_resp_print\n");
+      exit (1);
+    }
+
+  if (memcmp (RESP2INFO, d.data, strlen (RESP2INFO)) != 0)
+    {
+      printf ("expected (len %ld):\n%s\ngot (len %d):\n%.*s\n",
+             strlen (RESP2INFO), RESP2INFO, (int) d.size - 1,
+             (int) d.size, d.data);
+      fail ("ocsp response print failed\n");
+      exit (1);
+    }
+  gnutls_free (d.data);
+
+  /* cleanup */
+
+  gnutls_ocsp_resp_deinit (resp);
+}
+
+static void
+resp_verify (void)
+{
+  gnutls_ocsp_resp_t resp;
+  int ret;
+  gnutls_x509_crt_t cert = NULL, issuer = NULL, signer = NULL;
+  gnutls_x509_trust_list_t list;
+  unsigned verify;
+
+  /* init response */
+
+  ret = gnutls_ocsp_resp_init (&resp);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_resp_init\n");
+      exit (1);
+    }
+
+  /* import ocsp response */
+
+  ret = gnutls_ocsp_resp_import (resp, &blog_resp);
+  if (ret != 0)
+    {
+      fail ("gnutls_ocsp_resp_import %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_crt_init (&cert);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_crt_init (cert) %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_crt_init (&issuer);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_crt_init (issuer) %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_crt_init (&signer);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_crt_init (signer) %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_crt_import (cert, &blog_cert_data, GNUTLS_X509_FMT_PEM);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_crt_import (cert) %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_crt_import (issuer, &blog_issuer_data, GNUTLS_X509_FMT_PEM);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_crt_import (issuer) %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_crt_import (signer, &blog_signer_data, GNUTLS_X509_FMT_PEM);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_crt_import (signer) %d\n", ret);
+      exit (1);
+    }
+
+  /* check direct verify with signer (should succeed) */
+
+  ret = gnutls_ocsp_resp_verify_direct (resp, signer, &verify, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_ocsp_resp_verify_direct (signer) %d\n", ret);
+      exit (1);
+    }
+
+  if (verify != 0)
+    {
+      fail ("gnutls_ocsp_resp_verify_direct %d\n", verify);
+      exit (1);
+    }
+
+  /* check direct verify with issuer (should fail) */
+
+  ret = gnutls_ocsp_resp_verify_direct (resp, issuer, &verify, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_ocsp_resp_verify_direct (issuer) %d\n", ret);
+      exit (1);
+    }
+
+  if (verify != GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
+    {
+      fail ("gnutls_ocsp_resp_verify_direct2 %d\n", verify);
+      exit (1);
+    }
+
+  /* check direct verify with cert (should fail) */
+
+  ret = gnutls_ocsp_resp_verify_direct (resp, cert, &verify, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_ocsp_resp_verify_direct (cert) %d\n", ret);
+      exit (1);
+    }
+
+  if (verify != GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
+    {
+      fail ("gnutls_ocsp_resp_verify_direct3 %d\n", verify);
+      exit (1);
+    }
+
+  /* check trust verify with issuer (should succeed) */
+
+  ret = gnutls_x509_trust_list_init (&list, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_init %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_trust_list_add_cas (list, &issuer, 1, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_add_cas %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_ocsp_resp_verify (resp, list, &verify, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_ocsp_resp_verify (issuer) %d\n", ret);
+      exit (1);
+    }
+
+  if (verify != 0)
+    {
+      fail ("gnutls_ocsp_resp_verify %d\n", verify);
+      exit (1);
+    }
+
+  gnutls_x509_trust_list_deinit (list, 0);
+
+  /* check trust verify with signer (should succeed) */
+
+  ret = gnutls_x509_trust_list_init (&list, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_init %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_trust_list_add_cas (list, &signer, 1, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_add_cas %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_ocsp_resp_verify (resp, list, &verify, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_ocsp_resp_verify (issuer) %d\n", ret);
+      exit (1);
+    }
+
+  if (verify != 0)
+    {
+      fail ("gnutls_ocsp_resp_verify %d\n", verify);
+      exit (1);
+    }
+
+  gnutls_x509_trust_list_deinit (list, 0);
+
+  /* check trust verify with cert (should fail) */
+
+  ret = gnutls_x509_trust_list_init (&list, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_init %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_trust_list_add_cas (list, &cert, 1, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_add_cas %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_ocsp_resp_verify (resp, list, &verify, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_ocsp_resp_verify (issuer) %d\n", ret);
+      exit (1);
+    }
+
+  if (verify != GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
+    {
+      fail ("gnutls_ocsp_resp_verify %d\n", verify);
+      exit (1);
+    }
+
+  gnutls_x509_trust_list_deinit (list, 0);
+
+  /* check trust verify with all certs (should succeed) */
+
+  ret = gnutls_x509_trust_list_init (&list, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_init %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_trust_list_add_cas (list, &cert, 1, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_add_cas %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_trust_list_add_cas (list, &issuer, 1, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_add_cas %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_x509_trust_list_add_cas (list, &signer, 1, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_x509_trust_list_add_cas %d\n", ret);
+      exit (1);
+    }
+
+  ret = gnutls_ocsp_resp_verify (resp, list, &verify, 0);
+  if (ret < 0)
+    {
+      fail ("gnutls_ocsp_resp_verify (issuer) %d\n", ret);
+      exit (1);
+    }
+
+  if (verify != 0)
+    {
+      fail ("gnutls_ocsp_resp_verify %d\n", verify);
+      exit (1);
+    }
+
+  gnutls_x509_trust_list_deinit (list, 0);
+
+  /* cleanup */
+
+  gnutls_ocsp_resp_deinit (resp);
+  gnutls_x509_crt_deinit (cert);
+  gnutls_x509_crt_deinit (issuer);
+  gnutls_x509_crt_deinit (signer);
+}
+
+void
+doit (void)
+{
+  int ret;
+
+  ret = gnutls_global_init ();
+  if (ret < 0)
+    {
+      fail ("gnutls_global_init\n");
+      exit (1);
+    }
+
+  gnutls_global_set_log_function (tls_log_func);
+  gnutls_global_set_log_level (0);
+
+  ocsp_invalid_calls ();
+  req_parse ();
+  resp_import ();
+  req_addcert_id ();
+  req_addcert ();
+  resp_verify ();
+
+  /* we're done */
+
+  gnutls_global_deinit ();
+}