aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Toews <mastahyeti@gmail.com>2018-08-20 18:25:47 -0600
committerBen Toews <mastahyeti@gmail.com>2018-08-20 18:25:47 -0600
commit5e45f1306e99c498541af11f2b5747ed7dc9a561 (patch)
tree5f9f340497614332a601e298408c5b366b7fff13
parent86d7bbaf8347434038a76b309d57c078b21bdd0c (diff)
downloadruby-openssl-pr/204.tar.gz
ts: require list of allowed digest algos for Factorypr/204
-rw-r--r--ext/openssl/ossl_ts.c72
-rw-r--r--test/test_ts.rb62
2 files changed, 105 insertions, 29 deletions
diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c
index f4f945fb..b646089f 100644
--- a/ext/openssl/ossl_ts.c
+++ b/ext/openssl/ossl_ts.c
@@ -60,6 +60,7 @@
#define ossl_tsfac_get_serial_number(o) rb_attr_get((o),rb_intern("@serial_number"))
#define ossl_tsfac_get_gen_time(o) rb_attr_get((o),rb_intern("@gen_time"))
#define ossl_tsfac_get_additional_certs(o) rb_attr_get((o),rb_intern("@additional_certs"))
+#define ossl_tsfac_get_allowed_digests(o) rb_attr_get((o),rb_intern("@allowed_digests"))
static VALUE mTimestamp;
static VALUE eTimestampError;
@@ -1071,7 +1072,10 @@ ossl_ts_token_info_to_der(VALUE self)
static ASN1_INTEGER *
ossl_tsfac_serial_cb(struct TS_resp_ctx *ctx, void *data)
{
- return (ASN1_INTEGER *)data;
+ ASN1_INTEGER **snptr = (ASN1_INTEGER **)data;
+ ASN1_INTEGER *sn = *snptr;
+ *snptr = NULL;
+ return sn;
}
static int
@@ -1086,17 +1090,6 @@ ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, long *sec, long *usec)
* Creates a Response with the help of an OpenSSL::PKey, an
* OpenSSL::X509::Certificate and a Request.
*
- * The Request message imprint may have only been created using one of the
- * following algorithms:
- * * MD5
- * * SHA1
- * * SHA224
- * * SHA256
- * * SHA384
- * * SHA512
- * Otherwise creation of the timestamp token will fail and a TimestampError
- * will be raised.
- *
* Mandatory parameters for timestamp creation that need to be set in the
* Request:
*
@@ -1106,11 +1099,13 @@ ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, long *sec, long *usec)
* Mandatory parameters that need to be set in the Factory:
* * Factory#serial_number
* * Factory#gen_time
+ * * Factory#allowed_digests
*
* In addition one of either Request#policy_id or Factory#default_policy_id
* must be set.
*
- * Raises a TimestampError if creation fails.
+ * Raises a TimestampError if creation fails, though successfully created error
+ * responses may be returned.
*
* call-seq:
* factory.create_timestamp(key, certificate, request) -> Response
@@ -1118,7 +1113,7 @@ ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, long *sec, long *usec)
static VALUE
ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request)
{
- VALUE serial_number, def_policy_id, gen_time, additional_certs;
+ VALUE serial_number, def_policy_id, gen_time, additional_certs, allowed_digests;
VALUE str;
STACK_OF(X509) *inter_certs;
VALUE tsresp, ret = Qnil;
@@ -1169,7 +1164,7 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request)
goto end;
}
- TS_RESP_CTX_set_serial_cb(ctx, ossl_tsfac_serial_cb, asn1_serial);
+ TS_RESP_CTX_set_serial_cb(ctx, ossl_tsfac_serial_cb, &asn1_serial);
if (!TS_RESP_CTX_set_signer_cert(ctx, tsa_cert)) {
err_msg = "Certificate does not contain the timestamping extension";
goto end;
@@ -1193,12 +1188,20 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request)
TS_RESP_CTX_set_def_policy(ctx, TS_REQ_get_policy_id(req));
TS_RESP_CTX_set_time_cb(ctx, ossl_tsfac_time_cb, &lgen_time);
- TS_RESP_CTX_add_md(ctx, EVP_md5());
- TS_RESP_CTX_add_md(ctx, EVP_sha1());
- TS_RESP_CTX_add_md(ctx, EVP_sha224());
- TS_RESP_CTX_add_md(ctx, EVP_sha256());
- TS_RESP_CTX_add_md(ctx, EVP_sha384());
- TS_RESP_CTX_add_md(ctx, EVP_sha512());
+ allowed_digests = ossl_tsfac_get_allowed_digests(self);
+ if (rb_obj_is_kind_of(allowed_digests, rb_cArray)) {
+ int i;
+ VALUE rbmd;
+ const EVP_MD *md;
+
+ for (i = 0; i < RARRAY_LEN(allowed_digests); i++) {
+ rbmd = rb_ary_entry(allowed_digests, i);
+ md = (const EVP_MD *)rb_protect((VALUE (*)(VALUE))ossl_evp_get_digestbyname, rbmd, &status);
+ if (status)
+ goto end;
+ TS_RESP_CTX_add_md(ctx, md);
+ }
+ }
str = rb_protect(ossl_to_der, request, &status);
if (status)
@@ -1210,12 +1213,16 @@ ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request)
response = TS_RESP_create_response(ctx, req_bio);
BIO_free(req_bio);
- asn1_serial = NULL;
+
if (!response) {
err_msg = "Error during response generation";
goto end;
}
+ /* bad responses aren't exceptional, but openssl still sets error
+ * information. */
+ ossl_clear_error();
+
SetTSResponse(tsresp, response);
ret = tsresp;
@@ -1223,10 +1230,8 @@ end:
ASN1_INTEGER_free(asn1_serial);
ASN1_OBJECT_free(def_policy_id_obj);
TS_RESP_CTX_free(ctx);
- if (err_msg) {
- if (response) TS_RESP_free(response);
+ if (err_msg)
ossl_raise(eTimestampError, err_msg);
- }
if (status)
rb_jump_tag(status);
return ret;
@@ -1443,6 +1448,7 @@ Init_ossl_ts(void)
* fac = OpenSSL::Timestamp::Factory.new
* fac.gen_time = Time.now
* fac.serial_number = 1
+ * fac.allowed_digests = ["sha256", "sha384", "sha512"]
* #needed because the Request contained no policy identifier
* fac.default_policy_id = '1.2.3.4.5'
* fac.additional_certificates = [ inter1, inter2 ]
@@ -1486,11 +1492,23 @@ Init_ossl_ts(void)
* Must be an Array of OpenSSL::X509::Certificate.
*
* call-seq:
- * factory.additional_certs = [ cert1, cert2] -> [ cert1, cert2 ]
- * factory.additional_certs -> array or nil
+ * factory.additional_certs = [cert1, cert2] -> [ cert1, cert2 ]
+ * factory.additional_certs -> array or nil
+ *
+ * ===allowed_digests
+ *
+ * Sets or retrieves the digest algorithms that the factory is allowed
+ * create timestamps for. Known vulnerable or weak algorithms should not be
+ * allowed where possible.
+ * Must be an Array of String or OpenSSL::Digest subclass instances.
+ *
+ * call-seq:
+ * factory.allowed_digests = ["sha1", OpenSSL::Digest::SHA256.new] -> [ "sha1", OpenSSL::Digest::SHA256.new ]
+ * factory.allowed_digests -> array or nil
*
*/
cTimestampFactory = rb_define_class_under(mTimestamp, "Factory", rb_cObject);
+ rb_attr(cTimestampFactory, rb_intern("allowed_digests"), 1, 1, 0);
rb_attr(cTimestampFactory, rb_intern("default_policy_id"), 1, 1, 0);
rb_attr(cTimestampFactory, rb_intern("serial_number"), 1, 1, 0);
rb_attr(cTimestampFactory, rb_intern("gen_time"), 1, 1, 0);
diff --git a/test/test_ts.rb b/test/test_ts.rb
index 337fc4a0..734159c5 100644
--- a/test/test_ts.rb
+++ b/test/test_ts.rb
@@ -211,6 +211,7 @@ _end_of_pem_
time = Time.now
fac.gen_time = time
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
resp = OpenSSL::Timestamp::Response.new(resp)
@@ -250,17 +251,60 @@ _end_of_pem_
fac.create_timestamp(ee_key, ts_cert_ee, req)
end
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
assert_raises(OpenSSL::Timestamp::TimestampError) do
fac.create_timestamp(ee_key, ts_cert_ee, req)
end
fac.default_policy_id = "1.2.3.4.5"
- fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::GRANTED, fac.create_timestamp(ee_key, ts_cert_ee, req).status
fac.default_policy_id = nil
assert_raises(OpenSSL::Timestamp::TimestampError) do
fac.create_timestamp(ee_key, ts_cert_ee, req)
end
req.policy_id = "1.2.3.4.5"
- fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::GRANTED, fac.create_timestamp(ee_key, ts_cert_ee, req).status
+ end
+
+ def test_response_allowed_digests
+ req = OpenSSL::Timestamp::Request.new
+ req.algorithm = "SHA1"
+ req.message_imprint = OpenSSL::Digest::SHA1.digest("test")
+
+ fac = OpenSSL::Timestamp::Factory.new
+ fac.gen_time = Time.now
+ fac.serial_number = 1
+ fac.default_policy_id = "1.2.3.4.6"
+
+ # None allowed by default
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+ # Explicitly allow SHA1 (string)
+ fac.allowed_digests = ["sha1"]
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::GRANTED, resp.status
+
+ # Explicitly allow SHA1 (object)
+ fac.allowed_digests = [OpenSSL::Digest::SHA1.new]
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::GRANTED, resp.status
+
+ # Others not allowed
+ req.algorithm = "SHA256"
+ req.message_imprint = OpenSSL::Digest::SHA256.digest("test")
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+ # Non-Array
+ fac.allowed_digests = 123
+ resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+ assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+ # Non-String, non-Digest Array element
+ fac.allowed_digests = ["sha1", OpenSSL::Digest::SHA1.new, 123]
+ assert_raises(TypeError) do
+ fac.create_timestamp(ee_key, ts_cert_ee, req)
+ end
end
def test_response_default_policy
@@ -272,6 +316,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
fac.default_policy_id = "1.2.3.4.6"
resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
@@ -290,6 +335,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
assert_raises(OpenSSL::Timestamp::TimestampError) do
@@ -307,6 +353,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
fac.default_policy_id = "1.2.3.4.5"
resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
@@ -324,6 +371,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
fac.create_timestamp(ee_key, ts_cert_ee, req)
end
@@ -392,6 +440,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
fac.default_policy_id = "1.2.3.4.5"
ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
@@ -449,6 +498,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
fac.additional_certs = [intermediate_cert]
ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
assert_equal(2, ts.token.certificates.size)
@@ -468,6 +518,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
fac.additional_certs = [intermediate_cert, ca_cert]
ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
assert_equal(3, ts.token.certificates.size)
@@ -484,6 +535,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
#needed because the Request contained no policy identifier
fac.default_policy_id = '1.2.3.4.5'
fac.additional_certs = [ ts_cert_ee, intermediate_cert ]
@@ -505,6 +557,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
fac.additional_certs = [ intermediate_cert ]
ts1 = fac.create_timestamp(ee_key, ts_cert_ee, req)
ts1.verify(req, ca_store)
@@ -526,6 +579,7 @@ _end_of_pem_
time = Time.now
fac.gen_time = time
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
info = resp.token_info
@@ -558,6 +612,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
return fac.create_timestamp(ee_key, ts_cert_ee, req), req
end
@@ -573,6 +628,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
return fac.create_timestamp(ee_key, ts_cert_ee, req), req
end
@@ -587,6 +643,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
return fac.create_timestamp(ee_key, ts_cert_direct, req), req
end
@@ -602,6 +659,7 @@ _end_of_pem_
fac = OpenSSL::Timestamp::Factory.new
fac.gen_time = Time.now
fac.serial_number = 1
+ fac.allowed_digests = ["sha1"]
return fac.create_timestamp(ee_key, ts_cert_direct, req), req
end
end