/* Take a public-key S-Exp and convert it into a DER encoded publicKeyInfo */ gpg_error_t _ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, unsigned char **r_der, size_t *r_derlen) { gpg_error_t err; const unsigned char *s; char *endp; unsigned long n, n1; const unsigned char *oid; int oidlen; unsigned char *curve_oid = NULL; size_t curve_oidlen; pkalgo_t pkalgo; int i; struct { const char *name; int namelen; const unsigned char *value; int valuelen; } parm[10]; int parmidx; int idxtbl[10]; int idxtbllen; const char *parmdesc, *algoparmdesc; ksba_writer_t writer = NULL; void *algoparmseq_value = NULL; size_t algoparmseq_len; void *bitstr_value = NULL; size_t bitstr_len; if (!sexp) return gpg_error (GPG_ERR_INV_VALUE); s = sexp; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ s++; if (n != 10 || memcmp (s, "public-key", 10)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); s += 10; if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* Break out the algorithm ID */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ s++; oid = oid_from_buffer (s, n, &oidlen, &pkalgo); if (!oid) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); s += n; /* Collect all the values */ for (parmidx = 0; *s != ')' ; parmidx++) { if (parmidx >= DIM(parm)) return gpg_error (GPG_ERR_GENERAL); if (*s != '(') return gpg_error (digitp(s)? GPG_ERR_UNKNOWN_SEXP:GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].name = s; parm[parmidx].namelen = n; s += n; if (!digitp(s)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].value = s; parm[parmidx].valuelen = n; s += n; if ( *s != ')') return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ s++; } s++; /* We need another closing parenthesis. */ if ( *s != ')' ) return gpg_error (GPG_ERR_INV_SEXP); /* Describe the parameters in the order we want them and construct IDXTBL to access them. For DSA wie also set algoparmdesc so that we can later build the parameters for the algorithmIdentifier. */ algoparmdesc = NULL; switch (pkalgo) { case PKALGO_RSA: parmdesc = "ne"; break; case PKALGO_DSA: parmdesc = "y" ; algoparmdesc = "pqg"; break; case PKALGO_ECC: parmdesc = "Cq"; break; default: return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } idxtbllen = 0; for (s = parmdesc; *s; s++) { for (i=0; i < parmidx; i++) { assert (idxtbllen < DIM (idxtbl)); switch (*s) { case 'C': /* Magic value for "curve". */ if (parm[i].namelen == 5 && !memcmp (parm[i].name, "curve", 5)) { idxtbl[idxtbllen++] = i; i = parmidx; /* Break inner loop. */ } break; default: if (parm[i].namelen == 1 && parm[i].name[0] == *s) { idxtbl[idxtbllen++] = i; i = parmidx; /* Break inner loop. */ } break; } } } if (idxtbllen != strlen (parmdesc)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (pkalgo == PKALGO_ECC) { curve_oid = get_ecc_curve_oid (parm[idxtbl[0]].value, parm[idxtbl[0]].valuelen, &curve_oidlen); if (!curve_oid) return gpg_error (GPG_ERR_UNKNOWN_SEXP); } /* Create write object. */ err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* We create the keyinfo in 2 steps: 1. We build the inner one and encapsulate it in a bit string. 2. We create the outer sequence include the algorithm identifier and the bit string from step 1. */ if (pkalgo == PKALGO_ECC) { /* Write the bit string header and the number of unused bits. */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, parm[idxtbl[1]].valuelen + 1); if (!err) err = ksba_writer_write (writer, "", 1); /* And the actual raw value. */ if (!err) err = ksba_writer_write (writer, parm[idxtbl[1]].value, parm[idxtbl[1]].valuelen); if (err) goto leave; } else /* RSA and DSA */ { /* Calculate the size of the sequence value and the size of the bit string value. NOt ethat in case there is only one integer to write, no sequence is used. */ for (n=0, i=0; i < idxtbllen; i++ ) { n += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); n += parm[idxtbl[i]].valuelen; } n1 = 1; /* # of unused bits. */ if (idxtbllen > 1) n1 += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); n1 += n; /* Write the bit string header and the number of unused bits. */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, n1); if (!err) err = ksba_writer_write (writer, "", 1); if (err) goto leave; /* Write the sequence tag and the integers. */ if (idxtbllen > 1) err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1,n); if (err) goto leave; for (i=0; i < idxtbllen; i++) { /* fixme: we should make sure that the integer conforms to the ASN.1 encoding rules. */ err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); if (!err) err = ksba_writer_write (writer, parm[idxtbl[i]].value, parm[idxtbl[i]].valuelen); if (err) goto leave; } } /* Get the encoded bit string. */ bitstr_value = ksba_writer_snatch_mem (writer, &bitstr_len); if (!bitstr_value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* If the algorithmIdentifier requires a sequence with parameters, build them now. We can reuse the IDXTBL for that. */ if (algoparmdesc) { idxtbllen = 0; for (s = algoparmdesc; *s; s++) { for (i=0; i < parmidx; i++) { assert (idxtbllen < DIM (idxtbl)); if (parm[i].namelen == 1 && parm[i].name[0] == *s) { idxtbl[idxtbllen++] = i; break; } } } if (idxtbllen != strlen (algoparmdesc)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* Calculate the size of the sequence. */ for (n=0, i=0; i < idxtbllen; i++ ) { n += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); n += parm[idxtbl[i]].valuelen; } // n += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); /* Write the sequence tag followed by the integers. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (err) goto leave; for (i=0; i < idxtbllen; i++) { err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, parm[idxtbl[i]].valuelen); if (!err) err = ksba_writer_write (writer, parm[idxtbl[i]].value, parm[idxtbl[i]].valuelen); if (err) goto leave; } /* Get the encoded sequence. */ algoparmseq_value = ksba_writer_snatch_mem (writer, &algoparmseq_len); if (!algoparmseq_value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } } else algoparmseq_len = 0; /* Reinitialize the buffer to create the outer sequence. */ err = ksba_writer_set_mem (writer, 1024); if (err) goto leave; /* Calulate lengths. */ n = _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, oidlen); n += oidlen; if (algoparmseq_len) { n += algoparmseq_len; } else if (pkalgo == PKALGO_ECC) { n += _ksba_ber_count_tl (TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, curve_oidlen); n += curve_oidlen; } else if (pkalgo == PKALGO_RSA) { n += _ksba_ber_count_tl (TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } n1 = n; n1 += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); n1 += bitstr_len; /* The outer sequence. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1); if (err) goto leave; /* The sequence. */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (err) goto leave; /* The object id. */ err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID,CLASS_UNIVERSAL, 0, oidlen); if (!err) err = ksba_writer_write (writer, oid, oidlen); if (err) goto leave; /* The parameter. */ if (algoparmseq_len) { err = ksba_writer_write (writer, algoparmseq_value, algoparmseq_len); } else if (pkalgo == PKALGO_ECC) { err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, curve_oidlen); if (!err) err = ksba_writer_write (writer, curve_oid, curve_oidlen); } else if (pkalgo == PKALGO_RSA) { err = _ksba_ber_write_tl (writer, TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } if (err) goto leave; /* Append the pre-constructed bit string. */ err = ksba_writer_write (writer, bitstr_value, bitstr_len); if (err) goto leave; /* Get the result. */ *r_der = ksba_writer_snatch_mem (writer, r_derlen); if (!*r_der) err = gpg_error (GPG_ERR_ENOMEM); leave: ksba_writer_release (writer); xfree (bitstr_value); xfree (curve_oid); return err; }
/* The user has calculated the signatures and we can now write the signature */ static gpg_error_t sign_and_write (ksba_certreq_t cr) { gpg_error_t err; ksba_writer_t writer; void *value = NULL; size_t valuelen; err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 2048); if (err) goto leave; /* store the cri */ if (!cr->cri.der) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } err = ksba_writer_write (writer, cr->cri.der, cr->cri.derlen); if (err) goto leave; /* store the signatureAlgorithm */ if (!cr->sig_val.algo) return gpg_error (GPG_ERR_MISSING_VALUE); err = _ksba_der_write_algorithm_identifier (writer, cr->sig_val.algo, NULL, 0); if (err) goto leave; /* write the signature */ err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0, 1 + cr->sig_val.valuelen); if (!err) err = ksba_writer_write (writer, "", 1); if (!err) err = ksba_writer_write (writer, cr->sig_val.value, cr->sig_val.valuelen); if (err) goto leave; /* pack it into the outer sequence */ value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; /* write outer sequence */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; /* and finally write the result */ xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) err = gpg_error (GPG_ERR_ENOMEM); else if (!cr->writer) err = gpg_error (GPG_ERR_MISSING_ACTION); else err = ksba_writer_write (cr->writer, value, valuelen); leave: ksba_writer_release (writer); xfree (value); return err; }
/* Build the extension block and return it in R_DER and R_DERLEN. IF CERTMODE is true build X.509 certificate extension instead. */ static gpg_error_t build_extensions (ksba_certreq_t cr, int certmode, void **r_der, size_t *r_derlen) { gpg_error_t err; ksba_writer_t writer, w=NULL; struct extn_list_s *e; unsigned char *value = NULL; size_t valuelen; unsigned char *p; size_t n; *r_der = NULL; *r_derlen = 0; err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 2048); if (err) goto leave; err = ksba_writer_new (&w); if (err) goto leave; for (e=cr->extn_list; e; e = e->next) { err = ksba_writer_set_mem (w, e->derlen + 100); if (err) goto leave; err = ksba_oid_from_str (e->oid, &p, &n); if(err) goto leave; err = _ksba_ber_write_tl (w, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, n); if (!err) err = ksba_writer_write (w, p, n); xfree (p); if (e->critical) { err = _ksba_ber_write_tl (w, TYPE_BOOLEAN, CLASS_UNIVERSAL, 0, 1); if (!err) err = ksba_writer_write (w, "\xff", 1); if(err) goto leave; } err = _ksba_ber_write_tl (w, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0, e->derlen); if (!err) err = ksba_writer_write (w, e->der, e->derlen); if(err) goto leave; p = ksba_writer_snatch_mem (w, &n); if (!p) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n); if (!err) err = ksba_writer_write (writer, p, n); xfree (p); p = NULL; if (err) goto leave; } /* Embed all the sequences into another sequence */ value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } if (!certmode) { /* Now create the extension request sequence content */ err = ksba_writer_set_mem (writer, valuelen+100); if (err) goto leave; err = ksba_oid_from_str (oidstr_extensionReq, &p, &n); if(err) goto leave; err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, n); if (!err) err = ksba_writer_write (writer, p, n); xfree (p); p = NULL; if (err) return err; err = _ksba_ber_write_tl (writer, TYPE_SET, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); /* Put this all into a SEQUENCE */ xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } } *r_der = value; *r_derlen = valuelen; value = NULL; leave: ksba_writer_release (writer); ksba_writer_release (w); xfree (value); return err; }
/* Build a value tree from the already stored values. */ static gpg_error_t build_cri (ksba_certreq_t cr) { gpg_error_t err; ksba_writer_t writer; void *value = NULL; size_t valuelen; int certmode; /* If a serial number has been set, we don't create a CSR but a proper certificate. */ certmode = !!cr->x509.serial.der; err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 2048); if (err) goto leave; if (!cr->key.der) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } /* We write all stuff out to a temporary writer object, then use this object to create the cri and store the cri image */ if (certmode) { /* Store the version structure; version is 3 (encoded as 2): [0] { INTEGER 2 } */ err = ksba_writer_write (writer, "\xa0\x03\x02\x01\x02", 5); } else { /* Store version v1 (which is a 0). */ err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1); if (!err) err = ksba_writer_write (writer, "", 1); } if (err) goto leave; /* For a certificate we need to store the s/n, the signature algorithm identifier, the issuer DN and the validity. */ if (certmode) { /* Store the serial number. */ err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, cr->x509.serial.derlen); if (!err) err = ksba_writer_write (writer, cr->x509.serial.der, cr->x509.serial.derlen); if (err) goto leave; /* Store the signature algorithm identifier. */ if (!cr->x509.siginfo.der) err = gpg_error (GPG_ERR_MISSING_VALUE); else err = ksba_writer_write (writer, cr->x509.siginfo.der, cr->x509.siginfo.derlen); if (err) goto leave; /* Store the issuer DN. If no issuer DN has been set we use the subject DN. */ if (cr->x509.issuer.der) err = ksba_writer_write (writer, cr->x509.issuer.der, cr->x509.issuer.derlen); else if (cr->subject.der) err = ksba_writer_write (writer, cr->subject.der, cr->subject.derlen); else err = gpg_error (GPG_ERR_MISSING_VALUE); if (err) goto leave; /* Store the Validity. */ { unsigned char templ[36]; unsigned char *tp; tp = templ; *tp++ = 0x30; *tp++ = 0x22; *tp++ = TYPE_GENERALIZED_TIME; *tp++ = 15; if (cr->x509.not_before[0]) { if (_ksba_cmp_time (cr->x509.not_before, "20500101T000000") >= 0) { memcpy (tp, cr->x509.not_before, 8); tp += 8; memcpy (tp, cr->x509.not_before+9, 6); tp += 6; } else { tp[-2] = TYPE_UTC_TIME; tp[-1] = 13; memcpy (tp, cr->x509.not_before+2, 6); tp += 6; memcpy (tp, cr->x509.not_before+9, 6); tp += 6; } } else { tp[-2] = TYPE_UTC_TIME; tp[-1] = 13; memcpy (tp, "110101000000", 12); tp += 12; } *tp++ = 'Z'; *tp++ = TYPE_GENERALIZED_TIME; *tp++ = 15; if (cr->x509.not_after[0]) { if (_ksba_cmp_time (cr->x509.not_after, "20500101T000000") >= 0) { memcpy (tp, cr->x509.not_after, 8); tp += 8; memcpy (tp, cr->x509.not_after+9, 6); tp += 6; } else { tp[-2] = TYPE_UTC_TIME; tp[-1] = 13; memcpy (tp, cr->x509.not_after+2, 6); tp += 6; memcpy (tp, cr->x509.not_after+9, 6); tp += 6; } } else { memcpy (tp,"20630405170000", 14); tp += 14; } *tp++ = 'Z'; assert (tp - templ <= 36); templ[1] = tp - templ - 2; /* Fixup the sequence length. */ err = ksba_writer_write (writer, templ, tp - templ); if (err) goto leave; } } /* store the subject */ if (!cr->subject.der) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } err = ksba_writer_write (writer, cr->subject.der, cr->subject.derlen); if (err) goto leave; /* store the public key info */ err = ksba_writer_write (writer, cr->key.der, cr->key.derlen); if (err) goto leave; /* Copy generalNames objects to the extension list. */ if (cr->subject_alt_names) { err = add_general_names_to_extn (cr, cr->subject_alt_names, oidstr_subjectAltName); if (err) goto leave; while (cr->subject_alt_names) { struct general_names_s *tmp = cr->subject_alt_names->next; xfree (cr->subject_alt_names); cr->subject_alt_names = tmp; } cr->subject_alt_names = NULL; } /* Write the extensions. Note that the implicit SET OF is REQUIRED */ xfree (value); value = NULL; valuelen = 0; if (cr->extn_list) { err = build_extensions (cr, certmode, &value, &valuelen); if (err) goto leave; err = _ksba_ber_write_tl (writer, certmode? 3:0, CLASS_CONTEXT, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; } else { /* We can't write an object of length zero using our ber_write function. So we must open encode it. */ err = ksba_writer_write (writer, certmode? "\xa3\x02\x30":"\xa0\x02\x30", 4); if (err) goto leave; } /* pack it into the sequence */ xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* reinitialize the buffer to create the outer sequence */ err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; /* write outer sequence */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; /* and store the final result */ cr->cri.der = ksba_writer_snatch_mem (writer, &cr->cri.derlen); if (!cr->cri.der) err = gpg_error (GPG_ERR_ENOMEM); leave: ksba_writer_release (writer); xfree (value); return err; }
/* Build a value tree from the already stored values. */ static gpg_error_t build_cri (ksba_certreq_t cr) { gpg_error_t err; ksba_writer_t writer; void *value = NULL; size_t valuelen; err = ksba_writer_new (&writer); if (err) goto leave; err = ksba_writer_set_mem (writer, 2048); if (err) goto leave; /* We write all stuff out to a temporary writer object, then use this object to create the cri and store the cri image */ /* store version v1 (which is a 0) */ err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1); if (!err) err = ksba_writer_write (writer, "", 1); if (err) goto leave; /* store the subject */ if (!cr->subject.der) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } err = ksba_writer_write (writer, cr->subject.der, cr->subject.derlen); if (err) goto leave; /* store the public key info */ if (!cr->key.der) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } err = ksba_writer_write (writer, cr->key.der, cr->key.derlen); if (err) goto leave; /* Copy generalNames objects to the extension list. */ if (cr->subject_alt_names) { err = add_general_names_to_extn (cr, cr->subject_alt_names, oidstr_subjectAltName); if (err) goto leave; while (cr->subject_alt_names) { struct general_names_s *tmp = cr->subject_alt_names->next; xfree (cr->subject_alt_names); cr->subject_alt_names = tmp; } cr->subject_alt_names = NULL; } /* Write the extensions. Note that the implicit SET OF is REQUIRED */ xfree (value); value = NULL; valuelen = 0; if (cr->extn_list) { err = build_extensions (cr, &value, &valuelen); if (err) goto leave; err = _ksba_ber_write_tl (writer, 0, CLASS_CONTEXT, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; } else { /* We can't write an object of length zero using our ber_write function. So we must open encode it. */ err = ksba_writer_write (writer, "\xa0\x02\x30", 4); if (err) goto leave; } /* pack it into the sequence */ xfree (value); value = ksba_writer_snatch_mem (writer, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* reinitialize the buffer to create the outer sequence */ err = ksba_writer_set_mem (writer, valuelen+10); if (err) goto leave; /* write outer sequence */ err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (writer, value, valuelen); if (err) goto leave; /* and store the final result */ cr->cri.der = ksba_writer_snatch_mem (writer, &cr->cri.derlen); if (!cr->cri.der) err = gpg_error (GPG_ERR_ENOMEM); leave: ksba_writer_release (writer); xfree (value); return err; }