Class: Krypt::ASN1::ASN1Data
- Inherits:
-
Object
- Object
- Krypt::ASN1::ASN1Data
- Includes:
- Comparable
- Defined in:
- ext/krypt/core/krypt_asn1.c,
ext/krypt/core/krypt_asn1.c
Overview
The top-level class representing any ASN.1 object. When parsed by ASN1.decode, tagged values are always represented by an instance of ASN1Data.
The role of ASN1Data for parsing tagged values
When encoding an ASN.1 type it is inherently clear what original type (e.g. INTEGER, OCTET STRING etc.) this value has, regardless of its tagging. But opposed to the time an ASN.1 type is to be encoded, when parsing them it is not possible to deduce the “real type” of tagged values. This is why tagged values are generally parsed into ASN1Data instances, but with a different outcome for implicit and explicit tagging.
Example of a parsed implicitly tagged value
An implicitly 1-tagged INTEGER value will be parsed as an ASN1Data with
-
tag
equal to 1 -
tag_class
equal to:CONTEXT_SPECIFIC
-
value
equal to aString
that carries the raw encoding of the INTEGER.
This implies that a subsequent decoding step is required to completely decode implicitly tagged values.
Example of a parsed explicitly tagged value
An explicitly 1-tagged INTEGER value will be parsed as an ASN1Data with
-
tag
equal to 1 -
tag_class
equal to:CONTEXT_SPECIFIC
-
value
equal to anArray
with one single element, an instance of Krypt::ASN1::Integer, i.e. the inner element is the non-tagged primitive value, and the tagging is represented in the outer ASN1Data
Example - Decoding an implicitly tagged INTEGER
int = Krypt::ASN1::Integer.new(1, 0, :CONTEXT_SPECIFIC) # implicit 0-tagged
seq = Krypt::ASN1::Sequence.new( [int] )
der = seq.to_der
asn1 = Krypt::ASN1.decode(der)
# pp asn1 => #<Krypt::ASN1::Sequence:0x87326e0
# @infinite_length=false,
# @tag=16,
# @tag_class=:UNIVERSAL>
# pp asn1.value => [#<Krypt::ASN1::ASN1Data:0x87326f4
# @infinite_length=false,
# @tag=0,
# @tag_class=:CONTEXT_SPECIFIC>]
# pp asn1.value[0].value => "\x01"
raw_int = asn1.value[0]
# manually rewrite tag and tag class to make it an UNIVERSAL value
raw_int.tag = OpenSSL::ASN1::INTEGER
raw_int.tag_class = :UNIVERSAL
int2 = Krypt::ASN1.decode(raw_int)
puts int2.value # => 1
Example - Decoding an explicitly tagged INTEGER
int = Krypt::ASN1::Integer.new(1)
data = Krypt::ASN1Data.new([int], 0, :CONTEXT_SPECIFIC) # explicit 0-tagged
seq = Krypt::ASN1::Sequence.new( [data] )
der = seq.to_der
asn1 = Krypt::ASN1.decode(der)
# pp asn1 => #<Krypt::ASN1::Sequence:0x87326e0
# @infinite_length=false,
# @tag=16,
# @tag_class=:UNIVERSAL>
# pp asn1.value => [#<Krypt::ASN1::ASN1Data:0x87326f4
# @infinite_length=false,
# @tag=0,
# @tag_class=:CONTEXT_SPECIFIC>]
# pp asn1.value[0].value => [#<Krypt::ASN1::Integer:0x85bf308
# @infinite_length=false,
# @tag=2,
# @tag_class=:UNIVERSAL>]
int2 = asn1.value[0].value[0]
puts int2.value # => 1
Direct Known Subclasses
Instance Method Summary collapse
-
#<=>(b) ⇒ -1 | 0 | +1
ASN1Data includes the Comparable module.
-
#encode_to(io) ⇒ self
-
io
: an IO-like object supporting IO#write.
-
-
#infinite_length ⇒ Boolean
Returns either true or false, depending on whether the value is to be or was encoded using infinite length.
-
#infinite_length=(bool) ⇒ Boolean
-
bool
: either true or false, depending on whether the value shall be encoded using infinite length encoding or not.
-
-
#new(value, tag, tag_class) ⇒ ASN1Data
constructor
-
value
: the value to be associated.
-
- #tag ⇒ Object
-
#tag=(number) ⇒ Number
-
number
: aNumber
representing the tag number of this ASN1Data.
-
-
#tag_class ⇒ Symbol
Returns a
Symbol
representing the tag class of this ASN1Data. -
#tag_class=(sym) ⇒ Symbol
-
sym
: ASymbol
representing the tag class of this ASN1Data.
-
-
#to_der ⇒ DER-/BER-encoded String
Encodes this ASN1Data into a DER-encoded String value.
-
#value ⇒ Object
Obtain the value of an ASN1Data.
-
#value=(value) ⇒ Object
Set the value of an ASN1Data.
Constructor Details
#new(value, tag, tag_class) ⇒ ASN1Data
-
value
: the value to be associated. See Primitive for the mappings
between ASN.1 types and Ruby types.
-
tag
: aNumber
representing this value’s tag. -
tag_class
: aSymbol
representing one of the four valid tag classes
:UNIVERSAL
, :CONTEXT_SPECIFIC
, :APPLICATION
or :PRIVATE
.
Creates an ASN1Data from scratch.
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'ext/krypt/core/krypt_asn1.c', line 359
static VALUE
krypt_asn1_data_initialize(VALUE self, VALUE value, VALUE vtag, VALUE vtag_class)
{
ID stag_class;
int tag, tag_class, is_constructed;
krypt_asn1_data *data;
int_validate_tag_and_class(vtag, vtag_class);
tag = NUM2INT(vtag);
stag_class = SYM2ID(vtag_class);
if (stag_class == sKrypt_TC_EXPLICIT)
rb_raise(eKryptASN1Error, "Explicit tagging is only supported for explicit UNIVERSAL sub classes of ASN1Data");
if (stag_class == sKrypt_TC_UNIVERSAL && tag > 30)
rb_raise(eKryptASN1Error, "Tag too large for UNIVERSAL tag class");
if ((tag_class = krypt_asn1_tag_class_for_id(stag_class)) == KRYPT_ERR)
rb_raise(eKryptASN1Error, "Unknown tag class");
is_constructed = rb_respond_to(value, sKrypt_ID_EACH);
int_asn1_data_initialize(self, tag, tag_class, is_constructed, 0);
int_asn1_data_get(self, data);
data->update_cb = int_asn1_data_update_cb;
int_asn1_data_set_tag(self, vtag);
int_asn1_data_set_tag_class(self, vtag_class);
int_asn1_data_set_infinite_length(self, Qfalse);
int_asn1_data_set_value(self, value);
int_asn1_data_set_modified(data, 1); /* newly created is modified by default */
return self;
}
|
Instance Method Details
#<=>(b) ⇒ -1 | 0 | +1
ASN1Data includes the Comparable module.
<=> compares two instances of ASN1Data by comparing the bytes of their encoding. The order applied is SET order, i.e. a < b iff tag of a < tag of b. If tags are equal, SET OF order is applied, a lexicographical byte order. Element order is decided based on the first byte where two elements differ, the lower byte indicates the lower element.
If two elements differ in length, but are equal up to the last byte of the smaller element, the smaller element is the lower one.
Example
Given the following SET of values
[
Krypt::ASN1::OctetString.new("a"),
Krypt::ASN1::Null.new,
Krypt::ASN1::Boolean.new(true),
Krypt::ASN1::Integer.new(1)
]
the implied SET order is
[
Krypt::ASN1::Boolean.new(true),
Krypt::ASN1::Integer.new(1)
Krypt::ASN1::OctetString.new("a"),
Krypt::ASN1::Null.new,
]
Given the following byte representations of OCTET STRINGS,
[ "\x04\x06\aaabaa", "\x04\x01b", "\x04\x06aaabba", "\x04\x04aaab" ]
the SET OF order implied is
[ "\x04\x01b", "\x04\x04aaab", "\x04\x06aaabaa", "\x04\x06aaabba" ]
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 |
# File 'ext/krypt/core/krypt_asn1.c', line 1096
static VALUE
krypt_asn1_data_cmp(VALUE a, VALUE b)
{
VALUE vs1, vs2;
int result;
vs1 = krypt_asn1_data_to_der(a);
if (!rb_respond_to(b, sKrypt_ID_TO_DER)) return Qnil;
vs2 = krypt_to_der(b);
if(krypt_asn1_cmp_set_of((uint8_t *) RSTRING_PTR(vs1), (size_t) RSTRING_LEN(vs1),
(uint8_t *) RSTRING_PTR(vs2), (size_t) RSTRING_LEN(vs2), &result) == KRYPT_ERR) {
krypt_error_raise(eKryptASN1Error, "Error while comparing values");
}
return INT2NUM(result);
}
|
#encode_to(io) ⇒ self
-
io
: an IO-like object supporting IO#write
Encodes this ASN1Data into a DER-encoded String value by writing the contents to an IO-like object. Newly created ASN1Data are DER-encoded except for the possibility of infinite length encodings. If a value with BER encoding was parsed and is not modified, the BER encoding will be preserved when encoding it again.
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 |
# File 'ext/krypt/core/krypt_asn1.c', line 967
static VALUE
krypt_asn1_data_encode_to(VALUE self, VALUE io)
{
binyo_outstream *out;
krypt_asn1_data *data;
int result;
int_asn1_data_get(self, data);
out = binyo_outstream_new_value(io);
result = int_asn1_encode_to(out, data, self);
binyo_outstream_free(out);
if (result == KRYPT_ERR)
krypt_error_raise(eKryptASN1Error, "Error while encoding value");
return self;
}
|
#infinite_length ⇒ Boolean
Returns either true or false, depending on whether the value is to be or was encoded using infinite length. See ASN1Data#infinite_length= for details.
748 749 750 751 752 |
# File 'ext/krypt/core/krypt_asn1.c', line 748
static VALUE
krypt_asn1_data_get_inf_length(VALUE self)
{
return int_asn1_data_get_infinite_length(self);
}
|
#infinite_length=(bool) ⇒ Boolean
-
bool
: either true or false, depending on whether the value shall be
encoded using infinite length encoding or not
Set a Boolean
indicating whether the encoding shall be infinite length or not. In DER, every value has a finite length associated with it. But in scenarios where large amounts of data need to be transferred, it might be desirable to have some kind of streaming support available. For example, huge OCTET STRINGs are preferably sent in smaller-sized chunks, each at a time. This is possible in BER by setting the length bytes of an encoding to zero and thus indicating that the following value will be sent in chunks. Infinite length encodings are always constructed. The end of such a stream of chunks is indicated by sending a EndOfContents value. SETs and SEQUENCEs may use an infinite length encoding, but also primitive types such as e.g. OCTET STRINGS or BIT STRINGS may leverage this functionality (cf. ITU-T X.690).
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 |
# File 'ext/krypt/core/krypt_asn1.c', line 776
static VALUE
krypt_asn1_data_set_inf_length(VALUE self, VALUE inf_length)
{
krypt_asn1_data *data;
krypt_asn1_header *header;
int new_inf;
int_asn1_data_get(self, data);
header = data->object->header;
new_inf = RTEST(inf_length) ? 1 : 0;
if (header->is_infinite == new_inf)
return inf_length;
header->is_infinite = new_inf;
int_invalidate_length(header);
int_asn1_data_set_modified(data, 1);
int_asn1_data_set_infinite_length(self, new_inf ? Qtrue : Qfalse);
return inf_length;
}
|
#tag ⇒ Object
#tag=(number) ⇒ Number
-
number
: aNumber
representing the tag number of this ASN1Data.
Must not be nil
.
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 |
# File 'ext/krypt/core/krypt_asn1.c', line 632
static VALUE
krypt_asn1_data_set_tag(VALUE self, VALUE tag)
{
krypt_asn1_data *data;
krypt_asn1_header *header;
int new_tag;
int_asn1_data_get(self, data);
header = data->object->header;
new_tag = NUM2INT(tag);
if (header->tag == new_tag)
return tag;
header->tag = new_tag;
int_invalidate_tag(header);
if (data->update_cb)
data->update_cb(data);
int_asn1_data_set_modified(data, 1);
int_asn1_data_set_tag(self, tag);
return tag;
}
|
#tag_class ⇒ Symbol
Returns a Symbol
representing the tag class of this ASN1Data. Never nil
. See ASN1Data for possible values.
664 665 666 667 668 |
# File 'ext/krypt/core/krypt_asn1.c', line 664
static VALUE
krypt_asn1_data_get_tag_class(VALUE self)
{
return int_asn1_data_get_tag_class(self);
}
|
#tag_class=(sym) ⇒ Symbol
-
sym
: ASymbol
representing the tag class of this ASN1Data.
Must not be nil
. See ASN1Data for possible values.
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 |
# File 'ext/krypt/core/krypt_asn1.c', line 704
static VALUE
krypt_asn1_data_set_tag_class(VALUE self, VALUE tag_class)
{
krypt_asn1_data *data;
krypt_asn1_header *header;
int new_tag_class;
ID new_tc, old_tc;
int_asn1_data_get(self, data);
new_tc = SYM2ID(tag_class);
old_tc = SYM2ID(int_asn1_data_get_tag_class(self));
if (new_tc == old_tc)
return tag_class;
if (new_tc == sKrypt_TC_EXPLICIT && data->default_tag == -1)
rb_raise(eKryptASN1Error, "Cannot explicitly tag value with unknown default tag");
header = data->object->header;
if ((new_tag_class = krypt_asn1_tag_class_for_id(new_tc)) == KRYPT_ERR)
rb_raise(eKryptASN1Error, "Cannot set tag class");
header->tag_class = new_tag_class;
int_invalidate_tag(header);
if (data->update_cb)
data->update_cb(data);
if (int_asn1_handle_explicit_tagging(self, data, new_tc) == KRYPT_ERR)
rb_raise(eKryptASN1Error, "Tagging explicitly failed");
int_asn1_data_set_modified(data, 1);
int_asn1_data_set_tag_class(self, tag_class);
return tag_class;
}
|
#to_der ⇒ DER-/BER-encoded String
Encodes this ASN1Data into a DER-encoded String value. Newly created ASN1Data are DER-encoded except for the possibility of infinite length encodings. If a value with BER encoding was parsed and is not modified, the BER encoding will be preserved when encoding it again.
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 |
# File 'ext/krypt/core/krypt_asn1.c', line 1038
static VALUE
krypt_asn1_data_to_der(VALUE self)
{
krypt_asn1_data *data;
krypt_asn1_object *object;
int_asn1_data_get(self, data);
object = data->object;
if (object->bytes && object->header->tag_bytes && object->header->length_bytes)
return int_asn1_data_to_der_cached(data->object);
else
return int_asn1_data_to_der_non_cached(data, self);
}
|
#value ⇒ Object
Obtain the value of an ASN1Data. Please see Constructive and Primitive docs for the mappings between ASN.1 data types and Ruby classes.
841 842 843 844 845 846 847 |
# File 'ext/krypt/core/krypt_asn1.c', line 841
static VALUE
krypt_asn1_data_get_value(VALUE self)
{
if (int_asn1_decode_value(self) == KRYPT_ERR)
krypt_error_raise(eKryptASN1Error, "Error while decoding value");
return int_asn1_data_get_value(self);
}
|
#value=(value) ⇒ Object
Set the value of an ASN1Data. Please see Constructive and Primitive docs for the mappings between ASN.1 data types and Ruby classes.
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 |
# File 'ext/krypt/core/krypt_asn1.c', line 857
static VALUE
krypt_asn1_data_set_value(VALUE self, VALUE value)
{
krypt_asn1_data *data;
krypt_asn1_object *object;
int is_constructed;
int_asn1_data_get(self, data);
int_asn1_data_set_value(self, value);
/* Free data that is now stale */
object = data->object;
int_invalidate_value(object);
is_constructed = rb_respond_to(value, sKrypt_ID_EACH);
if (object->header->is_constructed != is_constructed) {
object->header->is_constructed = is_constructed;
int_invalidate_tag(object->header);
data->codec = int_codec_for(data->object);
}
int_asn1_data_set_modified(data, 1);
return value;
}
|