krypt-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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