From 5af0a758d4454587f7d5ed4ff4cf610968642837 Mon Sep 17 00:00:00 2001 From: gotoyuzo Date: Fri, 3 Oct 2003 08:58:01 +0000 Subject: * ext/openssl/ossl_asn1.c: add DER encoder and decoder. * ext/openssl/ossl_asn1.h: add OpenSSL::ASN1 module. * ext/openssl/ossl.c (Init_openssl): call Init_ossl_asn1. * ext/openssl/ossl.h: include ossl_asn1.h. * ext/openssl/extconf.rb: check if X509_ATTRIBUTE has field "single". * ext/openssl/ossl_x509attr.c (ossl_x509attr_set_value): accept DER encoded data argument. * ext/openssl/ossl_x509attr.c (ossl_x509attr_get_value): return DER encoded data in OpenSSL::ASN1 types. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4660 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/openssl/ossl_asn1.c | 947 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 943 insertions(+), 4 deletions(-) (limited to 'ext/openssl/ossl_asn1.c') diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index f0a6379e42..29859367dc 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -75,8 +75,7 @@ extern struct timeval rb_time_timeval(VALUE); time_t time_to_time_t(VALUE time) { - struct timeval t = rb_time_timeval(time); - return t.tv_sec; + return (time_t)NUM2LONG(rb_Integer(time)); } /* @@ -115,7 +114,8 @@ asn1integer_to_num(ASN1_INTEGER *ai) } #if DO_IT_VIA_RUBY -ASN1_INTEGER *num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) +ASN1_INTEGER * +num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) { BIGNUM *bn = NULL; @@ -135,7 +135,8 @@ ASN1_INTEGER *num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) return ai; } #else -ASN1_INTEGER *num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) +ASN1_INTEGER * +num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) { BIGNUM *bn = GetBNPtr(obj); @@ -146,3 +147,941 @@ ASN1_INTEGER *num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) } #endif +/********/ +/* + * ASN1 module + */ +#define ossl_asn1_get_value(o) rb_attr_get((o),rb_intern("@value")) +#define ossl_asn1_get_tag(o) rb_attr_get((o),rb_intern("@tag")) +#define ossl_asn1_get_tagging(o) rb_attr_get((o),rb_intern("@tagging")) +#define ossl_asn1_get_tag_class(o) rb_attr_get((o),rb_intern("@tag_class")) + +#define ossl_asn1_set_value(o,v) rb_iv_set((o),"@value",(v)) +#define ossl_asn1_set_tag(o,v) rb_iv_set((o),"@tag",(v)) +#define ossl_asn1_set_tagging(o,v) rb_iv_set((o),"@tagging",(v)) +#define ossl_asn1_set_tag_class(o,v) rb_iv_set((o),"@tag_class",(v)) + +VALUE mASN1; +VALUE eASN1Error; + +VALUE cASN1Data; +VALUE cASN1Primitive; +VALUE cASN1Constructive; + +VALUE cASN1Boolean; /* BOOLEAN */ +VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ +VALUE cASN1BitString; /* BIT STRING */ +VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ +VALUE cASN1NumericString, cASN1PrintableString; +VALUE cASN1T61String, cASN1VideotexString; +VALUE cASN1IA5String, cASN1GraphicString; +VALUE cASN1ISO64String, cASN1GeneralString; +VALUE cASN1UniversalString, cASN1BMPString; +VALUE cASN1Null; /* NULL */ +VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ +VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ +VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ + +static ID sIMPLICIT, sEXPLICIT; +static ID sUNIVERSAL, sAPPLICATION, sCONTEXT_SPECIFIC, sPRIVATE; + +/* + * Ruby to ASN1 converters + */ +static ASN1_BOOLEAN +obj_to_asn1bool(VALUE obj) +{ + return RTEST(obj) ? 255 : 0; +} + +static ASN1_INTEGER* +obj_to_asn1int(VALUE obj) +{ + return num_to_asn1integer(obj, NULL); +} + +static ASN1_BIT_STRING* +obj_to_asn1bstr(VALUE obj, long unused_bits) +{ + ASN1_BIT_STRING *bstr; + + if(unused_bits < 0) unused_bits = 0; + StringValue(obj); + if(!(bstr = ASN1_BIT_STRING_new())) + ossl_raise(eASN1Error, NULL); + ASN1_BIT_STRING_set(bstr, RSTRING(obj)->ptr, RSTRING(obj)->len); + bstr->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear */ + bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT|(unused_bits&0x07); + + return bstr; +} + +static ASN1_STRING* +obj_to_asn1str(VALUE obj) +{ + ASN1_STRING *str; + + StringValue(obj); + if(!(str = ASN1_STRING_new())) + ossl_raise(eASN1Error, NULL); + ASN1_STRING_set(str, RSTRING(obj)->ptr, RSTRING(obj)->len); + + return str; +} + +static ASN1_NULL* +obj_to_asn1null(VALUE obj) +{ + ASN1_NULL *null; + + if(!NIL_P(obj)) + ossl_raise(eASN1Error, "nil expected"); + if(!(null = ASN1_NULL_new())) + ossl_raise(eASN1Error, NULL); + + return null; +} + +static ASN1_OBJECT* +obj_to_asn1obj(VALUE obj) +{ + ASN1_OBJECT *a1obj; + + StringValue(obj); + a1obj = OBJ_txt2obj(RSTRING(obj)->ptr, 0); + if(!a1obj) a1obj = OBJ_txt2obj(RSTRING(obj)->ptr, 1); + if(!a1obj) ossl_raise(eASN1Error, "invalid OBJECT ID"); + + return a1obj; +} + +static ASN1_UTCTIME* +obj_to_asn1utime(VALUE time) +{ + time_t sec; + ASN1_UTCTIME *t; + + sec = time_to_time_t(time); + if(!(t = ASN1_UTCTIME_set(NULL, sec))) + ossl_raise(eASN1Error, NULL); + + return t; +} + +static ASN1_GENERALIZEDTIME* +obj_to_asn1gtime(VALUE time) +{ + time_t sec; + ASN1_GENERALIZEDTIME *t; + + sec = time_to_time_t(time); + if(!(t =ASN1_GENERALIZEDTIME_set(NULL, sec))) + ossl_raise(eASN1Error, NULL); + + return t; +} + +static ASN1_STRING* +obj_to_asn1derstr(VALUE obj) +{ + ASN1_STRING *a1str; + VALUE str; + + str = ossl_to_der(obj); + if(!(a1str = ASN1_STRING_new())) + ossl_raise(eASN1Error, NULL); + ASN1_STRING_set(a1str, RSTRING(str)->ptr, RSTRING(str)->len); + + return a1str; +} + +/* + * DER to Ruby converters + */ +static VALUE +decode_bool(unsigned char* der, int length) +{ + int bool; + unsigned char *p; + + p = der; + if((bool = d2i_ASN1_BOOLEAN(NULL, &p, length)) < 0) + ossl_raise(eASN1Error, NULL); + + return bool ? Qtrue : Qfalse; +} + +static VALUE +decode_int(unsigned char* der, int length) +{ + ASN1_INTEGER *ai; + unsigned char *p; + VALUE ret; + int status = 0; + + p = der; + if(!(ai = d2i_ASN1_INTEGER(NULL, &p, length))) + ossl_raise(eASN1Error, NULL); + ret = rb_protect((VALUE(*)_((VALUE)))asn1integer_to_num, + (VALUE)ai, &status); + ASN1_INTEGER_free(ai); + if(status) rb_jump_tag(status); + + return ret; +} + +static VALUE +decode_bstr(unsigned char* der, int length, long *unused_bits) +{ + ASN1_BIT_STRING *bstr; + unsigned char *p, *buf; + long len; + VALUE ret; + + p = der; + if(!(bstr = d2i_ASN1_BIT_STRING(NULL, &p, length))) + ossl_raise(eASN1Error, NULL); + len = bstr->length; + if(!(buf = OPENSSL_malloc(len))){ + ASN1_BIT_STRING_free(bstr); + ossl_raise(eASN1Error, NULL); + } + *unused_bits = 0; + if(bstr->flags & ASN1_STRING_FLAG_BITS_LEFT) + *unused_bits = bstr->flags & 0x07; + memcpy(buf, bstr->data, len); + ASN1_BIT_STRING_free(bstr); + ret = ossl_buf2str(buf, len); + + return ret; +} + +static VALUE +decode_enum(unsigned char* der, int length) +{ + ASN1_ENUMERATED *ai; + unsigned char *p; + VALUE ret; + int status = 0; + + p = der; + if(!(ai = d2i_ASN1_ENUMERATED(NULL, &p, length))) + ossl_raise(eASN1Error, NULL); + ret = rb_protect((VALUE(*)_((VALUE)))asn1integer_to_num, + (VALUE)ai, &status); + ASN1_ENUMERATED_free(ai); + if(status) rb_jump_tag(status); + + return ret; +} + +static VALUE +decode_null(unsigned char* der, int length) +{ + ASN1_NULL *null; + unsigned char *p; + + p = der; + if(!(null = d2i_ASN1_NULL(NULL, &p, length))) + ossl_raise(eASN1Error, NULL); + ASN1_NULL_free(null); + + return Qnil; +} + +static VALUE +decode_obj(unsigned char* der, int length) +{ + ASN1_OBJECT *obj; + unsigned char *p; + VALUE ret; + int nid; + BIO *bio; + + p = der; + if(!(obj = d2i_ASN1_OBJECT(NULL, &p, length))) + ossl_raise(eASN1Error, NULL); + if((nid = OBJ_obj2nid(obj)) != NID_undef){ + ASN1_OBJECT_free(obj); + ret = rb_str_new2(OBJ_nid2sn(nid)); + } + else{ + if(!(bio = BIO_new(BIO_s_mem()))){ + ASN1_OBJECT_free(obj); + ossl_raise(eASN1Error, NULL); + } + i2a_ASN1_OBJECT(bio, obj); + ASN1_OBJECT_free(obj); + ret = ossl_membio2str(bio); + } + + return ret; +} + +static VALUE +decode_time(unsigned char* der, int length) +{ + ASN1_TIME *time; + unsigned char *p; + VALUE ret; + int status = 0; + + p = der; + if(!(time = d2i_ASN1_TIME(NULL, &p, length))) + ossl_raise(eASN1Error, NULL); + ret = rb_protect((VALUE(*)_((VALUE)))asn1time_to_time, + (VALUE)time, &status); + ASN1_TIME_free(time); + if(status) rb_jump_tag(status); + + return ret; +} + +/********/ + +typedef struct { + char *name; + VALUE *klass; +} ossl_asn1_info_t; + +static ossl_asn1_info_t ossl_asn1_info[] = { + { "EOC", NULL, }, /* 0 */ + { "BOOLEAN", &cASN1Boolean, }, /* 1 */ + { "INTEGER", &cASN1Integer, }, /* 2 */ + { "BIT_STRING", &cASN1BitString, }, /* 3 */ + { "OCTET_STRING", &cASN1OctetString, }, /* 4 */ + { "NULL", &cASN1Null, }, /* 5 */ + { "OBJECT", &cASN1ObjectId, }, /* 6 */ + { "OBJECT_DESCRIPTOR", NULL, }, /* 7 */ + { "EXTERNAL", NULL, }, /* 8 */ + { "REAL", NULL, }, /* 9 */ + { "ENUMERATED", &cASN1Enumerated, }, /* 10 */ + { "EMBEDDED_PDV", NULL, }, /* 11 */ + { "UTF8STRING", &cASN1UTF8String, }, /* 12 */ + { "RELATIVE_OID", NULL, }, /* 13 */ + { "[UNIVERSAL 14]", NULL, }, /* 14 */ + { "[UNIVERSAL 15]", NULL, }, /* 15 */ + { "SEQUENCE", &cASN1Sequence, }, /* 16 */ + { "SET", &cASN1Set, }, /* 17 */ + { "NUMERICSTRING", &cASN1NumericString, }, /* 18 */ + { "PRINTABLESTRING", &cASN1PrintableString, }, /* 19 */ + { "T61STRING", &cASN1T61String, }, /* 20 */ + { "VIDEOTEXSTRING", &cASN1VideotexString, }, /* 21 */ + { "IA5STRING", &cASN1IA5String, }, /* 22 */ + { "UTCTIME", &cASN1UTCTime, }, /* 23 */ + { "GENERALIZEDTIME", &cASN1GeneralizedTime, }, /* 24 */ + { "GRAPHICSTRING", &cASN1GraphicString, }, /* 25 */ + { "ISO64STRING", &cASN1ISO64String, }, /* 26 */ + { "GENERALSTRING", &cASN1GeneralString, }, /* 27 */ + { "UNIVERSALSTRING", &cASN1UniversalString, }, /* 28 */ + { "CHARACTER_STRING", NULL, }, /* 29 */ + { "BMPSTRING", &cASN1BMPString, }, /* 30 */ +}; + +int ossl_asn1_info_size = (sizeof(ossl_asn1_info)/sizeof(ossl_asn1_info[0])); + +static int ossl_asn1_default_tag(VALUE obj); + +ASN1_TYPE* +ossl_asn1_get_asn1type(VALUE obj) +{ + ASN1_TYPE *ret; + VALUE value, rflag; + void *ptr; + void (*free_func)(); + long tag, flag; + + tag = ossl_asn1_default_tag(obj); + value = ossl_asn1_get_value(obj); + switch(tag){ + case V_ASN1_BOOLEAN: + ptr = (void*)obj_to_asn1bool(value); + free_func = NULL; + break; + case V_ASN1_INTEGER: /* FALLTHROUGH */ + case V_ASN1_ENUMERATED: + ptr = obj_to_asn1int(value); + free_func = ASN1_INTEGER_free; + break; + case V_ASN1_BIT_STRING: + rflag = rb_attr_get(obj, rb_intern("@unused_bits")); + flag = NIL_P(rflag) ? -1 : NUM2INT(rflag); + ptr = obj_to_asn1bstr(value, flag); + free_func = ASN1_BIT_STRING_free; + break; + case V_ASN1_NULL: + ptr = obj_to_asn1null(value); + free_func = ASN1_NULL_free; + break; + case V_ASN1_OCTET_STRING: /* FALLTHROUGH */ + case V_ASN1_UTF8STRING: /* FALLTHROUGH */ + case V_ASN1_NUMERICSTRING: /* FALLTHROUGH */ + case V_ASN1_PRINTABLESTRING: /* FALLTHROUGH */ + case V_ASN1_T61STRING: /* FALLTHROUGH */ + case V_ASN1_VIDEOTEXSTRING: /* FALLTHROUGH */ + case V_ASN1_IA5STRING: /* FALLTHROUGH */ + case V_ASN1_GRAPHICSTRING: /* FALLTHROUGH */ + case V_ASN1_ISO64STRING: /* FALLTHROUGH */ + case V_ASN1_GENERALSTRING: /* FALLTHROUGH */ + case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */ + case V_ASN1_BMPSTRING: + ptr = obj_to_asn1str(value); + free_func = ASN1_STRING_free; + break; + case V_ASN1_OBJECT: + ptr = obj_to_asn1obj(value); + free_func = ASN1_INTEGER_free; + break; + case V_ASN1_UTCTIME: + ptr = obj_to_asn1utime(value); + free_func = ASN1_TIME_free; + break; + case V_ASN1_GENERALIZEDTIME: + ptr = obj_to_asn1gtime(value); + free_func = ASN1_TIME_free; + break; + case V_ASN1_SET: /* FALLTHROUGH */ + case V_ASN1_SEQUENCE: + ptr = obj_to_asn1derstr(obj); + free_func = ASN1_STRING_free; + break; + default: + ossl_raise(eASN1Error, "unsupported ASN.1 type"); + } + if(!(ret = OPENSSL_malloc(sizeof(ASN1_TYPE)))){ + if(free_func) free_func(ptr); + ossl_raise(eASN1Error, "ASN1_TYPE alloc failure"); + } + memset(ret, 0, sizeof(ASN1_TYPE)); + ASN1_TYPE_set(ret, tag, ptr); + + return ret; +} + +static int +ossl_asn1_default_tag(VALUE obj) +{ + int i; + + for(i = 0; i < ossl_asn1_info_size; i++){ + if(ossl_asn1_info[i].klass && + rb_obj_is_kind_of(obj, *ossl_asn1_info[i].klass)){ + return i; + } + } + ossl_raise(eASN1Error, "not found universal tag for %s", + rb_class2name(CLASS_OF(obj))); + + return -1; /* dummy */ +} + +static int +ossl_asn1_tag(VALUE obj) +{ + VALUE tag; + + tag = ossl_asn1_get_tag(obj); + if(NIL_P(tag)) + ossl_raise(eASN1Error, "tag number not specified"); + + return NUM2INT(tag); +} + +static int +ossl_asn1_is_explicit(VALUE obj) +{ + VALUE s; + int ret = -1; + + s = ossl_asn1_get_tagging(obj); + if(NIL_P(s)) return 0; + else if(SYMBOL_P(s)){ + if (SYM2ID(s) == sIMPLICIT) + ret = 0; + else if (SYM2ID(s) == sEXPLICIT) + ret = 1; + } + if(ret < 0){ + ossl_raise(eASN1Error, "invalid tag default"); + } + + return ret; +} + +static int +ossl_asn1_tag_class(VALUE obj) +{ + VALUE s; + int ret = -1; + + s = ossl_asn1_get_tag_class(obj); + if(NIL_P(s)) ret = V_ASN1_UNIVERSAL; + else if(SYMBOL_P(s)){ + if (SYM2ID(s) == sUNIVERSAL) + ret = V_ASN1_UNIVERSAL; + else if (SYM2ID(s) == sAPPLICATION) + ret = V_ASN1_APPLICATION; + else if (SYM2ID(s) == sCONTEXT_SPECIFIC) + ret = V_ASN1_CONTEXT_SPECIFIC; + else if (SYM2ID(s) == sPRIVATE) + ret = V_ASN1_PRIVATE; + } + if(ret < 0){ + ossl_raise(eASN1Error, "invalid tag class"); + } + + return ret; +} + +static VALUE +ossl_asn1_class2sym(int tc) +{ + if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) + return ID2SYM(sPRIVATE); + else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) + return ID2SYM(sCONTEXT_SPECIFIC); + else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) + return ID2SYM(sAPPLICATION); + else + return ID2SYM(sUNIVERSAL); +} + +static VALUE +ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class) +{ + if(!SYMBOL_P(tag_class)) + ossl_raise(eASN1Error, "invalid tag class"); + if((SYM2ID(tag_class) == sUNIVERSAL) && NUM2INT(tag) > 31) + ossl_raise(eASN1Error, "too large tag number for Universal"); + ossl_asn1_set_tag(self, tag); + ossl_asn1_set_value(self, value); + ossl_asn1_set_tag_class(self, tag_class); + + return self; +} + +static VALUE +join_der_i(VALUE i, VALUE str) +{ + i = ossl_to_der_if_possible(i); + StringValue(i); + rb_str_append(str, i); + return Qnil; +} + +static VALUE +join_der(VALUE enumerable) +{ + VALUE str = rb_str_new(0, 0); + rb_iterate(rb_each, enumerable, join_der_i, str); + return str; +} + +static VALUE +ossl_asn1data_to_der(VALUE self) +{ + VALUE value, der; + int tag, tag_class, is_cons = 0; + long length; + unsigned char *p; + + value = ossl_asn1_get_value(self); + if(rb_obj_is_kind_of(value, rb_cArray)){ + is_cons = 1; + value = join_der(value); + } + StringValue(value); + + tag = ossl_asn1_tag(self); + tag_class = ossl_asn1_tag_class(self); + if((length = ASN1_object_size(1, RSTRING(value)->len, tag)) <= 0) + ossl_raise(eASN1Error, NULL); + der = rb_str_new(0, length); + p = RSTRING(der)->ptr; + ASN1_put_object(&p, is_cons, RSTRING(value)->len, tag, tag_class); + memcpy(p, RSTRING(value)->ptr, RSTRING(value)->len); + p += RSTRING(value)->len; + ossl_str_adjust(der, p); + + return der; +} + +static VALUE +ossl_asn1_decode0(unsigned char **pp, long length, long *offset, long depth, + int once, int yield) +{ + unsigned char *start, *p; + long len, off = *offset; + int hlen, tag, tc, j; + VALUE ary, asn1data, value, tag_class; + + ary = rb_ary_new(); + p = *pp; + while(length > 0){ + start = p; + j = ASN1_get_object(&p, &len, &tag, &tc, length); + if(j & 0x80) ossl_raise(eASN1Error, NULL); + hlen = p - start; + if(yield){ + VALUE arg = rb_ary_new(); + rb_ary_push(arg, LONG2NUM(depth)); + rb_ary_push(arg, LONG2NUM(off)); + rb_ary_push(arg, LONG2NUM(hlen)); + rb_ary_push(arg, LONG2NUM(len)); + rb_ary_push(arg, (j & V_ASN1_CONSTRUCTED) ? Qtrue : Qfalse); + rb_ary_push(arg, ossl_asn1_class2sym(tc)); + rb_ary_push(arg, INT2NUM(tag)); + rb_yield(arg); + } + length -= hlen; + off += hlen; + if(len > length) ossl_raise(eASN1Error, "value is too short"); + if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) + tag_class = sPRIVATE; + else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) + tag_class = sCONTEXT_SPECIFIC; + else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) + tag_class = sAPPLICATION; + else + tag_class = sUNIVERSAL; + if(j & V_ASN1_CONSTRUCTED){ + /* TODO: if j == 0x21 it is indefinite length object. */ + if((j == 0x21) && (len == 0)){ + long lastoff = off; + value = ossl_asn1_decode0(&p, length, &off, depth+1, 0, yield); + len = off - lastoff; + } + else value = ossl_asn1_decode0(&p, len, &off, depth+1, 0, yield); + } + else{ + value = rb_str_new(p, len); + p += len; + off += len; + } + if(tag_class == sUNIVERSAL && + tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass){ + VALUE klass = *ossl_asn1_info[tag].klass; + long flag; + if(!rb_obj_is_kind_of(value, rb_cArray)){ + switch(tag){ + case V_ASN1_BOOLEAN: + value = decode_bool(start, hlen+len); + break; + case V_ASN1_INTEGER: + value = decode_int(start, hlen+len); + break; + case V_ASN1_BIT_STRING: + value = decode_bstr(start, hlen+len, &flag); + break; + case V_ASN1_NULL: + value = decode_null(start, hlen+len); + break; + case V_ASN1_ENUMERATED: + value = decode_enum(start, hlen+len); + break; + case V_ASN1_OBJECT: + value = decode_obj(start, hlen+len); + break; + case V_ASN1_UTCTIME: /* FALLTHROUGH */ + case V_ASN1_GENERALIZEDTIME: + value = decode_time(start, hlen+len); + break; + default: + /* use original value */ + } + } + asn1data = rb_funcall(klass, rb_intern("new"), 1, value); + if(tag == V_ASN1_BIT_STRING){ + rb_iv_set(asn1data, "@unused_bits", LONG2NUM(flag)); + } + } + else{ + asn1data = rb_funcall(cASN1Data, rb_intern("new"), 3, + value, INT2NUM(tag), ID2SYM(tag_class)); + } + rb_ary_push(ary, asn1data); + length -= len; + if(once) break; + } + *pp = p; + *offset = off; + + return ary; +} + +static VALUE +ossl_asn1_traverse(VALUE self, VALUE obj) +{ + unsigned char *p; + long offset = 0; + + obj = ossl_to_der_if_possible(obj); + StringValue(obj); + p = RSTRING(obj)->ptr; + ossl_asn1_decode0(&p, RSTRING(obj)->len, &offset, 0, 0, 1); + + return obj; +} + +static VALUE +ossl_asn1_decode(VALUE self, VALUE obj) +{ + VALUE ret, ary; + unsigned char *p; + long offset = 0; + + obj = ossl_to_der_if_possible(obj); + StringValue(obj); + p = RSTRING(obj)->ptr; + ary = ossl_asn1_decode0(&p, RSTRING(obj)->len, &offset, 0, 1, 0); + ret = rb_ary_entry(ary, 0); + + return ret; +} + +static VALUE +ossl_asn1_decode_all(VALUE self, VALUE obj) +{ + VALUE ret; + unsigned char *p; + long offset = 0; + + obj = ossl_to_der_if_possible(obj); + StringValue(obj); + p = RSTRING(obj)->ptr; + ret = ossl_asn1_decode0(&p, RSTRING(obj)->len, &offset, 0, 0, 0); + + return ret; +} + +static VALUE +ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE value, tag, tagging, tag_class; + + rb_scan_args(argc, argv, "13", &value, &tag, &tagging, &tag_class); + if(argc > 1){ + if(NIL_P(tag)) + ossl_raise(eASN1Error, "must specify tag number"); + if(NIL_P(tagging)) + tagging = ID2SYM(sEXPLICIT); + if(!SYMBOL_P(tagging)) + ossl_raise(eASN1Error, "invelid tag default"); + if(NIL_P(tag_class)) + tag_class = ID2SYM(sCONTEXT_SPECIFIC); + if(!SYMBOL_P(tag_class)) + ossl_raise(eASN1Error, "invelid tag class"); + if(SYM2ID(tagging) == sIMPLICIT && NUM2INT(tag) > 31) + ossl_raise(eASN1Error, "too large tag number for Universal"); + } + else{ + tag = INT2NUM(ossl_asn1_default_tag(self)); + tagging = Qnil; + tag_class = ID2SYM(sUNIVERSAL); + } + ossl_asn1_set_tag(self, tag); + ossl_asn1_set_value(self, value); + ossl_asn1_set_tagging(self, tagging); + ossl_asn1_set_tag_class(self, tag_class); + + return self; +} + +static VALUE +ossl_asn1prim_to_der(VALUE self) +{ + ASN1_TYPE *asn1; + int tn, tc, explicit; + long length, reallen; + unsigned char *buf, *p; + VALUE str; + + tn = NUM2INT(ossl_asn1_get_tag(self)); + tc = ossl_asn1_tag_class(self); + explicit = ossl_asn1_is_explicit(self); + asn1 = ossl_asn1_get_asn1type(self); + + length = ASN1_object_size(1, i2d_ASN1_TYPE(asn1, NULL), tn); + if(!(buf = OPENSSL_malloc(length))){ + ASN1_TYPE_free(asn1); + ossl_raise(eASN1Error, "cannot alloc buffer"); + } + p = buf; + if(tc == V_ASN1_UNIVERSAL) i2d_ASN1_TYPE(asn1, &p); + else{ + if(explicit){ + ASN1_put_object(&p, 1, i2d_ASN1_TYPE(asn1, NULL), tn, tc); + i2d_ASN1_TYPE(asn1, &p); + } + else{ + i2d_ASN1_TYPE(asn1, &p); + *buf = tc | tn | (*buf & V_ASN1_CONSTRUCTED); + } + } + ASN1_TYPE_free(asn1); + reallen = p - buf; + assert(reallen <= length); + str = ossl_buf2str(buf, reallen); /* buf will be free in ossl_buf2str */ + + return str; +} + +static VALUE +ossl_asn1cons_to_der(self) +{ + int tag, tn, tc, explicit; + long seq_len, length; + unsigned char *p; + VALUE value, str; + + tag = ossl_asn1_default_tag(self); + tn = NUM2INT(ossl_asn1_get_tag(self)); + tc = ossl_asn1_tag_class(self); + explicit = ossl_asn1_is_explicit(self); + value = join_der(ossl_asn1_get_value(self)); + + seq_len = ASN1_object_size(1, RSTRING(value)->len, tag); + length = ASN1_object_size(1, seq_len, tn); + str = rb_str_new(0, length); + p = RSTRING(str)->ptr; + if(tc == V_ASN1_UNIVERSAL) + ASN1_put_object(&p, 1, RSTRING(value)->len, tn, tc); + else{ + if(explicit){ + ASN1_put_object(&p, 1, seq_len, tn, tc); + ASN1_put_object(&p, 1, RSTRING(value)->len, tag, V_ASN1_UNIVERSAL); + } + else ASN1_put_object(&p, 1, RSTRING(value)->len, tn, tc); + } + memcpy(p, RSTRING(value)->ptr, RSTRING(value)->len); + p += RSTRING(value)->len; + ossl_str_adjust(str, p); + + return str; +} + +static VALUE +ossl_asn1cons_each(VALUE self) +{ + rb_ary_each(ossl_asn1_get_value(self)); + return self; +} + +static VALUE +ossl_asn1obj_s_register(VALUE self, VALUE oid, VALUE sn, VALUE ln) +{ + StringValue(oid); + StringValue(sn); + StringValue(ln); + + if(!OBJ_create(RSTRING(oid)->ptr, RSTRING(sn)->ptr, RSTRING(ln)->ptr)) + ossl_raise(eASN1Error, NULL); + + return Qtrue; +} + +#define OSSL_ASN1_IMPL_FACTORY_METHOD(klass) \ +static VALUE ossl_asn1_##klass(int argc, VALUE *argv, VALUE self)\ +{ return rb_funcall3(cASN1##klass, rb_intern("new"), argc, argv); } + +OSSL_ASN1_IMPL_FACTORY_METHOD(Boolean) +OSSL_ASN1_IMPL_FACTORY_METHOD(Integer) +OSSL_ASN1_IMPL_FACTORY_METHOD(Enumerated) +OSSL_ASN1_IMPL_FACTORY_METHOD(BitString) +OSSL_ASN1_IMPL_FACTORY_METHOD(OctetString) +OSSL_ASN1_IMPL_FACTORY_METHOD(UTF8String) +OSSL_ASN1_IMPL_FACTORY_METHOD(NumericString) +OSSL_ASN1_IMPL_FACTORY_METHOD(PrintableString) +OSSL_ASN1_IMPL_FACTORY_METHOD(T61String) +OSSL_ASN1_IMPL_FACTORY_METHOD(VideotexString) +OSSL_ASN1_IMPL_FACTORY_METHOD(IA5String) +OSSL_ASN1_IMPL_FACTORY_METHOD(GraphicString) +OSSL_ASN1_IMPL_FACTORY_METHOD(ISO64String) +OSSL_ASN1_IMPL_FACTORY_METHOD(GeneralString) +OSSL_ASN1_IMPL_FACTORY_METHOD(UniversalString) +OSSL_ASN1_IMPL_FACTORY_METHOD(BMPString) +OSSL_ASN1_IMPL_FACTORY_METHOD(Null) +OSSL_ASN1_IMPL_FACTORY_METHOD(ObjectId) +OSSL_ASN1_IMPL_FACTORY_METHOD(UTCTime) +OSSL_ASN1_IMPL_FACTORY_METHOD(GeneralizedTime) +OSSL_ASN1_IMPL_FACTORY_METHOD(Sequence) +OSSL_ASN1_IMPL_FACTORY_METHOD(Set) + +void +Init_ossl_asn1() +{ + VALUE ary; + int i; + + sUNIVERSAL = rb_intern("UNIVERSAL"); + sCONTEXT_SPECIFIC = rb_intern("CONTEXT_SPECIFIC"); + sAPPLICATION = rb_intern("APPLICATION"); + sPRIVATE = rb_intern("PRIVATE"); + sEXPLICIT = rb_intern("EXPLICIT"); + sIMPLICIT = rb_intern("IMPLICIT"); + + mASN1 = rb_define_module_under(mOSSL, "ASN1"); + eASN1Error = rb_define_class_under(mASN1, "ASN1Error", eOSSLError); + rb_define_module_function(mASN1, "traverse", ossl_asn1_traverse, 1); + rb_define_module_function(mASN1, "decode", ossl_asn1_decode, 1); + rb_define_module_function(mASN1, "decode_all", ossl_asn1_decode_all, 1); + ary = rb_ary_new(); + rb_define_const(mASN1, "UNIVERSAL_TAG_NAME", ary); + for(i = 0; i < ossl_asn1_info_size; i++){ + if(ossl_asn1_info[i].name[0] == '[') continue; + rb_define_const(mASN1, ossl_asn1_info[i].name, INT2NUM(i)); + rb_ary_store(ary, i, rb_str_new2(ossl_asn1_info[i].name)); + } + + cASN1Data = rb_define_class_under(mASN1, "ASN1Data", rb_cObject); + rb_attr(cASN1Data, rb_intern("value"), 1, 1, Qtrue); + rb_attr(cASN1Data, rb_intern("tag"), 1, 1, Qtrue); + rb_attr(cASN1Data, rb_intern("tag_class"), 1, 1, Qtrue); + rb_define_method(cASN1Data, "initialize", ossl_asn1data_initialize, 3); + rb_define_method(cASN1Data, "to_der", ossl_asn1data_to_der, 0); + + cASN1Primitive = rb_define_class_under(mASN1, "Primitive", cASN1Data); + rb_attr(cASN1Primitive, rb_intern("tagging"), 1, 1, Qtrue); + rb_define_method(cASN1Primitive, "initialize", ossl_asn1_initialize, -1); + rb_define_method(cASN1Primitive, "to_der", ossl_asn1prim_to_der, 0); + + cASN1Constructive = rb_define_class_under(mASN1,"Constructive", cASN1Data); + rb_include_module(cASN1Constructive, rb_mEnumerable); + rb_attr(cASN1Constructive, rb_intern("tagging"), 1, 1, Qtrue); + rb_define_method(cASN1Constructive, "initialize", ossl_asn1_initialize, -1); + rb_define_method(cASN1Constructive, "to_der", ossl_asn1cons_to_der, 0); + rb_define_method(cASN1Constructive, "each", ossl_asn1cons_each, 0); + +#define OSSL_ASN1_DEFINE_CLASS(name, super) \ +do{\ + cASN1##name = rb_define_class_under(mASN1, #name, cASN1##super);\ + rb_define_module_function(mASN1, #name, ossl_asn1_##name, -1);\ +}while(0) + + OSSL_ASN1_DEFINE_CLASS(Boolean, Primitive); + OSSL_ASN1_DEFINE_CLASS(Integer, Primitive); + OSSL_ASN1_DEFINE_CLASS(Enumerated, Primitive); + OSSL_ASN1_DEFINE_CLASS(BitString, Primitive); + OSSL_ASN1_DEFINE_CLASS(OctetString, Primitive); + OSSL_ASN1_DEFINE_CLASS(UTF8String, Primitive); + OSSL_ASN1_DEFINE_CLASS(NumericString, Primitive); + OSSL_ASN1_DEFINE_CLASS(PrintableString, Primitive); + OSSL_ASN1_DEFINE_CLASS(T61String, Primitive); + OSSL_ASN1_DEFINE_CLASS(VideotexString, Primitive); + OSSL_ASN1_DEFINE_CLASS(IA5String, Primitive); + OSSL_ASN1_DEFINE_CLASS(GraphicString, Primitive); + OSSL_ASN1_DEFINE_CLASS(ISO64String, Primitive); + OSSL_ASN1_DEFINE_CLASS(GeneralString, Primitive); + OSSL_ASN1_DEFINE_CLASS(UniversalString, Primitive); + OSSL_ASN1_DEFINE_CLASS(BMPString, Primitive); + OSSL_ASN1_DEFINE_CLASS(Null, Primitive); + OSSL_ASN1_DEFINE_CLASS(ObjectId, Primitive); + OSSL_ASN1_DEFINE_CLASS(UTCTime, Primitive); + OSSL_ASN1_DEFINE_CLASS(GeneralizedTime, Primitive); + + OSSL_ASN1_DEFINE_CLASS(Sequence, Constructive); + OSSL_ASN1_DEFINE_CLASS(Set, Constructive); + + rb_define_singleton_method(cASN1ObjectId, "register", ossl_asn1obj_s_register, 3); + rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, Qtrue); +} -- cgit v1.2.3