Home | History | Annotate | Line # | Download | only in hx509
      1 /*	$NetBSD: ks_p12.c,v 1.2 2017/01/28 21:31:48 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2004 - 2007 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * 3. Neither the name of the Institute nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include "hx_locl.h"
     37 
     38 struct ks_pkcs12 {
     39     hx509_certs certs;
     40     char *fn;
     41 };
     42 
     43 typedef int (*collector_func)(hx509_context,
     44 			      struct hx509_collector *,
     45 			      const void *, size_t,
     46 			      const PKCS12_Attributes *);
     47 
     48 struct type {
     49     const heim_oid *oid;
     50     collector_func func;
     51 };
     52 
     53 static void
     54 parse_pkcs12_type(hx509_context, struct hx509_collector *, const heim_oid *,
     55 		  const void *, size_t, const PKCS12_Attributes *);
     56 
     57 
     58 static const PKCS12_Attribute *
     59 find_attribute(const PKCS12_Attributes *attrs, const heim_oid *oid)
     60 {
     61     size_t i;
     62     if (attrs == NULL)
     63 	return NULL;
     64     for (i = 0; i < attrs->len; i++)
     65 	if (der_heim_oid_cmp(oid, &attrs->val[i].attrId) == 0)
     66 	    return &attrs->val[i];
     67     return NULL;
     68 }
     69 
     70 static int
     71 keyBag_parser(hx509_context context,
     72 	      struct hx509_collector *c,
     73 	      const void *data, size_t length,
     74 	      const PKCS12_Attributes *attrs)
     75 {
     76     const PKCS12_Attribute *attr;
     77     PKCS8PrivateKeyInfo ki;
     78     const heim_octet_string *os = NULL;
     79     int ret;
     80 
     81     attr = find_attribute(attrs, &asn1_oid_id_pkcs_9_at_localKeyId);
     82     if (attr)
     83 	os = &attr->attrValues;
     84 
     85     ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
     86     if (ret)
     87 	return ret;
     88 
     89     _hx509_collector_private_key_add(context,
     90 				     c,
     91 				     &ki.privateKeyAlgorithm,
     92 				     NULL,
     93 				     &ki.privateKey,
     94 				     os);
     95     free_PKCS8PrivateKeyInfo(&ki);
     96     return 0;
     97 }
     98 
     99 static int
    100 ShroudedKeyBag_parser(hx509_context context,
    101 		      struct hx509_collector *c,
    102 		      const void *data, size_t length,
    103 		      const PKCS12_Attributes *attrs)
    104 {
    105     PKCS8EncryptedPrivateKeyInfo pk;
    106     heim_octet_string content;
    107     int ret;
    108 
    109     memset(&pk, 0, sizeof(pk));
    110 
    111     ret = decode_PKCS8EncryptedPrivateKeyInfo(data, length, &pk, NULL);
    112     if (ret)
    113 	return ret;
    114 
    115     ret = _hx509_pbe_decrypt(context,
    116 			     _hx509_collector_get_lock(c),
    117 			     &pk.encryptionAlgorithm,
    118 			     &pk.encryptedData,
    119 			     &content);
    120     free_PKCS8EncryptedPrivateKeyInfo(&pk);
    121     if (ret)
    122 	return ret;
    123 
    124     ret = keyBag_parser(context, c, content.data, content.length, attrs);
    125     der_free_octet_string(&content);
    126     return ret;
    127 }
    128 
    129 static int
    130 certBag_parser(hx509_context context,
    131 	       struct hx509_collector *c,
    132 	       const void *data, size_t length,
    133 	       const PKCS12_Attributes *attrs)
    134 {
    135     heim_error_t error = NULL;
    136     heim_octet_string os;
    137     hx509_cert cert;
    138     PKCS12_CertBag cb;
    139     int ret;
    140 
    141     ret = decode_PKCS12_CertBag(data, length, &cb, NULL);
    142     if (ret)
    143 	return ret;
    144 
    145     if (der_heim_oid_cmp(&asn1_oid_id_pkcs_9_at_certTypes_x509, &cb.certType)) {
    146 	free_PKCS12_CertBag(&cb);
    147 	return 0;
    148     }
    149 
    150     ret = decode_PKCS12_OctetString(cb.certValue.data,
    151 				    cb.certValue.length,
    152 				    &os,
    153 				    NULL);
    154     free_PKCS12_CertBag(&cb);
    155     if (ret)
    156 	return ret;
    157 
    158     cert = hx509_cert_init_data(context, os.data, os.length, &error);
    159     der_free_octet_string(&os);
    160     if (cert == NULL) {
    161 	ret = heim_error_get_code(error);
    162 	heim_release(error);
    163 	return ret;
    164     }
    165 
    166     ret = _hx509_collector_certs_add(context, c, cert);
    167     if (ret) {
    168 	hx509_cert_free(cert);
    169 	return ret;
    170     }
    171 
    172     {
    173 	const PKCS12_Attribute *attr;
    174 	const heim_oid *oids[] = {
    175 	    &asn1_oid_id_pkcs_9_at_localKeyId, &asn1_oid_id_pkcs_9_at_friendlyName
    176 	};
    177 	size_t i;
    178 
    179 	for  (i = 0; i < sizeof(oids)/sizeof(oids[0]); i++) {
    180 	    const heim_oid *oid = oids[i];
    181 	    attr = find_attribute(attrs, oid);
    182 	    if (attr)
    183 		_hx509_set_cert_attribute(context, cert, oid,
    184 					  &attr->attrValues);
    185 	}
    186     }
    187 
    188     hx509_cert_free(cert);
    189 
    190     return 0;
    191 }
    192 
    193 static int
    194 parse_safe_content(hx509_context context,
    195 		   struct hx509_collector *c,
    196 		   const unsigned char *p, size_t len)
    197 {
    198     PKCS12_SafeContents sc;
    199     int ret;
    200     size_t i;
    201 
    202     memset(&sc, 0, sizeof(sc));
    203 
    204     ret = decode_PKCS12_SafeContents(p, len, &sc, NULL);
    205     if (ret)
    206 	return ret;
    207 
    208     for (i = 0; i < sc.len ; i++)
    209 	parse_pkcs12_type(context,
    210 			  c,
    211 			  &sc.val[i].bagId,
    212 			  sc.val[i].bagValue.data,
    213 			  sc.val[i].bagValue.length,
    214 			  sc.val[i].bagAttributes);
    215 
    216     free_PKCS12_SafeContents(&sc);
    217     return 0;
    218 }
    219 
    220 static int
    221 safeContent_parser(hx509_context context,
    222 		   struct hx509_collector *c,
    223 		   const void *data, size_t length,
    224 		   const PKCS12_Attributes *attrs)
    225 {
    226     heim_octet_string os;
    227     int ret;
    228 
    229     ret = decode_PKCS12_OctetString(data, length, &os, NULL);
    230     if (ret)
    231 	return ret;
    232     ret = parse_safe_content(context, c, os.data, os.length);
    233     der_free_octet_string(&os);
    234     return ret;
    235 }
    236 
    237 static int
    238 encryptedData_parser(hx509_context context,
    239 		     struct hx509_collector *c,
    240 		     const void *data, size_t length,
    241 		     const PKCS12_Attributes *attrs)
    242 {
    243     heim_octet_string content;
    244     heim_oid contentType;
    245     int ret;
    246 
    247     memset(&contentType, 0, sizeof(contentType));
    248 
    249     ret = hx509_cms_decrypt_encrypted(context,
    250 				      _hx509_collector_get_lock(c),
    251 				      data, length,
    252 				      &contentType,
    253 				      &content);
    254     if (ret)
    255 	return ret;
    256 
    257     if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) == 0)
    258 	ret = parse_safe_content(context, c, content.data, content.length);
    259 
    260     der_free_octet_string(&content);
    261     der_free_oid(&contentType);
    262     return ret;
    263 }
    264 
    265 static int
    266 envelopedData_parser(hx509_context context,
    267 		     struct hx509_collector *c,
    268 		     const void *data, size_t length,
    269 		     const PKCS12_Attributes *attrs)
    270 {
    271     heim_octet_string content;
    272     heim_oid contentType;
    273     hx509_lock lock;
    274     int ret;
    275 
    276     memset(&contentType, 0, sizeof(contentType));
    277 
    278     lock = _hx509_collector_get_lock(c);
    279 
    280     ret = hx509_cms_unenvelope(context,
    281 			       _hx509_lock_unlock_certs(lock),
    282 			       0,
    283 			       data, length,
    284 			       NULL,
    285 			       0,
    286 			       &contentType,
    287 			       &content);
    288     if (ret) {
    289 	hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
    290 			       "PKCS12 failed to unenvelope");
    291 	return ret;
    292     }
    293 
    294     if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) == 0)
    295 	ret = parse_safe_content(context, c, content.data, content.length);
    296 
    297     der_free_octet_string(&content);
    298     der_free_oid(&contentType);
    299 
    300     return ret;
    301 }
    302 
    303 
    304 struct type bagtypes[] = {
    305     { &asn1_oid_id_pkcs12_keyBag, keyBag_parser },
    306     { &asn1_oid_id_pkcs12_pkcs8ShroudedKeyBag, ShroudedKeyBag_parser },
    307     { &asn1_oid_id_pkcs12_certBag, certBag_parser },
    308     { &asn1_oid_id_pkcs7_data, safeContent_parser },
    309     { &asn1_oid_id_pkcs7_encryptedData, encryptedData_parser },
    310     { &asn1_oid_id_pkcs7_envelopedData, envelopedData_parser }
    311 };
    312 
    313 static void
    314 parse_pkcs12_type(hx509_context context,
    315 		  struct hx509_collector *c,
    316 		  const heim_oid *oid,
    317 		  const void *data, size_t length,
    318 		  const PKCS12_Attributes *attrs)
    319 {
    320     size_t i;
    321 
    322     for (i = 0; i < sizeof(bagtypes)/sizeof(bagtypes[0]); i++)
    323 	if (der_heim_oid_cmp(bagtypes[i].oid, oid) == 0)
    324 	    (*bagtypes[i].func)(context, c, data, length, attrs);
    325 }
    326 
    327 static int
    328 p12_init(hx509_context context,
    329 	 hx509_certs certs, void **data, int flags,
    330 	 const char *residue, hx509_lock lock)
    331 {
    332     struct ks_pkcs12 *p12;
    333     size_t len;
    334     void *buf;
    335     PKCS12_PFX pfx;
    336     PKCS12_AuthenticatedSafe as;
    337     int ret;
    338     size_t i;
    339     struct hx509_collector *c;
    340 
    341     *data = NULL;
    342 
    343     if (lock == NULL)
    344 	lock = _hx509_empty_lock;
    345 
    346     ret = _hx509_collector_alloc(context, lock, &c);
    347     if (ret)
    348 	return ret;
    349 
    350     p12 = calloc(1, sizeof(*p12));
    351     if (p12 == NULL) {
    352 	ret = ENOMEM;
    353 	hx509_set_error_string(context, 0, ret, "out of memory");
    354 	goto out;
    355     }
    356 
    357     p12->fn = strdup(residue);
    358     if (p12->fn == NULL) {
    359 	ret = ENOMEM;
    360 	hx509_set_error_string(context, 0, ret, "out of memory");
    361 	goto out;
    362     }
    363 
    364     if (flags & HX509_CERTS_CREATE) {
    365 	ret = hx509_certs_init(context, "MEMORY:ks-file-create",
    366 			       0, lock, &p12->certs);
    367 	if (ret == 0)
    368 	    *data = p12;
    369 	goto out;
    370     }
    371 
    372     ret = rk_undumpdata(residue, &buf, &len);
    373     if (ret) {
    374 	hx509_clear_error_string(context);
    375 	goto out;
    376     }
    377 
    378     ret = decode_PKCS12_PFX(buf, len, &pfx, NULL);
    379     rk_xfree(buf);
    380     if (ret) {
    381 	hx509_set_error_string(context, 0, ret,
    382 			       "Failed to decode the PFX in %s", residue);
    383 	goto out;
    384     }
    385 
    386     if (der_heim_oid_cmp(&pfx.authSafe.contentType, &asn1_oid_id_pkcs7_data) != 0) {
    387 	free_PKCS12_PFX(&pfx);
    388 	ret = EINVAL;
    389 	hx509_set_error_string(context, 0, ret,
    390 			       "PKCS PFX isn't a pkcs7-data container");
    391 	goto out;
    392     }
    393 
    394     if (pfx.authSafe.content == NULL) {
    395 	free_PKCS12_PFX(&pfx);
    396 	ret = EINVAL;
    397 	hx509_set_error_string(context, 0, ret,
    398 			       "PKCS PFX missing data");
    399 	goto out;
    400     }
    401 
    402     {
    403 	heim_octet_string asdata;
    404 
    405 	ret = decode_PKCS12_OctetString(pfx.authSafe.content->data,
    406 					pfx.authSafe.content->length,
    407 					&asdata,
    408 					NULL);
    409 	free_PKCS12_PFX(&pfx);
    410 	if (ret) {
    411 	    hx509_clear_error_string(context);
    412 	    goto out;
    413 	}
    414 	ret = decode_PKCS12_AuthenticatedSafe(asdata.data,
    415 					      asdata.length,
    416 					      &as,
    417 					      NULL);
    418 	der_free_octet_string(&asdata);
    419 	if (ret) {
    420 	    hx509_clear_error_string(context);
    421 	    goto out;
    422 	}
    423     }
    424 
    425     for (i = 0; i < as.len; i++)
    426 	parse_pkcs12_type(context,
    427 			  c,
    428 			  &as.val[i].contentType,
    429 			  as.val[i].content->data,
    430 			  as.val[i].content->length,
    431 			  NULL);
    432 
    433     free_PKCS12_AuthenticatedSafe(&as);
    434 
    435     ret = _hx509_collector_collect_certs(context, c, &p12->certs);
    436     if (ret == 0)
    437 	*data = p12;
    438 
    439 out:
    440     _hx509_collector_free(c);
    441 
    442     if (ret && p12) {
    443 	if (p12->fn)
    444 	    free(p12->fn);
    445 	if (p12->certs)
    446 	    hx509_certs_free(&p12->certs);
    447 	free(p12);
    448     }
    449 
    450     return ret;
    451 }
    452 
    453 static int
    454 addBag(hx509_context context,
    455        PKCS12_AuthenticatedSafe *as,
    456        const heim_oid *oid,
    457        void *data,
    458        size_t length)
    459 {
    460     void *ptr;
    461     int ret;
    462 
    463     ptr = realloc(as->val, sizeof(as->val[0]) * (as->len + 1));
    464     if (ptr == NULL) {
    465 	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
    466 	return ENOMEM;
    467     }
    468     as->val = ptr;
    469 
    470     ret = der_copy_oid(oid, &as->val[as->len].contentType);
    471     if (ret) {
    472 	hx509_set_error_string(context, 0, ret, "out of memory");
    473 	return ret;
    474     }
    475 
    476     as->val[as->len].content = calloc(1, sizeof(*as->val[0].content));
    477     if (as->val[as->len].content == NULL) {
    478 	der_free_oid(&as->val[as->len].contentType);
    479 	hx509_set_error_string(context, 0, ENOMEM, "malloc out of memory");
    480 	return ENOMEM;
    481     }
    482 
    483     as->val[as->len].content->data = data;
    484     as->val[as->len].content->length = length;
    485 
    486     as->len++;
    487 
    488     return 0;
    489 }
    490 
    491 static int
    492 store_func(hx509_context context, void *ctx, hx509_cert c)
    493 {
    494     PKCS12_AuthenticatedSafe *as = ctx;
    495     PKCS12_OctetString os;
    496     PKCS12_CertBag cb;
    497     size_t size;
    498     int ret;
    499 
    500     memset(&os, 0, sizeof(os));
    501     memset(&cb, 0, sizeof(cb));
    502 
    503     os.data = NULL;
    504     os.length = 0;
    505 
    506     ret = hx509_cert_binary(context, c, &os);
    507     if (ret)
    508 	return ret;
    509 
    510     ASN1_MALLOC_ENCODE(PKCS12_OctetString,
    511 		       cb.certValue.data,cb.certValue.length,
    512 		       &os, &size, ret);
    513     free(os.data);
    514     if (ret)
    515 	goto out;
    516     ret = der_copy_oid(&asn1_oid_id_pkcs_9_at_certTypes_x509, &cb.certType);
    517     if (ret) {
    518 	free_PKCS12_CertBag(&cb);
    519 	goto out;
    520     }
    521     ASN1_MALLOC_ENCODE(PKCS12_CertBag, os.data, os.length,
    522 		       &cb, &size, ret);
    523     free_PKCS12_CertBag(&cb);
    524     if (ret)
    525 	goto out;
    526 
    527     ret = addBag(context, as, &asn1_oid_id_pkcs12_certBag, os.data, os.length);
    528 
    529     if (_hx509_cert_private_key_exportable(c)) {
    530 	hx509_private_key key = _hx509_cert_private_key(c);
    531 	PKCS8PrivateKeyInfo pki;
    532 
    533 	memset(&pki, 0, sizeof(pki));
    534 
    535 	ret = der_parse_hex_heim_integer("00", &pki.version);
    536 	if (ret)
    537 	    return ret;
    538 	ret = _hx509_private_key_oid(context, key,
    539 				     &pki.privateKeyAlgorithm.algorithm);
    540 	if (ret) {
    541 	    free_PKCS8PrivateKeyInfo(&pki);
    542 	    return ret;
    543 	}
    544 	ret = _hx509_private_key_export(context,
    545 					_hx509_cert_private_key(c),
    546 					HX509_KEY_FORMAT_DER,
    547 					&pki.privateKey);
    548 	if (ret) {
    549 	    free_PKCS8PrivateKeyInfo(&pki);
    550 	    return ret;
    551 	}
    552 	/* set attribute, asn1_oid_id_pkcs_9_at_localKeyId */
    553 
    554 	ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, os.data, os.length,
    555 			   &pki, &size, ret);
    556 	free_PKCS8PrivateKeyInfo(&pki);
    557 	if (ret)
    558 	    return ret;
    559 
    560 	ret = addBag(context, as, &asn1_oid_id_pkcs12_keyBag, os.data, os.length);
    561 	if (ret)
    562 	    return ret;
    563     }
    564 
    565 out:
    566     return ret;
    567 }
    568 
    569 static int
    570 p12_store(hx509_context context,
    571 	  hx509_certs certs, void *data, int flags, hx509_lock lock)
    572 {
    573     struct ks_pkcs12 *p12 = data;
    574     PKCS12_PFX pfx;
    575     PKCS12_AuthenticatedSafe as;
    576     PKCS12_OctetString asdata;
    577     size_t size;
    578     int ret;
    579 
    580     memset(&as, 0, sizeof(as));
    581     memset(&pfx, 0, sizeof(pfx));
    582 
    583     ret = hx509_certs_iter_f(context, p12->certs, store_func, &as);
    584     if (ret)
    585 	goto out;
    586 
    587     ASN1_MALLOC_ENCODE(PKCS12_AuthenticatedSafe, asdata.data, asdata.length,
    588 		       &as, &size, ret);
    589     free_PKCS12_AuthenticatedSafe(&as);
    590     if (ret)
    591 	return ret;
    592 
    593     ret = der_parse_hex_heim_integer("03", &pfx.version);
    594     if (ret) {
    595 	free(asdata.data);
    596 	goto out;
    597     }
    598 
    599     pfx.authSafe.content = calloc(1, sizeof(*pfx.authSafe.content));
    600 
    601     ASN1_MALLOC_ENCODE(PKCS12_OctetString,
    602 		       pfx.authSafe.content->data,
    603 		       pfx.authSafe.content->length,
    604 		       &asdata, &size, ret);
    605     free(asdata.data);
    606     if (ret)
    607 	goto out;
    608 
    609     ret = der_copy_oid(&asn1_oid_id_pkcs7_data, &pfx.authSafe.contentType);
    610     if (ret)
    611 	goto out;
    612 
    613     ASN1_MALLOC_ENCODE(PKCS12_PFX, asdata.data, asdata.length,
    614 		       &pfx, &size, ret);
    615     if (ret)
    616 	goto out;
    617 
    618 #if 0
    619     const struct _hx509_password *pw;
    620 
    621     pw = _hx509_lock_get_passwords(lock);
    622     if (pw != NULL) {
    623 	pfx.macData = calloc(1, sizeof(*pfx.macData));
    624 	if (pfx.macData == NULL) {
    625 	    ret = ENOMEM;
    626 	    hx509_set_error_string(context, 0, ret, "malloc out of memory");
    627 	    return ret;
    628 	}
    629 	if (pfx.macData == NULL) {
    630 	    free(asdata.data);
    631 	    goto out;
    632 	}
    633     }
    634     ret = calculate_hash(&aspath, pw, pfx.macData);
    635 #endif
    636 
    637     rk_dumpdata(p12->fn, asdata.data, asdata.length);
    638     free(asdata.data);
    639 
    640 out:
    641     free_PKCS12_AuthenticatedSafe(&as);
    642     free_PKCS12_PFX(&pfx);
    643 
    644     return ret;
    645 }
    646 
    647 
    648 static int
    649 p12_free(hx509_certs certs, void *data)
    650 {
    651     struct ks_pkcs12 *p12 = data;
    652     hx509_certs_free(&p12->certs);
    653     free(p12->fn);
    654     free(p12);
    655     return 0;
    656 }
    657 
    658 static int
    659 p12_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
    660 {
    661     struct ks_pkcs12 *p12 = data;
    662     return hx509_certs_add(context, p12->certs, c);
    663 }
    664 
    665 static int
    666 p12_iter_start(hx509_context context,
    667 	       hx509_certs certs,
    668 	       void *data,
    669 	       void **cursor)
    670 {
    671     struct ks_pkcs12 *p12 = data;
    672     return hx509_certs_start_seq(context, p12->certs, cursor);
    673 }
    674 
    675 static int
    676 p12_iter(hx509_context context,
    677 	 hx509_certs certs,
    678 	 void *data,
    679 	 void *cursor,
    680 	 hx509_cert *cert)
    681 {
    682     struct ks_pkcs12 *p12 = data;
    683     return hx509_certs_next_cert(context, p12->certs, cursor, cert);
    684 }
    685 
    686 static int
    687 p12_iter_end(hx509_context context,
    688 	     hx509_certs certs,
    689 	     void *data,
    690 	     void *cursor)
    691 {
    692     struct ks_pkcs12 *p12 = data;
    693     return hx509_certs_end_seq(context, p12->certs, cursor);
    694 }
    695 
    696 static struct hx509_keyset_ops keyset_pkcs12 = {
    697     "PKCS12",
    698     0,
    699     p12_init,
    700     p12_store,
    701     p12_free,
    702     p12_add,
    703     NULL,
    704     p12_iter_start,
    705     p12_iter,
    706     p12_iter_end,
    707     NULL,
    708     NULL,
    709     NULL
    710 };
    711 
    712 void
    713 _hx509_ks_pkcs12_register(hx509_context context)
    714 {
    715     _hx509_ks_register(context, &keyset_pkcs12);
    716 }
    717