Skip to content

Commit

Permalink
OpenSSL::ASN1.decode supports fractional times & time zones
Browse files Browse the repository at this point in the history
Using ASN1_TIME_to_tm added in openssl 1.1.1
  • Loading branch information
segiddins committed May 8, 2024
1 parent d2d6a99 commit 2451ed5
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 31 deletions.
2 changes: 2 additions & 0 deletions ext/openssl/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def find_openssl_library
x509_h = "openssl/x509.h".freeze
ts_h = "openssl/ts.h".freeze
ssl_h = "openssl/ssl.h".freeze
asn1_h = "openssl/asn1.h".freeze

# compile options
have_func("RAND_egd()", "openssl/rand.h")
Expand Down Expand Up @@ -192,6 +193,7 @@ def find_openssl_library
have_func("EVP_PKEY_check(NULL)", evp_h)
have_func("EVP_PKEY_new_raw_private_key(0, NULL, (unsigned char *)\"\", 0)", evp_h)
have_func("SSL_CTX_set_ciphersuites(NULL, \"\")", ssl_h)
have_func("ASN1_TIME_to_tm(NULL, NULL)", asn1_h)

# added in 3.0.0
have_func("SSL_set0_tmp_dh_pkey(NULL, NULL)", ssl_h)
Expand Down
47 changes: 45 additions & 2 deletions ext/openssl/ossl_asn1.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,51 @@ VALUE
asn1time_to_time(const ASN1_TIME *time)
{
struct tm tm;
VALUE argv[6];
VALUE argv[7];
int count;
int usec = 0;

memset(&tm, 0, sizeof(struct tm));

#ifdef HAVE_ASN1_TIME_TO_TM
(void)count;
if (!ASN1_TIME_to_tm(time, &tm)) {
ossl_raise(eASN1Error, "ASN1_TIME_to_tm");
}

tm.tm_year += 1900;
tm.tm_mon += 1;

if (time->length > 15 && time->data[14] == '.') {
const unsigned char *f = &time->data[14];
unsigned long f_len = 1;
while (14 + f_len < time->length && ISDIGIT(f[f_len]))
++f_len;
if (f_len > 1) {
char buf[10];
if (f_len > sizeof(buf) - 1)
f_len = sizeof(buf) - 1;
memcpy(buf, f + 1, f_len - 1);
buf[f_len - 1] = '\0';

usec = atoi(buf);
// adjust based on the number of digits
if (f_len < 2) {
usec *= 1000000;
} else if (f_len < 3) {
usec *= 100000;
} else if (f_len < 4) {
usec *= 10000;
} else if (f_len < 5) {
usec *= 1000;
} else if (f_len < 6) {
usec *= 100;
} else if (f_len < 7) {
usec *= 10;
}
}
}
#else
switch (time->type) {
case V_ASN1_UTCTIME:
count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ",
Expand Down Expand Up @@ -59,14 +99,17 @@ asn1time_to_time(const ASN1_TIME *time)
rb_warning("unknown time format");
return Qnil;
}
#endif

argv[0] = INT2NUM(tm.tm_year);
argv[1] = INT2NUM(tm.tm_mon);
argv[2] = INT2NUM(tm.tm_mday);
argv[3] = INT2NUM(tm.tm_hour);
argv[4] = INT2NUM(tm.tm_min);
argv[5] = INT2NUM(tm.tm_sec);
argv[6] = INT2NUM(usec);

return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv);
return rb_funcall2(rb_cTime, rb_intern("utc"), 7, argv);
}

static VALUE
Expand Down
63 changes: 34 additions & 29 deletions test/openssl/test_asn1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -399,49 +399,54 @@ def test_set
def test_utctime
encode_decode_test B(%w{ 17 0D }) + "160908234339Z".b,
OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 39))

if openssl?(1, 1, 1)
decode_test B(%w{ 17 11 }) + "500908234339+0930".b,
OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30"))
decode_test B(%w{ 17 0F }) + "5009082343-0930".b,
OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 0, "-09:30"))
assert_raise(OpenSSL::ASN1::ASN1Error) {
OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b)
}
assert_raise(OpenSSL::ASN1::ASN1Error) {
OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b)
}
end

begin
# possible range of UTCTime is 1969-2068 currently
encode_decode_test B(%w{ 17 0D }) + "690908234339Z".b,
OpenSSL::ASN1::UTCTime.new(Time.utc(1969, 9, 8, 23, 43, 39))
rescue OpenSSL::ASN1::ASN1Error
pend "No negative time_t support?"
end
# not implemented
# decode_test B(%w{ 17 11 }) + "500908234339+0930".b,
# OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30"))
# decode_test B(%w{ 17 0F }) + "5009082343-0930".b,
# OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 0, "-09:30"))
# assert_raise(OpenSSL::ASN1::ASN1Error) {
# OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b)
# }
# assert_raise(OpenSSL::ASN1::ASN1Error) {
# OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b)
# }
end

def test_generalizedtime
encode_decode_test B(%w{ 18 0F }) + "20161208193429Z".b,
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29))
encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b,
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39))
# not implemented
# decode_test B(%w{ 18 13 }) + "20161208193439+0930".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30"))
# decode_test B(%w{ 18 11 }) + "201612081934-0930".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:30"))
# decode_test B(%w{ 18 11 }) + "201612081934-09".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:00"))
# decode_test B(%w{ 18 0D }) + "2016120819.5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
# decode_test B(%w{ 18 0D }) + "2016120819,5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
# decode_test B(%w{ 18 0F }) + "201612081934.5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 30))
# decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5))
# assert_raise(OpenSSL::ASN1::ASN1Error) {
# OpenSSL::ASN1.decode(B(%w{ 18 0D }) + "201612081934Y".b)
# }

if openssl?(1, 1, 1)
decode_test B(%w{ 18 13 }) + "20161208193439+0930".b,
OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30"))
decode_test B(%w{ 18 11 }) + "201612081934-0930".b,
OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:30"))
# decode_test B(%w{ 18 0F }) + "201612081934-09".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:00"))
# decode_test B(%w{ 18 0D }) + "2016120819.5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
# decode_test B(%w{ 18 0D }) + "2016120819,5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
# decode_test B(%w{ 18 0F }) + "201612081934.5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 30))
decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b,
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5))
assert_raise(OpenSSL::ASN1::ASN1Error) {
OpenSSL::ASN1.decode(B(%w{ 18 0D }) + "201612081934Y".b)
}
end
end

def test_basic_asn1data
Expand Down

0 comments on commit 2451ed5

Please sign in to comment.