Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | * Support for ENcoding ASN.1 data based on BER/DER (Basic/Distinguished |
michael@0 | 7 | * Encoding Rules). |
michael@0 | 8 | */ |
michael@0 | 9 | |
michael@0 | 10 | #include "secasn1.h" |
michael@0 | 11 | |
michael@0 | 12 | typedef enum { |
michael@0 | 13 | beforeHeader, |
michael@0 | 14 | duringContents, |
michael@0 | 15 | duringGroup, |
michael@0 | 16 | duringSequence, |
michael@0 | 17 | afterContents, |
michael@0 | 18 | afterImplicit, |
michael@0 | 19 | afterInline, |
michael@0 | 20 | afterPointer, |
michael@0 | 21 | afterChoice, |
michael@0 | 22 | notInUse |
michael@0 | 23 | } sec_asn1e_parse_place; |
michael@0 | 24 | |
michael@0 | 25 | typedef enum { |
michael@0 | 26 | allDone, |
michael@0 | 27 | encodeError, |
michael@0 | 28 | keepGoing, |
michael@0 | 29 | needBytes |
michael@0 | 30 | } sec_asn1e_parse_status; |
michael@0 | 31 | |
michael@0 | 32 | typedef enum { |
michael@0 | 33 | hdr_normal = 0, /* encode header normally */ |
michael@0 | 34 | hdr_any = 1, /* header already encoded in content */ |
michael@0 | 35 | hdr_decoder = 2, /* template only used by decoder. skip it. */ |
michael@0 | 36 | hdr_optional = 3, /* optional component, to be omitted */ |
michael@0 | 37 | hdr_placeholder = 4 /* place holder for from_buf content */ |
michael@0 | 38 | } sec_asn1e_hdr_encoding; |
michael@0 | 39 | |
michael@0 | 40 | typedef struct sec_asn1e_state_struct { |
michael@0 | 41 | SEC_ASN1EncoderContext *top; |
michael@0 | 42 | const SEC_ASN1Template *theTemplate; |
michael@0 | 43 | void *src; |
michael@0 | 44 | |
michael@0 | 45 | struct sec_asn1e_state_struct *parent; /* aka prev */ |
michael@0 | 46 | struct sec_asn1e_state_struct *child; /* aka next */ |
michael@0 | 47 | |
michael@0 | 48 | sec_asn1e_parse_place place; /* where we are in encoding process */ |
michael@0 | 49 | |
michael@0 | 50 | /* |
michael@0 | 51 | * XXX explain the next fields as clearly as possible... |
michael@0 | 52 | */ |
michael@0 | 53 | unsigned char tag_modifiers; |
michael@0 | 54 | unsigned char tag_number; |
michael@0 | 55 | unsigned long underlying_kind; |
michael@0 | 56 | |
michael@0 | 57 | int depth; |
michael@0 | 58 | |
michael@0 | 59 | PRBool isExplicit, /* we are handling an isExplicit header */ |
michael@0 | 60 | indefinite, /* need end-of-contents */ |
michael@0 | 61 | is_string, /* encoding a simple string or an ANY */ |
michael@0 | 62 | may_stream, /* when streaming, do indefinite encoding */ |
michael@0 | 63 | optional, /* omit field if it has no contents */ |
michael@0 | 64 | disallowStreaming; /* disallow streaming in all sub-templates */ |
michael@0 | 65 | } sec_asn1e_state; |
michael@0 | 66 | |
michael@0 | 67 | /* |
michael@0 | 68 | * An "outsider" will have an opaque pointer to this, created by calling |
michael@0 | 69 | * SEC_ASN1EncoderStart(). It will be passed back in to all subsequent |
michael@0 | 70 | * calls to SEC_ASN1EncoderUpdate() and related routines, and when done |
michael@0 | 71 | * it is passed to SEC_ASN1EncoderFinish(). |
michael@0 | 72 | */ |
michael@0 | 73 | struct sec_EncoderContext_struct { |
michael@0 | 74 | PLArenaPool *our_pool; /* for our internal allocs */ |
michael@0 | 75 | |
michael@0 | 76 | sec_asn1e_state *current; |
michael@0 | 77 | sec_asn1e_parse_status status; |
michael@0 | 78 | |
michael@0 | 79 | PRBool streaming; |
michael@0 | 80 | PRBool from_buf; |
michael@0 | 81 | |
michael@0 | 82 | SEC_ASN1NotifyProc notify_proc; /* call before/after handling field */ |
michael@0 | 83 | void *notify_arg; /* argument to notify_proc */ |
michael@0 | 84 | PRBool during_notify; /* true during call to notify_proc */ |
michael@0 | 85 | |
michael@0 | 86 | SEC_ASN1WriteProc output_proc; /* pass encoded bytes to this */ |
michael@0 | 87 | void *output_arg; /* argument to that function */ |
michael@0 | 88 | }; |
michael@0 | 89 | |
michael@0 | 90 | |
michael@0 | 91 | static sec_asn1e_state * |
michael@0 | 92 | sec_asn1e_push_state (SEC_ASN1EncoderContext *cx, |
michael@0 | 93 | const SEC_ASN1Template *theTemplate, |
michael@0 | 94 | const void *src, PRBool new_depth) |
michael@0 | 95 | { |
michael@0 | 96 | sec_asn1e_state *state, *new_state; |
michael@0 | 97 | |
michael@0 | 98 | state = cx->current; |
michael@0 | 99 | |
michael@0 | 100 | new_state = (sec_asn1e_state*)PORT_ArenaZAlloc (cx->our_pool, |
michael@0 | 101 | sizeof(*new_state)); |
michael@0 | 102 | if (new_state == NULL) { |
michael@0 | 103 | cx->status = encodeError; |
michael@0 | 104 | return NULL; |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | new_state->top = cx; |
michael@0 | 108 | new_state->parent = state; |
michael@0 | 109 | new_state->theTemplate = theTemplate; |
michael@0 | 110 | new_state->place = notInUse; |
michael@0 | 111 | if (src != NULL) |
michael@0 | 112 | new_state->src = (char *)src + theTemplate->offset; |
michael@0 | 113 | |
michael@0 | 114 | if (state != NULL) { |
michael@0 | 115 | new_state->depth = state->depth; |
michael@0 | 116 | if (new_depth) |
michael@0 | 117 | new_state->depth++; |
michael@0 | 118 | state->child = new_state; |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | cx->current = new_state; |
michael@0 | 122 | return new_state; |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | |
michael@0 | 126 | static void |
michael@0 | 127 | sec_asn1e_scrub_state (sec_asn1e_state *state) |
michael@0 | 128 | { |
michael@0 | 129 | /* |
michael@0 | 130 | * Some default "scrubbing". |
michael@0 | 131 | * XXX right set of initializations? |
michael@0 | 132 | */ |
michael@0 | 133 | state->place = beforeHeader; |
michael@0 | 134 | state->indefinite = PR_FALSE; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | |
michael@0 | 138 | static void |
michael@0 | 139 | sec_asn1e_notify_before (SEC_ASN1EncoderContext *cx, void *src, int depth) |
michael@0 | 140 | { |
michael@0 | 141 | if (cx->notify_proc == NULL) |
michael@0 | 142 | return; |
michael@0 | 143 | |
michael@0 | 144 | cx->during_notify = PR_TRUE; |
michael@0 | 145 | (* cx->notify_proc) (cx->notify_arg, PR_TRUE, src, depth); |
michael@0 | 146 | cx->during_notify = PR_FALSE; |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | |
michael@0 | 150 | static void |
michael@0 | 151 | sec_asn1e_notify_after (SEC_ASN1EncoderContext *cx, void *src, int depth) |
michael@0 | 152 | { |
michael@0 | 153 | if (cx->notify_proc == NULL) |
michael@0 | 154 | return; |
michael@0 | 155 | |
michael@0 | 156 | cx->during_notify = PR_TRUE; |
michael@0 | 157 | (* cx->notify_proc) (cx->notify_arg, PR_FALSE, src, depth); |
michael@0 | 158 | cx->during_notify = PR_FALSE; |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | |
michael@0 | 162 | static sec_asn1e_state * |
michael@0 | 163 | sec_asn1e_init_state_based_on_template (sec_asn1e_state *state) |
michael@0 | 164 | { |
michael@0 | 165 | PRBool isExplicit, is_string, may_stream, optional, universal; |
michael@0 | 166 | PRBool disallowStreaming; |
michael@0 | 167 | unsigned char tag_modifiers; |
michael@0 | 168 | unsigned long encode_kind, under_kind; |
michael@0 | 169 | unsigned long tag_number; |
michael@0 | 170 | PRBool isInline = PR_FALSE; |
michael@0 | 171 | |
michael@0 | 172 | |
michael@0 | 173 | encode_kind = state->theTemplate->kind; |
michael@0 | 174 | |
michael@0 | 175 | universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) |
michael@0 | 176 | ? PR_TRUE : PR_FALSE; |
michael@0 | 177 | |
michael@0 | 178 | isExplicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE; |
michael@0 | 179 | encode_kind &= ~SEC_ASN1_EXPLICIT; |
michael@0 | 180 | |
michael@0 | 181 | optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE; |
michael@0 | 182 | encode_kind &= ~SEC_ASN1_OPTIONAL; |
michael@0 | 183 | |
michael@0 | 184 | PORT_Assert (!(isExplicit && universal)); /* bad templates */ |
michael@0 | 185 | |
michael@0 | 186 | may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE; |
michael@0 | 187 | encode_kind &= ~SEC_ASN1_MAY_STREAM; |
michael@0 | 188 | |
michael@0 | 189 | disallowStreaming = (encode_kind & SEC_ASN1_NO_STREAM) ? PR_TRUE : PR_FALSE; |
michael@0 | 190 | encode_kind &= ~SEC_ASN1_NO_STREAM; |
michael@0 | 191 | |
michael@0 | 192 | /* Just clear this to get it out of the way; we do not need it here */ |
michael@0 | 193 | encode_kind &= ~SEC_ASN1_DYNAMIC; |
michael@0 | 194 | |
michael@0 | 195 | if( encode_kind & SEC_ASN1_CHOICE ) { |
michael@0 | 196 | under_kind = SEC_ASN1_CHOICE; |
michael@0 | 197 | } else if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || |
michael@0 | 198 | (!universal && !isExplicit)) { |
michael@0 | 199 | const SEC_ASN1Template *subt; |
michael@0 | 200 | void *src = NULL; |
michael@0 | 201 | |
michael@0 | 202 | PORT_Assert ((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0); |
michael@0 | 203 | |
michael@0 | 204 | sec_asn1e_scrub_state (state); |
michael@0 | 205 | |
michael@0 | 206 | if (encode_kind & SEC_ASN1_POINTER) { |
michael@0 | 207 | src = *(void **)state->src; |
michael@0 | 208 | state->place = afterPointer; |
michael@0 | 209 | |
michael@0 | 210 | if (src == NULL) { |
michael@0 | 211 | /* |
michael@0 | 212 | * If this is optional, but NULL, then the field does |
michael@0 | 213 | * not need to be encoded. In this case we are done; |
michael@0 | 214 | * we do not want to push a subtemplate. |
michael@0 | 215 | */ |
michael@0 | 216 | if (optional) |
michael@0 | 217 | return state; |
michael@0 | 218 | |
michael@0 | 219 | /* |
michael@0 | 220 | * XXX this is an error; need to figure out |
michael@0 | 221 | * how to handle this |
michael@0 | 222 | */ |
michael@0 | 223 | } |
michael@0 | 224 | } else { |
michael@0 | 225 | src = state->src; |
michael@0 | 226 | if (encode_kind & SEC_ASN1_INLINE) { |
michael@0 | 227 | /* check that there are no extraneous bits */ |
michael@0 | 228 | /* PORT_Assert (encode_kind == SEC_ASN1_INLINE && !optional); */ |
michael@0 | 229 | state->place = afterInline; |
michael@0 | 230 | isInline = PR_TRUE; |
michael@0 | 231 | } else { |
michael@0 | 232 | /* |
michael@0 | 233 | * Save the tag modifiers and tag number here before moving |
michael@0 | 234 | * on to the next state in case this is a member of a |
michael@0 | 235 | * SEQUENCE OF |
michael@0 | 236 | */ |
michael@0 | 237 | state->tag_modifiers = (unsigned char) |
michael@0 | 238 | (encode_kind & (SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK)); |
michael@0 | 239 | state->tag_number = (unsigned char) |
michael@0 | 240 | (encode_kind & SEC_ASN1_TAGNUM_MASK); |
michael@0 | 241 | |
michael@0 | 242 | state->place = afterImplicit; |
michael@0 | 243 | state->optional = optional; |
michael@0 | 244 | } |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->src, PR_TRUE); |
michael@0 | 248 | if (isInline && optional) { |
michael@0 | 249 | /* we only handle a very limited set of optional inline cases at |
michael@0 | 250 | this time */ |
michael@0 | 251 | if (PR_FALSE != SEC_ASN1IsTemplateSimple(subt)) { |
michael@0 | 252 | /* we now know that the target is a SECItem*, so we can check |
michael@0 | 253 | if the source contains one */ |
michael@0 | 254 | SECItem* target = (SECItem*)state->src; |
michael@0 | 255 | if (!target || !target->data || !target->len) { |
michael@0 | 256 | /* no valid data to encode subtemplate */ |
michael@0 | 257 | return state; |
michael@0 | 258 | } |
michael@0 | 259 | } else { |
michael@0 | 260 | PORT_Assert(0); /* complex templates are not handled as |
michael@0 | 261 | inline optional */ |
michael@0 | 262 | } |
michael@0 | 263 | } |
michael@0 | 264 | state = sec_asn1e_push_state (state->top, subt, src, PR_FALSE); |
michael@0 | 265 | if (state == NULL) |
michael@0 | 266 | return state; |
michael@0 | 267 | |
michael@0 | 268 | if (universal) { |
michael@0 | 269 | /* |
michael@0 | 270 | * This is a POINTER or INLINE; just init based on that |
michael@0 | 271 | * and we are done. |
michael@0 | 272 | */ |
michael@0 | 273 | return sec_asn1e_init_state_based_on_template (state); |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | /* |
michael@0 | 277 | * This is an implicit, non-universal (meaning, application-private |
michael@0 | 278 | * or context-specific) field. This results in a "magic" tag but |
michael@0 | 279 | * encoding based on the underlying type. We pushed a new state |
michael@0 | 280 | * that is based on the subtemplate (the underlying type), but |
michael@0 | 281 | * now we will sort of alias it to give it some of our properties |
michael@0 | 282 | * (tag, optional status, etc.). |
michael@0 | 283 | * |
michael@0 | 284 | * NB: ALL the following flags in the subtemplate are disallowed |
michael@0 | 285 | * and/or ignored: EXPLICIT, OPTIONAL, INNER, INLINE, POINTER. |
michael@0 | 286 | */ |
michael@0 | 287 | |
michael@0 | 288 | under_kind = state->theTemplate->kind; |
michael@0 | 289 | if ((under_kind & SEC_ASN1_MAY_STREAM) && !disallowStreaming) { |
michael@0 | 290 | may_stream = PR_TRUE; |
michael@0 | 291 | } |
michael@0 | 292 | under_kind &= ~(SEC_ASN1_MAY_STREAM | SEC_ASN1_DYNAMIC); |
michael@0 | 293 | } else { |
michael@0 | 294 | under_kind = encode_kind; |
michael@0 | 295 | } |
michael@0 | 296 | |
michael@0 | 297 | /* |
michael@0 | 298 | * Sanity check that there are no unwanted bits marked in under_kind. |
michael@0 | 299 | * These bits were either removed above (after we recorded them) or |
michael@0 | 300 | * they simply should not be found (signalling a bad/broken template). |
michael@0 | 301 | * XXX is this the right set of bits to test here? (i.e. need to add |
michael@0 | 302 | * or remove any?) |
michael@0 | 303 | */ |
michael@0 | 304 | #define UNEXPECTED_FLAGS \ |
michael@0 | 305 | (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_SKIP | SEC_ASN1_INNER | \ |
michael@0 | 306 | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM | SEC_ASN1_INLINE | SEC_ASN1_POINTER) |
michael@0 | 307 | |
michael@0 | 308 | PORT_Assert ((under_kind & UNEXPECTED_FLAGS) == 0); |
michael@0 | 309 | under_kind &= ~UNEXPECTED_FLAGS; |
michael@0 | 310 | #undef UNEXPECTED_FLAGS |
michael@0 | 311 | |
michael@0 | 312 | if (encode_kind & SEC_ASN1_ANY) { |
michael@0 | 313 | PORT_Assert (encode_kind == under_kind); |
michael@0 | 314 | tag_modifiers = 0; |
michael@0 | 315 | tag_number = 0; |
michael@0 | 316 | is_string = PR_TRUE; |
michael@0 | 317 | } else { |
michael@0 | 318 | tag_modifiers = (unsigned char) |
michael@0 | 319 | (encode_kind & (SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK)); |
michael@0 | 320 | /* |
michael@0 | 321 | * XXX This assumes only single-octet identifiers. To handle |
michael@0 | 322 | * the HIGH TAG form we would need to do some more work, especially |
michael@0 | 323 | * in how to specify them in the template, because right now we |
michael@0 | 324 | * do not provide a way to specify more *tag* bits in encode_kind. |
michael@0 | 325 | */ |
michael@0 | 326 | tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK; |
michael@0 | 327 | |
michael@0 | 328 | is_string = PR_FALSE; |
michael@0 | 329 | switch (under_kind & SEC_ASN1_TAGNUM_MASK) { |
michael@0 | 330 | case SEC_ASN1_SET: |
michael@0 | 331 | /* |
michael@0 | 332 | * XXX A plain old SET (as opposed to a SET OF) is not implemented. |
michael@0 | 333 | * If it ever is, remove this assert... |
michael@0 | 334 | */ |
michael@0 | 335 | PORT_Assert ((under_kind & SEC_ASN1_GROUP) != 0); |
michael@0 | 336 | /* fallthru */ |
michael@0 | 337 | case SEC_ASN1_SEQUENCE: |
michael@0 | 338 | tag_modifiers |= SEC_ASN1_CONSTRUCTED; |
michael@0 | 339 | break; |
michael@0 | 340 | case SEC_ASN1_BIT_STRING: |
michael@0 | 341 | case SEC_ASN1_BMP_STRING: |
michael@0 | 342 | case SEC_ASN1_GENERALIZED_TIME: |
michael@0 | 343 | case SEC_ASN1_IA5_STRING: |
michael@0 | 344 | case SEC_ASN1_OCTET_STRING: |
michael@0 | 345 | case SEC_ASN1_PRINTABLE_STRING: |
michael@0 | 346 | case SEC_ASN1_T61_STRING: |
michael@0 | 347 | case SEC_ASN1_UNIVERSAL_STRING: |
michael@0 | 348 | case SEC_ASN1_UTC_TIME: |
michael@0 | 349 | case SEC_ASN1_UTF8_STRING: |
michael@0 | 350 | case SEC_ASN1_VISIBLE_STRING: |
michael@0 | 351 | /* |
michael@0 | 352 | * We do not yet know if we will be constructing the string, |
michael@0 | 353 | * so we have to wait to do this final tag modification. |
michael@0 | 354 | */ |
michael@0 | 355 | is_string = PR_TRUE; |
michael@0 | 356 | break; |
michael@0 | 357 | } |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | state->tag_modifiers = tag_modifiers; |
michael@0 | 361 | state->tag_number = (unsigned char)tag_number; |
michael@0 | 362 | state->underlying_kind = under_kind; |
michael@0 | 363 | state->isExplicit = isExplicit; |
michael@0 | 364 | state->may_stream = may_stream; |
michael@0 | 365 | state->is_string = is_string; |
michael@0 | 366 | state->optional = optional; |
michael@0 | 367 | state->disallowStreaming = disallowStreaming; |
michael@0 | 368 | |
michael@0 | 369 | sec_asn1e_scrub_state (state); |
michael@0 | 370 | |
michael@0 | 371 | return state; |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | |
michael@0 | 375 | static void |
michael@0 | 376 | sec_asn1e_write_part (sec_asn1e_state *state, |
michael@0 | 377 | const char *buf, unsigned long len, |
michael@0 | 378 | SEC_ASN1EncodingPart part) |
michael@0 | 379 | { |
michael@0 | 380 | SEC_ASN1EncoderContext *cx; |
michael@0 | 381 | |
michael@0 | 382 | cx = state->top; |
michael@0 | 383 | (* cx->output_proc) (cx->output_arg, buf, len, state->depth, part); |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | |
michael@0 | 387 | /* |
michael@0 | 388 | * XXX This assumes only single-octet identifiers. To handle |
michael@0 | 389 | * the HIGH TAG form we would need to modify this interface and |
michael@0 | 390 | * teach it to properly encode the special form. |
michael@0 | 391 | */ |
michael@0 | 392 | static void |
michael@0 | 393 | sec_asn1e_write_identifier_bytes (sec_asn1e_state *state, unsigned char value) |
michael@0 | 394 | { |
michael@0 | 395 | char byte; |
michael@0 | 396 | |
michael@0 | 397 | byte = (char) value; |
michael@0 | 398 | sec_asn1e_write_part (state, &byte, 1, SEC_ASN1_Identifier); |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | int |
michael@0 | 402 | SEC_ASN1EncodeLength(unsigned char *buf,int value) { |
michael@0 | 403 | int lenlen; |
michael@0 | 404 | |
michael@0 | 405 | lenlen = SEC_ASN1LengthLength (value); |
michael@0 | 406 | if (lenlen == 1) { |
michael@0 | 407 | buf[0] = value; |
michael@0 | 408 | } else { |
michael@0 | 409 | int i; |
michael@0 | 410 | |
michael@0 | 411 | i = lenlen - 1; |
michael@0 | 412 | buf[0] = 0x80 | i; |
michael@0 | 413 | while (i) { |
michael@0 | 414 | buf[i--] = value; |
michael@0 | 415 | value >>= 8; |
michael@0 | 416 | } |
michael@0 | 417 | PORT_Assert (value == 0); |
michael@0 | 418 | } |
michael@0 | 419 | return lenlen; |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | static void |
michael@0 | 423 | sec_asn1e_write_length_bytes (sec_asn1e_state *state, unsigned long value, |
michael@0 | 424 | PRBool indefinite) |
michael@0 | 425 | { |
michael@0 | 426 | int lenlen; |
michael@0 | 427 | unsigned char buf[sizeof(unsigned long) + 1]; |
michael@0 | 428 | |
michael@0 | 429 | if (indefinite) { |
michael@0 | 430 | PORT_Assert (value == 0); |
michael@0 | 431 | buf[0] = 0x80; |
michael@0 | 432 | lenlen = 1; |
michael@0 | 433 | } else { |
michael@0 | 434 | lenlen = SEC_ASN1EncodeLength(buf,value); |
michael@0 | 435 | } |
michael@0 | 436 | |
michael@0 | 437 | sec_asn1e_write_part (state, (char *) buf, lenlen, SEC_ASN1_Length); |
michael@0 | 438 | } |
michael@0 | 439 | |
michael@0 | 440 | |
michael@0 | 441 | static void |
michael@0 | 442 | sec_asn1e_write_contents_bytes (sec_asn1e_state *state, |
michael@0 | 443 | const char *buf, unsigned long len) |
michael@0 | 444 | { |
michael@0 | 445 | sec_asn1e_write_part (state, buf, len, SEC_ASN1_Contents); |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | |
michael@0 | 449 | static void |
michael@0 | 450 | sec_asn1e_write_end_of_contents_bytes (sec_asn1e_state *state) |
michael@0 | 451 | { |
michael@0 | 452 | const char eoc[2] = {0, 0}; |
michael@0 | 453 | |
michael@0 | 454 | sec_asn1e_write_part (state, eoc, 2, SEC_ASN1_EndOfContents); |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | static int |
michael@0 | 458 | sec_asn1e_which_choice |
michael@0 | 459 | ( |
michael@0 | 460 | void *src, |
michael@0 | 461 | const SEC_ASN1Template *theTemplate |
michael@0 | 462 | ) |
michael@0 | 463 | { |
michael@0 | 464 | int rv; |
michael@0 | 465 | unsigned int which = *(unsigned int *)src; |
michael@0 | 466 | |
michael@0 | 467 | for( rv = 1, theTemplate++; theTemplate->kind != 0; rv++, theTemplate++ ) { |
michael@0 | 468 | if( which == theTemplate->size ) { |
michael@0 | 469 | return rv; |
michael@0 | 470 | } |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | return 0; |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | static unsigned long |
michael@0 | 477 | sec_asn1e_contents_length (const SEC_ASN1Template *theTemplate, void *src, |
michael@0 | 478 | PRBool disallowStreaming, PRBool insideIndefinite, |
michael@0 | 479 | sec_asn1e_hdr_encoding *pHdrException) |
michael@0 | 480 | { |
michael@0 | 481 | unsigned long encode_kind, underlying_kind; |
michael@0 | 482 | PRBool isExplicit, optional, universal, may_stream; |
michael@0 | 483 | unsigned long len; |
michael@0 | 484 | |
michael@0 | 485 | /* |
michael@0 | 486 | * This function currently calculates the length in all cases |
michael@0 | 487 | * except the following: when writing out the contents of a |
michael@0 | 488 | * template that belongs to a state where it was a sub-template |
michael@0 | 489 | * with the SEC_ASN1_MAY_STREAM bit set and it's parent had the |
michael@0 | 490 | * optional bit set. The information that the parent is optional |
michael@0 | 491 | * and that we should return the length of 0 when that length is |
michael@0 | 492 | * present since that means the optional field is no longer present. |
michael@0 | 493 | * So we add the disallowStreaming flag which is passed in when |
michael@0 | 494 | * writing the contents, but for all recursive calls to |
michael@0 | 495 | * sec_asn1e_contents_length, we pass PR_FALSE, because this |
michael@0 | 496 | * function correctly calculates the length for children templates |
michael@0 | 497 | * from that point on. Confused yet? At least you didn't have |
michael@0 | 498 | * to figure it out. ;) -javi |
michael@0 | 499 | */ |
michael@0 | 500 | encode_kind = theTemplate->kind; |
michael@0 | 501 | |
michael@0 | 502 | universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) |
michael@0 | 503 | ? PR_TRUE : PR_FALSE; |
michael@0 | 504 | |
michael@0 | 505 | isExplicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE; |
michael@0 | 506 | encode_kind &= ~SEC_ASN1_EXPLICIT; |
michael@0 | 507 | |
michael@0 | 508 | optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE; |
michael@0 | 509 | encode_kind &= ~SEC_ASN1_OPTIONAL; |
michael@0 | 510 | |
michael@0 | 511 | PORT_Assert (!(isExplicit && universal)); /* bad templates */ |
michael@0 | 512 | |
michael@0 | 513 | may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE; |
michael@0 | 514 | encode_kind &= ~SEC_ASN1_MAY_STREAM; |
michael@0 | 515 | |
michael@0 | 516 | /* Just clear this to get it out of the way; we do not need it here */ |
michael@0 | 517 | encode_kind &= ~SEC_ASN1_DYNAMIC; |
michael@0 | 518 | |
michael@0 | 519 | if (encode_kind & SEC_ASN1_NO_STREAM) { |
michael@0 | 520 | disallowStreaming = PR_TRUE; |
michael@0 | 521 | } |
michael@0 | 522 | encode_kind &= ~SEC_ASN1_NO_STREAM; |
michael@0 | 523 | |
michael@0 | 524 | if (encode_kind & SEC_ASN1_CHOICE) { |
michael@0 | 525 | void *src2; |
michael@0 | 526 | int indx = sec_asn1e_which_choice(src, theTemplate); |
michael@0 | 527 | if (0 == indx) { |
michael@0 | 528 | /* XXX set an error? "choice not found" */ |
michael@0 | 529 | /* state->top->status = encodeError; */ |
michael@0 | 530 | return 0; |
michael@0 | 531 | } |
michael@0 | 532 | |
michael@0 | 533 | src2 = (void *) |
michael@0 | 534 | ((char *)src - theTemplate->offset + theTemplate[indx].offset); |
michael@0 | 535 | |
michael@0 | 536 | return sec_asn1e_contents_length(&theTemplate[indx], src2, |
michael@0 | 537 | disallowStreaming, insideIndefinite, |
michael@0 | 538 | pHdrException); |
michael@0 | 539 | } |
michael@0 | 540 | |
michael@0 | 541 | if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || !universal) { |
michael@0 | 542 | /* XXX any bits we want to disallow (PORT_Assert against) here? */ |
michael@0 | 543 | theTemplate = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE); |
michael@0 | 544 | if (encode_kind & SEC_ASN1_POINTER) { |
michael@0 | 545 | src = *(void **)src; |
michael@0 | 546 | if (src == NULL) { |
michael@0 | 547 | *pHdrException = optional ? hdr_optional : hdr_normal; |
michael@0 | 548 | return 0; |
michael@0 | 549 | } |
michael@0 | 550 | } else if (encode_kind & SEC_ASN1_INLINE) { |
michael@0 | 551 | /* check that there are no extraneous bits */ |
michael@0 | 552 | if (optional) { |
michael@0 | 553 | if (PR_FALSE != SEC_ASN1IsTemplateSimple(theTemplate)) { |
michael@0 | 554 | /* we now know that the target is a SECItem*, so we can check |
michael@0 | 555 | if the source contains one */ |
michael@0 | 556 | SECItem* target = (SECItem*)src; |
michael@0 | 557 | if (!target || !target->data || !target->len) { |
michael@0 | 558 | /* no valid data to encode subtemplate */ |
michael@0 | 559 | *pHdrException = hdr_optional; |
michael@0 | 560 | return 0; |
michael@0 | 561 | } |
michael@0 | 562 | } else { |
michael@0 | 563 | PORT_Assert(0); /* complex templates not handled as inline |
michael@0 | 564 | optional */ |
michael@0 | 565 | } |
michael@0 | 566 | } |
michael@0 | 567 | } |
michael@0 | 568 | |
michael@0 | 569 | src = (char *)src + theTemplate->offset; |
michael@0 | 570 | |
michael@0 | 571 | /* recurse to find the length of the subtemplate */ |
michael@0 | 572 | len = sec_asn1e_contents_length (theTemplate, src, disallowStreaming, |
michael@0 | 573 | insideIndefinite, pHdrException); |
michael@0 | 574 | if (len == 0 && optional) { |
michael@0 | 575 | *pHdrException = hdr_optional; |
michael@0 | 576 | } else if (isExplicit) { |
michael@0 | 577 | if (*pHdrException == hdr_any) { |
michael@0 | 578 | /* *we* do not want to add in a header, |
michael@0 | 579 | ** but our caller still does. |
michael@0 | 580 | */ |
michael@0 | 581 | *pHdrException = hdr_normal; |
michael@0 | 582 | } else if (*pHdrException == hdr_normal) { |
michael@0 | 583 | /* if the inner content exists, our length is |
michael@0 | 584 | * len(identifier) + len(length) + len(innercontent) |
michael@0 | 585 | * XXX we currently assume len(identifier) == 1; |
michael@0 | 586 | * to support a high-tag-number this would need to be smarter. |
michael@0 | 587 | */ |
michael@0 | 588 | len += 1 + SEC_ASN1LengthLength (len); |
michael@0 | 589 | } |
michael@0 | 590 | } |
michael@0 | 591 | return len; |
michael@0 | 592 | } |
michael@0 | 593 | underlying_kind = encode_kind; |
michael@0 | 594 | |
michael@0 | 595 | /* This is only used in decoding; it plays no part in encoding. */ |
michael@0 | 596 | if (underlying_kind & SEC_ASN1_SAVE) { |
michael@0 | 597 | /* check that there are no extraneous bits */ |
michael@0 | 598 | PORT_Assert (underlying_kind == SEC_ASN1_SAVE); |
michael@0 | 599 | *pHdrException = hdr_decoder; |
michael@0 | 600 | return 0; |
michael@0 | 601 | } |
michael@0 | 602 | |
michael@0 | 603 | #define UNEXPECTED_FLAGS \ |
michael@0 | 604 | (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_INLINE | SEC_ASN1_POINTER |\ |
michael@0 | 605 | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM | SEC_ASN1_SAVE | SEC_ASN1_SKIP) |
michael@0 | 606 | |
michael@0 | 607 | /* Having any of these bits is not expected here... */ |
michael@0 | 608 | PORT_Assert ((underlying_kind & UNEXPECTED_FLAGS) == 0); |
michael@0 | 609 | underlying_kind &= ~UNEXPECTED_FLAGS; |
michael@0 | 610 | #undef UNEXPECTED_FLAGS |
michael@0 | 611 | |
michael@0 | 612 | if (underlying_kind & SEC_ASN1_CHOICE) { |
michael@0 | 613 | void *src2; |
michael@0 | 614 | int indx = sec_asn1e_which_choice(src, theTemplate); |
michael@0 | 615 | if (0 == indx) { |
michael@0 | 616 | /* XXX set an error? "choice not found" */ |
michael@0 | 617 | /* state->top->status = encodeError; */ |
michael@0 | 618 | return 0; |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | src2 = (void *) |
michael@0 | 622 | ((char *)src - theTemplate->offset + theTemplate[indx].offset); |
michael@0 | 623 | len = sec_asn1e_contents_length(&theTemplate[indx], src2, |
michael@0 | 624 | disallowStreaming, insideIndefinite, |
michael@0 | 625 | pHdrException); |
michael@0 | 626 | } else { |
michael@0 | 627 | switch (underlying_kind) { |
michael@0 | 628 | case SEC_ASN1_SEQUENCE_OF: |
michael@0 | 629 | case SEC_ASN1_SET_OF: |
michael@0 | 630 | { |
michael@0 | 631 | const SEC_ASN1Template *tmpt; |
michael@0 | 632 | void *sub_src; |
michael@0 | 633 | unsigned long sub_len; |
michael@0 | 634 | void **group; |
michael@0 | 635 | |
michael@0 | 636 | len = 0; |
michael@0 | 637 | |
michael@0 | 638 | group = *(void ***)src; |
michael@0 | 639 | if (group == NULL) |
michael@0 | 640 | break; |
michael@0 | 641 | |
michael@0 | 642 | tmpt = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE); |
michael@0 | 643 | |
michael@0 | 644 | for (; *group != NULL; group++) { |
michael@0 | 645 | sub_src = (char *)(*group) + tmpt->offset; |
michael@0 | 646 | sub_len = sec_asn1e_contents_length (tmpt, sub_src, |
michael@0 | 647 | disallowStreaming, |
michael@0 | 648 | insideIndefinite, |
michael@0 | 649 | pHdrException); |
michael@0 | 650 | len += sub_len; |
michael@0 | 651 | /* |
michael@0 | 652 | * XXX The 1 below is the presumed length of the identifier; |
michael@0 | 653 | * to support a high-tag-number this would need to be smarter. |
michael@0 | 654 | */ |
michael@0 | 655 | if (*pHdrException == hdr_normal) |
michael@0 | 656 | len += 1 + SEC_ASN1LengthLength (sub_len); |
michael@0 | 657 | } |
michael@0 | 658 | } |
michael@0 | 659 | break; |
michael@0 | 660 | |
michael@0 | 661 | case SEC_ASN1_SEQUENCE: |
michael@0 | 662 | case SEC_ASN1_SET: |
michael@0 | 663 | { |
michael@0 | 664 | const SEC_ASN1Template *tmpt; |
michael@0 | 665 | void *sub_src; |
michael@0 | 666 | unsigned long sub_len; |
michael@0 | 667 | |
michael@0 | 668 | len = 0; |
michael@0 | 669 | for (tmpt = theTemplate + 1; tmpt->kind; tmpt++) { |
michael@0 | 670 | sub_src = (char *)src + tmpt->offset; |
michael@0 | 671 | sub_len = sec_asn1e_contents_length (tmpt, sub_src, |
michael@0 | 672 | disallowStreaming, |
michael@0 | 673 | insideIndefinite, |
michael@0 | 674 | pHdrException); |
michael@0 | 675 | len += sub_len; |
michael@0 | 676 | /* |
michael@0 | 677 | * XXX The 1 below is the presumed length of the identifier; |
michael@0 | 678 | * to support a high-tag-number this would need to be smarter. |
michael@0 | 679 | */ |
michael@0 | 680 | if (*pHdrException == hdr_normal) |
michael@0 | 681 | len += 1 + SEC_ASN1LengthLength (sub_len); |
michael@0 | 682 | } |
michael@0 | 683 | } |
michael@0 | 684 | break; |
michael@0 | 685 | |
michael@0 | 686 | case SEC_ASN1_BIT_STRING: |
michael@0 | 687 | /* convert bit length to byte */ |
michael@0 | 688 | len = (((SECItem *)src)->len + 7) >> 3; |
michael@0 | 689 | /* bit string contents involve an extra octet */ |
michael@0 | 690 | if (len) |
michael@0 | 691 | len++; |
michael@0 | 692 | break; |
michael@0 | 693 | |
michael@0 | 694 | case SEC_ASN1_INTEGER: |
michael@0 | 695 | /* ASN.1 INTEGERs are signed. |
michael@0 | 696 | * If the source is an unsigned integer, the encoder will need |
michael@0 | 697 | * to handle the conversion here. |
michael@0 | 698 | */ |
michael@0 | 699 | { |
michael@0 | 700 | unsigned char *buf = ((SECItem *)src)->data; |
michael@0 | 701 | SECItemType integerType = ((SECItem *)src)->type; |
michael@0 | 702 | len = ((SECItem *)src)->len; |
michael@0 | 703 | while (len > 0) { |
michael@0 | 704 | if (*buf != 0) { |
michael@0 | 705 | if (*buf & 0x80 && integerType == siUnsignedInteger) { |
michael@0 | 706 | len++; /* leading zero needed to make number signed */ |
michael@0 | 707 | } |
michael@0 | 708 | break; /* reached beginning of number */ |
michael@0 | 709 | } |
michael@0 | 710 | if (len == 1) { |
michael@0 | 711 | break; /* the number 0 */ |
michael@0 | 712 | } |
michael@0 | 713 | if (buf[1] & 0x80) { |
michael@0 | 714 | break; /* leading zero already present */ |
michael@0 | 715 | } |
michael@0 | 716 | /* extraneous leading zero, keep going */ |
michael@0 | 717 | buf++; |
michael@0 | 718 | len--; |
michael@0 | 719 | } |
michael@0 | 720 | } |
michael@0 | 721 | break; |
michael@0 | 722 | |
michael@0 | 723 | default: |
michael@0 | 724 | len = ((SECItem *)src)->len; |
michael@0 | 725 | break; |
michael@0 | 726 | } /* end switch */ |
michael@0 | 727 | |
michael@0 | 728 | #ifndef WHAT_PROBLEM_DOES_THIS_SOLVE |
michael@0 | 729 | /* if we're streaming, we may have a secitem w/len 0 as placeholder */ |
michael@0 | 730 | if (!len && insideIndefinite && may_stream && !disallowStreaming) { |
michael@0 | 731 | len = 1; |
michael@0 | 732 | } |
michael@0 | 733 | #endif |
michael@0 | 734 | } /* end else */ |
michael@0 | 735 | |
michael@0 | 736 | if (len == 0 && optional) |
michael@0 | 737 | *pHdrException = hdr_optional; |
michael@0 | 738 | else if (underlying_kind == SEC_ASN1_ANY) |
michael@0 | 739 | *pHdrException = hdr_any; |
michael@0 | 740 | else |
michael@0 | 741 | *pHdrException = hdr_normal; |
michael@0 | 742 | |
michael@0 | 743 | return len; |
michael@0 | 744 | } |
michael@0 | 745 | |
michael@0 | 746 | |
michael@0 | 747 | static void |
michael@0 | 748 | sec_asn1e_write_header (sec_asn1e_state *state) |
michael@0 | 749 | { |
michael@0 | 750 | unsigned long contents_length; |
michael@0 | 751 | unsigned char tag_number, tag_modifiers; |
michael@0 | 752 | sec_asn1e_hdr_encoding hdrException = hdr_normal; |
michael@0 | 753 | PRBool indefinite = PR_FALSE; |
michael@0 | 754 | |
michael@0 | 755 | PORT_Assert (state->place == beforeHeader); |
michael@0 | 756 | |
michael@0 | 757 | tag_number = state->tag_number; |
michael@0 | 758 | tag_modifiers = state->tag_modifiers; |
michael@0 | 759 | |
michael@0 | 760 | if (state->underlying_kind == SEC_ASN1_ANY) { |
michael@0 | 761 | state->place = duringContents; |
michael@0 | 762 | return; |
michael@0 | 763 | } |
michael@0 | 764 | |
michael@0 | 765 | if (state->underlying_kind & SEC_ASN1_CHOICE) { |
michael@0 | 766 | int indx = sec_asn1e_which_choice(state->src, state->theTemplate); |
michael@0 | 767 | if( 0 == indx ) { |
michael@0 | 768 | /* XXX set an error? "choice not found" */ |
michael@0 | 769 | state->top->status = encodeError; |
michael@0 | 770 | return; |
michael@0 | 771 | } |
michael@0 | 772 | state->place = afterChoice; |
michael@0 | 773 | state = sec_asn1e_push_state(state->top, &state->theTemplate[indx], |
michael@0 | 774 | (char *)state->src - state->theTemplate->offset, |
michael@0 | 775 | PR_TRUE); |
michael@0 | 776 | if (state) { |
michael@0 | 777 | /* |
michael@0 | 778 | * Do the "before" field notification. |
michael@0 | 779 | */ |
michael@0 | 780 | sec_asn1e_notify_before (state->top, state->src, state->depth); |
michael@0 | 781 | state = sec_asn1e_init_state_based_on_template (state); |
michael@0 | 782 | } |
michael@0 | 783 | return; |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | /* The !isString test below is apparently intended to ensure that all |
michael@0 | 787 | ** constructed types receive indefinite length encoding. |
michael@0 | 788 | */ |
michael@0 | 789 | indefinite = (PRBool) |
michael@0 | 790 | (state->top->streaming && state->may_stream && |
michael@0 | 791 | (state->top->from_buf || !state->is_string)); |
michael@0 | 792 | |
michael@0 | 793 | /* |
michael@0 | 794 | * If we are doing a definite-length encoding, first we have to |
michael@0 | 795 | * walk the data structure to calculate the entire contents length. |
michael@0 | 796 | * If we are doing an indefinite-length encoding, we still need to |
michael@0 | 797 | * know if the contents is: |
michael@0 | 798 | * optional and to be omitted, or |
michael@0 | 799 | * an ANY (header is pre-encoded), or |
michael@0 | 800 | * a SAVE or some other kind of template used only by the decoder. |
michael@0 | 801 | * So, we call this function either way. |
michael@0 | 802 | */ |
michael@0 | 803 | contents_length = sec_asn1e_contents_length (state->theTemplate, |
michael@0 | 804 | state->src, |
michael@0 | 805 | state->disallowStreaming, |
michael@0 | 806 | indefinite, |
michael@0 | 807 | &hdrException); |
michael@0 | 808 | /* |
michael@0 | 809 | * We might be told explicitly not to put out a header. |
michael@0 | 810 | * But it can also be the case, via a pushed subtemplate, that |
michael@0 | 811 | * sec_asn1e_contents_length could not know that this field is |
michael@0 | 812 | * really optional. So check for that explicitly, too. |
michael@0 | 813 | */ |
michael@0 | 814 | if (hdrException != hdr_normal || |
michael@0 | 815 | (contents_length == 0 && state->optional)) { |
michael@0 | 816 | state->place = afterContents; |
michael@0 | 817 | if (state->top->streaming && |
michael@0 | 818 | state->may_stream && |
michael@0 | 819 | state->top->from_buf) { |
michael@0 | 820 | /* we did not find an optional indefinite string, so we |
michael@0 | 821 | * don't encode it. However, if TakeFromBuf is on, we stop |
michael@0 | 822 | * here anyway to give our caller a chance to intercept at the |
michael@0 | 823 | * same point where we would stop if the field were present. |
michael@0 | 824 | */ |
michael@0 | 825 | state->top->status = needBytes; |
michael@0 | 826 | } |
michael@0 | 827 | return; |
michael@0 | 828 | } |
michael@0 | 829 | |
michael@0 | 830 | if (indefinite) { |
michael@0 | 831 | /* |
michael@0 | 832 | * We need to put out an indefinite-length encoding. |
michael@0 | 833 | * The only universal types that can be constructed are SETs, |
michael@0 | 834 | * SEQUENCEs, and strings; so check that it is one of those, |
michael@0 | 835 | * or that it is not universal (e.g. context-specific). |
michael@0 | 836 | */ |
michael@0 | 837 | state->indefinite = PR_TRUE; |
michael@0 | 838 | PORT_Assert ((tag_number == SEC_ASN1_SET) |
michael@0 | 839 | || (tag_number == SEC_ASN1_SEQUENCE) |
michael@0 | 840 | || ((tag_modifiers & SEC_ASN1_CLASS_MASK) != 0) |
michael@0 | 841 | || state->is_string); |
michael@0 | 842 | tag_modifiers |= SEC_ASN1_CONSTRUCTED; |
michael@0 | 843 | contents_length = 0; |
michael@0 | 844 | } |
michael@0 | 845 | |
michael@0 | 846 | sec_asn1e_write_identifier_bytes (state, |
michael@0 | 847 | (unsigned char)(tag_number | tag_modifiers)); |
michael@0 | 848 | sec_asn1e_write_length_bytes (state, contents_length, state->indefinite); |
michael@0 | 849 | |
michael@0 | 850 | if (contents_length == 0 && !state->indefinite) { |
michael@0 | 851 | /* |
michael@0 | 852 | * If no real contents to encode, then we are done with this field. |
michael@0 | 853 | */ |
michael@0 | 854 | state->place = afterContents; |
michael@0 | 855 | return; |
michael@0 | 856 | } |
michael@0 | 857 | |
michael@0 | 858 | /* |
michael@0 | 859 | * An EXPLICIT is nothing but an outer header, which we have already |
michael@0 | 860 | * written. Now we need to do the inner header and contents. |
michael@0 | 861 | */ |
michael@0 | 862 | if (state->isExplicit) { |
michael@0 | 863 | const SEC_ASN1Template *subt = |
michael@0 | 864 | SEC_ASN1GetSubtemplate(state->theTemplate, state->src, PR_TRUE); |
michael@0 | 865 | state->place = afterContents; |
michael@0 | 866 | state = sec_asn1e_push_state (state->top, subt, state->src, PR_TRUE); |
michael@0 | 867 | if (state != NULL) |
michael@0 | 868 | state = sec_asn1e_init_state_based_on_template (state); |
michael@0 | 869 | return; |
michael@0 | 870 | } |
michael@0 | 871 | |
michael@0 | 872 | switch (state->underlying_kind) { |
michael@0 | 873 | case SEC_ASN1_SET_OF: |
michael@0 | 874 | case SEC_ASN1_SEQUENCE_OF: |
michael@0 | 875 | /* |
michael@0 | 876 | * We need to push a child to handle each member. |
michael@0 | 877 | */ |
michael@0 | 878 | { |
michael@0 | 879 | void **group; |
michael@0 | 880 | const SEC_ASN1Template *subt; |
michael@0 | 881 | |
michael@0 | 882 | group = *(void ***)state->src; |
michael@0 | 883 | if (group == NULL || *group == NULL) { |
michael@0 | 884 | /* |
michael@0 | 885 | * Group is empty; we are done. |
michael@0 | 886 | */ |
michael@0 | 887 | state->place = afterContents; |
michael@0 | 888 | return; |
michael@0 | 889 | } |
michael@0 | 890 | state->place = duringGroup; |
michael@0 | 891 | subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->src, |
michael@0 | 892 | PR_TRUE); |
michael@0 | 893 | state = sec_asn1e_push_state (state->top, subt, *group, PR_TRUE); |
michael@0 | 894 | if (state != NULL) |
michael@0 | 895 | state = sec_asn1e_init_state_based_on_template (state); |
michael@0 | 896 | } |
michael@0 | 897 | break; |
michael@0 | 898 | |
michael@0 | 899 | case SEC_ASN1_SEQUENCE: |
michael@0 | 900 | case SEC_ASN1_SET: |
michael@0 | 901 | /* |
michael@0 | 902 | * We need to push a child to handle the individual fields. |
michael@0 | 903 | */ |
michael@0 | 904 | state->place = duringSequence; |
michael@0 | 905 | state = sec_asn1e_push_state (state->top, state->theTemplate + 1, |
michael@0 | 906 | state->src, PR_TRUE); |
michael@0 | 907 | if (state != NULL) { |
michael@0 | 908 | /* |
michael@0 | 909 | * Do the "before" field notification. |
michael@0 | 910 | */ |
michael@0 | 911 | sec_asn1e_notify_before (state->top, state->src, state->depth); |
michael@0 | 912 | state = sec_asn1e_init_state_based_on_template (state); |
michael@0 | 913 | } |
michael@0 | 914 | break; |
michael@0 | 915 | |
michael@0 | 916 | default: |
michael@0 | 917 | /* |
michael@0 | 918 | * I think we do not need to do anything else. |
michael@0 | 919 | * XXX Correct? |
michael@0 | 920 | */ |
michael@0 | 921 | state->place = duringContents; |
michael@0 | 922 | break; |
michael@0 | 923 | } |
michael@0 | 924 | } |
michael@0 | 925 | |
michael@0 | 926 | |
michael@0 | 927 | static void |
michael@0 | 928 | sec_asn1e_write_contents_from_buf (sec_asn1e_state *state, |
michael@0 | 929 | const char *buf, unsigned long len) |
michael@0 | 930 | { |
michael@0 | 931 | PORT_Assert (state->place == duringContents); |
michael@0 | 932 | PORT_Assert (state->top->from_buf); |
michael@0 | 933 | PORT_Assert (state->may_stream && !state->disallowStreaming); |
michael@0 | 934 | |
michael@0 | 935 | /* |
michael@0 | 936 | * Probably they just turned on "take from buf", but have not |
michael@0 | 937 | * yet given us any bytes. If there is nothing in the buffer |
michael@0 | 938 | * then we have nothing to do but return and wait. |
michael@0 | 939 | */ |
michael@0 | 940 | if (buf == NULL || len == 0) { |
michael@0 | 941 | state->top->status = needBytes; |
michael@0 | 942 | return; |
michael@0 | 943 | } |
michael@0 | 944 | /* |
michael@0 | 945 | * We are streaming, reading from a passed-in buffer. |
michael@0 | 946 | * This means we are encoding a simple string or an ANY. |
michael@0 | 947 | * For the former, we need to put out a substring, with its |
michael@0 | 948 | * own identifier and length. For an ANY, we just write it |
michael@0 | 949 | * out as is (our caller is required to ensure that it |
michael@0 | 950 | * is a properly encoded entity). |
michael@0 | 951 | */ |
michael@0 | 952 | PORT_Assert (state->is_string); /* includes ANY */ |
michael@0 | 953 | if (state->underlying_kind != SEC_ASN1_ANY) { |
michael@0 | 954 | unsigned char identifier; |
michael@0 | 955 | |
michael@0 | 956 | /* |
michael@0 | 957 | * Create the identifier based on underlying_kind. We cannot |
michael@0 | 958 | * use tag_number and tag_modifiers because this can be an |
michael@0 | 959 | * implicitly encoded field. In that case, the underlying |
michael@0 | 960 | * substrings *are* encoded with their real tag. |
michael@0 | 961 | */ |
michael@0 | 962 | identifier = (unsigned char) |
michael@0 | 963 | (state->underlying_kind & SEC_ASN1_TAG_MASK); |
michael@0 | 964 | /* |
michael@0 | 965 | * The underlying kind should just be a simple string; there |
michael@0 | 966 | * should be no bits like CONTEXT_SPECIFIC or CONSTRUCTED set. |
michael@0 | 967 | */ |
michael@0 | 968 | PORT_Assert ((identifier & SEC_ASN1_TAGNUM_MASK) == identifier); |
michael@0 | 969 | /* |
michael@0 | 970 | * Write out the tag and length for the substring. |
michael@0 | 971 | */ |
michael@0 | 972 | sec_asn1e_write_identifier_bytes (state, identifier); |
michael@0 | 973 | if (state->underlying_kind == SEC_ASN1_BIT_STRING) { |
michael@0 | 974 | char byte; |
michael@0 | 975 | /* |
michael@0 | 976 | * Assume we have a length in bytes but we need to output |
michael@0 | 977 | * a proper bit string. This interface only works for bit |
michael@0 | 978 | * strings that are full multiples of 8. If support for |
michael@0 | 979 | * real, variable length bit strings is needed then the |
michael@0 | 980 | * caller will have to know to pass in a bit length instead |
michael@0 | 981 | * of a byte length and then this code will have to |
michael@0 | 982 | * perform the encoding necessary (length written is length |
michael@0 | 983 | * in bytes plus 1, and the first octet of string is the |
michael@0 | 984 | * number of bits remaining between the end of the bit |
michael@0 | 985 | * string and the next byte boundary). |
michael@0 | 986 | */ |
michael@0 | 987 | sec_asn1e_write_length_bytes (state, len + 1, PR_FALSE); |
michael@0 | 988 | byte = 0; |
michael@0 | 989 | sec_asn1e_write_contents_bytes (state, &byte, 1); |
michael@0 | 990 | } else { |
michael@0 | 991 | sec_asn1e_write_length_bytes (state, len, PR_FALSE); |
michael@0 | 992 | } |
michael@0 | 993 | } |
michael@0 | 994 | sec_asn1e_write_contents_bytes (state, buf, len); |
michael@0 | 995 | state->top->status = needBytes; |
michael@0 | 996 | } |
michael@0 | 997 | |
michael@0 | 998 | static void |
michael@0 | 999 | sec_asn1e_write_contents (sec_asn1e_state *state) |
michael@0 | 1000 | { |
michael@0 | 1001 | unsigned long len = 0; |
michael@0 | 1002 | |
michael@0 | 1003 | PORT_Assert (state->place == duringContents); |
michael@0 | 1004 | |
michael@0 | 1005 | switch (state->underlying_kind) { |
michael@0 | 1006 | case SEC_ASN1_SET: |
michael@0 | 1007 | case SEC_ASN1_SEQUENCE: |
michael@0 | 1008 | PORT_Assert (0); |
michael@0 | 1009 | break; |
michael@0 | 1010 | |
michael@0 | 1011 | case SEC_ASN1_BIT_STRING: |
michael@0 | 1012 | { |
michael@0 | 1013 | SECItem *item; |
michael@0 | 1014 | char rem; |
michael@0 | 1015 | |
michael@0 | 1016 | item = (SECItem *)state->src; |
michael@0 | 1017 | len = (item->len + 7) >> 3; |
michael@0 | 1018 | rem = (unsigned char)((len << 3) - item->len); /* remaining bits */ |
michael@0 | 1019 | sec_asn1e_write_contents_bytes (state, &rem, 1); |
michael@0 | 1020 | sec_asn1e_write_contents_bytes (state, (char *) item->data, len); |
michael@0 | 1021 | } |
michael@0 | 1022 | break; |
michael@0 | 1023 | |
michael@0 | 1024 | case SEC_ASN1_BMP_STRING: |
michael@0 | 1025 | /* The number of bytes must be divisable by 2 */ |
michael@0 | 1026 | if ((((SECItem *)state->src)->len) % 2) { |
michael@0 | 1027 | SEC_ASN1EncoderContext *cx; |
michael@0 | 1028 | |
michael@0 | 1029 | cx = state->top; |
michael@0 | 1030 | cx->status = encodeError; |
michael@0 | 1031 | break; |
michael@0 | 1032 | } |
michael@0 | 1033 | /* otherwise, fall through to write the content */ |
michael@0 | 1034 | goto process_string; |
michael@0 | 1035 | |
michael@0 | 1036 | case SEC_ASN1_UNIVERSAL_STRING: |
michael@0 | 1037 | /* The number of bytes must be divisable by 4 */ |
michael@0 | 1038 | if ((((SECItem *)state->src)->len) % 4) { |
michael@0 | 1039 | SEC_ASN1EncoderContext *cx; |
michael@0 | 1040 | |
michael@0 | 1041 | cx = state->top; |
michael@0 | 1042 | cx->status = encodeError; |
michael@0 | 1043 | break; |
michael@0 | 1044 | } |
michael@0 | 1045 | /* otherwise, fall through to write the content */ |
michael@0 | 1046 | goto process_string; |
michael@0 | 1047 | |
michael@0 | 1048 | case SEC_ASN1_INTEGER: |
michael@0 | 1049 | /* ASN.1 INTEGERs are signed. If the source is an unsigned |
michael@0 | 1050 | * integer, the encoder will need to handle the conversion here. |
michael@0 | 1051 | */ |
michael@0 | 1052 | { |
michael@0 | 1053 | unsigned int blen; |
michael@0 | 1054 | unsigned char *buf; |
michael@0 | 1055 | SECItemType integerType; |
michael@0 | 1056 | blen = ((SECItem *)state->src)->len; |
michael@0 | 1057 | buf = ((SECItem *)state->src)->data; |
michael@0 | 1058 | integerType = ((SECItem *)state->src)->type; |
michael@0 | 1059 | while (blen > 0) { |
michael@0 | 1060 | if (*buf & 0x80 && integerType == siUnsignedInteger) { |
michael@0 | 1061 | char zero = 0; /* write a leading 0 */ |
michael@0 | 1062 | sec_asn1e_write_contents_bytes(state, &zero, 1); |
michael@0 | 1063 | /* and then the remaining buffer */ |
michael@0 | 1064 | sec_asn1e_write_contents_bytes(state, |
michael@0 | 1065 | (char *)buf, blen); |
michael@0 | 1066 | break; |
michael@0 | 1067 | } |
michael@0 | 1068 | /* Check three possibilities: |
michael@0 | 1069 | * 1. No leading zeros, msb of MSB is not 1; |
michael@0 | 1070 | * 2. The number is zero itself; |
michael@0 | 1071 | * 3. Encoding a signed integer with a leading zero, |
michael@0 | 1072 | * keep the zero so that the number is positive. |
michael@0 | 1073 | */ |
michael@0 | 1074 | if (*buf != 0 || |
michael@0 | 1075 | blen == 1 || |
michael@0 | 1076 | (buf[1] & 0x80 && integerType != siUnsignedInteger) ) |
michael@0 | 1077 | { |
michael@0 | 1078 | sec_asn1e_write_contents_bytes(state, |
michael@0 | 1079 | (char *)buf, blen); |
michael@0 | 1080 | break; |
michael@0 | 1081 | } |
michael@0 | 1082 | /* byte is 0, continue */ |
michael@0 | 1083 | buf++; |
michael@0 | 1084 | blen--; |
michael@0 | 1085 | } |
michael@0 | 1086 | } |
michael@0 | 1087 | /* done with this content */ |
michael@0 | 1088 | break; |
michael@0 | 1089 | |
michael@0 | 1090 | process_string: |
michael@0 | 1091 | default: |
michael@0 | 1092 | { |
michael@0 | 1093 | SECItem *item; |
michael@0 | 1094 | |
michael@0 | 1095 | item = (SECItem *)state->src; |
michael@0 | 1096 | sec_asn1e_write_contents_bytes (state, (char *) item->data, |
michael@0 | 1097 | item->len); |
michael@0 | 1098 | } |
michael@0 | 1099 | break; |
michael@0 | 1100 | } |
michael@0 | 1101 | state->place = afterContents; |
michael@0 | 1102 | } |
michael@0 | 1103 | |
michael@0 | 1104 | /* |
michael@0 | 1105 | * We are doing a SET OF or SEQUENCE OF, and have just finished an item. |
michael@0 | 1106 | */ |
michael@0 | 1107 | static void |
michael@0 | 1108 | sec_asn1e_next_in_group (sec_asn1e_state *state) |
michael@0 | 1109 | { |
michael@0 | 1110 | sec_asn1e_state *child; |
michael@0 | 1111 | void **group; |
michael@0 | 1112 | void *member; |
michael@0 | 1113 | |
michael@0 | 1114 | PORT_Assert (state->place == duringGroup); |
michael@0 | 1115 | PORT_Assert (state->child != NULL); |
michael@0 | 1116 | |
michael@0 | 1117 | child = state->child; |
michael@0 | 1118 | |
michael@0 | 1119 | group = *(void ***)state->src; |
michael@0 | 1120 | |
michael@0 | 1121 | /* |
michael@0 | 1122 | * Find placement of current item. |
michael@0 | 1123 | */ |
michael@0 | 1124 | member = (char *)(state->child->src) - child->theTemplate->offset; |
michael@0 | 1125 | while (*group != member) |
michael@0 | 1126 | group++; |
michael@0 | 1127 | |
michael@0 | 1128 | /* |
michael@0 | 1129 | * Move forward to next item. |
michael@0 | 1130 | */ |
michael@0 | 1131 | group++; |
michael@0 | 1132 | if (*group == NULL) { |
michael@0 | 1133 | /* |
michael@0 | 1134 | * That was our last one; we are done now. |
michael@0 | 1135 | */ |
michael@0 | 1136 | child->place = notInUse; |
michael@0 | 1137 | state->place = afterContents; |
michael@0 | 1138 | return; |
michael@0 | 1139 | } |
michael@0 | 1140 | child->src = (char *)(*group) + child->theTemplate->offset; |
michael@0 | 1141 | |
michael@0 | 1142 | /* |
michael@0 | 1143 | * Re-"push" child. |
michael@0 | 1144 | */ |
michael@0 | 1145 | sec_asn1e_scrub_state (child); |
michael@0 | 1146 | state->top->current = child; |
michael@0 | 1147 | } |
michael@0 | 1148 | |
michael@0 | 1149 | |
michael@0 | 1150 | /* |
michael@0 | 1151 | * We are moving along through a sequence; move forward by one, |
michael@0 | 1152 | * (detecting end-of-sequence when it happens). |
michael@0 | 1153 | */ |
michael@0 | 1154 | static void |
michael@0 | 1155 | sec_asn1e_next_in_sequence (sec_asn1e_state *state) |
michael@0 | 1156 | { |
michael@0 | 1157 | sec_asn1e_state *child; |
michael@0 | 1158 | |
michael@0 | 1159 | PORT_Assert (state->place == duringSequence); |
michael@0 | 1160 | PORT_Assert (state->child != NULL); |
michael@0 | 1161 | |
michael@0 | 1162 | child = state->child; |
michael@0 | 1163 | |
michael@0 | 1164 | /* |
michael@0 | 1165 | * Do the "after" field notification. |
michael@0 | 1166 | */ |
michael@0 | 1167 | sec_asn1e_notify_after (state->top, child->src, child->depth); |
michael@0 | 1168 | |
michael@0 | 1169 | /* |
michael@0 | 1170 | * Move forward. |
michael@0 | 1171 | */ |
michael@0 | 1172 | child->theTemplate++; |
michael@0 | 1173 | if (child->theTemplate->kind == 0) { |
michael@0 | 1174 | /* |
michael@0 | 1175 | * We are done with this sequence. |
michael@0 | 1176 | */ |
michael@0 | 1177 | child->place = notInUse; |
michael@0 | 1178 | state->place = afterContents; |
michael@0 | 1179 | return; |
michael@0 | 1180 | } |
michael@0 | 1181 | |
michael@0 | 1182 | /* |
michael@0 | 1183 | * Reset state and push. |
michael@0 | 1184 | */ |
michael@0 | 1185 | |
michael@0 | 1186 | child->src = (char *)state->src + child->theTemplate->offset; |
michael@0 | 1187 | |
michael@0 | 1188 | /* |
michael@0 | 1189 | * Do the "before" field notification. |
michael@0 | 1190 | */ |
michael@0 | 1191 | sec_asn1e_notify_before (state->top, child->src, child->depth); |
michael@0 | 1192 | |
michael@0 | 1193 | state->top->current = child; |
michael@0 | 1194 | (void) sec_asn1e_init_state_based_on_template (child); |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | |
michael@0 | 1198 | static void |
michael@0 | 1199 | sec_asn1e_after_contents (sec_asn1e_state *state) |
michael@0 | 1200 | { |
michael@0 | 1201 | PORT_Assert (state->place == afterContents); |
michael@0 | 1202 | |
michael@0 | 1203 | if (state->indefinite) |
michael@0 | 1204 | sec_asn1e_write_end_of_contents_bytes (state); |
michael@0 | 1205 | |
michael@0 | 1206 | /* |
michael@0 | 1207 | * Just make my parent be the current state. It will then clean |
michael@0 | 1208 | * up after me and free me (or reuse me). |
michael@0 | 1209 | */ |
michael@0 | 1210 | state->top->current = state->parent; |
michael@0 | 1211 | } |
michael@0 | 1212 | |
michael@0 | 1213 | |
michael@0 | 1214 | /* |
michael@0 | 1215 | * This function is called whether or not we are streaming; if we |
michael@0 | 1216 | * *are* streaming, our caller can also instruct us to take bytes |
michael@0 | 1217 | * from the passed-in buffer (at buf, for length len, which is likely |
michael@0 | 1218 | * bytes but could even mean bits if the current field is a bit string). |
michael@0 | 1219 | * If we have been so instructed, we will gobble up bytes from there |
michael@0 | 1220 | * (rather than from our src structure) and output them, and then |
michael@0 | 1221 | * we will just return, expecting to be called again -- either with |
michael@0 | 1222 | * more bytes or after our caller has instructed us that we are done |
michael@0 | 1223 | * (for now) with the buffer. |
michael@0 | 1224 | */ |
michael@0 | 1225 | SECStatus |
michael@0 | 1226 | SEC_ASN1EncoderUpdate (SEC_ASN1EncoderContext *cx, |
michael@0 | 1227 | const char *buf, unsigned long len) |
michael@0 | 1228 | { |
michael@0 | 1229 | sec_asn1e_state *state; |
michael@0 | 1230 | |
michael@0 | 1231 | if (cx->status == needBytes) { |
michael@0 | 1232 | cx->status = keepGoing; |
michael@0 | 1233 | } |
michael@0 | 1234 | |
michael@0 | 1235 | while (cx->status == keepGoing) { |
michael@0 | 1236 | state = cx->current; |
michael@0 | 1237 | switch (state->place) { |
michael@0 | 1238 | case beforeHeader: |
michael@0 | 1239 | sec_asn1e_write_header (state); |
michael@0 | 1240 | break; |
michael@0 | 1241 | case duringContents: |
michael@0 | 1242 | if (cx->from_buf) |
michael@0 | 1243 | sec_asn1e_write_contents_from_buf (state, buf, len); |
michael@0 | 1244 | else |
michael@0 | 1245 | sec_asn1e_write_contents (state); |
michael@0 | 1246 | break; |
michael@0 | 1247 | case duringGroup: |
michael@0 | 1248 | sec_asn1e_next_in_group (state); |
michael@0 | 1249 | break; |
michael@0 | 1250 | case duringSequence: |
michael@0 | 1251 | sec_asn1e_next_in_sequence (state); |
michael@0 | 1252 | break; |
michael@0 | 1253 | case afterContents: |
michael@0 | 1254 | sec_asn1e_after_contents (state); |
michael@0 | 1255 | break; |
michael@0 | 1256 | case afterImplicit: |
michael@0 | 1257 | case afterInline: |
michael@0 | 1258 | case afterPointer: |
michael@0 | 1259 | case afterChoice: |
michael@0 | 1260 | /* |
michael@0 | 1261 | * These states are more documentation than anything. |
michael@0 | 1262 | * They just need to force a pop. |
michael@0 | 1263 | */ |
michael@0 | 1264 | PORT_Assert (!state->indefinite); |
michael@0 | 1265 | state->place = afterContents; |
michael@0 | 1266 | break; |
michael@0 | 1267 | case notInUse: |
michael@0 | 1268 | default: |
michael@0 | 1269 | /* This is not an error, but rather a plain old BUG! */ |
michael@0 | 1270 | PORT_Assert (0); |
michael@0 | 1271 | cx->status = encodeError; |
michael@0 | 1272 | break; |
michael@0 | 1273 | } |
michael@0 | 1274 | |
michael@0 | 1275 | if (cx->status == encodeError) |
michael@0 | 1276 | break; |
michael@0 | 1277 | |
michael@0 | 1278 | /* It might have changed, so we have to update our local copy. */ |
michael@0 | 1279 | state = cx->current; |
michael@0 | 1280 | |
michael@0 | 1281 | /* If it is NULL, we have popped all the way to the top. */ |
michael@0 | 1282 | if (state == NULL) { |
michael@0 | 1283 | cx->status = allDone; |
michael@0 | 1284 | break; |
michael@0 | 1285 | } |
michael@0 | 1286 | } |
michael@0 | 1287 | |
michael@0 | 1288 | if (cx->status == encodeError) { |
michael@0 | 1289 | return SECFailure; |
michael@0 | 1290 | } |
michael@0 | 1291 | |
michael@0 | 1292 | return SECSuccess; |
michael@0 | 1293 | } |
michael@0 | 1294 | |
michael@0 | 1295 | |
michael@0 | 1296 | void |
michael@0 | 1297 | SEC_ASN1EncoderFinish (SEC_ASN1EncoderContext *cx) |
michael@0 | 1298 | { |
michael@0 | 1299 | /* |
michael@0 | 1300 | * XXX anything else that needs to be finished? |
michael@0 | 1301 | */ |
michael@0 | 1302 | |
michael@0 | 1303 | PORT_FreeArena (cx->our_pool, PR_FALSE); |
michael@0 | 1304 | } |
michael@0 | 1305 | |
michael@0 | 1306 | |
michael@0 | 1307 | SEC_ASN1EncoderContext * |
michael@0 | 1308 | SEC_ASN1EncoderStart (const void *src, const SEC_ASN1Template *theTemplate, |
michael@0 | 1309 | SEC_ASN1WriteProc output_proc, void *output_arg) |
michael@0 | 1310 | { |
michael@0 | 1311 | PLArenaPool *our_pool; |
michael@0 | 1312 | SEC_ASN1EncoderContext *cx; |
michael@0 | 1313 | |
michael@0 | 1314 | our_pool = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE); |
michael@0 | 1315 | if (our_pool == NULL) |
michael@0 | 1316 | return NULL; |
michael@0 | 1317 | |
michael@0 | 1318 | cx = (SEC_ASN1EncoderContext*)PORT_ArenaZAlloc (our_pool, sizeof(*cx)); |
michael@0 | 1319 | if (cx == NULL) { |
michael@0 | 1320 | PORT_FreeArena (our_pool, PR_FALSE); |
michael@0 | 1321 | return NULL; |
michael@0 | 1322 | } |
michael@0 | 1323 | |
michael@0 | 1324 | cx->our_pool = our_pool; |
michael@0 | 1325 | cx->output_proc = output_proc; |
michael@0 | 1326 | cx->output_arg = output_arg; |
michael@0 | 1327 | |
michael@0 | 1328 | cx->status = keepGoing; |
michael@0 | 1329 | |
michael@0 | 1330 | if (sec_asn1e_push_state(cx, theTemplate, src, PR_FALSE) == NULL |
michael@0 | 1331 | || sec_asn1e_init_state_based_on_template (cx->current) == NULL) { |
michael@0 | 1332 | /* |
michael@0 | 1333 | * Trouble initializing (probably due to failed allocations) |
michael@0 | 1334 | * requires that we just give up. |
michael@0 | 1335 | */ |
michael@0 | 1336 | PORT_FreeArena (our_pool, PR_FALSE); |
michael@0 | 1337 | return NULL; |
michael@0 | 1338 | } |
michael@0 | 1339 | |
michael@0 | 1340 | return cx; |
michael@0 | 1341 | } |
michael@0 | 1342 | |
michael@0 | 1343 | |
michael@0 | 1344 | /* |
michael@0 | 1345 | * XXX Do we need a FilterProc, too? |
michael@0 | 1346 | */ |
michael@0 | 1347 | |
michael@0 | 1348 | |
michael@0 | 1349 | void |
michael@0 | 1350 | SEC_ASN1EncoderSetNotifyProc (SEC_ASN1EncoderContext *cx, |
michael@0 | 1351 | SEC_ASN1NotifyProc fn, void *arg) |
michael@0 | 1352 | { |
michael@0 | 1353 | cx->notify_proc = fn; |
michael@0 | 1354 | cx->notify_arg = arg; |
michael@0 | 1355 | } |
michael@0 | 1356 | |
michael@0 | 1357 | |
michael@0 | 1358 | void |
michael@0 | 1359 | SEC_ASN1EncoderClearNotifyProc (SEC_ASN1EncoderContext *cx) |
michael@0 | 1360 | { |
michael@0 | 1361 | cx->notify_proc = NULL; |
michael@0 | 1362 | cx->notify_arg = NULL; /* not necessary; just being clean */ |
michael@0 | 1363 | } |
michael@0 | 1364 | |
michael@0 | 1365 | void |
michael@0 | 1366 | SEC_ASN1EncoderAbort(SEC_ASN1EncoderContext *cx, int error) |
michael@0 | 1367 | { |
michael@0 | 1368 | PORT_Assert(cx); |
michael@0 | 1369 | PORT_SetError(error); |
michael@0 | 1370 | cx->status = encodeError; |
michael@0 | 1371 | } |
michael@0 | 1372 | |
michael@0 | 1373 | void |
michael@0 | 1374 | SEC_ASN1EncoderSetStreaming (SEC_ASN1EncoderContext *cx) |
michael@0 | 1375 | { |
michael@0 | 1376 | /* XXX is there a way to check that we are "between" fields here? */ |
michael@0 | 1377 | |
michael@0 | 1378 | cx->streaming = PR_TRUE; |
michael@0 | 1379 | } |
michael@0 | 1380 | |
michael@0 | 1381 | |
michael@0 | 1382 | void |
michael@0 | 1383 | SEC_ASN1EncoderClearStreaming (SEC_ASN1EncoderContext *cx) |
michael@0 | 1384 | { |
michael@0 | 1385 | /* XXX is there a way to check that we are "between" fields here? */ |
michael@0 | 1386 | |
michael@0 | 1387 | cx->streaming = PR_FALSE; |
michael@0 | 1388 | } |
michael@0 | 1389 | |
michael@0 | 1390 | |
michael@0 | 1391 | void |
michael@0 | 1392 | SEC_ASN1EncoderSetTakeFromBuf (SEC_ASN1EncoderContext *cx) |
michael@0 | 1393 | { |
michael@0 | 1394 | /* |
michael@0 | 1395 | * XXX is there a way to check that we are "between" fields here? this |
michael@0 | 1396 | * needs to include a check for being in between groups of items in |
michael@0 | 1397 | * a SET_OF or SEQUENCE_OF. |
michael@0 | 1398 | */ |
michael@0 | 1399 | PORT_Assert (cx->streaming); |
michael@0 | 1400 | |
michael@0 | 1401 | cx->from_buf = PR_TRUE; |
michael@0 | 1402 | } |
michael@0 | 1403 | |
michael@0 | 1404 | |
michael@0 | 1405 | void |
michael@0 | 1406 | SEC_ASN1EncoderClearTakeFromBuf (SEC_ASN1EncoderContext *cx) |
michael@0 | 1407 | { |
michael@0 | 1408 | /* we should actually be taking from buf *now* */ |
michael@0 | 1409 | PORT_Assert (cx->from_buf); |
michael@0 | 1410 | if (! cx->from_buf) /* if not, just do nothing */ |
michael@0 | 1411 | return; |
michael@0 | 1412 | |
michael@0 | 1413 | cx->from_buf = PR_FALSE; |
michael@0 | 1414 | |
michael@0 | 1415 | if (cx->status == needBytes) { |
michael@0 | 1416 | cx->status = keepGoing; |
michael@0 | 1417 | cx->current->place = afterContents; |
michael@0 | 1418 | } |
michael@0 | 1419 | } |
michael@0 | 1420 | |
michael@0 | 1421 | |
michael@0 | 1422 | SECStatus |
michael@0 | 1423 | SEC_ASN1Encode (const void *src, const SEC_ASN1Template *theTemplate, |
michael@0 | 1424 | SEC_ASN1WriteProc output_proc, void *output_arg) |
michael@0 | 1425 | { |
michael@0 | 1426 | SEC_ASN1EncoderContext *ecx; |
michael@0 | 1427 | SECStatus rv; |
michael@0 | 1428 | |
michael@0 | 1429 | ecx = SEC_ASN1EncoderStart (src, theTemplate, output_proc, output_arg); |
michael@0 | 1430 | if (ecx == NULL) |
michael@0 | 1431 | return SECFailure; |
michael@0 | 1432 | |
michael@0 | 1433 | rv = SEC_ASN1EncoderUpdate (ecx, NULL, 0); |
michael@0 | 1434 | |
michael@0 | 1435 | SEC_ASN1EncoderFinish (ecx); |
michael@0 | 1436 | return rv; |
michael@0 | 1437 | } |
michael@0 | 1438 | |
michael@0 | 1439 | |
michael@0 | 1440 | /* |
michael@0 | 1441 | * XXX depth and data_kind are unused; is there a PC way to silence warnings? |
michael@0 | 1442 | * (I mean "politically correct", not anything to do with intel/win platform) |
michael@0 | 1443 | */ |
michael@0 | 1444 | static void |
michael@0 | 1445 | sec_asn1e_encode_item_count (void *arg, const char *buf, unsigned long len, |
michael@0 | 1446 | int depth, SEC_ASN1EncodingPart data_kind) |
michael@0 | 1447 | { |
michael@0 | 1448 | unsigned long *count; |
michael@0 | 1449 | |
michael@0 | 1450 | count = (unsigned long*)arg; |
michael@0 | 1451 | PORT_Assert (count != NULL); |
michael@0 | 1452 | |
michael@0 | 1453 | *count += len; |
michael@0 | 1454 | } |
michael@0 | 1455 | |
michael@0 | 1456 | |
michael@0 | 1457 | /* XXX depth and data_kind are unused; is there a PC way to silence warnings? */ |
michael@0 | 1458 | static void |
michael@0 | 1459 | sec_asn1e_encode_item_store (void *arg, const char *buf, unsigned long len, |
michael@0 | 1460 | int depth, SEC_ASN1EncodingPart data_kind) |
michael@0 | 1461 | { |
michael@0 | 1462 | SECItem *dest; |
michael@0 | 1463 | |
michael@0 | 1464 | dest = (SECItem*)arg; |
michael@0 | 1465 | PORT_Assert (dest != NULL); |
michael@0 | 1466 | |
michael@0 | 1467 | PORT_Memcpy (dest->data + dest->len, buf, len); |
michael@0 | 1468 | dest->len += len; |
michael@0 | 1469 | } |
michael@0 | 1470 | |
michael@0 | 1471 | |
michael@0 | 1472 | /* |
michael@0 | 1473 | * Allocate an entire SECItem, or just the data part of it, to hold |
michael@0 | 1474 | * "len" bytes of stuff. Allocate from the given pool, if specified, |
michael@0 | 1475 | * otherwise just do a vanilla PORT_Alloc. |
michael@0 | 1476 | * |
michael@0 | 1477 | * XXX This seems like a reasonable general-purpose function (for SECITEM_)? |
michael@0 | 1478 | */ |
michael@0 | 1479 | static SECItem * |
michael@0 | 1480 | sec_asn1e_allocate_item (PLArenaPool *poolp, SECItem *dest, unsigned long len) |
michael@0 | 1481 | { |
michael@0 | 1482 | if (poolp != NULL) { |
michael@0 | 1483 | void *release; |
michael@0 | 1484 | |
michael@0 | 1485 | release = PORT_ArenaMark (poolp); |
michael@0 | 1486 | if (dest == NULL) |
michael@0 | 1487 | dest = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem)); |
michael@0 | 1488 | if (dest != NULL) { |
michael@0 | 1489 | dest->data = (unsigned char*)PORT_ArenaAlloc (poolp, len); |
michael@0 | 1490 | if (dest->data == NULL) { |
michael@0 | 1491 | dest = NULL; |
michael@0 | 1492 | } |
michael@0 | 1493 | } |
michael@0 | 1494 | if (dest == NULL) { |
michael@0 | 1495 | /* one or both allocations failed; release everything */ |
michael@0 | 1496 | PORT_ArenaRelease (poolp, release); |
michael@0 | 1497 | } else { |
michael@0 | 1498 | /* everything okay; unmark the arena */ |
michael@0 | 1499 | PORT_ArenaUnmark (poolp, release); |
michael@0 | 1500 | } |
michael@0 | 1501 | } else { |
michael@0 | 1502 | SECItem *indest; |
michael@0 | 1503 | |
michael@0 | 1504 | indest = dest; |
michael@0 | 1505 | if (dest == NULL) |
michael@0 | 1506 | dest = (SECItem*)PORT_Alloc (sizeof(SECItem)); |
michael@0 | 1507 | if (dest != NULL) { |
michael@0 | 1508 | dest->type = siBuffer; |
michael@0 | 1509 | dest->data = (unsigned char*)PORT_Alloc (len); |
michael@0 | 1510 | if (dest->data == NULL) { |
michael@0 | 1511 | if (indest == NULL) |
michael@0 | 1512 | PORT_Free (dest); |
michael@0 | 1513 | dest = NULL; |
michael@0 | 1514 | } |
michael@0 | 1515 | } |
michael@0 | 1516 | } |
michael@0 | 1517 | |
michael@0 | 1518 | return dest; |
michael@0 | 1519 | } |
michael@0 | 1520 | |
michael@0 | 1521 | |
michael@0 | 1522 | SECItem * |
michael@0 | 1523 | SEC_ASN1EncodeItem (PLArenaPool *poolp, SECItem *dest, const void *src, |
michael@0 | 1524 | const SEC_ASN1Template *theTemplate) |
michael@0 | 1525 | { |
michael@0 | 1526 | unsigned long encoding_length; |
michael@0 | 1527 | SECStatus rv; |
michael@0 | 1528 | |
michael@0 | 1529 | PORT_Assert (dest == NULL || dest->data == NULL); |
michael@0 | 1530 | |
michael@0 | 1531 | encoding_length = 0; |
michael@0 | 1532 | rv = SEC_ASN1Encode (src, theTemplate, |
michael@0 | 1533 | sec_asn1e_encode_item_count, &encoding_length); |
michael@0 | 1534 | if (rv != SECSuccess) |
michael@0 | 1535 | return NULL; |
michael@0 | 1536 | |
michael@0 | 1537 | dest = sec_asn1e_allocate_item (poolp, dest, encoding_length); |
michael@0 | 1538 | if (dest == NULL) |
michael@0 | 1539 | return NULL; |
michael@0 | 1540 | |
michael@0 | 1541 | /* XXX necessary? This really just checks for a bug in the allocate fn */ |
michael@0 | 1542 | PORT_Assert (dest->data != NULL); |
michael@0 | 1543 | if (dest->data == NULL) |
michael@0 | 1544 | return NULL; |
michael@0 | 1545 | |
michael@0 | 1546 | dest->len = 0; |
michael@0 | 1547 | (void) SEC_ASN1Encode (src, theTemplate, sec_asn1e_encode_item_store, dest); |
michael@0 | 1548 | |
michael@0 | 1549 | PORT_Assert (encoding_length == dest->len); |
michael@0 | 1550 | return dest; |
michael@0 | 1551 | } |
michael@0 | 1552 | |
michael@0 | 1553 | |
michael@0 | 1554 | static SECItem * |
michael@0 | 1555 | sec_asn1e_integer(PLArenaPool *poolp, SECItem *dest, unsigned long value, |
michael@0 | 1556 | PRBool is_unsigned) |
michael@0 | 1557 | { |
michael@0 | 1558 | unsigned long copy; |
michael@0 | 1559 | unsigned char sign; |
michael@0 | 1560 | int len = 0; |
michael@0 | 1561 | |
michael@0 | 1562 | /* |
michael@0 | 1563 | * Determine the length of the encoded value (minimum of 1). |
michael@0 | 1564 | */ |
michael@0 | 1565 | copy = value; |
michael@0 | 1566 | do { |
michael@0 | 1567 | len++; |
michael@0 | 1568 | sign = (unsigned char)(copy & 0x80); |
michael@0 | 1569 | copy >>= 8; |
michael@0 | 1570 | } while (copy); |
michael@0 | 1571 | |
michael@0 | 1572 | /* |
michael@0 | 1573 | * If 'value' is non-negative, and the high bit of the last |
michael@0 | 1574 | * byte we counted was set, we need to add one to the length so |
michael@0 | 1575 | * we put a high-order zero byte in the encoding. |
michael@0 | 1576 | */ |
michael@0 | 1577 | if (sign && (is_unsigned || (long)value >= 0)) |
michael@0 | 1578 | len++; |
michael@0 | 1579 | |
michael@0 | 1580 | /* |
michael@0 | 1581 | * Allocate the item (if necessary) and the data pointer within. |
michael@0 | 1582 | */ |
michael@0 | 1583 | dest = sec_asn1e_allocate_item (poolp, dest, len); |
michael@0 | 1584 | if (dest == NULL) |
michael@0 | 1585 | return NULL; |
michael@0 | 1586 | |
michael@0 | 1587 | /* |
michael@0 | 1588 | * Store the value, byte by byte, in the item. |
michael@0 | 1589 | */ |
michael@0 | 1590 | dest->len = len; |
michael@0 | 1591 | while (len) { |
michael@0 | 1592 | dest->data[--len] = (unsigned char)value; |
michael@0 | 1593 | value >>= 8; |
michael@0 | 1594 | } |
michael@0 | 1595 | PORT_Assert (value == 0); |
michael@0 | 1596 | |
michael@0 | 1597 | return dest; |
michael@0 | 1598 | } |
michael@0 | 1599 | |
michael@0 | 1600 | |
michael@0 | 1601 | SECItem * |
michael@0 | 1602 | SEC_ASN1EncodeInteger(PLArenaPool *poolp, SECItem *dest, long value) |
michael@0 | 1603 | { |
michael@0 | 1604 | return sec_asn1e_integer (poolp, dest, (unsigned long) value, PR_FALSE); |
michael@0 | 1605 | } |
michael@0 | 1606 | |
michael@0 | 1607 | |
michael@0 | 1608 | SECItem * |
michael@0 | 1609 | SEC_ASN1EncodeUnsignedInteger(PLArenaPool *poolp, |
michael@0 | 1610 | SECItem *dest, unsigned long value) |
michael@0 | 1611 | { |
michael@0 | 1612 | return sec_asn1e_integer (poolp, dest, value, PR_TRUE); |
michael@0 | 1613 | } |