Skip to content

Commit 3e4c23e

Browse files
authored
Merge pull request #974 from rhenium/ky/asn1-time-decode-tm
asn1: use ASN1_TIME_to_tm() to decode UTCTime and GeneralizedTime
2 parents 1426c6d + 73484f6 commit 3e4c23e

File tree

2 files changed

+117
-106
lines changed

2 files changed

+117
-106
lines changed

ext/openssl/ossl_asn1.c

Lines changed: 71 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -9,64 +9,81 @@
99
*/
1010
#include "ossl.h"
1111

12-
static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset,
13-
int depth, int yield, long *num_read);
14-
static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self);
12+
/********/
13+
/*
14+
* ASN1 module
15+
*/
16+
#define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE)
17+
#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG)
18+
#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING)
19+
#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS)
20+
#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH)
21+
22+
#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v))
23+
#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v))
24+
#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v))
25+
#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v))
26+
#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v))
27+
28+
VALUE mASN1;
29+
static VALUE eASN1Error;
30+
31+
VALUE cASN1Data;
32+
static VALUE cASN1Primitive;
33+
static VALUE cASN1Constructive;
34+
35+
static VALUE cASN1EndOfContent;
36+
static VALUE cASN1Boolean; /* BOOLEAN */
37+
static VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */
38+
static VALUE cASN1BitString; /* BIT STRING */
39+
static VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */
40+
static VALUE cASN1NumericString, cASN1PrintableString;
41+
static VALUE cASN1T61String, cASN1VideotexString;
42+
static VALUE cASN1IA5String, cASN1GraphicString;
43+
static VALUE cASN1ISO64String, cASN1GeneralString;
44+
static VALUE cASN1UniversalString, cASN1BMPString;
45+
static VALUE cASN1Null; /* NULL */
46+
static VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */
47+
static VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */
48+
static VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */
49+
50+
static VALUE sym_IMPLICIT, sym_EXPLICIT;
51+
static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE;
52+
static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS;
53+
static ID id_each;
1554

1655
/*
1756
* DATE conversion
1857
*/
58+
static VALUE
59+
time_utc_new(VALUE args)
60+
{
61+
return rb_funcallv(rb_cTime, rb_intern("utc"), 6, (VALUE *)args);
62+
}
63+
64+
static VALUE
65+
time_utc_new_rescue(VALUE args, VALUE exc)
66+
{
67+
rb_raise(eASN1Error, "invalid time");
68+
}
69+
1970
VALUE
2071
asn1time_to_time(const ASN1_TIME *time)
2172
{
2273
struct tm tm;
23-
VALUE argv[6];
24-
int count;
25-
26-
memset(&tm, 0, sizeof(struct tm));
27-
28-
switch (time->type) {
29-
case V_ASN1_UTCTIME:
30-
count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ",
31-
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min,
32-
&tm.tm_sec);
33-
34-
if (count == 5) {
35-
tm.tm_sec = 0;
36-
} else if (count != 6) {
37-
ossl_raise(rb_eTypeError, "bad UTCTIME format: \"%s\"",
38-
time->data);
39-
}
40-
if (tm.tm_year < 50) {
41-
tm.tm_year += 2000;
42-
} else {
43-
tm.tm_year += 1900;
44-
}
45-
break;
46-
case V_ASN1_GENERALIZEDTIME:
47-
count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ",
48-
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min,
49-
&tm.tm_sec);
50-
if (count == 5) {
51-
tm.tm_sec = 0;
52-
}
53-
else if (count != 6) {
54-
ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"",
55-
time->data);
56-
}
57-
break;
58-
default:
59-
rb_warning("unknown time format");
60-
return Qnil;
61-
}
62-
argv[0] = INT2NUM(tm.tm_year);
63-
argv[1] = INT2NUM(tm.tm_mon);
64-
argv[2] = INT2NUM(tm.tm_mday);
65-
argv[3] = INT2NUM(tm.tm_hour);
66-
argv[4] = INT2NUM(tm.tm_min);
67-
argv[5] = INT2NUM(tm.tm_sec);
68-
69-
return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv);
74+
if (!ASN1_TIME_to_tm(time, &tm))
75+
ossl_raise(eASN1Error, "ASN1_TIME_to_tm");
76+
77+
VALUE args[] = {
78+
INT2NUM(tm.tm_year + 1900),
79+
INT2NUM(tm.tm_mon + 1),
80+
INT2NUM(tm.tm_mday),
81+
INT2NUM(tm.tm_hour),
82+
INT2NUM(tm.tm_min),
83+
INT2NUM(tm.tm_sec),
84+
};
85+
return rb_rescue2(time_utc_new, (VALUE)args, time_utc_new_rescue, Qnil,
86+
rb_eArgError, 0);
7087
}
7188

7289
static VALUE
@@ -190,49 +207,6 @@ ossl_asn1obj_to_string_long_name(const ASN1_OBJECT *obj)
190207
return ossl_asn1obj_to_string_oid(obj);
191208
}
192209

193-
/********/
194-
/*
195-
* ASN1 module
196-
*/
197-
#define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE)
198-
#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG)
199-
#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING)
200-
#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS)
201-
#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH)
202-
203-
#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v))
204-
#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v))
205-
#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v))
206-
#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v))
207-
#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v))
208-
209-
VALUE mASN1;
210-
static VALUE eASN1Error;
211-
212-
VALUE cASN1Data;
213-
static VALUE cASN1Primitive;
214-
static VALUE cASN1Constructive;
215-
216-
static VALUE cASN1EndOfContent;
217-
static VALUE cASN1Boolean; /* BOOLEAN */
218-
static VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */
219-
static VALUE cASN1BitString; /* BIT STRING */
220-
static VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */
221-
static VALUE cASN1NumericString, cASN1PrintableString;
222-
static VALUE cASN1T61String, cASN1VideotexString;
223-
static VALUE cASN1IA5String, cASN1GraphicString;
224-
static VALUE cASN1ISO64String, cASN1GeneralString;
225-
static VALUE cASN1UniversalString, cASN1BMPString;
226-
static VALUE cASN1Null; /* NULL */
227-
static VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */
228-
static VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */
229-
static VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */
230-
231-
static VALUE sym_IMPLICIT, sym_EXPLICIT;
232-
static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE;
233-
static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS;
234-
static ID id_each;
235-
236210
/*
237211
* Ruby to ASN1 converters
238212
*/
@@ -777,6 +751,10 @@ ossl_asn1data_to_der(VALUE self)
777751
}
778752
}
779753

754+
static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self);
755+
static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset,
756+
int depth, int yield, long *num_read);
757+
780758
static VALUE
781759
int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag,
782760
VALUE tc, long *num_read)

test/openssl/test_asn1.rb

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -426,42 +426,75 @@ def test_utctime
426426
OpenSSL::ASN1::UTCTime.new(Time.new(2049, 12, 31, 23, 0, 0, "-04:00")).to_der
427427
}
428428

429-
# not implemented
429+
# UTC offset (BER): ASN1_TIME_to_tm() may or may not support it
430430
# decode_test B(%w{ 17 11 }) + "500908234339+0930".b,
431431
# OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30"))
432432
# decode_test B(%w{ 17 0F }) + "5009082343-0930".b,
433433
# OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 0, "-09:30"))
434-
# assert_raise(OpenSSL::ASN1::ASN1Error) {
435-
# OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b)
436-
# }
437-
# assert_raise(OpenSSL::ASN1::ASN1Error) {
438-
# OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b)
439-
# }
434+
435+
# Seconds is omitted (BER)
436+
# decode_test B(%w{ 18 0D }) + "201612081934Z".b,
437+
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0))
438+
439+
# Fractional seconds is not allowed in UTCTime
440+
assert_raise(OpenSSL::ASN1::ASN1Error) {
441+
OpenSSL::ASN1.decode(B(%w{ 17 0F }) + "160908234339.5Z".b)
442+
}
443+
444+
# Missing "Z"
445+
assert_raise(OpenSSL::ASN1::ASN1Error) {
446+
OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b)
447+
}
448+
assert_raise(OpenSSL::ASN1::ASN1Error) {
449+
OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b)
450+
}
440451
end
441452

442453
def test_generalizedtime
443454
encode_decode_test B(%w{ 18 0F }) + "20161208193429Z".b,
444455
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29))
445456
encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b,
446457
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39))
447-
# not implemented
458+
459+
# Fractional seconds (DER). Not supported by ASN1_TIME_to_tm()
460+
# because struct tm cannot store it.
461+
# encode_decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b,
462+
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5))
463+
464+
# UTC offset (BER): ASN1_TIME_to_tm() may or may not support it
448465
# decode_test B(%w{ 18 13 }) + "20161208193439+0930".b,
449466
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30"))
450467
# decode_test B(%w{ 18 11 }) + "201612081934-0930".b,
451468
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:30"))
452469
# decode_test B(%w{ 18 11 }) + "201612081934-09".b,
453470
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:00"))
471+
472+
# Minutes and seconds are omitted (BER)
473+
# decode_test B(%w{ 18 0B }) + "2016120819Z".b,
474+
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 0, 0))
475+
# Fractional hours (BER)
454476
# decode_test B(%w{ 18 0D }) + "2016120819.5Z".b,
455477
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
478+
# Fractional hours with "," as the decimal separator (BER)
456479
# decode_test B(%w{ 18 0D }) + "2016120819,5Z".b,
457480
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
481+
482+
# Seconds is omitted (BER)
483+
# decode_test B(%w{ 18 0D }) + "201612081934Z".b,
484+
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0))
485+
# Fractional minutes (BER)
458486
# decode_test B(%w{ 18 0F }) + "201612081934.5Z".b,
459487
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 30))
460-
# decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b,
461-
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5))
462-
# assert_raise(OpenSSL::ASN1::ASN1Error) {
463-
# OpenSSL::ASN1.decode(B(%w{ 18 0D }) + "201612081934Y".b)
464-
# }
488+
489+
# Missing "Z"
490+
assert_raise(OpenSSL::ASN1::ASN1Error) {
491+
OpenSSL::ASN1.decode(B(%w{ 18 0F }) + "20161208193429Y".b)
492+
}
493+
494+
# Encoding year out of range
495+
assert_raise(OpenSSL::ASN1::ASN1Error) {
496+
OpenSSL::ASN1::GeneralizedTime.new(Time.utc(10000, 9, 8, 23, 43, 39)).to_der
497+
}
465498
end
466499

467500
def test_basic_asn1data

0 commit comments

Comments
 (0)