Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34140187
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
42 KB
Subscribers
None
View Options
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
Details
Attached
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
Attached To
rK libksba
Event Timeline
Log In to Comment