class OpenSSL::OCSP::BasicResponse - RDoc Documentation

# class OpenSSL::OCSP::BasicResponse

An OpenSSL::OCSP::BasicResponse contains the status of a certificate check which is created from an OpenSSL::OCSP::Request. A BasicResponse is more detailed than a Response.

### Public Class Methods

OpenSSL::OCSP::BasicResponse.new(der_string = nil) → basic_response click to toggle source

Creates a new BasicResponse. If der_string is given, decodes der_string as DER.

    static VALUE
    ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self)
    {
        VALUE arg;
        OCSP_BASICRESP *res, *res_new;
        const unsigned char *p;

        rb_scan_args(argc, argv, "01", &arg);
        if (!NIL_P(arg)) {
            GetOCSPBasicRes(self, res);
            arg = ossl_to_der_if_possible(arg);
            StringValue(arg);
            p = (unsigned char *)RSTRING_PTR(arg);
            res_new = d2i_OCSP_BASICRESP(NULL, &p, RSTRING_LEN(arg));
            if (!res_new)
                ossl_raise(eOCSPError, "d2i_OCSP_BASICRESP");
            SetOCSPBasicRes(self, res_new);
            OCSP_BASICRESP_free(res);
        }

        return self;
    }

### Public Instance Methods

add_nonce(nonce = nil) click to toggle source

Adds nonce to this response. If no nonce was provided a random nonce will be added.

    static VALUE
    ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self)
    {
        OCSP_BASICRESP *bs;
        VALUE val;
        int ret;

        rb_scan_args(argc, argv, "01", &val);
        if(NIL_P(val)) {
            GetOCSPBasicRes(self, bs);
            ret = OCSP_basic_add1_nonce(bs, NULL, -1);
        }
        else{
            StringValue(val);
            GetOCSPBasicRes(self, bs);
            ret = OCSP_basic_add1_nonce(bs, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val));
        }
        if(!ret) ossl_raise(eOCSPError, NULL);

        return self;
    }

add_status(certificate_id, status, reason, revocation_time, this_update, next_update, extensions) → basic_response click to toggle source

Adds a certificate status for certificate_id. status is the status, and must be one of these:

- OpenSSL::OCSP::V_CERTSTATUS_GOOD

- OpenSSL::OCSP::V_CERTSTATUS_REVOKED

- OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN

reason and revocation_time can be given only when status is OpenSSL::OCSP::V_CERTSTATUS_REVOKED. reason describes the reason for the revocation, and must be one of OpenSSL::OCSP::REVOKED_STATUS_* constants. revocation_time is the time when the certificate is revoked.

this_update and next_update indicate the time at which ths status is verified to be correct and the time at or before which newer information will be available, respectively. next_update is optional.

extensions is an Array of OpenSSL::X509::Extension to be included in the SingleResponse. This is also optional.

Note that the times, revocation_time, this_update and next_update can be specified in either of Integer or Time object. If they are Integer, it is treated as the relative seconds from the current time.

    static VALUE
    ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status,
                             VALUE reason, VALUE revtime,
                             VALUE thisupd, VALUE nextupd, VALUE ext)
    {
        OCSP_BASICRESP *bs;
        OCSP_SINGLERESP *single;
        OCSP_CERTID *id;
        ASN1_TIME *ths = NULL, *nxt = NULL, *rev = NULL;
        int st, rsn = 0, error = 0, rstatus = 0;
        long i;
        VALUE tmp;

        GetOCSPBasicRes(self, bs);
        GetOCSPCertId(cid, id);
        st = NUM2INT(status);
        if (!NIL_P(ext)) { /* All ext's members must be X509::Extension */
            ext = rb_check_array_type(ext);
            for (i = 0; i < RARRAY_LEN(ext); i++)
                OSSL_Check_Kind(RARRAY_AREF(ext, i), cX509Ext);
        }

        if (st == V_OCSP_CERTSTATUS_REVOKED) {
            rsn = NUM2INT(reason);
            tmp = rb_protect(add_status_convert_time, revtime, &rstatus);
            if (rstatus) goto err;
            rev = (ASN1_TIME *)tmp;
        }

        tmp = rb_protect(add_status_convert_time, thisupd, &rstatus);
        if (rstatus) goto err;
        ths = (ASN1_TIME *)tmp;

        if (!NIL_P(nextupd)) {
            tmp = rb_protect(add_status_convert_time, nextupd, &rstatus);
            if (rstatus) goto err;
            nxt = (ASN1_TIME *)tmp;
        }

        if(!(single = OCSP_basic_add1_status(bs, id, st, rsn, rev, ths, nxt))){
            error = 1;
            goto err;
        }

        if(!NIL_P(ext)){
            X509_EXTENSION *x509ext;

            for(i = 0; i < RARRAY_LEN(ext); i++){
                x509ext = GetX509ExtPtr(RARRAY_AREF(ext, i));
                if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){
                    error = 1;
                    goto err;
                }
            }
        }

     err:
        ASN1_TIME_free(ths);
        ASN1_TIME_free(nxt);
        ASN1_TIME_free(rev);
        if(error) ossl_raise(eOCSPError, NULL);
        if(rstatus) rb_jump_tag(rstatus);

        return self;
    }

copy_nonce(request) → Integer click to toggle source

Copies the nonce from request into this response. Returns 1 on success and 0 on failure.

    static VALUE
    ossl_ocspbres_copy_nonce(VALUE self, VALUE request)
    {
        OCSP_BASICRESP *bs;
        OCSP_REQUEST *req;
        int ret;

        GetOCSPBasicRes(self, bs);
        GetOCSPReq(request, req);
        ret = OCSP_copy_nonce(bs, req);

        return INT2NUM(ret);
    }

find_response(certificate_id) → SingleResponse | nil click to toggle source

Returns a SingleResponse whose CertId matches with certificate_id, or `nil` if this BasicResponse does not contain it.

    static VALUE
    ossl_ocspbres_find_response(VALUE self, VALUE target)
    {
        OCSP_BASICRESP *bs;
        OCSP_SINGLERESP *sres, *sres_new;
        OCSP_CERTID *id;
        int n;

        GetOCSPCertId(target, id);
        GetOCSPBasicRes(self, bs);

        if ((n = OCSP_resp_find(bs, id, -1)) == -1)
            return Qnil;

        sres = OCSP_resp_get0(bs, n);
        sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres);
        if (!sres_new)
            ossl_raise(eOCSPError, "ASN1_item_dup");

        return ossl_ocspsres_new(sres_new);
    }

initialize_copy(p1) click to toggle source

    static VALUE
    ossl_ocspbres_initialize_copy(VALUE self, VALUE other)
    {
        OCSP_BASICRESP *bs, *bs_old, *bs_new;

        rb_check_frozen(self);
        GetOCSPBasicRes(self, bs_old);
        GetOCSPBasicRes(other, bs);

        bs_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs);
        if (!bs_new)
            ossl_raise(eOCSPError, "ASN1_item_dup");

        SetOCSPBasicRes(self, bs_new);
        OCSP_BASICRESP_free(bs_old);

        return self;
    }

responses → Array of SingleResponse click to toggle source

Returns an Array of SingleResponse for this BasicResponse.

    static VALUE
    ossl_ocspbres_get_responses(VALUE self)
    {
        OCSP_BASICRESP *bs;
        VALUE ret;
        int count, i;

        GetOCSPBasicRes(self, bs);
        count = OCSP_resp_count(bs);
        ret = rb_ary_new2(count);

        for (i = 0; i < count; i++) {
            OCSP_SINGLERESP *sres, *sres_new;

            sres = OCSP_resp_get0(bs, i);
            sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres);
            if (!sres_new)
                ossl_raise(eOCSPError, "ASN1_item_dup");

            rb_ary_push(ret, ossl_ocspsres_new(sres_new));
        }

        return ret;
    }

sign(cert, key, certs = nil, flags = 0, digest = nil) → self click to toggle source

Signs this OCSP response using the cert, key and optional digest. This behaves in the similar way as OpenSSL::OCSP::Request#sign.

flags can include:

OpenSSL::OCSP::NOCERTS

  don't include certificates

OpenSSL::OCSP::NOTIME

  don't set producedAt

OpenSSL::OCSP::RESPID_KEY

  use signer's public key hash as responderID

    static VALUE
    ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self)
    {
        VALUE signer_cert, signer_key, certs, flags, digest;
        OCSP_BASICRESP *bs;
        X509 *signer;
        EVP_PKEY *key;
        STACK_OF(X509) *x509s = NULL;
        unsigned long flg = 0;
        const EVP_MD *md;
        int ret;

        rb_scan_args(argc, argv, "23", &signer_cert, &signer_key, &certs, &flags, &digest);
        GetOCSPBasicRes(self, bs);
        signer = GetX509CertPtr(signer_cert);
        key = GetPrivPKeyPtr(signer_key);
        if (!NIL_P(flags))
            flg = NUM2INT(flags);
        if (NIL_P(digest))
            md = EVP_sha1();
        else
            md = ossl_evp_get_digestbyname(digest);
        if (NIL_P(certs))
            flg |= OCSP_NOCERTS;
        else
            x509s = ossl_x509_ary2sk(certs);

        ret = OCSP_basic_sign(bs, signer, key, md, x509s, flg);
        sk_X509_pop_free(x509s, X509_free);
        if (!ret) ossl_raise(eOCSPError, NULL);

        return self;
    }

status → statuses click to toggle source

Returns an Array of statuses for this response. Each status contains a CertificateId, the status (0 for good, 1 for revoked, 2 for unknown), the reason for the status, the revocation time, the time of this update, the time for the next update and a list of OpenSSL::X509::Extension.

This should be superseded by #responses and find_response that return SingleResponse.

    static VALUE
    ossl_ocspbres_get_status(VALUE self)
    {
        OCSP_BASICRESP *bs;
        OCSP_SINGLERESP *single;
        OCSP_CERTID *cid;
        ASN1_TIME *revtime, *thisupd, *nextupd;
        int status, reason;
        X509_EXTENSION *x509ext;
        VALUE ret, ary, ext;
        int count, ext_count, i, j;

        GetOCSPBasicRes(self, bs);
        ret = rb_ary_new();
        count = OCSP_resp_count(bs);
        for(i = 0; i < count; i++){
            single = OCSP_resp_get0(bs, i);
            if(!single) continue;

            revtime = thisupd = nextupd = NULL;
            status = OCSP_single_get0_status(single, &reason, &revtime,
                                             &thisupd, &nextupd);
            if(status < 0) continue;
            if(!(cid = OCSP_CERTID_dup((OCSP_CERTID *)OCSP_SINGLERESP_get0_id(single)))) /* FIXME */
                ossl_raise(eOCSPError, NULL);
            ary = rb_ary_new();
            rb_ary_push(ary, ossl_ocspcertid_new(cid));
            rb_ary_push(ary, INT2NUM(status));
            rb_ary_push(ary, INT2NUM(reason));
            rb_ary_push(ary, revtime ? asn1time_to_time(revtime) : Qnil);
            rb_ary_push(ary, thisupd ? asn1time_to_time(thisupd) : Qnil);
            rb_ary_push(ary, nextupd ? asn1time_to_time(nextupd) : Qnil);
            ext = rb_ary_new();
            ext_count = OCSP_SINGLERESP_get_ext_count(single);
            for(j = 0; j < ext_count; j++){
                x509ext = OCSP_SINGLERESP_get_ext(single, j);
                rb_ary_push(ext, ossl_x509ext_new(x509ext));
            }
            rb_ary_push(ary, ext);
            rb_ary_push(ret, ary);
        }

        return ret;
    }

to_der → String click to toggle source

Encodes this basic response into a DER-encoded string.

    static VALUE
    ossl_ocspbres_to_der(VALUE self)
    {
        OCSP_BASICRESP *res;
        VALUE str;
        long len;
        unsigned char *p;

        GetOCSPBasicRes(self, res);
        if ((len = i2d_OCSP_BASICRESP(res, NULL)) <= 0)
            ossl_raise(eOCSPError, NULL);
        str = rb_str_new(0, len);
        p = (unsigned char *)RSTRING_PTR(str);
        if (i2d_OCSP_BASICRESP(res, &p) <= 0)
            ossl_raise(eOCSPError, NULL);
        ossl_str_adjust(str, p);

        return str;
    }

verify(certificates, store, flags = 0) → true or false click to toggle source

Verifies the signature of the response using the given certificates and store. This works in the similar way as OpenSSL::OCSP::Request#verify.

    static VALUE
    ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self)
    {
        VALUE certs, store, flags;
        OCSP_BASICRESP *bs;
        STACK_OF(X509) *x509s;
        X509_STORE *x509st;
        int flg, result;

        rb_scan_args(argc, argv, "21", &certs, &store, &flags);
        GetOCSPBasicRes(self, bs);
        x509st = GetX509StorePtr(store);
        flg = NIL_P(flags) ? 0 : NUM2INT(flags);
        x509s = ossl_x509_ary2sk(certs);
    #if (OPENSSL_VERSION_NUMBER < 0x1000202fL) || defined(LIBRESSL_VERSION_NUMBER)
        /*
         * OpenSSL had a bug that it doesn't use the certificates in x509s for
         * verifying the chain. This can be a problem when the response is signed by
         * a certificate issued by an intermediate CA.
         *
         *       root_ca
         *         |
         *   intermediate_ca
         *         |-------------|
         *     end_entity    ocsp_signer
         *
         * When the certificate hierarchy is like this, and the response contains
         * only ocsp_signer certificate, the following code wrongly fails.
         *
         *   store = OpenSSL::X509::Store.new; store.add_cert(root_ca)
         *   basic_response.verify([intermediate_ca], store)
         *
         * So add the certificates in x509s to the embedded certificates list first.
         *
         * This is fixed in OpenSSL 0.9.8zg, 1.0.0s, 1.0.1n, 1.0.2b. But it still
         * exists in LibreSSL 2.1.10, 2.2.9, 2.3.6, 2.4.1.
         */
        if (!(flg & (OCSP_NOCHAIN | OCSP_NOVERIFY)) &&
            sk_X509_num(x509s) && sk_X509_num(bs->certs)) {
            int i;

            bs = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs);
            if (!bs) {
                sk_X509_pop_free(x509s, X509_free);
                ossl_raise(eOCSPError, "ASN1_item_dup");
            }

            for (i = 0; i < sk_X509_num(x509s); i++) {
                if (!OCSP_basic_add1_cert(bs, sk_X509_value(x509s, i))) {
                    sk_X509_pop_free(x509s, X509_free);
                    OCSP_BASICRESP_free(bs);
                    ossl_raise(eOCSPError, "OCSP_basic_add1_cert");
                }
            }
            result = OCSP_basic_verify(bs, x509s, x509st, flg);
            OCSP_BASICRESP_free(bs);
        }
        else {
            result = OCSP_basic_verify(bs, x509s, x509st, flg);
        }
    #else
        result = OCSP_basic_verify(bs, x509s, x509st, flg);
    #endif
        sk_X509_pop_free(x509s, X509_free);
        if (result <= 0)
            ossl_clear_error();

        return result > 0 ? Qtrue : Qfalse;
    }