From 230fd6b7b66f0b72b1cf7fc300652a1adf94ba1a Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Thu, 3 Oct 2002 12:38:52 +0000 Subject: Preliminary streaming ASN1 encode support. --- crypto/asn1/tasn_enc.c | 239 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 176 insertions(+), 63 deletions(-) (limited to 'crypto/asn1/tasn_enc.c') diff --git a/crypto/asn1/tasn_enc.c b/crypto/asn1/tasn_enc.c index f6c8ddef0a..33d6366e02 100644 --- a/crypto/asn1/tasn_enc.c +++ b/crypto/asn1/tasn_enc.c @@ -3,7 +3,7 @@ * project 2000. */ /* ==================================================================== - * Copyright (c) 2000 The OpenSSL Project. All rights reserved. + * Copyright (c) 2000-2002 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -63,38 +63,57 @@ #include #include -static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass); -static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *seq, unsigned char **out, int skcontlen, const ASN1_ITEM *item, int isset); +static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out, + const ASN1_ITEM *it, + int tag, int aclass); +static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out, + int skcontlen, const ASN1_ITEM *item, + int do_sort, int iclass); +static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out, + const ASN1_TEMPLATE *tt, + int tag, int aclass); +static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out, + const ASN1_ITEM *it, int flags); + +/* Top level i2d equivalents: the 'ndef' variant instructs the encoder + * to use indefinite length constructed encoding, where appropriate + */ + +int ASN1_item_ndef_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it) +{ + return asn1_item_flags_i2d(val, out, it, ASN1_TFLG_NDEF); +} -/* Encode an ASN1 item, this is compatible with the +int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it) +{ + return asn1_item_flags_i2d(val, out, it, 0); +} + +/* Encode an ASN1 item, this is use by the * standard 'i2d' function. 'out' points to - * a buffer to output the data to, in future we will - * have more advanced versions that can output data - * a piece at a time and this will simply be a special - * case. + * a buffer to output the data to. * * The new i2d has one additional feature. If the output * buffer is NULL (i.e. *out == NULL) then a buffer is * allocated and populated with the encoding. */ - -int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it) +static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it, int flags) { if(out && !*out) { unsigned char *p, *buf; int len; - len = ASN1_item_ex_i2d(&val, NULL, it, -1, 0); + len = ASN1_item_ex_i2d(&val, NULL, it, -1, flags); if(len <= 0) return len; buf = OPENSSL_malloc(len); if(!buf) return -1; p = buf; - ASN1_item_ex_i2d(&val, &p, it, -1, 0); + ASN1_item_ex_i2d(&val, &p, it, -1, flags); *out = buf; return len; } - - return ASN1_item_ex_i2d(&val, out, it, -1, 0); + + return ASN1_item_ex_i2d(&val, out, it, -1, flags); } /* Encode an item, taking care of IMPLICIT tagging (if any). @@ -102,31 +121,34 @@ int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it) * used in external types. */ -int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass) +int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, + const ASN1_ITEM *it, int tag, int aclass) { const ASN1_TEMPLATE *tt = NULL; unsigned char *p = NULL; - int i, seqcontlen, seqlen; - ASN1_STRING *strtmp; + int i, seqcontlen, seqlen, ndef = 1; const ASN1_COMPAT_FUNCS *cf; const ASN1_EXTERN_FUNCS *ef; const ASN1_AUX *aux = it->funcs; - ASN1_aux_cb *asn1_cb; - if((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval) return 0; - if(aux && aux->asn1_cb) asn1_cb = aux->asn1_cb; - else asn1_cb = 0; + ASN1_aux_cb *asn1_cb = 0; + + if((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval) + return 0; + + if(aux && aux->asn1_cb) + asn1_cb = aux->asn1_cb; switch(it->itype) { case ASN1_ITYPE_PRIMITIVE: if(it->templates) - return ASN1_template_i2d(pval, out, it->templates); + return asn1_template_ex_i2d(pval, out, it->templates, + tag, aclass); return asn1_i2d_ex_primitive(pval, out, it, tag, aclass); break; case ASN1_ITYPE_MSTRING: - strtmp = (ASN1_STRING *)*pval; - return asn1_i2d_ex_primitive(pval, out, it, -1, 0); + return asn1_i2d_ex_primitive(pval, out, it, -1, aclass); case ASN1_ITYPE_CHOICE: if(asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it)) @@ -137,7 +159,8 @@ int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it const ASN1_TEMPLATE *chtt; chtt = it->templates + i; pchval = asn1_get_field_ptr(pval, chtt); - return ASN1_template_i2d(pchval, out, chtt); + return asn1_template_ex_i2d(pchval, out, chtt, + -1, aclass); } /* Fixme: error condition if selector out of range */ if(asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it)) @@ -161,6 +184,12 @@ int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it *p = aclass | tag | (*p & V_ASN1_CONSTRUCTED); return i; + case ASN1_ITYPE_NDEF_SEQUENCE: +//fprintf(stderr, "NDEF sequence from %s flags %d\n", it->sname, aclass & ASN1_TFLG_NDEF); + /* Use indefinite length constructed if requested */ + if (aclass & ASN1_TFLG_NDEF) ndef = 2; + /* fall through */ + case ASN1_ITYPE_SEQUENCE: i = asn1_enc_restore(&seqcontlen, out, pval, it); /* An error occurred */ @@ -172,7 +201,9 @@ int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it /* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */ if(tag == -1) { tag = V_ASN1_SEQUENCE; - aclass = V_ASN1_UNIVERSAL; + /* Retain any other flags in aclass */ + aclass = (aclass & ~ASN1_TFLG_TAG_CLASS) + | V_ASN1_UNIVERSAL; } if(asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it)) return 0; @@ -184,13 +215,13 @@ int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it if(!seqtt) return 0; pseqval = asn1_get_field_ptr(pval, seqtt); /* FIXME: check for errors in enhanced version */ - /* FIXME: special handling of indefinite length encoding */ - seqcontlen += ASN1_template_i2d(pseqval, NULL, seqtt); + seqcontlen += asn1_template_ex_i2d(pseqval, NULL, seqtt, + -1, aclass); } - seqlen = ASN1_object_size(1, seqcontlen, tag); + seqlen = ASN1_object_size(ndef, seqcontlen, tag); if(!out) return seqlen; /* Output SEQUENCE header */ - ASN1_put_object(out, 1, seqcontlen, tag, aclass); + ASN1_put_object(out, ndef, seqcontlen, tag, aclass); for(i = 0, tt = it->templates; i < it->tcount; tt++, i++) { const ASN1_TEMPLATE *seqtt; ASN1_VALUE **pseqval; @@ -198,8 +229,9 @@ int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it if(!seqtt) return 0; pseqval = asn1_get_field_ptr(pval, seqtt); /* FIXME: check for errors in enhanced version */ - ASN1_template_i2d(pseqval, out, seqtt); + asn1_template_ex_i2d(pseqval, out, seqtt, -1, aclass); } + if (ndef == 2) ASN1_put_eoc(out); if(asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it)) return 0; return seqlen; @@ -210,42 +242,91 @@ int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it return 0; } -int ASN1_template_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_TEMPLATE *tt) +static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_TEMPLATE *tt, int tag, int iclass) { - int i, ret, flags, aclass; + int i, ret, flags, ttag, tclass, ndef; flags = tt->flags; - aclass = flags & ASN1_TFLG_TAG_CLASS; + /* Work out tag and class to use: tagging may come + * either from the template or the arguments, not both + * because this would create ambiguity. Additionally + * the iclass argument may contain some additional flags + * which should be noted and passed down to other levels. + */ + if (flags & ASN1_TFLG_TAG_MASK) + { + /* Error if argument and template tagging */ + if (tag != -1) + /* FIXME: error code here */ + return -1; + /* Get tagging from template */ + ttag = tt->tag; + tclass = flags & ASN1_TFLG_TAG_CLASS; + } + else if (tag != -1) + { + /* No template tagging, get from arguments */ + ttag = tag; + tclass = iclass & ASN1_TFLG_TAG_CLASS; + } + else + { + ttag = -1; + tclass = 0; + } + /* + * Remove any class mask from iflag. + */ + iclass &= ~ASN1_TFLG_TAG_CLASS; + + /* At this point 'ttag' contains the outer tag to use, + * 'tclass' is the class and iclass is any flags passed + * to this function. + */ + + /* if template and arguments require ndef, use it */ + if ((flags & ASN1_TFLG_NDEF) && (iclass & ASN1_TFLG_NDEF)) + ndef = 2; + else ndef = 1; + if(flags & ASN1_TFLG_SK_MASK) { /* SET OF, SEQUENCE OF */ STACK_OF(ASN1_VALUE) *sk = (STACK_OF(ASN1_VALUE) *)*pval; int isset, sktag, skaclass; int skcontlen, sklen; ASN1_VALUE *skitem; + if(!*pval) return 0; + if(flags & ASN1_TFLG_SET_OF) { isset = 1; /* 2 means we reorder */ if(flags & ASN1_TFLG_SEQUENCE_OF) isset = 2; } else isset = 0; - /* First work out inner tag value */ - if(flags & ASN1_TFLG_IMPTAG) { - sktag = tt->tag; - skaclass = aclass; + + /* Work out inner tag value: if EXPLICIT + * or no tagging use underlying type. + */ + if((ttag != -1) && !(flags & ASN1_TFLG_EXPTAG)) { + sktag = ttag; + skaclass = tclass; } else { skaclass = V_ASN1_UNIVERSAL; if(isset) sktag = V_ASN1_SET; else sktag = V_ASN1_SEQUENCE; } - /* Now work out length of items */ + + /* Determine total length of items */ skcontlen = 0; for(i = 0; i < sk_ASN1_VALUE_num(sk); i++) { skitem = sk_ASN1_VALUE_value(sk, i); - skcontlen += ASN1_item_ex_i2d(&skitem, NULL, ASN1_ITEM_ptr(tt->item), -1, 0); + skcontlen += ASN1_item_ex_i2d(&skitem, NULL, + ASN1_ITEM_ptr(tt->item), + -1, iclass); } - sklen = ASN1_object_size(1, skcontlen, sktag); + sklen = ASN1_object_size(ndef, skcontlen, sktag); /* If EXPLICIT need length of surrounding tag */ if(flags & ASN1_TFLG_EXPTAG) - ret = ASN1_object_size(1, sklen, tt->tag); + ret = ASN1_object_size(ndef, sklen, ttag); else ret = sklen; if(!out) return ret; @@ -253,35 +334,43 @@ int ASN1_template_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_TEMPLAT /* Now encode this lot... */ /* EXPLICIT tag */ if(flags & ASN1_TFLG_EXPTAG) - ASN1_put_object(out, 1, sklen, tt->tag, aclass); + ASN1_put_object(out, ndef, sklen, ttag, tclass); /* SET or SEQUENCE and IMPLICIT tag */ - ASN1_put_object(out, 1, skcontlen, sktag, skaclass); - /* And finally the stuff itself */ - asn1_set_seq_out(sk, out, skcontlen, ASN1_ITEM_ptr(tt->item), isset); + ASN1_put_object(out, ndef, skcontlen, sktag, skaclass); + /* And the stuff itself */ + asn1_set_seq_out(sk, out, skcontlen, ASN1_ITEM_ptr(tt->item), + isset, iclass); + if (ndef == 2) { + ASN1_put_eoc(out); + if(flags & ASN1_TFLG_EXPTAG) + ASN1_put_eoc(out); + } return ret; } - + if(flags & ASN1_TFLG_EXPTAG) { /* EXPLICIT tagging */ /* Find length of tagged item */ - i = ASN1_item_ex_i2d(pval, NULL, ASN1_ITEM_ptr(tt->item), -1, 0); + i = ASN1_item_ex_i2d(pval, NULL, ASN1_ITEM_ptr(tt->item), + -1, iclass); if(!i) return 0; /* Find length of EXPLICIT tag */ - ret = ASN1_object_size(1, i, tt->tag); + ret = ASN1_object_size(ndef, i, ttag); if(out) { /* Output tag and item */ - ASN1_put_object(out, 1, i, tt->tag, aclass); - ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), -1, 0); + ASN1_put_object(out, ndef, i, ttag, tclass); + ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), + -1, iclass); + if (ndef == 2) ASN1_put_eoc(out); } return ret; } - if(flags & ASN1_TFLG_IMPTAG) { - /* IMPLICIT tagging */ - return ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), tt->tag, aclass); - } - /* Nothing special: treat as normal */ - return ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), -1, 0); + + /* Either normal or IMPLICIT tagging: combine class and flags */ + return ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), + ttag, tclass | iclass); + } /* Temporary structure used to hold DER encoding of items for SET OF */ @@ -304,7 +393,9 @@ static int der_cmp(const void *a, const void *b) /* Output the content octets of SET OF or SEQUENCE OF */ -static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out, int skcontlen, const ASN1_ITEM *item, int do_sort) +static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out, + int skcontlen, const ASN1_ITEM *item, + int do_sort, int iclass) { int i; ASN1_VALUE *skitem; @@ -323,7 +414,7 @@ static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out, int s if(!do_sort) { for(i = 0; i < sk_ASN1_VALUE_num(sk); i++) { skitem = sk_ASN1_VALUE_value(sk, i); - ASN1_item_i2d(skitem, out, item); + ASN1_item_ex_i2d(&skitem, out, item, -1, iclass); } return 1; } @@ -332,7 +423,7 @@ static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out, int s for(i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++) { skitem = sk_ASN1_VALUE_value(sk, i); tder->data = p; - tder->length = ASN1_item_i2d(skitem, &p, item); + tder->length = ASN1_item_ex_i2d(&skitem, &p, item, -1, iclass); tder->field = skitem; } /* Now sort them */ @@ -359,6 +450,7 @@ static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out, const A int len; int utype; int usetag; + int ndef = 0; utype = it->utype; @@ -381,19 +473,27 @@ static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out, const A /* -1 means omit type */ - if(len == -1) return 0; + if(len == -1) + return 0; + + /* -2 return is special meaning use ndef */ + if (len == -2) + { + ndef = 2; + len = 0; + } /* If not implicitly tagged get tag from underlying type */ if(tag == -1) tag = utype; /* Output tag+length followed by content octets */ if(out) { - if(usetag) ASN1_put_object(out, 0, len, tag, aclass); + if(usetag) ASN1_put_object(out, ndef, len, tag, aclass); asn1_ex_i2c(pval, *out, &utype, it); *out += len; } - if(usetag) return ASN1_object_size(0, len, tag); + if(usetag) return ASN1_object_size(ndef, len, tag); return len; } @@ -486,6 +586,19 @@ int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *putype, const ASN1_ default: /* All based on ASN1_STRING and handled the same */ strtmp = (ASN1_STRING *)*pval; + /* Special handling for NDEF */ + if ((it->size == ASN1_TFLG_NDEF) + && (strtmp->flags & ASN1_STRING_FLAG_NDEF)) + { + if (cout) + { + strtmp->data = cout; + strtmp->length = 0; + ASN1_put_eoc(&cout); + } + /* Special return code */ + return -2; + } cont = strtmp->data; len = strtmp->length; -- cgit v1.2.3