1 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
2 | * All rights reserved. |
3 | * |
4 | * This package is an SSL implementation written |
5 | * by Eric Young (eay@cryptsoft.com). |
6 | * The implementation was written so as to conform with Netscapes SSL. |
7 | * |
8 | * This library is free for commercial and non-commercial use as long as |
9 | * the following conditions are aheared to. The following conditions |
10 | * apply to all code found in this distribution, be it the RC4, RSA, |
11 | * lhash, DES, etc., code; not just the SSL code. The SSL documentation |
12 | * included with this distribution is covered by the same copyright terms |
13 | * except that the holder is Tim Hudson (tjh@cryptsoft.com). |
14 | * |
15 | * Copyright remains Eric Young's, and as such any Copyright notices in |
16 | * the code are not to be removed. |
17 | * If this package is used in a product, Eric Young should be given attribution |
18 | * as the author of the parts of the library used. |
19 | * This can be in the form of a textual message at program startup or |
20 | * in documentation (online or textual) provided with the package. |
21 | * |
22 | * Redistribution and use in source and binary forms, with or without |
23 | * modification, are permitted provided that the following conditions |
24 | * are met: |
25 | * 1. Redistributions of source code must retain the copyright |
26 | * notice, this list of conditions and the following disclaimer. |
27 | * 2. Redistributions in binary form must reproduce the above copyright |
28 | * notice, this list of conditions and the following disclaimer in the |
29 | * documentation and/or other materials provided with the distribution. |
30 | * 3. All advertising materials mentioning features or use of this software |
31 | * must display the following acknowledgement: |
32 | * "This product includes cryptographic software written by |
33 | * Eric Young (eay@cryptsoft.com)" |
34 | * The word 'cryptographic' can be left out if the rouines from the library |
35 | * being used are not cryptographic related :-). |
36 | * 4. If you include any Windows specific code (or a derivative thereof) from |
37 | * the apps directory (application code) you must include an acknowledgement: |
38 | * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" |
39 | * |
40 | * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND |
41 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
42 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
43 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
44 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
45 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
46 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
47 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
48 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
49 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
50 | * SUCH DAMAGE. |
51 | * |
52 | * The licence and distribution terms for any publically available version or |
53 | * derivative of this code cannot be changed. i.e. this code cannot simply be |
54 | * copied and put under another distribution licence |
55 | * [including the GNU Public Licence.] */ |
56 | |
57 | #include <ctype.h> |
58 | #include <string.h> |
59 | |
60 | #include <openssl/asn1.h> |
61 | #include <openssl/asn1t.h> |
62 | #include <openssl/buf.h> |
63 | #include <openssl/err.h> |
64 | #include <openssl/mem.h> |
65 | #include <openssl/obj.h> |
66 | #include <openssl/stack.h> |
67 | #include <openssl/x509.h> |
68 | |
69 | #include "../asn1/asn1_locl.h" |
70 | #include "../internal.h" |
71 | |
72 | |
73 | typedef STACK_OF(X509_NAME_ENTRY) STACK_OF_X509_NAME_ENTRY; |
74 | DEFINE_STACK_OF(STACK_OF_X509_NAME_ENTRY) |
75 | |
76 | /* |
77 | * Maximum length of X509_NAME: much larger than anything we should |
78 | * ever see in practice. |
79 | */ |
80 | |
81 | #define X509_NAME_MAX (1024 * 1024) |
82 | |
83 | static int x509_name_ex_d2i(ASN1_VALUE **val, |
84 | const unsigned char **in, long len, |
85 | const ASN1_ITEM *it, |
86 | int tag, int aclass, char opt, ASN1_TLC *ctx); |
87 | |
88 | static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out, |
89 | const ASN1_ITEM *it, int tag, int aclass); |
90 | static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it); |
91 | static void x509_name_ex_free(ASN1_VALUE **val, const ASN1_ITEM *it); |
92 | |
93 | static int x509_name_encode(X509_NAME *a); |
94 | static int x509_name_canon(X509_NAME *a); |
95 | static int asn1_string_canon(ASN1_STRING *out, ASN1_STRING *in); |
96 | static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) * intname, |
97 | unsigned char **in); |
98 | |
99 | ASN1_SEQUENCE(X509_NAME_ENTRY) = { |
100 | ASN1_SIMPLE(X509_NAME_ENTRY, object, ASN1_OBJECT), |
101 | ASN1_SIMPLE(X509_NAME_ENTRY, value, ASN1_PRINTABLE) |
102 | } ASN1_SEQUENCE_END(X509_NAME_ENTRY) |
103 | |
104 | IMPLEMENT_ASN1_FUNCTIONS(X509_NAME_ENTRY) |
105 | IMPLEMENT_ASN1_DUP_FUNCTION(X509_NAME_ENTRY) |
106 | |
107 | /* |
108 | * For the "Name" type we need a SEQUENCE OF { SET OF X509_NAME_ENTRY } so |
109 | * declare two template wrappers for this |
110 | */ |
111 | |
112 | ASN1_ITEM_TEMPLATE(X509_NAME_ENTRIES) = |
113 | ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_OF, 0, RDNS, X509_NAME_ENTRY) |
114 | ASN1_ITEM_TEMPLATE_END(X509_NAME_ENTRIES) |
115 | |
116 | ASN1_ITEM_TEMPLATE(X509_NAME_INTERNAL) = |
117 | ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Name, X509_NAME_ENTRIES) |
118 | ASN1_ITEM_TEMPLATE_END(X509_NAME_INTERNAL) |
119 | |
120 | /* |
121 | * Normally that's where it would end: we'd have two nested STACK structures |
122 | * representing the ASN1. Unfortunately X509_NAME uses a completely different |
123 | * form and caches encodings so we have to process the internal form and |
124 | * convert to the external form. |
125 | */ |
126 | |
127 | static const ASN1_EXTERN_FUNCS x509_name_ff = { |
128 | NULL, |
129 | x509_name_ex_new, |
130 | x509_name_ex_free, |
131 | 0, /* Default clear behaviour is OK */ |
132 | x509_name_ex_d2i, |
133 | x509_name_ex_i2d, |
134 | NULL, |
135 | }; |
136 | |
137 | IMPLEMENT_EXTERN_ASN1(X509_NAME, V_ASN1_SEQUENCE, x509_name_ff) |
138 | |
139 | IMPLEMENT_ASN1_FUNCTIONS(X509_NAME) |
140 | |
141 | IMPLEMENT_ASN1_DUP_FUNCTION(X509_NAME) |
142 | |
143 | static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it) |
144 | { |
145 | X509_NAME *ret = NULL; |
146 | ret = OPENSSL_malloc(sizeof(X509_NAME)); |
147 | if (!ret) |
148 | goto memerr; |
149 | if ((ret->entries = sk_X509_NAME_ENTRY_new_null()) == NULL) |
150 | goto memerr; |
151 | if ((ret->bytes = BUF_MEM_new()) == NULL) |
152 | goto memerr; |
153 | ret->canon_enc = NULL; |
154 | ret->canon_enclen = 0; |
155 | ret->modified = 1; |
156 | *val = (ASN1_VALUE *)ret; |
157 | return 1; |
158 | |
159 | memerr: |
160 | OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE); |
161 | if (ret) { |
162 | if (ret->entries) |
163 | sk_X509_NAME_ENTRY_free(ret->entries); |
164 | OPENSSL_free(ret); |
165 | } |
166 | return 0; |
167 | } |
168 | |
169 | static void x509_name_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it) |
170 | { |
171 | X509_NAME *a; |
172 | if (!pval || !*pval) |
173 | return; |
174 | a = (X509_NAME *)*pval; |
175 | |
176 | BUF_MEM_free(a->bytes); |
177 | sk_X509_NAME_ENTRY_pop_free(a->entries, X509_NAME_ENTRY_free); |
178 | if (a->canon_enc) |
179 | OPENSSL_free(a->canon_enc); |
180 | OPENSSL_free(a); |
181 | *pval = NULL; |
182 | } |
183 | |
184 | static void local_sk_X509_NAME_ENTRY_free(STACK_OF(X509_NAME_ENTRY) *ne) |
185 | { |
186 | sk_X509_NAME_ENTRY_free(ne); |
187 | } |
188 | |
189 | static void local_sk_X509_NAME_ENTRY_pop_free(STACK_OF(X509_NAME_ENTRY) *ne) |
190 | { |
191 | sk_X509_NAME_ENTRY_pop_free(ne, X509_NAME_ENTRY_free); |
192 | } |
193 | |
194 | static int x509_name_ex_d2i(ASN1_VALUE **val, |
195 | const unsigned char **in, long len, |
196 | const ASN1_ITEM *it, int tag, int aclass, |
197 | char opt, ASN1_TLC *ctx) |
198 | { |
199 | const unsigned char *p = *in, *q; |
200 | union { |
201 | STACK_OF(STACK_OF_X509_NAME_ENTRY) *s; |
202 | ASN1_VALUE *a; |
203 | } intname = { |
204 | NULL |
205 | }; |
206 | union { |
207 | X509_NAME *x; |
208 | ASN1_VALUE *a; |
209 | } nm = { |
210 | NULL |
211 | }; |
212 | size_t i, j; |
213 | int ret; |
214 | STACK_OF(X509_NAME_ENTRY) *entries; |
215 | X509_NAME_ENTRY *entry; |
216 | /* Bound the size of an X509_NAME we are willing to parse. */ |
217 | if (len > X509_NAME_MAX) { |
218 | len = X509_NAME_MAX; |
219 | } |
220 | q = p; |
221 | |
222 | /* Get internal representation of Name */ |
223 | ret = ASN1_item_ex_d2i(&intname.a, |
224 | &p, len, ASN1_ITEM_rptr(X509_NAME_INTERNAL), |
225 | tag, aclass, opt, ctx); |
226 | |
227 | if (ret <= 0) |
228 | return ret; |
229 | |
230 | if (*val) |
231 | x509_name_ex_free(val, NULL); |
232 | if (!x509_name_ex_new(&nm.a, NULL)) |
233 | goto err; |
234 | /* We've decoded it: now cache encoding */ |
235 | if (!BUF_MEM_grow(nm.x->bytes, p - q)) |
236 | goto err; |
237 | OPENSSL_memcpy(nm.x->bytes->data, q, p - q); |
238 | |
239 | /* Convert internal representation to X509_NAME structure */ |
240 | for (i = 0; i < sk_STACK_OF_X509_NAME_ENTRY_num(intname.s); i++) { |
241 | entries = sk_STACK_OF_X509_NAME_ENTRY_value(intname.s, i); |
242 | for (j = 0; j < sk_X509_NAME_ENTRY_num(entries); j++) { |
243 | entry = sk_X509_NAME_ENTRY_value(entries, j); |
244 | entry->set = i; |
245 | if (!sk_X509_NAME_ENTRY_push(nm.x->entries, entry)) |
246 | goto err; |
247 | (void)sk_X509_NAME_ENTRY_set(entries, j, NULL); |
248 | } |
249 | } |
250 | ret = x509_name_canon(nm.x); |
251 | if (!ret) |
252 | goto err; |
253 | sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, |
254 | local_sk_X509_NAME_ENTRY_free); |
255 | nm.x->modified = 0; |
256 | *val = nm.a; |
257 | *in = p; |
258 | return ret; |
259 | err: |
260 | if (nm.x != NULL) |
261 | X509_NAME_free(nm.x); |
262 | sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, |
263 | local_sk_X509_NAME_ENTRY_pop_free); |
264 | OPENSSL_PUT_ERROR(X509, ERR_R_ASN1_LIB); |
265 | return 0; |
266 | } |
267 | |
268 | static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out, |
269 | const ASN1_ITEM *it, int tag, int aclass) |
270 | { |
271 | int ret; |
272 | X509_NAME *a = (X509_NAME *)*val; |
273 | if (a->modified) { |
274 | ret = x509_name_encode(a); |
275 | if (ret < 0) |
276 | return ret; |
277 | ret = x509_name_canon(a); |
278 | if (ret < 0) |
279 | return ret; |
280 | } |
281 | ret = a->bytes->length; |
282 | if (out != NULL) { |
283 | OPENSSL_memcpy(*out, a->bytes->data, ret); |
284 | *out += ret; |
285 | } |
286 | return ret; |
287 | } |
288 | |
289 | static int x509_name_encode(X509_NAME *a) |
290 | { |
291 | union { |
292 | STACK_OF(STACK_OF_X509_NAME_ENTRY) *s; |
293 | ASN1_VALUE *a; |
294 | } intname = { |
295 | NULL |
296 | }; |
297 | int len; |
298 | unsigned char *p; |
299 | STACK_OF(X509_NAME_ENTRY) *entries = NULL; |
300 | X509_NAME_ENTRY *entry; |
301 | int set = -1; |
302 | size_t i; |
303 | intname.s = sk_STACK_OF_X509_NAME_ENTRY_new_null(); |
304 | if (!intname.s) |
305 | goto memerr; |
306 | for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) { |
307 | entry = sk_X509_NAME_ENTRY_value(a->entries, i); |
308 | if (entry->set != set) { |
309 | entries = sk_X509_NAME_ENTRY_new_null(); |
310 | if (!entries) |
311 | goto memerr; |
312 | if (!sk_STACK_OF_X509_NAME_ENTRY_push(intname.s, entries)) { |
313 | sk_X509_NAME_ENTRY_free(entries); |
314 | goto memerr; |
315 | } |
316 | set = entry->set; |
317 | } |
318 | if (!sk_X509_NAME_ENTRY_push(entries, entry)) |
319 | goto memerr; |
320 | } |
321 | len = ASN1_item_ex_i2d(&intname.a, NULL, |
322 | ASN1_ITEM_rptr(X509_NAME_INTERNAL), -1, -1); |
323 | if (!BUF_MEM_grow(a->bytes, len)) |
324 | goto memerr; |
325 | p = (unsigned char *)a->bytes->data; |
326 | ASN1_item_ex_i2d(&intname.a, |
327 | &p, ASN1_ITEM_rptr(X509_NAME_INTERNAL), -1, -1); |
328 | sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, |
329 | local_sk_X509_NAME_ENTRY_free); |
330 | a->modified = 0; |
331 | return len; |
332 | memerr: |
333 | sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, |
334 | local_sk_X509_NAME_ENTRY_free); |
335 | OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE); |
336 | return -1; |
337 | } |
338 | |
339 | /* |
340 | * This function generates the canonical encoding of the Name structure. In |
341 | * it all strings are converted to UTF8, leading, trailing and multiple |
342 | * spaces collapsed, converted to lower case and the leading SEQUENCE header |
343 | * removed. In future we could also normalize the UTF8 too. By doing this |
344 | * comparison of Name structures can be rapidly perfomed by just using |
345 | * OPENSSL_memcmp() of the canonical encoding. By omitting the leading SEQUENCE name |
346 | * constraints of type dirName can also be checked with a simple OPENSSL_memcmp(). |
347 | */ |
348 | |
349 | static int x509_name_canon(X509_NAME *a) |
350 | { |
351 | unsigned char *p; |
352 | STACK_OF(STACK_OF_X509_NAME_ENTRY) *intname = NULL; |
353 | STACK_OF(X509_NAME_ENTRY) *entries = NULL; |
354 | X509_NAME_ENTRY *entry, *tmpentry = NULL; |
355 | int set = -1, ret = 0, len; |
356 | size_t i; |
357 | |
358 | if (a->canon_enc) { |
359 | OPENSSL_free(a->canon_enc); |
360 | a->canon_enc = NULL; |
361 | } |
362 | /* Special case: empty X509_NAME => null encoding */ |
363 | if (sk_X509_NAME_ENTRY_num(a->entries) == 0) { |
364 | a->canon_enclen = 0; |
365 | return 1; |
366 | } |
367 | intname = sk_STACK_OF_X509_NAME_ENTRY_new_null(); |
368 | if (!intname) |
369 | goto err; |
370 | for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) { |
371 | entry = sk_X509_NAME_ENTRY_value(a->entries, i); |
372 | if (entry->set != set) { |
373 | entries = sk_X509_NAME_ENTRY_new_null(); |
374 | if (!entries) |
375 | goto err; |
376 | if (!sk_STACK_OF_X509_NAME_ENTRY_push(intname, entries)) { |
377 | sk_X509_NAME_ENTRY_free(entries); |
378 | goto err; |
379 | } |
380 | set = entry->set; |
381 | } |
382 | tmpentry = X509_NAME_ENTRY_new(); |
383 | if (tmpentry == NULL) |
384 | goto err; |
385 | tmpentry->object = OBJ_dup(entry->object); |
386 | if (!asn1_string_canon(tmpentry->value, entry->value)) |
387 | goto err; |
388 | if (!sk_X509_NAME_ENTRY_push(entries, tmpentry)) |
389 | goto err; |
390 | tmpentry = NULL; |
391 | } |
392 | |
393 | /* Finally generate encoding */ |
394 | |
395 | len = i2d_name_canon(intname, NULL); |
396 | if (len < 0) { |
397 | goto err; |
398 | } |
399 | a->canon_enclen = len; |
400 | |
401 | p = OPENSSL_malloc(a->canon_enclen); |
402 | |
403 | if (!p) |
404 | goto err; |
405 | |
406 | a->canon_enc = p; |
407 | |
408 | i2d_name_canon(intname, &p); |
409 | |
410 | ret = 1; |
411 | |
412 | err: |
413 | |
414 | if (tmpentry) |
415 | X509_NAME_ENTRY_free(tmpentry); |
416 | if (intname) |
417 | sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname, |
418 | local_sk_X509_NAME_ENTRY_pop_free); |
419 | return ret; |
420 | } |
421 | |
422 | /* Bitmap of all the types of string that will be canonicalized. */ |
423 | |
424 | #define ASN1_MASK_CANON \ |
425 | (B_ASN1_UTF8STRING | B_ASN1_BMPSTRING | B_ASN1_UNIVERSALSTRING \ |
426 | | B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_IA5STRING \ |
427 | | B_ASN1_VISIBLESTRING) |
428 | |
429 | static int asn1_string_canon(ASN1_STRING *out, ASN1_STRING *in) |
430 | { |
431 | unsigned char *to, *from; |
432 | int len, i; |
433 | |
434 | /* If type not in bitmask just copy string across */ |
435 | if (!(ASN1_tag2bit(in->type) & ASN1_MASK_CANON)) { |
436 | if (!ASN1_STRING_copy(out, in)) |
437 | return 0; |
438 | return 1; |
439 | } |
440 | |
441 | out->type = V_ASN1_UTF8STRING; |
442 | out->length = ASN1_STRING_to_UTF8(&out->data, in); |
443 | if (out->length == -1) |
444 | return 0; |
445 | |
446 | to = out->data; |
447 | from = to; |
448 | |
449 | len = out->length; |
450 | |
451 | /* |
452 | * Convert string in place to canonical form. Ultimately we may need to |
453 | * handle a wider range of characters but for now ignore anything with |
454 | * MSB set and rely on the isspace() and tolower() functions. |
455 | */ |
456 | |
457 | /* Ignore leading spaces */ |
458 | while ((len > 0) && !(*from & 0x80) && isspace(*from)) { |
459 | from++; |
460 | len--; |
461 | } |
462 | |
463 | to = from + len; |
464 | |
465 | /* Ignore trailing spaces */ |
466 | while ((len > 0) && !(to[-1] & 0x80) && isspace(to[-1])) { |
467 | to--; |
468 | len--; |
469 | } |
470 | |
471 | to = out->data; |
472 | |
473 | i = 0; |
474 | while (i < len) { |
475 | /* If MSB set just copy across */ |
476 | if (*from & 0x80) { |
477 | *to++ = *from++; |
478 | i++; |
479 | } |
480 | /* Collapse multiple spaces */ |
481 | else if (isspace(*from)) { |
482 | /* Copy one space across */ |
483 | *to++ = ' '; |
484 | /* |
485 | * Ignore subsequent spaces. Note: don't need to check len here |
486 | * because we know the last character is a non-space so we can't |
487 | * overflow. |
488 | */ |
489 | do { |
490 | from++; |
491 | i++; |
492 | } |
493 | while (!(*from & 0x80) && isspace(*from)); |
494 | } else { |
495 | *to++ = OPENSSL_tolower(*from); |
496 | from++; |
497 | i++; |
498 | } |
499 | } |
500 | |
501 | out->length = to - out->data; |
502 | |
503 | return 1; |
504 | |
505 | } |
506 | |
507 | static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) * _intname, |
508 | unsigned char **in) |
509 | { |
510 | int len, ltmp; |
511 | size_t i; |
512 | ASN1_VALUE *v; |
513 | STACK_OF(ASN1_VALUE) *intname = (STACK_OF(ASN1_VALUE) *)_intname; |
514 | |
515 | len = 0; |
516 | for (i = 0; i < sk_ASN1_VALUE_num(intname); i++) { |
517 | v = sk_ASN1_VALUE_value(intname, i); |
518 | ltmp = ASN1_item_ex_i2d(&v, in, |
519 | ASN1_ITEM_rptr(X509_NAME_ENTRIES), -1, -1); |
520 | if (ltmp < 0) |
521 | return ltmp; |
522 | len += ltmp; |
523 | } |
524 | return len; |
525 | } |
526 | |
527 | int X509_NAME_set(X509_NAME **xn, X509_NAME *name) |
528 | { |
529 | if ((name = X509_NAME_dup(name)) == NULL) |
530 | return 0; |
531 | X509_NAME_free(*xn); |
532 | *xn = name; |
533 | return 1; |
534 | } |
535 | |
536 | IMPLEMENT_ASN1_SET_OF(X509_NAME_ENTRY) |
537 | |
538 | int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne) |
539 | { |
540 | return ne->set; |
541 | } |
542 | |
543 | int X509_NAME_get0_der(X509_NAME *nm, const unsigned char **pder, |
544 | size_t *pderlen) |
545 | { |
546 | /* Make sure encoding is valid */ |
547 | if (i2d_X509_NAME(nm, NULL) <= 0) |
548 | return 0; |
549 | if (pder != NULL) |
550 | *pder = (unsigned char *)nm->bytes->data; |
551 | if (pderlen != NULL) |
552 | *pderlen = nm->bytes->length; |
553 | return 1; |
554 | } |
555 | |