krypt-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/LICENSE +20 -0
  2. data/ext/krypt/core/Makefile +221 -0
  3. data/ext/krypt/core/binyo-error.h +40 -0
  4. data/ext/krypt/core/binyo-io-buffer.h +54 -0
  5. data/ext/krypt/core/binyo-io.h +131 -0
  6. data/ext/krypt/core/extconf.h +8 -0
  7. data/ext/krypt/core/extconf.rb +80 -0
  8. data/ext/krypt/core/krypt-core.c +110 -0
  9. data/ext/krypt/core/krypt-core.h +97 -0
  10. data/ext/krypt/core/krypt-core.o +0 -0
  11. data/ext/krypt/core/krypt-provider.h +86 -0
  12. data/ext/krypt/core/krypt_asn1-internal.c +681 -0
  13. data/ext/krypt/core/krypt_asn1-internal.h +117 -0
  14. data/ext/krypt/core/krypt_asn1-internal.o +0 -0
  15. data/ext/krypt/core/krypt_asn1.c +2109 -0
  16. data/ext/krypt/core/krypt_asn1.h +88 -0
  17. data/ext/krypt/core/krypt_asn1.o +0 -0
  18. data/ext/krypt/core/krypt_asn1_codec.c +973 -0
  19. data/ext/krypt/core/krypt_asn1_codec.o +0 -0
  20. data/ext/krypt/core/krypt_asn1_in_adapter.c +178 -0
  21. data/ext/krypt/core/krypt_asn1_in_adapter.o +0 -0
  22. data/ext/krypt/core/krypt_asn1_in_chunked.c +292 -0
  23. data/ext/krypt/core/krypt_asn1_in_chunked.o +0 -0
  24. data/ext/krypt/core/krypt_asn1_in_definite.c +156 -0
  25. data/ext/krypt/core/krypt_asn1_in_definite.o +0 -0
  26. data/ext/krypt/core/krypt_asn1_parser.c +592 -0
  27. data/ext/krypt/core/krypt_asn1_parser.o +0 -0
  28. data/ext/krypt/core/krypt_asn1_template-internal.h +185 -0
  29. data/ext/krypt/core/krypt_asn1_template.c +459 -0
  30. data/ext/krypt/core/krypt_asn1_template.h +56 -0
  31. data/ext/krypt/core/krypt_asn1_template.o +0 -0
  32. data/ext/krypt/core/krypt_asn1_template_encoder.c +76 -0
  33. data/ext/krypt/core/krypt_asn1_template_encoder.o +0 -0
  34. data/ext/krypt/core/krypt_asn1_template_parser.c +1176 -0
  35. data/ext/krypt/core/krypt_asn1_template_parser.o +0 -0
  36. data/ext/krypt/core/krypt_b64-internal.h +38 -0
  37. data/ext/krypt/core/krypt_b64.c +391 -0
  38. data/ext/krypt/core/krypt_b64.h +41 -0
  39. data/ext/krypt/core/krypt_b64.o +0 -0
  40. data/ext/krypt/core/krypt_digest.c +391 -0
  41. data/ext/krypt/core/krypt_digest.h +51 -0
  42. data/ext/krypt/core/krypt_digest.o +0 -0
  43. data/ext/krypt/core/krypt_error.c +221 -0
  44. data/ext/krypt/core/krypt_error.h +46 -0
  45. data/ext/krypt/core/krypt_error.o +0 -0
  46. data/ext/krypt/core/krypt_hex-internal.h +36 -0
  47. data/ext/krypt/core/krypt_hex.c +255 -0
  48. data/ext/krypt/core/krypt_hex.h +41 -0
  49. data/ext/krypt/core/krypt_hex.o +0 -0
  50. data/ext/krypt/core/krypt_io.c +65 -0
  51. data/ext/krypt/core/krypt_io.h +56 -0
  52. data/ext/krypt/core/krypt_io.o +0 -0
  53. data/ext/krypt/core/krypt_io_in_pem.c +397 -0
  54. data/ext/krypt/core/krypt_io_in_pem.o +0 -0
  55. data/ext/krypt/core/krypt_missing.c +238 -0
  56. data/ext/krypt/core/krypt_missing.h +62 -0
  57. data/ext/krypt/core/krypt_missing.o +0 -0
  58. data/ext/krypt/core/krypt_pem.c +171 -0
  59. data/ext/krypt/core/krypt_pem.o +0 -0
  60. data/ext/krypt/core/krypt_provider-internal.h +40 -0
  61. data/ext/krypt/core/krypt_provider.c +136 -0
  62. data/ext/krypt/core/krypt_provider.o +0 -0
  63. data/ext/krypt/core/kryptcore.so +0 -0
  64. data/ext/krypt/core/mkmf.log +130 -0
  65. data/lib/krypt-core/version.rb +3 -0
  66. data/lib/krypt-core.rb +35 -0
  67. data/lib/kryptcore.so +0 -0
  68. data/spec/README +2 -0
  69. data/test/README +2 -0
  70. data/test/res/certificate.cer +0 -0
  71. data/test/resources.rb +48 -0
  72. data/test/scratch.rb +17 -0
  73. metadata +150 -0
@@ -0,0 +1,592 @@
1
+ /*
2
+ * krypt-core API - C implementation
3
+ *
4
+ * Copyright (c) 2011-2013
5
+ * Hiroshi Nakamura <nahi@ruby-lang.org>
6
+ * Martin Bosslet <martin.bosslet@gmail.com>
7
+ * All rights reserved.
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining
10
+ * a copy of this software and associated documentation files (the
11
+ * "Software"), to deal in the Software without restriction, including
12
+ * without limitation the rights to use, copy, modify, merge, publish,
13
+ * distribute, sublicense, and/or sell copies of the Software, and to
14
+ * permit persons to whom the Software is furnished to do so, subject to
15
+ * the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be
18
+ * included in all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ */
28
+
29
+ #include "krypt-core.h"
30
+ #include "krypt_asn1-internal.h"
31
+
32
+ VALUE cKryptASN1Parser;
33
+ VALUE cKryptASN1Header;
34
+
35
+ typedef struct krypt_asn1_parsed_header_st {
36
+ binyo_instream *in;
37
+ krypt_asn1_header *header;
38
+ VALUE tag;
39
+ VALUE tag_class;
40
+ VALUE constructed;
41
+ VALUE infinite;
42
+ VALUE length;
43
+ VALUE header_length;
44
+ VALUE value;
45
+
46
+ int consumed;
47
+ VALUE cached_stream;
48
+ } krypt_asn1_parsed_header;
49
+
50
+ static void
51
+ int_parsed_header_mark(krypt_asn1_parsed_header *header)
52
+ {
53
+ if (!header) return;
54
+
55
+ binyo_instream_mark(header->in);
56
+
57
+ rb_gc_mark(header->tag);
58
+ rb_gc_mark(header->tag_class);
59
+ rb_gc_mark(header->constructed);
60
+ rb_gc_mark(header->infinite);
61
+ rb_gc_mark(header->length);
62
+ rb_gc_mark(header->header_length);
63
+ if (header->value != Qnil)
64
+ rb_gc_mark(header->value);
65
+ if (header->cached_stream != Qnil)
66
+ rb_gc_mark(header->cached_stream);
67
+ }
68
+
69
+ static void
70
+ int_parsed_header_free(krypt_asn1_parsed_header *header)
71
+ {
72
+ if (!header) return;
73
+
74
+ binyo_instream_free(header->in);
75
+ krypt_asn1_header_free(header->header);
76
+ xfree(header);
77
+ }
78
+
79
+ #define int_asn1_parsed_header_set(klass, obj, header) do { \
80
+ if (!(header)) { \
81
+ rb_raise(eKryptError, "Uninitialized header"); \
82
+ } \
83
+ (obj) = Data_Wrap_Struct((klass), int_parsed_header_mark, int_parsed_header_free, (header)); \
84
+ } while (0)
85
+
86
+ #define int_asn1_parsed_header_get(obj, header) do { \
87
+ Data_Get_Struct((obj), krypt_asn1_parsed_header, (header)); \
88
+ if (!(header)) { \
89
+ rb_raise(eKryptError, "Uninitialized header"); \
90
+ } \
91
+ } while (0)
92
+
93
+ /* Header code */
94
+
95
+ static VALUE
96
+ int_asn1_header_new(binyo_instream *in, krypt_asn1_header *header)
97
+ {
98
+ VALUE obj;
99
+ ID tag_class;
100
+ krypt_asn1_parsed_header *parsed_header;
101
+
102
+ parsed_header = ALLOC(krypt_asn1_parsed_header);
103
+ parsed_header->tag = INT2NUM(header->tag);
104
+ if (!(tag_class = krypt_asn1_tag_class_for_int(header->tag_class))) return Qnil;
105
+ parsed_header->tag_class = ID2SYM(tag_class);
106
+ parsed_header->constructed = header->is_constructed ? Qtrue : Qfalse;
107
+ parsed_header->infinite = header->is_infinite ? Qtrue : Qfalse;
108
+ parsed_header->length = LONG2NUM(header->length);
109
+ parsed_header->header_length = LONG2NUM(header->tag_len + header->length_len);
110
+ parsed_header->in = in;
111
+ parsed_header->header = header;
112
+ parsed_header->value = Qnil;
113
+ parsed_header->consumed = 0;
114
+ parsed_header->cached_stream = Qnil;
115
+
116
+ int_asn1_parsed_header_set(cKryptASN1Header, obj, parsed_header);
117
+ return obj;
118
+ }
119
+
120
+ #define KRYPT_ASN1_HEADER_GET_DEFINE(attr) \
121
+ static VALUE \
122
+ krypt_asn1_header_get_##attr(VALUE self) \
123
+ { \
124
+ krypt_asn1_parsed_header *header; \
125
+ int_asn1_parsed_header_get(self, header); \
126
+ return header->attr; \
127
+ }
128
+
129
+ /**
130
+ * Document-method: Krypt::ASN1::Header#tag
131
+ *
132
+ * call-seq:
133
+ * header.tag -> Number
134
+ *
135
+ * A +Number+ representing the tag of this Header. Never +nil+.
136
+ */
137
+ KRYPT_ASN1_HEADER_GET_DEFINE(tag)
138
+
139
+ /**
140
+ * Document-method: Krypt::ASN1::Header#tag_class
141
+ *
142
+ * call-seq:
143
+ * header.tag_class -> Symbol
144
+ *
145
+ * A +Symbol+ representing the tag class of this Header. Never +nil+.
146
+ * See Krypt::ASN1::ASN1Data for possible values.
147
+ */
148
+ KRYPT_ASN1_HEADER_GET_DEFINE(tag_class)
149
+
150
+ /**
151
+ * Document-method: Krypt::ASN1::Header#constructed?
152
+ *
153
+ * call-seq:
154
+ * header.constructed? -> true or false
155
+ *
156
+ * +true+ if the current Header belongs to a constructed value, +false+
157
+ * otherwise.
158
+ */
159
+ KRYPT_ASN1_HEADER_GET_DEFINE(constructed)
160
+
161
+ /**
162
+ * Document-method: Krypt::ASN1::Header#infinite?
163
+ *
164
+ * call-seq:
165
+ * header.infinite? -> true or false
166
+ *
167
+ * +true+ if the current Header is encoded using infinite length, +false+
168
+ * otherwise. Note that an infinite length-encoded value is automatically
169
+ * constructed, i.e. header.constructed? => header.infinite?
170
+ */
171
+ KRYPT_ASN1_HEADER_GET_DEFINE(infinite)
172
+
173
+ /**
174
+ * Document-method: Krypt::ASN1::Header#length
175
+ *
176
+ * call-seq:
177
+ * header.length -> Number
178
+ *
179
+ * Returns a +Number+ representing the raw byte length of the associated value.
180
+ * It is +0+ is the Header represents an infinite length-encoded value. Never
181
+ * +nil+.
182
+ */
183
+ KRYPT_ASN1_HEADER_GET_DEFINE(length)
184
+
185
+ /**
186
+ * Document-method: Krypt::ASN1::Header#header_length
187
+ *
188
+ * call-seq:
189
+ * header.header_length -> Number
190
+ *
191
+ * Returns the byte size of the raw header encoding. Never +nil+.
192
+ */
193
+ KRYPT_ASN1_HEADER_GET_DEFINE(header_length)
194
+
195
+ /**
196
+ * call-seq:
197
+ * header.encode_to(io) -> self
198
+ *
199
+ * * +io+: an IO-like object supporting IO#write
200
+ *
201
+ * Writes the raw Header encoding to an IO-like object supporting IO#write.
202
+ * May raise Krypt::ASN1::SerializeError if encoding fails.
203
+ */
204
+ static VALUE
205
+ krypt_asn1_header_encode_to(VALUE self, VALUE io)
206
+ {
207
+ krypt_asn1_parsed_header *header;
208
+ binyo_outstream *out;
209
+ int result;
210
+
211
+ int_asn1_parsed_header_get(self, header);
212
+
213
+ if (!(out = binyo_outstream_new_value(io)))
214
+ krypt_error_raise(eKryptASN1SerializeError, "Error while trying to access the stream");
215
+
216
+ result = krypt_asn1_header_encode(out, header->header);
217
+ binyo_outstream_free(out);
218
+ if (result == KRYPT_ERR)
219
+ krypt_error_raise(eKryptASN1SerializeError, "Error while encoding header");
220
+ return self;
221
+ }
222
+
223
+ /**
224
+ * call-seq:
225
+ * header.bytes -> String
226
+ *
227
+ * Returns a +String+ containing the raw byte encoding of this Header.
228
+ */
229
+ static VALUE
230
+ krypt_asn1_header_bytes(VALUE self)
231
+ {
232
+ krypt_asn1_parsed_header *header;
233
+ uint8_t *bytes;
234
+ size_t size;
235
+ binyo_outstream *out;
236
+ VALUE ret;
237
+
238
+ int_asn1_parsed_header_get(self, header);
239
+
240
+ out = binyo_outstream_new_bytes();
241
+ if (krypt_asn1_header_encode(out, header->header) == KRYPT_ERR) {
242
+ binyo_outstream_free(out);
243
+ krypt_error_raise(eKryptASN1SerializeError, "Error while encoding ASN.1 header");
244
+ }
245
+ size = binyo_outstream_bytes_get_bytes_free(out, &bytes);
246
+ ret = rb_str_new((const char *)bytes, size);
247
+ rb_enc_associate(ret, rb_ascii8bit_encoding());
248
+ xfree(bytes);
249
+ return ret;
250
+ }
251
+
252
+ /**
253
+ * call-seq:
254
+ * header.skip_value -> nil
255
+ *
256
+ * Simply moves the "cursor" on the underlying IO forward by skipping over
257
+ * the bytes that represent the value associated with this Header. After
258
+ * having called +skip_value+, the next Header can be parsed from the
259
+ * underlying IO with Parser#next.
260
+ */
261
+ static VALUE
262
+ krypt_asn1_header_skip_value(VALUE self)
263
+ {
264
+ krypt_asn1_parsed_header *header;
265
+
266
+ int_asn1_parsed_header_get(self, header);
267
+ if (krypt_asn1_skip_value(header->in, header->header) == KRYPT_ERR)
268
+ krypt_error_raise(eKryptASN1ParseError, "Skipping the value failed");
269
+ return Qnil;
270
+ }
271
+
272
+ /**
273
+ * call-seq:
274
+ * header.value -> String or nil
275
+ *
276
+ * Returns the raw byte encoding of the associated value. Also moves the
277
+ * "cursor" on the underlying IO forward. After having called value, the
278
+ * next Header can be parsed from the underlying IO with Parser#next.
279
+ * Once read, the value will be cached and subsequent calls to #value will
280
+ * have no effect on the underlying stream.
281
+ *
282
+ * If there is no value (indicated * by Header#length == 0), it returns
283
+ * +nil+.
284
+ *
285
+ * May raise Krypt::ASN1::ParseError if an Instream was already obtained by
286
+ * Header#value_io, because the underlying stream can only be consumed once.
287
+ */
288
+ static VALUE
289
+ krypt_asn1_header_value(VALUE self)
290
+ {
291
+ krypt_asn1_parsed_header *header;
292
+
293
+ int_asn1_parsed_header_get(self, header);
294
+
295
+ if (header->consumed && header->cached_stream != Qnil)
296
+ rb_raise(eKryptASN1ParseError, "The stream has already been consumed");
297
+
298
+ /* TODO: sync */
299
+ if (!header->consumed && header->value == Qnil) {
300
+ uint8_t *value;
301
+ size_t length;
302
+ int tag;
303
+
304
+ if (krypt_asn1_get_value(header->in, header->header, &value, &length) == KRYPT_ERR)
305
+ rb_raise(eKryptASN1ParseError, "Parsing the value failed");
306
+ tag = header->header->tag;
307
+
308
+ if (length != 0 || (tag != TAGS_NULL && tag != TAGS_END_OF_CONTENTS)) {
309
+ header->value = rb_str_new((const char *)value, length);
310
+ rb_enc_associate(header->value, rb_ascii8bit_encoding());
311
+ }
312
+
313
+ header->consumed = 1;
314
+ xfree(value);
315
+ }
316
+
317
+ return header->value;
318
+ }
319
+
320
+ static VALUE
321
+ int_header_cache_stream(binyo_instream *in, krypt_asn1_header *header, int values_only)
322
+ {
323
+ binyo_instream *value_stream;
324
+
325
+ value_stream = krypt_asn1_get_value_stream(in, header, values_only);
326
+ return krypt_instream_adapter_new(value_stream);
327
+ }
328
+
329
+ /**
330
+ * call-seq:
331
+ * header.value_io -> Instream
332
+ *
333
+ * Returns a Krypt::ASN1::Instream that allows consuming the value in
334
+ * streaming manner rather than buffering it in a +String+ and consuming
335
+ * it at once. Note that once an Instream was obtained in this way,
336
+ * all calls to Header#value will raise a ParseError. Subsequent calls
337
+ * to +value_io+ are possible, however, the Instream instance is cached.
338
+ *
339
+ * May raise Krypt::ASN1::ParseError if the associated value was already
340
+ * consumed by a call to Header#value.
341
+ */
342
+ static VALUE
343
+ krypt_asn1_header_value_io(int argc, VALUE *argv, VALUE self)
344
+ {
345
+ krypt_asn1_parsed_header *header;
346
+ VALUE values_only;
347
+
348
+ rb_scan_args(argc, argv, "01", &values_only);
349
+
350
+ int_asn1_parsed_header_get(self, header);
351
+ if (header->consumed && header->cached_stream == Qnil)
352
+ rb_raise(eKryptASN1ParseError, "The stream has already been consumed");
353
+
354
+ /*TODO: synchronization */
355
+ if (header->cached_stream == Qnil) {
356
+ if (NIL_P(values_only))
357
+ values_only = Qtrue;
358
+
359
+ header->consumed = 1;
360
+ header->cached_stream = int_header_cache_stream(header->in,
361
+ header->header,
362
+ values_only == Qtrue);
363
+ }
364
+
365
+ return header->cached_stream;
366
+ }
367
+
368
+ /**
369
+ * call-seq:
370
+ * header.to_s -> String
371
+ *
372
+ * Prints out the information about this Header in a human-readable format
373
+ * without consuming (and therefore also not displaying) the associated
374
+ * value.
375
+ */
376
+ static VALUE
377
+ krypt_asn1_header_to_s(VALUE self)
378
+ {
379
+ VALUE str;
380
+ krypt_asn1_parsed_header *header;
381
+ ID to_s;
382
+
383
+ int_asn1_parsed_header_get(self, header);
384
+ to_s = rb_intern("to_s");
385
+
386
+ str = rb_str_new2("Tag: ");
387
+ rb_str_append(str, rb_funcall(header->tag, to_s, 0));
388
+ rb_str_append(str, rb_str_new2(" Tag Class: "));
389
+ rb_str_append(str, rb_funcall(header->tag_class, to_s, 0));
390
+ rb_str_append(str, rb_str_new2(" Length: "));
391
+ rb_str_append(str, rb_funcall(header->length, to_s, 0));
392
+ rb_str_append(str, rb_str_new2(" Header Length: "));
393
+ rb_str_append(str, rb_funcall(header->header_length, to_s, 0));
394
+ rb_str_append(str, rb_str_new2(" Constructed: "));
395
+ rb_str_append(str, rb_funcall(header->constructed, to_s, 0));
396
+ rb_str_append(str, rb_str_new2(" Infinite Length: "));
397
+ rb_str_append(str, rb_funcall(header->infinite, to_s, 0));
398
+
399
+ return str;
400
+ }
401
+
402
+ /* End Header code */
403
+
404
+ /**
405
+ * call-seq:
406
+ * parser.next(io) -> Header or nil
407
+ *
408
+ * * +io+: an IO-like object supporting IO#read and IO#seek
409
+ * Returns a Header if parsing was successful or nil if the end of the stream
410
+ * has been reached. May raise ParseError in case an error occurred.
411
+ */
412
+ static VALUE
413
+ krypt_asn1_parser_next(VALUE self, VALUE io)
414
+ {
415
+ binyo_instream *in;
416
+ krypt_asn1_header *header;
417
+ int result;
418
+ VALUE ret;
419
+ int type = TYPE(io);
420
+
421
+ if (type == T_STRING)
422
+ rb_raise(rb_eArgError, "Argument for next must respond to read");
423
+
424
+ if (!(in = binyo_instream_new_value(io)))
425
+ rb_raise(rb_eArgError, "Argument for next must respond to read");
426
+
427
+ result = krypt_asn1_next_header(in, &header);
428
+ if (result == KRYPT_ERR) goto error;
429
+ if (result == KRYPT_ASN1_EOF) {
430
+ binyo_instream_free(in);
431
+ return Qnil;
432
+ }
433
+
434
+ ret = int_asn1_header_new(in, header);
435
+ if (NIL_P(ret)) {
436
+ krypt_asn1_header_free(header);
437
+ goto error;
438
+ }
439
+
440
+ return ret;
441
+
442
+ error:
443
+ binyo_instream_free(in);
444
+ rb_raise(eKryptASN1ParseError, "Error while parsing header");
445
+ }
446
+
447
+ /* End Parser code */
448
+
449
+ void
450
+ Init_krypt_asn1_parser(void)
451
+ {
452
+ #if 0
453
+ mKrypt = rb_define_module("Krypt");
454
+ mKryptASN1 = rb_define_module_under(mKrypt, "ASN1"); /* Let RDoc know */
455
+ #endif
456
+
457
+ /**
458
+ * Document-class: Krypt::ASN1::Parser
459
+ *
460
+ * Low-level interface that allows to parse DER-encoded ASN.1 structures
461
+ * in a truly streaming fashion using a "Pull Parser" model. The Pull
462
+ * Parser model for stream-based parsing is considered as more convenient
463
+ * than an event-based model due to its similarity to an equivalent
464
+ * non-streaming parsing model and the fact that the typical callback-based
465
+ * implementation of an event-based model gets complicated very quickly.
466
+ *
467
+ * Pull parsing can be imagined as moving a cursor forward on the stream
468
+ * by deciding to parse a particular token at the current stream position,
469
+ * thus "pulling" stream tokens on demand.
470
+ *
471
+ * The Parser itself is stateless (i.e. can be reused safely on different
472
+ * streams) and operates on any IO-like object that supports IO#read and
473
+ * IO#seek.
474
+ *
475
+ * Calling Parser#next on an IO will attempt to read a DER Header of
476
+ * a DER-encoded object (cf. http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf).
477
+ * DER, the Distinguished Encoding Rules, are an encoding scheme for
478
+ * encoding ASN.1 data structures into a binary format. DER is a TLV
479
+ * (Tag-Length-Value) encoding, that is the Header of a DER-encoded
480
+ * object can be thought of as the combination of Tag and Length.
481
+ *
482
+ * Parser#next may either return a Header, +nil+ if the end of the stream
483
+ * was reached or raise a ParseError if an error occured. Upon succesful
484
+ * parsing of a Header, there are several choices on how to proceed.
485
+ *
486
+ * If the current Header represents a primitive value (may be detected
487
+ * by verifying that Header#constructed? is +false+), the ways to move
488
+ * forward on the stream are:
489
+ * * skipping over the Header's value (the V in TLV) using
490
+ * Header#skip_value
491
+ * * reading the value in one pass using Header#value
492
+ * * obtaining an Instream of the value in order to read it
493
+ * streaming-based using Header#value_io
494
+ * Please note that immediately calling Parser#next on a stream from
495
+ * whom a Header of a primitive value was just read will fail because
496
+ * the only option to proceed in that case is either skipping or
497
+ * consuming the value first. For more details on primitive values,
498
+ * please have a look at Krypt::ASN1::Primitive.
499
+ *
500
+ * If the current Header represents a constructed value
501
+ * (Header#constructed? is +true+), there is another option still.
502
+ * First, you may interpret the value of the constructed value in
503
+ * its entirety, using the methods described above for primitive
504
+ * values. The value in such a case represents the raw encoding of the
505
+ * <b>entire</b> sequence of inner elements of that constructed encoding.
506
+ * For example, for a SEQUENCE with n elements, the value will be the
507
+ * concatenated encodings of every single of the n elements in successive
508
+ * order. But, if you wish to parse the inner elements, too, there is the
509
+ * additional option of parsing another Header immediately, effectively
510
+ * "descending" into the nested structure. Similarly to how constructed
511
+ * values can be nested, one can recursively descend with Parser into
512
+ * these nested structures by parsing another Header with Parser#next
513
+ * instead of merely consuming the constructed Header's value. This is
514
+ * best illustrated using an
515
+ *
516
+ * === Example: Reading all objects contained within a constructed DER
517
+ * io = # IO representing a DER-encoded ASN.1 structure
518
+ * parser = Krypt::ASN1::Parser.new
519
+ * while header = parser.next do
520
+ * unless header.constructed?
521
+ * # Primitive -> consume/skip value
522
+ * value = header.value
523
+ * # Process value
524
+ * end
525
+ * # Constructed -> parse another Header immediately
526
+ * end
527
+ *
528
+ * in contrast to
529
+ *
530
+ * === Example: Reading the entire value of a constructed DER at once
531
+ * io = # IO representing a DER-encoded ASN.1 structure
532
+ * parser = Krypt::ASN1::Parser.new
533
+ * header = parser.next
534
+ * value = header.value # Reads the entire encodings of the nested elements
535
+ * puts parser.next == nil # -> true, since the header and value of the
536
+ * outmost constructed value is the entire
537
+ * content of the stream
538
+ *
539
+ * More details on constructed values can be found in the documentation
540
+ * of Krypt::ASN1::Constructive.
541
+ */
542
+ cKryptASN1Parser = rb_define_class_under(mKryptASN1, "Parser", rb_cObject);
543
+ rb_define_method(cKryptASN1Parser, "next", krypt_asn1_parser_next, 1);
544
+
545
+ /**
546
+ * Document-class: Krypt::ASN1::Header
547
+ *
548
+ * These are the tokens returned by Parser#next and cannot be instantiated
549
+ * on their own. A Header represents the Tag and Length part of a TLV
550
+ * (Tag-Length-Value) DER encoding, and it also allows to move the "cursor"
551
+ * on an IO forward by consuming or skipping the associated value (the V).
552
+ *
553
+ * The Header itself contains tag and length information of what was just
554
+ * parsed:
555
+ *
556
+ * * tag number (Header#tag)
557
+ * * tag class (Header#tag_class)
558
+ * * whether the header is constructed or not (Header#constructed?)
559
+ * * whether it is an infinite length value or not (Header#infinite?)
560
+ * * the length in bytes of the associated value (Header#length/size)
561
+ * * the length of the raw Header encoding (Header#header_length/header_size)
562
+ *
563
+ * In addition, there are three ways to consume the value that is associated
564
+ * with a Header:
565
+ *
566
+ * * by skipping it (Header#skip_value)
567
+ * * by reading the value in one single pass (Header#value)
568
+ * * or by obtaining an Instream of the value bytes so that it can be consumed
569
+ * in a streaming fashion (Header#value_io)
570
+ *
571
+ * Access to the raw encoding of the Header is given by either retrieving
572
+ * a String containing the encoding with Header#bytes or by encoding it to
573
+ * an IO-like object supporting IO#write using Header#encode_to.
574
+ */
575
+ cKryptASN1Header = rb_define_class_under(mKryptASN1, "Header", rb_cObject);
576
+ rb_define_method(cKryptASN1Header, "tag", krypt_asn1_header_get_tag, 0);
577
+ rb_define_method(cKryptASN1Header, "tag_class", krypt_asn1_header_get_tag_class, 0);
578
+ rb_define_method(cKryptASN1Header, "constructed?", krypt_asn1_header_get_constructed, 0);
579
+ rb_define_method(cKryptASN1Header, "infinite?", krypt_asn1_header_get_infinite, 0);
580
+ rb_define_method(cKryptASN1Header, "length", krypt_asn1_header_get_length, 0);
581
+ rb_define_alias(cKryptASN1Header, "size", "length");
582
+ rb_define_method(cKryptASN1Header, "header_length", krypt_asn1_header_get_header_length, 0);
583
+ rb_define_alias(cKryptASN1Header, "header_size", "header_length");
584
+ rb_define_method(cKryptASN1Header, "encode_to", krypt_asn1_header_encode_to, 1);
585
+ rb_define_method(cKryptASN1Header, "bytes", krypt_asn1_header_bytes, 0);
586
+ rb_define_method(cKryptASN1Header, "skip_value", krypt_asn1_header_skip_value, 0);
587
+ rb_define_method(cKryptASN1Header, "value", krypt_asn1_header_value, 0);
588
+ rb_define_method(cKryptASN1Header, "value_io", krypt_asn1_header_value_io, -1);
589
+ rb_define_method(cKryptASN1Header, "to_s", krypt_asn1_header_to_s, 0);
590
+ rb_undef_method(CLASS_OF(cKryptASN1Header), "new"); /* private constructor */
591
+ }
592
+
Binary file