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.
- 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
|