Page MenuHome GnuPG

No OneTemporary

diff --git a/src/ocsp.c b/src/ocsp.c
new file mode 100644
index 0000000..599656f
--- /dev/null
+++ b/src/ocsp.c
@@ -0,0 +1,1233 @@
+/* ocsp.c - OCSP (rfc2560)
+ * Copyright (C) 2003 g10 Code GmbH
+ *
+ * This file is part of KSBA.
+ *
+ * KSBA 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 Fountion; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * KSBA 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "util.h"
+
+#include "cert.h"
+#include "convert.h"
+#include "keyinfo.h"
+#include "der-encoder.h"
+#include "ber-help.h"
+#include "ocsp.h"
+
+
+#if 1
+static void
+dump_hex (const unsigned char *p, size_t n)
+{
+ if (!p)
+ fputs ("none", stderr);
+ else
+ {
+ for (; n; n--, p++)
+ fprintf (stderr, "%02X", *p);
+ }
+}
+#endif
+
+
+
+static const char oidstr_sha1[] = "1.3.14.3.2.26";
+static const char oidstr_ocspBasic[] = "1.3.6.1.5.5.7.48.1.1";
+
+
+static void
+parse_skip (unsigned char const **buf, size_t *len, struct tag_info *ti)
+{
+ if (ti->length)
+ {
+ assert (ti->length <= *len);
+ *len -= ti->length;
+ *buf += ti->length;
+ }
+}
+
+static gpg_error_t
+parse_sequence (unsigned char const **buf, size_t *len, struct tag_info *ti)
+{
+ gpg_error_t err;
+
+ err = _ksba_ber_parse_tl (buf, len, ti);
+ if (err)
+ ;
+ else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_SEQUENCE
+ && ti->is_constructed) )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (ti->length > *len)
+ err = gpg_error (GPG_ERR_BAD_BER);
+ return err;
+}
+
+static gpg_error_t
+parse_enumerated (unsigned char const **buf, size_t *len, struct tag_info *ti,
+ size_t maxlen)
+{
+ gpg_error_t err;
+
+ err = _ksba_ber_parse_tl (buf, len, ti);
+ if (err)
+ ;
+ else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_ENUMERATED
+ && !ti->is_constructed) )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (!ti->length)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else if (maxlen && ti->length > maxlen)
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ else if (ti->length > *len)
+ err = gpg_error (GPG_ERR_BAD_BER);
+
+ return err;
+}
+
+static gpg_error_t
+parse_integer (unsigned char const **buf, size_t *len, struct tag_info *ti)
+{
+ gpg_error_t err;
+
+ err = _ksba_ber_parse_tl (buf, len, ti);
+ if (err)
+ ;
+ else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_INTEGER
+ && !ti->is_constructed) )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (!ti->length)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else if (ti->length > *len)
+ err = gpg_error (GPG_ERR_BAD_BER);
+
+ return err;
+}
+
+static gpg_error_t
+parse_octet_string (unsigned char const **buf, size_t *len, struct tag_info *ti)
+{
+ gpg_error_t err;
+
+ err= _ksba_ber_parse_tl (buf, len, ti);
+ if (err)
+ ;
+ else if (!(ti->class == CLASS_UNIVERSAL && ti->tag == TYPE_OCTET_STRING
+ && !ti->is_constructed) )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (!ti->length)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else if (ti->length > *len)
+ err = gpg_error (GPG_ERR_BAD_BER);
+
+ return err;
+}
+
+static gpg_error_t
+parse_object_id_into_str (unsigned char const **buf, size_t *len, char **oid)
+{
+ struct tag_info ti;
+ gpg_error_t err;
+
+ *oid = NULL;
+ err = _ksba_ber_parse_tl (buf, len, &ti);
+ if (err)
+ ;
+ else if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID
+ && !ti.is_constructed) )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (!ti.length)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else if (ti.length > *len)
+ err = gpg_error (GPG_ERR_BAD_BER);
+ else if (!(*oid = ksba_oid_to_str (*buf, ti.length)))
+ err = gpg_error_from_errno (errno);
+ else
+ {
+ *buf += ti.length;
+ *len -= ti.length;
+ }
+ return err;
+}
+
+
+static gpg_error_t
+parse_asntime_into_isotime (unsigned char const **buf, size_t *len,
+ ksba_isotime_t isotime)
+{
+ struct tag_info ti;
+ gpg_error_t err;
+
+ err = _ksba_ber_parse_tl (buf, len, &ti);
+ if (err)
+ ;
+ else if ( !(ti.class == CLASS_UNIVERSAL
+ && (ti.tag == TYPE_UTC_TIME || ti.tag == TYPE_GENERALIZED_TIME)
+ && !ti.is_constructed) )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (!(err = _ksba_asntime_to_iso (*buf, ti.length, isotime)))
+ parse_skip (buf, len, &ti);
+
+ return err;
+}
+
+
+static gpg_error_t
+parse_context_tag (unsigned char const **buf, size_t *len, struct tag_info *ti,
+ int tag)
+{
+ gpg_error_t err;
+
+ err = _ksba_ber_parse_tl (buf, len, ti);
+ if (err)
+ ;
+ if (!(ti->class == CLASS_CONTEXT && ti->tag == tag && ti->is_constructed) )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (ti->length > *len)
+ err = gpg_error (GPG_ERR_BAD_BER);
+
+ return err;
+}
+
+
+
+/* Create a new OCSP object and retrun it in R_OCSP. Return 0 on
+ success or an error code.
+ */
+gpg_error_t
+ksba_ocsp_new (ksba_ocsp_t *r_ocsp)
+{
+ *r_ocsp = xtrycalloc (1, sizeof **r_ocsp);
+ if (!*r_ocsp)
+ return gpg_error_from_errno (errno);
+ return 0;
+}
+
+
+static void
+release_ocsp_certlist (struct ocsp_certlist_s *cl)
+{
+ while (cl)
+ {
+ struct ocsp_certlist_s *tmp = cl->next;
+ ksba_cert_release (cl->cert);
+ xfree (cl);
+ cl = tmp;
+ }
+}
+
+
+/* Release the OCSP object and all its resources. Passing NULL for
+ OCSP is a valid nop. */
+void
+ksba_ocsp_release (ksba_ocsp_t ocsp)
+{
+ struct ocsp_reqitem_s *ri;
+
+ if (!ocsp)
+ return;
+ xfree (ocsp->digest_oid);
+ for (; (ri=ocsp->requestlist); ri = ocsp->requestlist )
+ {
+ ocsp->requestlist = ri->next;
+ ksba_cert_release (ri->cert);
+ ksba_cert_release (ri->issuer_cert);
+ xfree (ri->serialno);
+ }
+ xfree (ocsp->sigval);
+ release_ocsp_certlist (ocsp->received_certs);
+ xfree (ocsp);
+}
+
+
+/* Associate the certificate CERT for which the status is to be
+ requested and its issuer certificate ISSUER_CERT with the OCSP
+ context. This function may be called multiple time to create a
+ list of certificate requests to get combined into one actual
+ request. */
+gpg_error_t
+ksba_ocsp_add_certs (ksba_ocsp_t ocsp,
+ ksba_cert_t cert, ksba_cert_t issuer_cert)
+{
+ struct ocsp_reqitem_s *ri;
+
+ if (!ocsp || !cert || !issuer_cert)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ ri = xtrycalloc (1, sizeof *ri);
+ if (!ri)
+ return gpg_error_from_errno (errno);
+ ksba_cert_ref (cert);
+ ri->cert = cert;
+ ksba_cert_ref (issuer_cert);
+ ri->issuer_cert = issuer_cert;
+
+ ri->next = ocsp->requestlist;
+ ocsp->requestlist = ri;
+
+ return 0;
+}
+
+
+
+/* Set the hash algorithm to be used for signing the request to OID.
+ Using this function will force the creation of a signed
+ request. */
+gpg_error_t
+ksba_ocsp_set_digest_algo (ksba_ocsp_t ocsp, const char *oid)
+{
+ if (!ocsp || !oid || !*oid)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (ocsp->digest_oid)
+ xfree (ocsp->digest_oid);
+ ocsp->digest_oid = xtrystrdup (oid);
+ if (!ocsp->digest_oid)
+ return gpg_error_from_errno (errno);
+ return 0;
+}
+
+
+/* Compute the SHA-1 nameHash for the certificate CERT and put it in
+ the buffer SHA1_BUFFER which must have been allocated to at least
+ 20 bytes. */
+static gpg_error_t
+issuer_name_hash (ksba_cert_t cert, unsigned char *sha1_buffer)
+{
+ gpg_error_t err;
+ const unsigned char *ptr;
+ size_t length, dummy;
+
+ err = _ksba_cert_get_issuer_dn_ptr (cert, &ptr, &length);
+ if (!err)
+ {
+ err = _ksba_hash_buffer (NULL, ptr, length, 20, sha1_buffer, &dummy);
+ if (!err && dummy != 20)
+ err = gpg_error (GPG_ERR_BUG);
+ }
+ return err;
+}
+
+/* Compute the SHA-1 hash of the public key of CERT and put it in teh
+ buffer SHA1_BUFFER which must have been allocated with at least 20
+ bytes. */
+static gpg_error_t
+issuer_key_hash (ksba_cert_t cert, unsigned char *sha1_buffer)
+{
+ gpg_error_t err;
+ const unsigned char *ptr;
+ size_t length, dummy;
+
+ err = _ksba_cert_get_public_key_ptr (cert, &ptr, &length);
+ if (!err)
+ {
+ err = _ksba_hash_buffer (NULL, ptr, length, 20, sha1_buffer, &dummy);
+ if (!err && dummy != 20)
+ err = gpg_error (GPG_ERR_BUG);
+ }
+ return err;
+}
+
+
+/* Build a request from the current context. The function checks that
+ all necessary information have been set and then returns an
+ allocated buffer with the resulting request.
+ */
+gpg_error_t
+ksba_ocsp_build_request (ksba_ocsp_t ocsp,
+ unsigned char **r_buffer, size_t *r_buflen)
+{
+ gpg_error_t err;
+ struct ocsp_reqitem_s *ri;
+ unsigned char *p;
+ const unsigned char *der;
+ size_t derlen;
+ ksba_writer_t w1 = NULL;
+ ksba_writer_t w2 = NULL;
+ ksba_writer_t w3 = NULL;
+ ksba_writer_t w4, w5, w6, w7; /* Used as aliases. */
+
+ if (!ocsp || !r_buffer || !r_buflen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *r_buffer = NULL;
+ *r_buflen = 0;
+
+ if (!ocsp->requestlist)
+ return gpg_error (GPG_ERR_MISSING_ACTION);
+
+ /* Create three writer objects for construction of the request. */
+ err = ksba_writer_new (&w3);
+ if (!err)
+ err = ksba_writer_set_mem (w3, 2048);
+ if (!err)
+ err = ksba_writer_new (&w2);
+ if (!err)
+ err = ksba_writer_new (&w1);
+ if (err)
+ goto leave;
+
+
+ /* Loop over all single requests. */
+ for (ri=ocsp->requestlist; ri; ri = ri->next)
+ {
+ err = ksba_writer_set_mem (w2, 256);
+ if (!err)
+ err = ksba_writer_set_mem (w1, 256);
+ if (err)
+ goto leave;
+
+ /* Write the AlgorithmIdentifier. */
+ err = _ksba_der_write_algorithm_identifier (w1, oidstr_sha1, NULL, 0);
+ if (err)
+ goto leave;
+
+ /* Compute the issuerNameHash and write it into the CertID object. */
+ err = issuer_name_hash (ri->issuer_cert, ri->issuer_name_hash);
+ if (!err)
+ err = _ksba_ber_write_tl (w1, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0,20);
+ if (!err)
+ err = ksba_writer_write (w1, ri->issuer_name_hash, 20);
+ if(err)
+ goto leave;
+
+ /* Compute the issuerKeyHash and write it. */
+ err = issuer_key_hash (ri->issuer_cert, ri->issuer_key_hash);
+ if (!err)
+ err = _ksba_ber_write_tl (w1, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0,20);
+ if (!err)
+ err = ksba_writer_write (w1, ri->issuer_key_hash, 20);
+ if (err)
+ goto leave;
+
+ /* Write the serialNumber of the certificate to be checked. */
+ err = _ksba_cert_get_serial_ptr (ri->cert, &der, &derlen);
+ if (!err)
+ err = _ksba_ber_write_tl (w1, TYPE_INTEGER, CLASS_UNIVERSAL, 0, derlen);
+ if (!err)
+ err = ksba_writer_write (w1, der, derlen);
+ if (err)
+ goto leave;
+ xfree (ri->serialno);
+ ri->serialno = xtrymalloc (derlen);
+ if (!ri->serialno)
+ err = gpg_error_from_errno (errno);
+ if (err)
+ goto leave;
+ memcpy (ri->serialno, der, derlen);
+ ri->serialnolen = derlen;
+
+
+ /* Now write it out as a sequence to the outer certID object. */
+ p = ksba_writer_snatch_mem (w1, &derlen);
+ if (!p)
+ {
+ err = ksba_writer_error (w1);
+ goto leave;
+ }
+ err = _ksba_ber_write_tl (w2, TYPE_SEQUENCE, CLASS_UNIVERSAL,
+ 1, derlen);
+ if (!err)
+ err = ksba_writer_write (w2, p, derlen);
+ xfree (p); p = NULL;
+ if (err)
+ goto leave;
+
+ /* Here we would write singleRequestExtensions. */
+
+ /* Now write it out as a sequence to the outer Request object. */
+ p = ksba_writer_snatch_mem (w2, &derlen);
+ if (!p)
+ {
+ err = ksba_writer_error (w2);
+ goto leave;
+ }
+ err = _ksba_ber_write_tl (w3, TYPE_SEQUENCE, CLASS_UNIVERSAL,
+ 1, derlen);
+ if (!err)
+ err = ksba_writer_write (w3, p, derlen);
+ xfree (p); p = NULL;
+ if (err)
+ goto leave;
+
+ } /* End of looping over single requests. */
+
+ /* Reuse writers; for clarity, use new names. */
+ w4 = w1;
+ w5 = w2;
+ err = ksba_writer_set_mem (w4, 2048);
+ if (!err)
+ err = ksba_writer_set_mem (w5, 2048);
+ if (err)
+ goto leave;
+
+ /* Put a sequence tag before the requestList. */
+ p = ksba_writer_snatch_mem (w3, &derlen);
+ if (!p)
+ {
+ err = ksba_writer_error (w3);
+ goto leave;
+ }
+ err = _ksba_ber_write_tl (w4, TYPE_SEQUENCE, CLASS_UNIVERSAL,
+ 1, derlen);
+ if (!err)
+ err = ksba_writer_write (w4, p, derlen);
+ xfree (p); p = NULL;
+ if (err)
+ goto leave;
+
+
+ /* Write the tbsRequest. */
+
+ /* The version is default, thus we don't write it. */
+
+ /* The requesterName would go here. */
+
+ /* Write the requestList. */
+ p = ksba_writer_snatch_mem (w4, &derlen);
+ if (!p)
+ {
+ err = ksba_writer_error (w4);
+ goto leave;
+ }
+ err = _ksba_ber_write_tl (w5, TYPE_SEQUENCE, CLASS_UNIVERSAL,
+ 1, derlen);
+ if (!err)
+ err = ksba_writer_write (w5, p, derlen);
+ xfree (p); p = NULL;
+ if (err)
+ goto leave;
+
+ /* The requestExtensions would go here. */
+
+ /* Reuse writers; for clarity, use new names. */
+ w6 = w3;
+ w7 = w4;
+ err = ksba_writer_set_mem (w6, 2048);
+ if (!err)
+ err = ksba_writer_set_mem (w7, 2048);
+ if (err)
+ goto leave;
+
+ /* Prepend a sequence tag. */
+ p = ksba_writer_snatch_mem (w5, &derlen);
+ if (!p)
+ {
+ err = ksba_writer_error (w5);
+ goto leave;
+ }
+ err = _ksba_ber_write_tl (w6, TYPE_SEQUENCE, CLASS_UNIVERSAL,
+ 1, derlen);
+ if (!err)
+ err = ksba_writer_write (w6, p, derlen);
+ xfree (p); p = NULL;
+ if (err)
+ goto leave;
+
+ /* Write the ocspRequest. */
+
+ /* Note that we do not support the optional Signature, becuase this
+ saves us one writer object. */
+
+ /* Prepend a sequence tag. */
+/* p = ksba_writer_snatch_mem (w6, &derlen); */
+/* if (!p) */
+/* { */
+/* err = ksba_writer_error (w6); */
+/* goto leave; */
+/* } */
+/* err = _ksba_ber_write_tl (w7, TYPE_SEQUENCE, CLASS_UNIVERSAL, */
+/* 1, derlen); */
+/* if (!err) */
+/* err = ksba_writer_write (w7, p, derlen); */
+/* xfree (p); p = NULL; */
+/* if (err) */
+/* goto leave; */
+
+
+ /* Read out the entire request. */
+ p = ksba_writer_snatch_mem (w6, &derlen);
+ if (!p)
+ {
+ err = ksba_writer_error (w6);
+ goto leave;
+ }
+ *r_buffer = p;
+ *r_buflen = derlen;
+ /* Ready. */
+
+ leave:
+ ksba_writer_release (w3);
+ ksba_writer_release (w2);
+ ksba_writer_release (w1);
+ return err;
+}
+
+
+
+
+/* Parse the first part of a response:
+
+ 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 }
+
+ On success the RESPONSE_STATUS field of OCSP will be set to the
+ response status and DATA will now point to the first byte in the
+ octet string of the response; RLEN will be set to the length of
+ this octet string. Note thate DATALEN is also updated but might
+ point to a value larger than RLEN points to, if the provided data
+ is a part of a larger image. */
+static gpg_error_t
+parse_response_status (ksba_ocsp_t ocsp,
+ unsigned char const **data, size_t *datalen,
+ size_t *rlength)
+{
+ gpg_error_t err;
+ struct tag_info ti;
+ char *oid;
+
+ *rlength = 0;
+ /* Parse the OCSPResponse sequence. */
+ err = parse_sequence (data, datalen, &ti);
+ if (err)
+ return err;
+ /* Parse the OCSPResponseStatus. */
+ err = parse_enumerated (data, datalen, &ti, 1);
+ if (err)
+ return err;
+ switch (**data)
+ {
+ case 0: ocsp->response_status = KSBA_OCSP_RSPSTATUS_SUCCESS; break;
+ case 1: ocsp->response_status = KSBA_OCSP_RSPSTATUS_MALFORMED; break;
+ case 2: ocsp->response_status = KSBA_OCSP_RSPSTATUS_INTERNAL; break;
+ case 3: ocsp->response_status = KSBA_OCSP_RSPSTATUS_TRYLATER; break;
+ case 5: ocsp->response_status = KSBA_OCSP_RSPSTATUS_SIGREQUIRED; break;
+ case 6: ocsp->response_status = KSBA_OCSP_RSPSTATUS_UNAUTHORIZED; break;
+ default: ocsp->response_status = KSBA_OCSP_RSPSTATUS_OTHER; break;
+ }
+ parse_skip (data, datalen, &ti);
+
+ if (ocsp->response_status)
+ return 0; /* This is an error reponse; we have to stop here. */
+
+ /* We have a successful reponse status, thus we check that
+ ResponseBytes are actually available. */
+ err = parse_context_tag (data, datalen, &ti, 0);
+ if (err)
+ return err;
+ err = parse_sequence (data, datalen, &ti);
+ if (err)
+ return err;
+ err = parse_object_id_into_str (data, datalen, &oid);
+ if (err)
+ return err;
+ if (strcmp (oid, oidstr_ocspBasic))
+ {
+ xfree (oid);
+ return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+ }
+ xfree (oid);
+
+ /* Check that the next field is an octet string. */
+ err = parse_octet_string (data, datalen, &ti);
+ if (err)
+ return err;
+ *rlength = ti.length;
+ return 0;
+}
+
+/* Parse the object:
+
+ 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
+
+*/
+
+static gpg_error_t
+parse_single_response (ksba_ocsp_t ocsp,
+ unsigned char const **data, size_t *datalen)
+{
+ gpg_error_t err;
+ struct tag_info ti;
+ const unsigned char *savedata;
+ const unsigned char *endptr;
+ size_t savedatalen;
+ size_t n;
+ char *oid;
+ ksba_isotime_t this_update, next_update, revocation_time;
+ ksba_crl_reason_t reason = 0;
+ int look_for_request;
+ const unsigned char *name_hash;
+ const unsigned char *key_hash;
+ const unsigned char *serialno;
+ size_t serialnolen;
+ struct ocsp_reqitem_s *request_item = NULL;
+
+ /* The SingeResponse sequence. */
+ err = parse_sequence (data, datalen, &ti);
+ if (err)
+ return err;
+ endptr = *data + ti.length;
+
+ /* The CertID is
+ SEQUENCE {
+ hashAlgorithm AlgorithmIdentifier,
+ issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ serialNumber CertificateSerialNumber }
+ */
+ err = parse_sequence (data, datalen, &ti);
+ if (err)
+ return err;
+ err = _ksba_parse_algorithm_identifier (*data, *datalen, &n, &oid);
+ if (err)
+ return err;
+ assert (n <= *datalen);
+ *data += n;
+ *datalen -= n;
+ fprintf (stderr, "algorithmIdentifier is `%s'\n", oid);
+ look_for_request = !strcmp (oid, oidstr_sha1);
+ xfree (oid);
+
+ err = parse_octet_string (data, datalen, &ti);
+ if (err)
+ return err;
+ name_hash = *data;
+ fprintf (stderr, "issuerNameHash=");
+ dump_hex (*data, ti.length);
+ putc ('\n', stderr);
+ if (ti.length != 20)
+ look_for_request = 0; /* Can't be a SHA-1 digest. */
+ parse_skip (data, datalen, &ti);
+
+ err = parse_octet_string (data, datalen, &ti);
+ if (err)
+ return err;
+ key_hash = *data;
+ fprintf (stderr, "issuerKeyHash=");
+ dump_hex (*data, ti.length);
+ putc ('\n', stderr);
+ if (ti.length != 20)
+ look_for_request = 0; /* Can't be a SHA-1 digest. */
+ parse_skip (data, datalen, &ti);
+
+ err= parse_integer (data, datalen, &ti);
+ if (err)
+ return err;
+ serialno = *data;
+ serialnolen = ti.length;
+ fprintf (stderr, "serialNumber=");
+ dump_hex (*data, ti.length);
+ putc ('\n', stderr);
+ parse_skip (data, datalen, &ti);
+
+ if (look_for_request)
+ {
+ for (request_item = ocsp->requestlist;
+ request_item; request_item = request_item->next)
+ if (!memcmp (request_item->issuer_name_hash, name_hash, 20)
+ && !memcmp (request_item->issuer_key_hash, key_hash, 20)
+ && request_item->serialnolen == serialnolen
+ && !memcmp (request_item->serialno, serialno, serialnolen))
+ break; /* Got it. */
+ }
+
+
+ /*
+ CertStatus ::= CHOICE {
+ good [0] IMPLICIT NULL,
+ revoked [1] IMPLICIT RevokedInfo,
+ unknown [2] IMPLICIT UnknownInfo }
+ */
+ *revocation_time = 0;
+ err = _ksba_ber_parse_tl (data, datalen, &ti);
+ if (err)
+ return err;
+ if (ti.length > *datalen)
+ return gpg_error (GPG_ERR_BAD_BER);
+ else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && !ti.is_constructed)
+ { /* good */
+ if (!ti.length)
+ ; /* Cope with zero length objects. */
+ else if (*datalen && !**data)
+ { /* Skip the NULL. */
+ *datalen--;
+ *data++;
+ }
+ else
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+ else if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed)
+ { /* revoked */
+ err = parse_asntime_into_isotime (data, datalen, revocation_time);
+ if (err)
+ return err;
+ fprintf (stderr, "revocationTime=%s\n", revocation_time);
+
+ savedata = *data;
+ savedatalen = *datalen;
+ err = parse_context_tag (data, datalen, &ti, 0);
+ if (err)
+ {
+ *data = savedata;
+ *datalen = savedatalen;
+ }
+ else
+ { /* Got a revocationReason. */
+ err = parse_enumerated (data, datalen, &ti, 1);
+ if (err)
+ return err;
+ switch (**data)
+ {
+ case 0: reason = KSBA_CRLREASON_UNSPECIFIED; break;
+ case 1: reason = KSBA_CRLREASON_KEY_COMPROMISE; break;
+ case 2: reason = KSBA_CRLREASON_CA_COMPROMISE; break;
+ case 3: reason = KSBA_CRLREASON_AFFILIATION_CHANGED; break;
+ case 4: reason = KSBA_CRLREASON_SUPERSEDED; break;
+ case 5: reason = KSBA_CRLREASON_CESSATION_OF_OPERATION; break;
+ case 6: reason = KSBA_CRLREASON_CERTIFICATE_HOLD; break;
+ case 8: reason = KSBA_CRLREASON_REMOVE_FROM_CRL; break;
+ case 9: reason = KSBA_CRLREASON_PRIVILEGE_WITHDRAWN; break;
+ case 10: reason = KSBA_CRLREASON_AA_COMPROMISE; break;
+ default: reason = KSBA_CRLREASON_OTHER; break;
+ }
+ parse_skip (data, datalen, &ti);
+ }
+ fprintf (stderr, "revocationReason=%04x\n", reason);
+ }
+ else if (ti.class == CLASS_CONTEXT && ti.tag == 2 && !ti.is_constructed
+ && *datalen)
+ { /* unknown */
+ if (!ti.length)
+ ; /* Cope with zero length objects. */
+ else if (!**data)
+ { /* Skip the NULL. */
+ *datalen--;
+ *data++;
+ }
+ else /* The comment indicates that an enumeration may come here. */
+ {
+ err = parse_enumerated (data, datalen, &ti, 0);
+ if (err)
+ return err;
+ fprintf (stderr, "unknownReason with an enum of length %u detected\n",
+ (unsigned int)ti.length);
+ parse_skip (data, datalen, &ti);
+ }
+ }
+ else
+ err = gpg_error (GPG_ERR_INV_OBJ);
+
+ /* thisUpdate. */
+ err = parse_asntime_into_isotime (data, datalen, this_update);
+ if (err)
+ return err;
+ fprintf (stderr, "thisUpdate=%s\n", this_update);
+
+
+ /* nextUpdate is optional. */
+ if (*data >= endptr)
+ return 0;
+ *next_update = 0;
+ err = _ksba_ber_parse_tl (data, datalen, &ti);
+ if (err)
+ return err;
+ if (ti.length > *datalen)
+ return gpg_error (GPG_ERR_BAD_BER);
+ else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)
+ { /* have nextUpdate */
+ err = parse_asntime_into_isotime (data, datalen, next_update);
+ if (err)
+ return err;
+ fprintf (stderr, "nextUpdate=%s\n", this_update);
+ }
+ else if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed)
+ { /* Undo that read. */
+ *data -= ti.nhdr;
+ *datalen += ti.nhdr;
+ }
+ else
+ err = gpg_error (GPG_ERR_INV_OBJ);
+
+ /* singleExtensions is optional */
+ if (*data >= endptr)
+ return 0;
+ err = _ksba_ber_parse_tl (data, datalen, &ti);
+ if (err)
+ return err;
+ if (ti.length > *datalen)
+ return gpg_error (GPG_ERR_BAD_BER);
+ if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed)
+ {
+ parse_skip (data, datalen, &ti); /* FIXME */
+ }
+ else
+ err = gpg_error (GPG_ERR_INV_OBJ);
+
+ return 0;
+}
+
+/* Parse the object:
+
+ 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 }
+
+
+*/
+static gpg_error_t
+parse_response_data (ksba_ocsp_t ocsp,
+ unsigned char const **data, size_t *datalen)
+{
+ gpg_error_t err;
+ struct tag_info ti;
+ const unsigned char *savedata;
+ size_t savedatalen;
+ size_t responses_length;
+ ksba_isotime_t produced_at;
+
+ /* The out er sequence. */
+ err = parse_sequence (data, datalen, &ti);
+ if (err)
+ return err;
+
+ /* The optional version field. */
+ savedata = *data;
+ savedatalen = *datalen;
+ err = parse_context_tag (data, datalen, &ti, 0);
+ if (err)
+ {
+ *data = savedata;
+ *datalen = savedatalen;
+ }
+ else
+ {
+ /* FIXME: check that the version matches. */
+ parse_skip (data, datalen, &ti);
+ }
+
+ /* The responderID field. */
+ err = _ksba_ber_parse_tl (data, datalen, &ti);
+ if (err)
+ return err;
+ if (ti.length > *datalen)
+ return gpg_error (GPG_ERR_BAD_BER);
+ else if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed)
+ { /* byName. */
+ parse_skip (data, datalen, &ti); /* FIXME */
+ }
+ else if (ti.class == CLASS_CONTEXT && ti.tag == 2 && ti.is_constructed)
+ { /* byKey. */
+ parse_skip (data, datalen, &ti); /* FIXME */
+ }
+ else
+ err = gpg_error (GPG_ERR_INV_OBJ);
+
+ /* The producedAt field. */
+ err = parse_asntime_into_isotime (data, datalen, produced_at);
+ if (err)
+ return err;
+
+ /* The responses field set. */
+ err = parse_sequence (data, datalen, &ti);
+ if (err )
+ return err;
+ responses_length = ti.length;
+ while (responses_length)
+ {
+ savedatalen = *datalen;
+ err = parse_single_response (ocsp, data, datalen);
+ if (err)
+ return err;
+ assert (responses_length >= savedatalen - *datalen);
+ responses_length -= savedatalen - *datalen;
+ }
+
+ /* The optional responseExtensions set. */
+ savedata = *data;
+ savedatalen = *datalen;
+ err = parse_context_tag (data, datalen, &ti, 1);
+ if (!err)
+ {
+ /* FIXME: parse responseExtensions. */
+ parse_skip (data, datalen, &ti);
+ }
+ else if (gpg_err_code (err) == GPG_ERR_INV_OBJ)
+ {
+ *data = savedata;
+ *datalen = savedatalen;
+ }
+ else
+ return err;
+
+ return 0;
+}
+
+
+/* Parse the entire response message pointed to by MSG of length
+ MSGLEN. */
+static gpg_error_t
+parse_response (ksba_ocsp_t ocsp, const unsigned char *msg, size_t msglen)
+{
+ gpg_error_t err;
+ struct tag_info ti;
+ const unsigned char *msgstart;
+ const unsigned char *endptr;
+ const char *s;
+ size_t len;
+
+
+ msgstart = msg;
+ err = parse_response_status (ocsp, &msg, &msglen, &len);
+ if (err)
+ return err;
+ msglen = len; /* We don't care about any extra bytes provided to us. */
+ if (ocsp->response_status)
+ {
+ fprintf (stderr,"response status found to be %d - stop\n",
+ ocsp->response_status);
+ return 0;
+ }
+
+ /* Now that we are sure that it is a BasicOCSPResponse, we can parse
+ the really important things:
+
+ BasicOCSPResponse ::= SEQUENCE {
+ tbsResponseData ResponseData,
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING,
+ certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+ err = parse_sequence (&msg, &msglen, &ti);
+ if (err)
+ return err;
+ endptr = msg + ti.length;
+
+ ocsp->hash_offset = msg - msgstart;
+ err = parse_response_data (ocsp, &msg, &msglen);
+ if (err)
+ return err;
+ ocsp->hash_length = msg - msgstart - ocsp->hash_offset;
+
+ /* The signatureAlgorithm and the signature. We only need to get the
+ length of both objects and let a specialized function do the
+ actual parsing. */
+ s = msg;
+ len = msglen;
+ err = parse_sequence (&msg, &msglen, &ti);
+ if (err)
+ return err;
+ parse_skip (&msg, &msglen, &ti);
+ err= _ksba_ber_parse_tl (&msg, &msglen, &ti);
+ if (err)
+ return err;
+ if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BIT_STRING
+ && !ti.is_constructed) )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (!ti.length)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else if (ti.length > msglen)
+ err = gpg_error (GPG_ERR_BAD_BER);
+ parse_skip (&msg, &msglen, &ti);
+ len = len - msglen;
+ xfree (ocsp->sigval); ocsp->sigval = NULL;
+ err = _ksba_sigval_to_sexp (s, len, &ocsp->sigval);
+ if (err)
+ return err;
+
+ /* Parse the optional sequence of certificates. */
+ if (msg >= endptr)
+ return 0; /* It's optional, so stop now. */
+ err = parse_context_tag (&msg, &msglen, &ti, 0);
+ if (gpg_err_code (err) == GPG_ERR_INV_OBJ)
+ return 0; /* Not the right tag. Stop here. */
+ if (err)
+ return err;
+ err = parse_sequence (&msg, &msglen, &ti);
+ if (err)
+ return err;
+ if (ti.ndef)
+ return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
+
+ {
+ ksba_cert_t cert;
+ struct ocsp_certlist_s *cl, **cl_tail;
+
+ assert (!ocsp->received_certs);
+ cl_tail = &ocsp->received_certs;
+ endptr = msg + ti.length;
+ while (msg < endptr)
+ {
+ /* Find the length of the certificate. */
+ s = msg;
+ err = parse_sequence (&msg, &msglen, &ti);
+ if (err)
+ return err;
+ err = ksba_cert_new (&cert);
+ if (err)
+ return err;
+ err = ksba_cert_init_from_mem (cert, msg, ti.length);
+ if (err)
+ {
+ ksba_cert_release (cert);
+ return err;
+ }
+ parse_skip (&msg, &msglen, &ti);
+ cl = xtrycalloc (1, sizeof *cl);
+ if (!cl)
+ err = gpg_error_from_errno (errno);
+ if (err)
+ {
+ ksba_cert_release (cert);
+ return gpg_error (GPG_ERR_ENOMEM);
+ }
+ cl->cert = cert;
+
+ *cl_tail = cl;
+ cl_tail = &ocsp->received_certs;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Given the OCSP context and a binary reponse message of MSGLEN bytes
+ in MSG, this fucntion parses the response and prepares it for
+ signature verification. The status from the server is retruned in
+ RESPONSE_STATUS and must be checked even if the fucntion returns
+ without an error. */
+gpg_error_t
+ksba_ocsp_parse_response (ksba_ocsp_t ocsp,
+ const unsigned char *msg, size_t msglen,
+ ksba_ocsp_response_status_t *response_status)
+{
+ gpg_error_t err;
+
+ if (!ocsp || !msg || !msglen || !response_status)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ ocsp->response_status = KSBA_OCSP_RSPSTATUS_NONE;
+ release_ocsp_certlist (ocsp->received_certs);
+ ocsp->received_certs = NULL;
+ ocsp->hash_length = 0;
+
+ err = parse_response (ocsp, msg, msglen);
+ *response_status = ocsp->response_status;
+
+ /* FIXME: find duplicates in the requets list and set them to the
+ same status. */
+
+ return err;
+}
+
+
+/* Return the digest algorithm to be used for the signature or NULL in
+ case of an error. The returned pointer is valid as long as the
+ context is valid and no other ksba_ocsp_parse_response or
+ ksba_ocsp_build_request has been used. */
+const char *
+ksba_ocsp_get_digest_algo (ksba_ocsp_t ocsp)
+{
+ return ocsp? ocsp->digest_oid : NULL;
+}
+
+
+/* Hash the data of the response using the hash function HASHER which
+ will be passed HASHER_ARG as its first argument and a pointer and a
+ length of the data to be hashed. This hash function might be called
+ several times and should update the hash context. The algorithm to
+ be used for the hashing can be retrieved using
+ ksba_ocsp_get_digest_algo. Note that MSG and MSGLEN should be
+ indentical to the values passed to ksba_ocsp_parse_response. */
+gpg_error_t
+ksba_ocsp_hash_response (ksba_ocsp_t ocsp,
+ const unsigned char *msg, size_t msglen,
+ void (*hasher)(void *, const void *, size_t length),
+ void *hasher_arg)
+
+{
+ if (!ocsp || !msg || !hasher)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!ocsp->hash_length)
+ return gpg_error (GPG_ERR_MISSING_ACTION);
+ if (ocsp->hash_offset + ocsp->hash_length >= msglen)
+ return gpg_error (GPG_ERR_CONFLICT);
+
+ hasher (hasher_arg, msg + ocsp->hash_offset, ocsp->hash_length);
+ return 0;
+}
+
+
+/* Return the actual signature in a format suitable to be used as
+ input to Libgcrypt's verification function. The caller must free
+ the returned string and that function may be called only once after
+ a successful ksba_ocsp_parse_response. Returns NULL for an invalid
+ handle or if no signature is available. */
+ksba_sexp_t
+ksba_ocsp_get_sig_val (ksba_ocsp_t ocsp)
+{
+ ksba_sexp_t p;
+
+ if (!ocsp || !ocsp->sigval )
+ return NULL;
+
+ p = ocsp->sigval;
+ ocsp->sigval = NULL;
+ return p;
+}
+
diff --git a/src/ocsp.h b/src/ocsp.h
new file mode 100644
index 0000000..bf27689
--- /dev/null
+++ b/src/ocsp.h
@@ -0,0 +1,131 @@
+/* ocsp.h - OCSP (rfc2560)
+ * Copyright (C) 2003 g10 Code GmbH
+ *
+ * This file is part of KSBA.
+ *
+ * KSBA 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * KSBA 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef OCSP_H
+#define OCSP_H 1
+
+#include "ksba.h"
+
+typedef enum
+ {
+ KSBA_OCSP_RSPSTATUS_SUCCESS = 0,
+ KSBA_OCSP_RSPSTATUS_MALFORMED = 1,
+ KSBA_OCSP_RSPSTATUS_INTERNAL = 2,
+ KSBA_OCSP_RSPSTATUS_TRYLATER = 3,
+ KSBA_OCSP_RSPSTATUS_SIGREQUIRED = 5,
+ KSBA_OCSP_RSPSTATUS_UNAUTHORIZED = 6,
+ KSBA_OCSP_RSPSTATUS_OTHER = 254,
+ KSBA_OCSP_RSPSTATUS_NONE = 255
+ }
+ksba_ocsp_response_status_t;
+
+
+/* A structure to keep a information about a single status request. */
+struct ocsp_reqitem_s {
+ struct ocsp_reqitem_s *next;
+
+ ksba_cert_t cert; /* The target certificate for the request. */
+ ksba_cert_t issuer_cert; /* And the certificate of the issuer. */
+
+ /* The next 4 fields are used to match a response with a request. */
+ unsigned char issuer_name_hash[20]; /* The hash as used by the request. */
+ unsigned char issuer_key_hash[20]; /* The hash as used by the request. */
+ unsigned char *serialno; /* A malloced copy of the serial number. */
+ size_t serialnolen; /* and its length. */
+
+ /* The actual status as parsed from the response. */
+ int got_answer; /* Set to true if a corresponding response
+ has been found. */
+ int is_revoked; /* Set to true if the target certificate
+ has been revoked. */
+ ksba_isotime_t this_update; /* The thisUpdate value from the response. */
+ ksba_isotime_t next_update; /* The nextUpdate value from the response. */
+ ksba_isotime_t revocation_time; /* The indicated revocation time. */
+
+};
+
+
+/* A structure to store certificates read from a response. */
+struct ocsp_certlist_s {
+ struct ocsp_certlist_s *next;
+ ksba_cert_t cert;
+};
+
+
+
+/* A structure used as context for the ocsp subsystem. */
+struct ksba_ocsp_s {
+
+ char *digest_oid; /* The OID of the digest algorithm to be
+ used for a request. */
+
+ ksba_reader_t reader; /* The reader used to parse responses. */
+
+ /* The hash fucntion and its argument to be used by this object. */
+ void (*hash_fnc)(void *, const void *, size_t);
+ void *hash_fnc_arg;
+
+ struct ocsp_reqitem_s *requestlist; /* The list of request items. */
+
+ size_t hash_offset; /* What area of a response is to be */
+ size_t hash_length; /* hashed. */
+
+ ksba_ocsp_response_status_t response_status; /* Status of the response. */
+ ksba_sexp_t sigval; /* The signature value. */
+ struct ocsp_certlist_s *received_certs; /* Certificates received in
+ the response. */
+
+};
+
+
+
+
+/* Stuff to be moved into ksba.h */
+
+
+typedef struct ksba_ocsp_s *ksba_ocsp_t;
+
+gpg_error_t ksba_ocsp_new (ksba_ocsp_t *r_oscp);
+void ksba_ocsp_release (ksba_ocsp_t ocsp);
+gpg_error_t ksba_ocsp_add_certs (ksba_ocsp_t ocsp,
+ ksba_cert_t cert, ksba_cert_t issuer_cert);
+gpg_error_t ksba_ocsp_set_digest_algo (ksba_ocsp_t ocsp, const char *oid);
+gpg_error_t ksba_ocsp_build_request (ksba_ocsp_t ocsp,
+ unsigned char **r_buffer,
+ size_t *r_buflen);
+
+
+gpg_error_t ksba_ocsp_parse_response (
+ ksba_ocsp_t ocsp,
+ const unsigned char *msg, size_t msglen,
+ ksba_ocsp_response_status_t *response_status);
+
+const char *ksba_ocsp_get_digest_algo (ksba_ocsp_t ocsp);
+
+gpg_error_t ksba_ocsp_hash_response (ksba_ocsp_t ocsp,
+ const unsigned char *msg, size_t msglen,
+ void (*hasher)(void *, const void *,
+ size_t length),
+ void *hasher_arg);
+
+ksba_sexp_t ksba_ocsp_get_sig_val (ksba_ocsp_t ocsp);
+
+
+#endif /*OCSP_H*/
diff --git a/src/sexp-parse.h b/src/sexp-parse.h
new file mode 100644
index 0000000..e7cfd96
--- /dev/null
+++ b/src/sexp-parse.h
@@ -0,0 +1,99 @@
+/* sexp-parse.h - S-expression helper functions for canonical encodings.
+ * Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef SEXP_PARSE_H
+#define SEXP_PARSE_H
+
+#include <gpg-error.h>
+
+/* Return the length of the next S-Exp part and update the pointer to
+ the first data byte. 0 is returned on error */
+static inline size_t
+snext (unsigned char const **buf)
+{
+ const unsigned char *s;
+ int n;
+
+ s = *buf;
+ for (n=0; *s && *s != ':' && (*s >= '0' && *s <= '9'); s++)
+ n = n*10 + (*s - '0');
+ if (!n || *s != ':')
+ return 0; /* we don't allow empty lengths */
+ *buf = s+1;
+ return n;
+}
+
+/* Skip over the S-Expression BUF points to and update BUF to point to
+ the chacter right behind. DEPTH gives the initial number of open
+ lists and may be passed as a positive number to skip over the
+ remainder of an S-Expression if the current position is somewhere
+ in an S-Expression. The function may return an error code if it
+ encounters an impossible conditions */
+static inline gpg_error_t
+sskip (unsigned char const **buf, int *depth)
+{
+ const unsigned char *s = *buf;
+ size_t n;
+ int d = *depth;
+
+ while (d > 0)
+ {
+ if (*s == '(')
+ {
+ d++;
+ s++;
+ }
+ else if (*s == ')')
+ {
+ d--;
+ s++;
+ }
+ else
+ {
+ if (!d)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ n = snext (&s);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s += n;
+ }
+ }
+ *buf = s;
+ *depth = d;
+ return 0;
+}
+
+
+/* Check whether the the string at the address BUF points to matches
+ the token. Return true on match and update BUF to point behind the
+ token. Return false and does not update the buffer if it does not
+ match. */
+static inline int
+smatch (unsigned char const **buf, size_t buflen, const char *token)
+{
+ size_t toklen = strlen (token);
+
+ if (buflen != toklen || memcmp (*buf, token, toklen))
+ return 0;
+ *buf += toklen;
+ return 1;
+}
+
+#endif /*SEXP_PARSE_H*/

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 9, 12:55 AM (1 d, 1 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
46/10/4266983e44601f0a15cb44394e66

Event Timeline