Skip to content

Commit 27e4bad

Browse files
committed
asn1: harmonize OpenSSL::ASN1::*#to_der
Extract the common paths for code reduction.
1 parent 3fde14b commit 27e4bad

File tree

2 files changed

+108
-128
lines changed

2 files changed

+108
-128
lines changed

ext/openssl/ossl_asn1.c

Lines changed: 92 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
*/
1010
#include "ossl.h"
1111

12-
static VALUE join_der(VALUE enumerable);
1312
static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset,
1413
int depth, int yield, long *num_read);
1514
static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self);
@@ -595,20 +594,6 @@ ossl_asn1_tag(VALUE obj)
595594
return NUM2INT(tag);
596595
}
597596

598-
static int
599-
ossl_asn1_is_explicit(VALUE obj)
600-
{
601-
VALUE s;
602-
603-
s = ossl_asn1_get_tagging(obj);
604-
if (NIL_P(s) || s == sym_IMPLICIT)
605-
return 0;
606-
else if (s == sym_EXPLICIT)
607-
return 1;
608-
else
609-
ossl_raise(eASN1Error, "invalid tag default");
610-
}
611-
612597
static int
613598
ossl_asn1_tag_class(VALUE obj)
614599
{
@@ -670,22 +655,50 @@ ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class)
670655
}
671656

672657
static VALUE
673-
join_der_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, str))
658+
to_der_internal(VALUE self, int constructed, int indef_len, VALUE body)
674659
{
675-
i = ossl_to_der_if_possible(i);
676-
StringValue(i);
677-
rb_str_append(str, i);
678-
return Qnil;
679-
}
660+
int encoding = constructed ? indef_len ? 2 : 1 : 0;
661+
int tag_class = ossl_asn1_tag_class(self);
662+
int tag_number = ossl_asn1_tag(self);
663+
int default_tag_number = ossl_asn1_default_tag(self);
664+
int body_length, total_length;
665+
VALUE str;
666+
unsigned char *p;
680667

681-
static VALUE
682-
join_der(VALUE enumerable)
683-
{
684-
VALUE str = rb_str_new(0, 0);
685-
rb_block_call(enumerable, id_each, 0, 0, join_der_i, str);
668+
body_length = RSTRING_LENINT(body);
669+
if (ossl_asn1_get_tagging(self) == sym_EXPLICIT) {
670+
int inner_length, e_encoding = indef_len ? 2 : 1;
671+
672+
if (default_tag_number == -1)
673+
ossl_raise(eASN1Error, "explicit tagging of unknown tag");
674+
675+
inner_length = ASN1_object_size(encoding, body_length, default_tag_number);
676+
total_length = ASN1_object_size(e_encoding, inner_length, tag_number);
677+
str = rb_str_new(NULL, total_length);
678+
p = (unsigned char *)RSTRING_PTR(str);
679+
/* Put explicit tag */
680+
ASN1_put_object(&p, e_encoding, inner_length, tag_number, tag_class);
681+
/* Append inner object */
682+
ASN1_put_object(&p, encoding, body_length, default_tag_number, V_ASN1_UNIVERSAL);
683+
memcpy(p, RSTRING_PTR(body), body_length);
684+
p += body_length;
685+
if (indef_len)
686+
ASN1_put_eoc(&p); /* For wrapper object */
687+
}
688+
else {
689+
total_length = ASN1_object_size(encoding, body_length, tag_number);
690+
str = rb_str_new(NULL, total_length);
691+
p = (unsigned char *)RSTRING_PTR(str);
692+
ASN1_put_object(&p, encoding, body_length, tag_number, tag_class);
693+
memcpy(p, RSTRING_PTR(body), body_length);
694+
p += body_length;
695+
}
696+
ossl_str_adjust(str, p);
686697
return str;
687698
}
688699

700+
static VALUE ossl_asn1prim_to_der(VALUE);
701+
static VALUE ossl_asn1cons_to_der(VALUE);
689702
/*
690703
* call-seq:
691704
* asn1.to_der => DER-encoded String
@@ -698,36 +711,16 @@ join_der(VALUE enumerable)
698711
static VALUE
699712
ossl_asn1data_to_der(VALUE self)
700713
{
701-
VALUE value, der, inf_length;
702-
int tag, tag_class, is_cons = 0;
703-
long length;
704-
unsigned char *p;
714+
VALUE value = ossl_asn1_get_value(self);
705715

706-
value = ossl_asn1_get_value(self);
707-
if(rb_obj_is_kind_of(value, rb_cArray)){
708-
is_cons = 1;
709-
value = join_der(value);
710-
}
711-
StringValue(value);
712-
713-
tag = ossl_asn1_tag(self);
714-
tag_class = ossl_asn1_tag_class(self);
715-
inf_length = ossl_asn1_get_indefinite_length(self);
716-
if (inf_length == Qtrue) {
717-
if (is_cons == 0)
718-
ossl_raise(eASN1Error, "indefinite form used for primitive encoding");
719-
is_cons = 2;
716+
if (rb_obj_is_kind_of(value, rb_cArray))
717+
return ossl_asn1cons_to_der(self);
718+
else {
719+
if (RTEST(ossl_asn1_get_indefinite_length(self)))
720+
ossl_raise(eASN1Error, "indefinite length form cannot be used " \
721+
"with primitive encoding");
722+
return ossl_asn1prim_to_der(self);
720723
}
721-
if((length = ASN1_object_size(is_cons, RSTRING_LENINT(value), tag)) <= 0)
722-
ossl_raise(eASN1Error, NULL);
723-
der = rb_str_new(0, length);
724-
p = (unsigned char *)RSTRING_PTR(der);
725-
ASN1_put_object(&p, is_cons, RSTRING_LENINT(value), tag, tag_class);
726-
memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value));
727-
p += RSTRING_LEN(value);
728-
ossl_str_adjust(der, p);
729-
730-
return der;
731724
}
732725

733726
static VALUE
@@ -1115,6 +1108,12 @@ ossl_asn1eoc_initialize(VALUE self) {
11151108
return self;
11161109
}
11171110

1111+
static VALUE
1112+
ossl_asn1eoc_to_der(VALUE self)
1113+
{
1114+
return rb_str_new("\0\0", 2);
1115+
}
1116+
11181117
/*
11191118
* call-seq:
11201119
* asn1.to_der => DER-encoded String
@@ -1125,37 +1124,38 @@ static VALUE
11251124
ossl_asn1prim_to_der(VALUE self)
11261125
{
11271126
ASN1_TYPE *asn1;
1128-
int tn, tc, explicit;
1129-
long len, reallen;
1130-
unsigned char *buf, *p;
1127+
long alllen, bodylen;
1128+
unsigned char *p0, *p1;
1129+
int j, tag, tc, state;
11311130
VALUE str;
11321131

1133-
tn = ossl_asn1_tag(self);
1134-
tc = ossl_asn1_tag_class(self);
1135-
explicit = ossl_asn1_is_explicit(self);
1136-
asn1 = ossl_asn1_get_asn1type(self);
1132+
if (ossl_asn1_default_tag(self) == -1) {
1133+
str = ossl_asn1_get_value(self);
1134+
return to_der_internal(self, 0, 0, StringValue(str));
1135+
}
11371136

1138-
len = ASN1_object_size(1, i2d_ASN1_TYPE(asn1, NULL), tn);
1139-
if(!(buf = OPENSSL_malloc(len))){
1137+
asn1 = ossl_asn1_get_asn1type(self);
1138+
alllen = i2d_ASN1_TYPE(asn1, NULL);
1139+
if (alllen < 0) {
11401140
ASN1_TYPE_free(asn1);
1141-
ossl_raise(eASN1Error, "cannot alloc buffer");
1141+
ossl_raise(eASN1Error, "i2d_ASN1_TYPE");
11421142
}
1143-
p = buf;
1144-
if (tc == V_ASN1_UNIVERSAL) {
1145-
i2d_ASN1_TYPE(asn1, &p);
1146-
} else if (explicit) {
1147-
ASN1_put_object(&p, 1, i2d_ASN1_TYPE(asn1, NULL), tn, tc);
1148-
i2d_ASN1_TYPE(asn1, &p);
1149-
} else {
1150-
i2d_ASN1_TYPE(asn1, &p);
1151-
*buf = tc | tn | (*buf & V_ASN1_CONSTRUCTED);
1143+
str = ossl_str_new(NULL, alllen, &state);
1144+
if (state) {
1145+
ASN1_TYPE_free(asn1);
1146+
rb_jump_tag(state);
11521147
}
1148+
p0 = p1 = (unsigned char *)RSTRING_PTR(str);
1149+
i2d_ASN1_TYPE(asn1, &p0);
11531150
ASN1_TYPE_free(asn1);
1154-
reallen = p - buf;
1155-
assert(reallen <= len);
1156-
str = ossl_buf2str((char *)buf, rb_long2int(reallen)); /* buf will be free in ossl_buf2str */
1151+
assert(p0 - p1 == alllen);
11571152

1158-
return str;
1153+
/* Strip header since to_der_internal() wants only the payload */
1154+
j = ASN1_get_object((const unsigned char **)&p1, &bodylen, &tag, &tc, alllen);
1155+
if (j & 0x80)
1156+
ossl_raise(eASN1Error, "ASN1_get_object"); /* should not happen */
1157+
1158+
return to_der_internal(self, 0, 0, rb_str_drop_bytes(str, alllen - bodylen));
11591159
}
11601160

11611161
/*
@@ -1167,59 +1167,22 @@ ossl_asn1prim_to_der(VALUE self)
11671167
static VALUE
11681168
ossl_asn1cons_to_der(VALUE self)
11691169
{
1170-
int tn, tc, explicit, constructed, value_length;
1171-
unsigned char *p;
1172-
VALUE value, str, inf_length;
1173-
1174-
tn = ossl_asn1_tag(self);
1175-
tc = ossl_asn1_tag_class(self);
1176-
inf_length = ossl_asn1_get_indefinite_length(self);
1177-
explicit = ossl_asn1_is_explicit(self);
1178-
value = join_der(ossl_asn1_get_value(self));
1179-
value_length = RSTRING_LENINT(value);
1180-
constructed = RTEST(inf_length) ? 2 : 1;
1181-
1182-
if (explicit) {
1183-
int length, inner_length, default_tag;
1184-
1185-
default_tag = ossl_asn1_default_tag(self);
1186-
/*
1187-
* non-universal tag class class && explicit tagging; this is not
1188-
* possible because the inner tag number is unknown.
1189-
*/
1190-
if (default_tag == -1)
1191-
ossl_raise(eASN1Error, "explicit tagging of unknown tag");
1192-
1193-
inner_length = ASN1_object_size(constructed, value_length, default_tag);
1194-
length = ASN1_object_size(constructed, inner_length, tn);
1195-
str = rb_str_new(0, length);
1196-
p = (unsigned char *)RSTRING_PTR(str);
1197-
ASN1_put_object(&p, constructed, inner_length, tn, tc);
1198-
ASN1_put_object(&p, constructed, value_length, default_tag, V_ASN1_UNIVERSAL);
1199-
}
1200-
else {
1201-
int length;
1202-
1203-
length = ASN1_object_size(constructed, value_length, tn);
1204-
str = rb_str_new(0, length);
1205-
p = (unsigned char *)RSTRING_PTR(str);
1206-
ASN1_put_object(&p, constructed, value_length, tn, tc);
1170+
VALUE ary, str;
1171+
long i;
1172+
int indef_len;
1173+
1174+
indef_len = RTEST(ossl_asn1_get_indefinite_length(self));
1175+
ary = rb_convert_type(ossl_asn1_get_value(self), T_ARRAY, "Array", "to_a");
1176+
str = rb_str_new(NULL, 0);
1177+
for (i = 0; i < RARRAY_LEN(ary); i++) {
1178+
VALUE item = RARRAY_AREF(ary, i);
1179+
1180+
item = ossl_to_der_if_possible(item);
1181+
StringValue(item);
1182+
rb_str_append(str, item);
12071183
}
12081184

1209-
memcpy(p, RSTRING_PTR(value), value_length);
1210-
p += value_length;
1211-
1212-
/* In this case we need an additional EOC (one for the explicit part and
1213-
* one for the Constructive itself. The EOC for the Constructive is
1214-
* supplied by the user, but that for the "explicit wrapper" must be
1215-
* added here.
1216-
*/
1217-
if (explicit && RTEST(inf_length)) {
1218-
ASN1_put_eoc(&p);
1219-
}
1220-
ossl_str_adjust(str, p);
1221-
1222-
return str;
1185+
return to_der_internal(self, 1, indef_len, str);
12231186
}
12241187

12251188
/*
@@ -1842,6 +1805,7 @@ do{\
18421805
rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, 0);
18431806

18441807
rb_define_method(cASN1EndOfContent, "initialize", ossl_asn1eoc_initialize, 0);
1808+
rb_define_method(cASN1EndOfContent, "to_der", ossl_asn1eoc_to_der, 0);
18451809

18461810
class_tag_map = rb_hash_new();
18471811
rb_hash_aset(class_tag_map, cASN1EndOfContent, INT2NUM(V_ASN1_EOC));

test/test_asn1.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,12 @@ def test_basic_asn1data
434434
end
435435

436436
def test_basic_primitive
437+
encode_test B(%w{ 00 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 0)
438+
encode_test B(%w{ 01 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :UNIVERSAL)
439+
encode_test B(%w{ 81 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :CONTEXT_SPECIFIC)
440+
encode_test B(%w{ 01 02 AB CD }), OpenSSL::ASN1::Primitive.new(B(%w{ AB CD }), 1)
441+
assert_raise(TypeError) { OpenSSL::ASN1::Primitive.new([], 1).to_der }
442+
437443
prim = OpenSSL::ASN1::Integer.new(50)
438444
assert_equal false, prim.indefinite_length
439445
assert_not_respond_to prim, :indefinite_length=
@@ -477,6 +483,11 @@ def test_prim_implicit_tagging
477483
assert_equal :APPLICATION, decoded.tag_class
478484
assert_equal 1, decoded.tag
479485
assert_equal B(%w{ 01 }), decoded.value
486+
487+
# Special behavior: Encoding universal types with non-default 'tag'
488+
# attribute and nil tagging method.
489+
int3 = OpenSSL::ASN1::Integer.new(1, 1)
490+
encode_test B(%w{ 01 01 01 }), int3
480491
end
481492

482493
def test_cons_explicit_tagging
@@ -505,6 +516,11 @@ def test_cons_implicit_tagging
505516
seq3 = OpenSSL::ASN1::Sequence.new(content3, 1, :IMPLICIT)
506517
seq3.indefinite_length = true
507518
encode_test B(%w{ A1 80 05 00 00 00 }), seq3
519+
520+
# Special behavior: Encoding universal types with non-default 'tag'
521+
# attribute and nil tagging method.
522+
seq4 = OpenSSL::ASN1::Sequence.new([], 1)
523+
encode_test B(%w{ 21 00 }), seq4
508524
end
509525

510526
def test_octet_string_constructed_tagging

0 commit comments

Comments
 (0)