krypt-core 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/ext/krypt/core/Makefile +221 -0
- data/ext/krypt/core/binyo-error.h +40 -0
- data/ext/krypt/core/binyo-io-buffer.h +54 -0
- data/ext/krypt/core/binyo-io.h +131 -0
- data/ext/krypt/core/extconf.h +8 -0
- data/ext/krypt/core/extconf.rb +80 -0
- data/ext/krypt/core/krypt-core.c +110 -0
- data/ext/krypt/core/krypt-core.h +97 -0
- data/ext/krypt/core/krypt-core.o +0 -0
- data/ext/krypt/core/krypt-provider.h +86 -0
- data/ext/krypt/core/krypt_asn1-internal.c +681 -0
- data/ext/krypt/core/krypt_asn1-internal.h +117 -0
- data/ext/krypt/core/krypt_asn1-internal.o +0 -0
- data/ext/krypt/core/krypt_asn1.c +2109 -0
- data/ext/krypt/core/krypt_asn1.h +88 -0
- data/ext/krypt/core/krypt_asn1.o +0 -0
- data/ext/krypt/core/krypt_asn1_codec.c +973 -0
- data/ext/krypt/core/krypt_asn1_codec.o +0 -0
- data/ext/krypt/core/krypt_asn1_in_adapter.c +178 -0
- data/ext/krypt/core/krypt_asn1_in_adapter.o +0 -0
- data/ext/krypt/core/krypt_asn1_in_chunked.c +292 -0
- data/ext/krypt/core/krypt_asn1_in_chunked.o +0 -0
- data/ext/krypt/core/krypt_asn1_in_definite.c +156 -0
- data/ext/krypt/core/krypt_asn1_in_definite.o +0 -0
- data/ext/krypt/core/krypt_asn1_parser.c +592 -0
- data/ext/krypt/core/krypt_asn1_parser.o +0 -0
- data/ext/krypt/core/krypt_asn1_template-internal.h +185 -0
- data/ext/krypt/core/krypt_asn1_template.c +459 -0
- data/ext/krypt/core/krypt_asn1_template.h +56 -0
- data/ext/krypt/core/krypt_asn1_template.o +0 -0
- data/ext/krypt/core/krypt_asn1_template_encoder.c +76 -0
- data/ext/krypt/core/krypt_asn1_template_encoder.o +0 -0
- data/ext/krypt/core/krypt_asn1_template_parser.c +1176 -0
- data/ext/krypt/core/krypt_asn1_template_parser.o +0 -0
- data/ext/krypt/core/krypt_b64-internal.h +38 -0
- data/ext/krypt/core/krypt_b64.c +391 -0
- data/ext/krypt/core/krypt_b64.h +41 -0
- data/ext/krypt/core/krypt_b64.o +0 -0
- data/ext/krypt/core/krypt_digest.c +391 -0
- data/ext/krypt/core/krypt_digest.h +51 -0
- data/ext/krypt/core/krypt_digest.o +0 -0
- data/ext/krypt/core/krypt_error.c +221 -0
- data/ext/krypt/core/krypt_error.h +46 -0
- data/ext/krypt/core/krypt_error.o +0 -0
- data/ext/krypt/core/krypt_hex-internal.h +36 -0
- data/ext/krypt/core/krypt_hex.c +255 -0
- data/ext/krypt/core/krypt_hex.h +41 -0
- data/ext/krypt/core/krypt_hex.o +0 -0
- data/ext/krypt/core/krypt_io.c +65 -0
- data/ext/krypt/core/krypt_io.h +56 -0
- data/ext/krypt/core/krypt_io.o +0 -0
- data/ext/krypt/core/krypt_io_in_pem.c +397 -0
- data/ext/krypt/core/krypt_io_in_pem.o +0 -0
- data/ext/krypt/core/krypt_missing.c +238 -0
- data/ext/krypt/core/krypt_missing.h +62 -0
- data/ext/krypt/core/krypt_missing.o +0 -0
- data/ext/krypt/core/krypt_pem.c +171 -0
- data/ext/krypt/core/krypt_pem.o +0 -0
- data/ext/krypt/core/krypt_provider-internal.h +40 -0
- data/ext/krypt/core/krypt_provider.c +136 -0
- data/ext/krypt/core/krypt_provider.o +0 -0
- data/ext/krypt/core/kryptcore.so +0 -0
- data/ext/krypt/core/mkmf.log +130 -0
- data/lib/krypt-core/version.rb +3 -0
- data/lib/krypt-core.rb +35 -0
- data/lib/kryptcore.so +0 -0
- data/spec/README +2 -0
- data/test/README +2 -0
- data/test/res/certificate.cer +0 -0
- data/test/resources.rb +48 -0
- data/test/scratch.rb +17 -0
- 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
|