summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/hx509
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/hx509')
-rw-r--r--third_party/heimdal/lib/hx509/ChangeLog2749
-rw-r--r--third_party/heimdal/lib/hx509/Makefile.am398
-rw-r--r--third_party/heimdal/lib/hx509/NTMakefile180
-rw-r--r--third_party/heimdal/lib/hx509/TODO61
-rw-r--r--third_party/heimdal/lib/hx509/ca.c3093
-rw-r--r--third_party/heimdal/lib/hx509/cert.c3892
-rw-r--r--third_party/heimdal/lib/hx509/char_map.h45
-rw-r--r--third_party/heimdal/lib/hx509/cms.c1718
-rw-r--r--third_party/heimdal/lib/hx509/collector.c331
-rw-r--r--third_party/heimdal/lib/hx509/crypto-ec.c533
-rw-r--r--third_party/heimdal/lib/hx509/crypto.c2804
-rw-r--r--third_party/heimdal/lib/hx509/data/PKITS.pdfbin0 -> 754584 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/PKITS_data.zipbin0 -> 2149008 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/bleichenbacher-bad.pem12
-rw-r--r--third_party/heimdal/lib/hx509/data/bleichenbacher-good.pem12
-rw-r--r--third_party/heimdal/lib/hx509/data/bleichenbacher-sf-pad-correct.pem16
-rw-r--r--third_party/heimdal/lib/hx509/data/ca.crt32
-rw-r--r--third_party/heimdal/lib/hx509/data/ca.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/crl1.crl16
-rw-r--r--third_party/heimdal/lib/hx509/data/crl1.derbin0 -> 649 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/eccurve.pem3
-rwxr-xr-xthird_party/heimdal/lib/hx509/data/gen-req.sh360
-rw-r--r--third_party/heimdal/lib/hx509/data/https.crt116
-rw-r--r--third_party/heimdal/lib/hx509/data/https.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/j.pem26
-rw-r--r--third_party/heimdal/lib/hx509/data/kdc.crt122
-rw-r--r--third_party/heimdal/lib/hx509/data/kdc.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/key.derbin0 -> 609 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/key2.derbin0 -> 610 bytes
-rwxr-xr-xthird_party/heimdal/lib/hx509/data/mkcert.sh83
-rw-r--r--third_party/heimdal/lib/hx509/data/n0ll.pem47
-rw-r--r--third_party/heimdal/lib/hx509/data/nist-data91
-rw-r--r--third_party/heimdal/lib/hx509/data/nist-data2291
-rw-r--r--third_party/heimdal/lib/hx509/data/nist-result231
-rw-r--r--third_party/heimdal/lib/hx509/data/no-proxy-test.crt30
-rw-r--r--third_party/heimdal/lib/hx509/data/no-proxy-test.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-req1.derbin0 -> 105 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-req2.derbin0 -> 105 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-resp1-2.derbin0 -> 999 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-resp1-3.derbin0 -> 363 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-resp1-ca.derbin0 -> 2159 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-resp1-keyhash.derbin0 -> 2060 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-resp1-ocsp-no-cert.derbin0 -> 748 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-resp1-ocsp.derbin0 -> 2078 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-resp1.derbin0 -> 918 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-resp2.derbin0 -> 2095 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-responder.crt119
-rw-r--r--third_party/heimdal/lib/hx509/data/ocsp-responder.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/openssl.1.0.cnf190
-rw-r--r--third_party/heimdal/lib/hx509/data/openssl.1.1.cnf185
-rw-r--r--third_party/heimdal/lib/hx509/data/pkinit-ec.crt81
-rw-r--r--third_party/heimdal/lib/hx509/data/pkinit-ec.key5
-rw-r--r--third_party/heimdal/lib/hx509/data/pkinit-proxy-chain.crt149
-rw-r--r--third_party/heimdal/lib/hx509/data/pkinit-proxy.crt30
-rw-r--r--third_party/heimdal/lib/hx509/data/pkinit-proxy.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/pkinit-pw.key54
-rw-r--r--third_party/heimdal/lib/hx509/data/pkinit.crt119
-rw-r--r--third_party/heimdal/lib/hx509/data/pkinit.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy-level-test.crt31
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy-level-test.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy-test.crt30
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy-test.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy10-child-child-test.crt32
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy10-child-child-test.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy10-child-test.crt31
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy10-child-test.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy10-test.crt30
-rw-r--r--third_party/heimdal/lib/hx509/data/proxy10-test.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/revoke.crt116
-rw-r--r--third_party/heimdal/lib/hx509/data/revoke.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r1TestCA.cert.pem12
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r1TestCA.key.pem5
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r1TestCA.pem17
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r2TestClient.cert.pem12
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r2TestClient.key.pem5
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r2TestClient.pem17
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r2TestServer.cert.pem12
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r2TestServer.key.pem5
-rw-r--r--third_party/heimdal/lib/hx509/data/secp256r2TestServer.pem17
-rw-r--r--third_party/heimdal/lib/hx509/data/sf-class2-root.pem24
-rw-r--r--third_party/heimdal/lib/hx509/data/static-file84
-rw-r--r--third_party/heimdal/lib/hx509/data/sub-ca.crt123
-rw-r--r--third_party/heimdal/lib/hx509/data/sub-ca.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/sub-cert.crt116
-rw-r--r--third_party/heimdal/lib/hx509/data/sub-cert.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/sub-cert.p12bin0 -> 7072 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/tcg-devid.pem24
-rw-r--r--third_party/heimdal/lib/hx509/data/tcg-ek-cp.pem24
-rw-r--r--third_party/heimdal/lib/hx509/data/test-ds-only.crt117
-rw-r--r--third_party/heimdal/lib/hx509/data/test-ds-only.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/test-enveloped-aes-128bin0 -> 3547 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-enveloped-aes-256bin0 -> 3547 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-enveloped-desbin0 -> 3527 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-enveloped-des-ede3bin0 -> 3530 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-enveloped-rc2-128bin0 -> 3535 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-enveloped-rc2-40bin0 -> 3536 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-enveloped-rc2-64bin0 -> 3535 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-ke-only.crt117
-rw-r--r--third_party/heimdal/lib/hx509/data/test-ke-only.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/test-nopw.p12bin0 -> 5510 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-pw.key54
-rw-r--r--third_party/heimdal/lib/hx509/data/test-signed-databin0 -> 5057 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-signed-data-noattrbin0 -> 4826 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-signed-data-noattr-nocertsbin0 -> 3537 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-signed-sha-1bin0 -> 5037 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-signed-sha-256bin0 -> 5057 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test-signed-sha-512bin0 -> 5090 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/test.combined.crt168
-rw-r--r--third_party/heimdal/lib/hx509/data/test.crt116
-rw-r--r--third_party/heimdal/lib/hx509/data/test.key52
-rw-r--r--third_party/heimdal/lib/hx509/data/test.p12bin0 -> 5608 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/win-u16-in-printablestring.derbin0 -> 772 bytes
-rw-r--r--third_party/heimdal/lib/hx509/data/yutaka-pad-broken-ca.pem16
-rw-r--r--third_party/heimdal/lib/hx509/data/yutaka-pad-broken-cert.pem18
-rw-r--r--third_party/heimdal/lib/hx509/data/yutaka-pad-ok-ca.pem16
-rw-r--r--third_party/heimdal/lib/hx509/data/yutaka-pad-ok-cert.pem18
-rw-r--r--third_party/heimdal/lib/hx509/data/yutaka-pad.key15
-rw-r--r--third_party/heimdal/lib/hx509/doxygen.c85
-rw-r--r--third_party/heimdal/lib/hx509/env.c245
-rw-r--r--third_party/heimdal/lib/hx509/error.c220
-rw-r--r--third_party/heimdal/lib/hx509/file.c387
-rw-r--r--third_party/heimdal/lib/hx509/hx509.h221
-rw-r--r--third_party/heimdal/lib/hx509/hx509_err.et110
-rw-r--r--third_party/heimdal/lib/hx509/hx_locl.h330
-rw-r--r--third_party/heimdal/lib/hx509/hxtool-commands.in1057
-rw-r--r--third_party/heimdal/lib/hx509/hxtool-version.rc36
-rw-r--r--third_party/heimdal/lib/hx509/hxtool.c3162
-rw-r--r--third_party/heimdal/lib/hx509/keyset.c846
-rw-r--r--third_party/heimdal/lib/hx509/ks_dir.c231
-rw-r--r--third_party/heimdal/lib/hx509/ks_file.c800
-rw-r--r--third_party/heimdal/lib/hx509/ks_keychain.c627
-rw-r--r--third_party/heimdal/lib/hx509/ks_mem.c224
-rw-r--r--third_party/heimdal/lib/hx509/ks_null.c101
-rw-r--r--third_party/heimdal/lib/hx509/ks_p11.c1230
-rw-r--r--third_party/heimdal/lib/hx509/ks_p12.c756
-rw-r--r--third_party/heimdal/lib/hx509/libhx509-exports.def288
-rw-r--r--third_party/heimdal/lib/hx509/lock.c252
-rw-r--r--third_party/heimdal/lib/hx509/name.c1529
-rw-r--r--third_party/heimdal/lib/hx509/peer.c237
-rw-r--r--third_party/heimdal/lib/hx509/print.c1144
-rw-r--r--third_party/heimdal/lib/hx509/quote.py101
-rw-r--r--third_party/heimdal/lib/hx509/ref/pkcs11.h1722
-rw-r--r--third_party/heimdal/lib/hx509/req.c1520
-rw-r--r--third_party/heimdal/lib/hx509/revoke.c1650
-rw-r--r--third_party/heimdal/lib/hx509/sel-gram.y130
-rw-r--r--third_party/heimdal/lib/hx509/sel-lex.l148
-rw-r--r--third_party/heimdal/lib/hx509/sel.c240
-rw-r--r--third_party/heimdal/lib/hx509/sel.h100
-rw-r--r--third_party/heimdal/lib/hx509/softp11.c1777
-rw-r--r--third_party/heimdal/lib/hx509/test_ca.in480
-rw-r--r--third_party/heimdal/lib/hx509/test_cert.in84
-rw-r--r--third_party/heimdal/lib/hx509/test_chain.in256
-rw-r--r--third_party/heimdal/lib/hx509/test_cms.in514
-rw-r--r--third_party/heimdal/lib/hx509/test_crypto.in192
-rw-r--r--third_party/heimdal/lib/hx509/test_expr.c87
-rw-r--r--third_party/heimdal/lib/hx509/test_java_pkcs11.in73
-rw-r--r--third_party/heimdal/lib/hx509/test_name.c468
-rw-r--r--third_party/heimdal/lib/hx509/test_nist.in117
-rw-r--r--third_party/heimdal/lib/hx509/test_nist2.in136
-rw-r--r--third_party/heimdal/lib/hx509/test_nist_cert.in68
-rw-r--r--third_party/heimdal/lib/hx509/test_nist_pkcs12.in77
-rw-r--r--third_party/heimdal/lib/hx509/test_pkcs11.in62
-rw-r--r--third_party/heimdal/lib/hx509/test_query.in203
-rw-r--r--third_party/heimdal/lib/hx509/test_req.in135
-rw-r--r--third_party/heimdal/lib/hx509/test_soft_pkcs11.c228
-rw-r--r--third_party/heimdal/lib/hx509/test_windows.in89
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-available113
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-available25
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-available36
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-select1
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-select11
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-select21
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-select31
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-select41
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-select51
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-select61
-rw-r--r--third_party/heimdal/lib/hx509/tst-crypto-select71
-rw-r--r--third_party/heimdal/lib/hx509/version-script.map302
178 files changed, 45491 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/hx509/ChangeLog b/third_party/heimdal/lib/hx509/ChangeLog
new file mode 100644
index 0000000..d00f1f3
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ChangeLog
@@ -0,0 +1,2749 @@
+2008-07-14 Love Hörnquist Åstrand <lha@kth.se>
+
+ * hxtool.c: Break out print_eval_types().
+
+2008-06-21 Love Hörnquist Åstrand <lha@kth.se>
+
+ * ks_p12.c: pass in time_now to unevelope
+
+ * cms.c: Pass in time_now to unevelope, us verify context time in
+ verify_signed.
+
+2008-05-23 Love Hörnquist Åstrand <lha@kth.se>
+
+ * hx_locl.h: Include <limits.h> for TYPE_MAX defines.
+
+2008-04-29 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * sel-lex.l: Use _hx509_sel_yyerror() instead of error_message().
+
+2008-04-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * sel-lex.l: Include <config.h>
+
+2008-04-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Update make-proto usage.
+
+2008-04-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ca.c: BasicConstraints.pathLenConstraint unsigned int.
+
+ * sel-lex.l: Prefix sel_error with _hx509_ since its global on
+ platforms w/o symbol versioning.
+
+ * sel.h: rename yyerror to sel_yyerror in the whole library, not
+ just the lexer
+
+ * sel-lex.l: rename yyerror to sel_yyerror in the whole library,
+ not just the lexer
+
+2008-04-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * sel-lex.l: Rename yyerror to sel_yyerror and make it static.
+
+2008-04-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hx509.h: Make self-standing by including missing files.
+
+2008-04-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: Use unsigned where appropriate.
+
+ * softp11.c: call va_start before using vsnprintf.
+
+ * crypto.c: make refcount slightly more sane.
+
+ * keyset.c: make refcount slightly more sane.
+
+ * cert.c: make refcount slightly more sane.
+
+2008-03-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_nist2.in: Try to find unzip.
+
+2008-03-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * version-script.map: add missing symbols
+
+ * spnego: Make delegated credentials delegated directly, Oleg
+ Sharoiko pointed out that it always didnt work with the old
+ code. Also add som missing cred and context pass-thou functions in
+ the SPNEGO layer.
+
+2008-03-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * rename to be more consistent, export for teting
+
+ * Add language to support querying certificates to find a
+ match. Support constructs like "1.3.6.1.5.2.3.5" IN
+ %{certificate.eku} AND %{certificate.subject} TAILMATCH "C=SE".
+
+2008-02-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * version-script.map: add hx509_pem_read
+
+ * hxtool-commands.in: Add --pem to cms-verify-sd.
+
+ * test_cms.in: Test verifying PEM signature files.
+
+ * hxtool.c: Support verifying PEM signature files.
+
+2008-02-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: libhx509_la_OBJECTS depends on hx_locl.h
+
+2008-02-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Use ldap-prep (with libwind) to compare names
+
+2008-01-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c (hx509_query_match_eku): update to support the NULL
+ eku (reset), clearify the old behaivor with regards repetitive
+ calls.
+
+ * Add matching on EKU, validate EKUs, add hxtool matching glue,
+ add check. Adapted from pach from Tim Miller of Mitre
+
+2008-01-21 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_soft_pkcs11.c: use func for more C_ functions.
+
+2008-01-18 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * version-script.map: Export hx509_free_error_string().
+
+2008-01-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * version-script.map: only export C_GetFunctionList
+
+ * test_soft_pkcs11.c: use C_GetFunctionList
+
+ * softp11.c: fix comment, remove label.
+
+ * softp11.c: Add option app-fatal to control if softtoken should
+ abort() on erroneous input from applications.
+
+2008-01-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_pkcs11.in: Test password less certificates too
+
+ * keyset.c: document HX509_CERTS_UNPROTECT_ALL
+
+ * ks_file.c: Support HX509_CERTS_UNPROTECT_ALL.
+
+ * hx509.h: Add HX509_CERTS_UNPROTECT_ALL.
+
+ * test_soft_pkcs11.c: Only log in if needed.
+
+2008-01-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * softp11.c: Support PINs to login to the store.
+
+ * Makefile.am: add java pkcs11 test
+
+ * test_java_pkcs11.in: first version of disable java test
+
+ * softp11.c: Drop unused stuff.
+
+ * cert.c: Spelling, Add hx509_cert_get_SPKI_AlgorithmIdentifier,
+ remove unused stuff, add hx509_context to some functions.
+
+ * softp11.c: Add more glue to figure out what keytype this
+ certificate is using.
+
+2008-01-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_pkcs11.in: test debug
+
+ * Add a PKCS11 provider supporting signing and verifing sigatures.
+
+2008-01-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * version-script.map: Replace hx509_name_to_der_name with
+ hx509_name_binary.
+
+ * print.c: make print_func static
+
+2007-12-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * print.c: doxygen
+
+ * env.c: doxygen
+
+ * doxygen.c: add more groups
+
+ * ca.c: doxygen.
+
+2007-12-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ca.c: doxygen
+
+2007-12-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * error.c: doxygen
+
+2007-12-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * More documentation
+
+ * lock.c: Add page referance
+
+ * keyset.c: some more documentation.
+
+ * cms.c: Doxygen documentation.
+
+2007-12-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * *.[ch]: More documentation
+
+2007-12-09 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * handle refcount on NULL.
+
+ * test_nist_pkcs12.in: drop echo -n, doesn't work with posix sh
+
+2007-12-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_nist2.in: Print that this is version 2 of the tests
+
+ * test_nist.in: Drop printing of $id.
+
+ * hx509.h: Add HX509_VHN_F_ALLOW_NO_MATCH.
+
+ * name.c: spelling.
+
+ * cert.c: make work the doxygen.
+
+ * name.c: fix doxygen compiling.
+
+ * Makefile.am: add doxygen.c
+
+ * doxygen.c: Add doxygen main page.
+
+ * cert.c: Add doxygen.
+
+ * revoke.c (_hx509_revoke_ref): new function.
+
+2007-11-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_keychain.c: Check if SecKeyGetCSPHandle needs prototype.
+
+2007-08-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * data/nist-data: Make work on case senstive filesystems too.
+
+2007-08-09 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c: match rfc822 contrains better, provide better error
+ strings.
+
+2007-08-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c: "self-signed doesn't count" doesn't apply to trust
+ anchor certificate. make trust anchor check consistant.
+
+ * revoke.c: make compile.
+
+ * revoke.c (verify_crl): set error strings.
+
+ * revoke.c (verify_crl): handle with the signer is the
+ CRLsigner (shortcut).
+
+ * cert.c: Fix NC, comment on how to use _hx509_check_key_usage.
+
+2007-08-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_nist2.in, Makefile, test/nist*: Add nist pkits tests.
+
+ * revoke.c: Update to use CERT_REVOKED error, shortcut out of OCSP
+ checking when OCSP reply is a revocation reply.
+
+ * hx509_err.et: Make CERT_REVOKED error OCSP/CRL agnostic.
+
+ * name.c (_hx509_Name_to_string): make printableString handle
+ space (0x20) diffrences as required by rfc3280.
+
+ * revoke.c: Search for the right issuer when looking for the
+ issuer of the CRL signer.
+
+2007-08-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * revoke.c: Handle CRL signing certificate better, try to not
+ revalidate invalid CRLs over and over.
+
+2007-08-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c: remove stale comment.
+
+ * test_nist.in: Unpack PKITS_data.zip and run tests.
+
+ * test_nist_cert.in: Adapt to new nist pkits framework.
+
+ * test_nist_pkcs12.in: Adapt to new nist pkits framework.
+
+ * Makefile.am: clean PKITS_data
+
+2007-07-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Add version-script.map to EXTRA_DIST
+
+2007-07-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Add depenency on asn1_compile for asn1 built files.
+
+2007-07-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * peer.c: update (c), indent.
+
+ * Makefile.am: New library version.
+
+2007-06-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: Add sha2 types.
+
+ * ref/pkcs11.h: Sync with scute.
+
+ * ref/pkcs11.h: Add sha2 CKM's.
+
+ * print.c: Print authorityInfoAccess.
+
+ * cert.c: Rename proxyCertInfo oid.
+
+ * ca.c: Rename proxyCertInfo oid.
+
+ * print.c: Rename proxyCertInfo oid.
+
+2007-06-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ca.in: Adapt to new request handling.
+
+ * req.c: Allow export some of the request parameters.
+
+ * hxtool-commands.in: Adapt to new request handling.
+
+ * hxtool.c: Adapt to new request handling.
+
+ * test_req.in: Adapt to new request handling.
+
+ * version-script.map: Add initialize_hx_error_table_r.
+
+ * req.c: Move _hx509_request_print here.
+
+ * hxtool.c: use _hx509_request_print
+
+ * version-script.map: Export more crap^W semiprivate functions.
+
+ * hxtool.c: don't _hx509_abort
+
+ * version-script.map: add missing ;
+
+2007-06-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c: Use hx509_crypto_random_iv.
+
+ * crypto.c: Split out the iv creation from hx509_crypto_encrypt
+ since _hx509_pbe_encrypt needs to use the iv from the s2k
+ function.
+
+ * test_cert.in: Test PEM and DER FILE writing functionallity.
+
+ * ks_file.c: Add writing DER certificates.
+
+ * hxtool.c: Update to new hx509_pem_write().
+
+ * test_cms.in: test creation of PEM signeddata.
+
+ * hx509.h: PEM struct/function declarations.
+
+ * ks_file.c: Use PEM encoding/decoding functions.
+
+ * file.c: PEM encode/decoding functions.
+
+ * ks_file.c: Use hx509_pem_write.
+
+ * version-script.map: Export some semi-private functions.
+
+ * hxtool.c: Enable writing out signed data as a pem attachment.
+
+ * hxtool-commands.in (cms-create-signed): add --pem
+
+ * file.c (hx509_pem_write): Add.
+
+ * test_ca.in: Issue and test null subject cert.
+
+ * cert.c: Match is first component is in a CN=.
+
+ * test_ca.in: Test hostname if first CN.
+
+ * Makefile.am: Add version script.
+
+ * version-script.map: Limited exported symbols.
+
+ * test_ca.in: test --hostname.
+
+ * test_chain.in: test max-depth
+
+ * hx509.h: fixate HX509_HN_HOSTNAME at 0.
+
+ * hxtool-commands.in: add --hostname add --max-depth
+
+ * cert.c: Verify hostname and max-depth.
+
+ * hxtool.c: Verify hostname and test max-depth.
+
+2007-06-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_cms.in: Test --id-by-name.
+
+ * hxtool-commands.in: add cms-create-sd --id-by-name
+
+ * hxtool.c: Use HX509_CMS_SIGATURE_ID_NAME.
+
+ * cms.c: Implement and use HX509_CMS_SIGATURE_ID_NAME.
+
+ * hx509.h: Add HX509_CMS_SIGATURE_ID_NAME, use subject name for
+ CMS.Identifier. hx509_hostname_type: add hostname type for
+ matching.
+
+ * cert.c (match_general_name): more strict rfc822Name matching.
+ (hx509_verify_hostname): add hostname type for matching.
+
+2007-06-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: Make compile again.
+
+ * hxtool.c: Added peap-server for to make windows peap clients
+ happy.
+
+ * hxtool.c: Unify parse_oid code.
+
+ * hxtool.c: Implement --content-type.
+
+ * hxtool-commands.in: Add content-type.
+
+ * test_cert.in: more cert and keyset tests.
+
+2007-06-18 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * revoke.c: Avoid stomping on NULL.
+
+ * revoke.c: Avoid reusing i.
+
+ * cert.c: Provide __attribute__ for _hx509_abort.
+
+ * ks_file.c: Fail if not finding iv.
+
+ * keyset.c: Avoid useing freed memory.
+
+ * crypto.c: Free memory in failure case.
+
+ * crypto.c: Free memory in failure case.
+
+2007-06-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * *.c: Add hx509_cert_init_data and use everywhere
+
+ * hx_locl.h: Now that KEYCHAIN:system-anchors is fast again, use
+ that.
+
+ * ks_keychain.c: Implement trust anchor support with
+ SecTrustCopyAnchorCertificates.
+
+ * keyset.c: Set ref to 1 for the new object.
+
+ * cert.c: Fix logic for allow_default_trust_anchors
+
+ * keyset.c: Add refcounting to keystores.
+
+ * cert.c: Change logic for default trust anchors, make it be
+ either default trust anchor, the user supplied, or non at all.
+
+2007-06-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Add data/j.pem.
+
+ * Makefile.am: Add test_windows.in.
+
+2007-06-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_keychain.c: rename functions, leaks less memory and more
+ paranoia.
+
+ * test_cms.in: Test cms peer-alg.
+
+ * crypto.c (rsa_create_signature): make oid_id_pkcs1_rsaEncryption
+ mean rsa-with-sha1 but oid oid_id_pkcs1_rsaEncryption in algorithm
+ field. XXX should probably use another algorithmIdentifier for
+ this.
+
+ * peer.c: Make free function return void.
+
+ * cms.c (hx509_cms_create_signed_1): Use hx509_peer_info to select
+ the signature algorithm too.
+
+ * hxtool-commands.in: Add cms-create-sd --peer-alg.
+
+ * req.c: Use _hx509_crypto_default_sig_alg.
+
+ * test_windows.in: Create crl, because everyone needs one.
+
+ * Makefile.am: add wcrl.crl
+
+2007-06-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hx_locl.h: Disable KEYCHAIN for now, its slow.
+
+ * cms.c: When we are not using pkcs7-data, avoid seing
+ signedAttributes since some clients get upset by that (pkcs7 based
+ or just plain broken).
+
+ * ks_keychain.c: Provide rsa signatures.
+
+ * ks_keychain.c: Limit the searches to the selected keychain.
+
+ * ks_keychain.c: include -framework Security specific header files
+ after #ifdef
+
+ * ks_keychain.c: Find and attach private key (does not provide
+ operations yet though).
+
+ * ks_p11.c: Prefix rsa method with p11_
+
+ * ks_keychain.c: Allow opening a specific chain, making "system"
+ special and be the system X509Anchors file. By not specifing any
+ keychain ("KEYCHAIN:"), all keychains are probed.
+
+2007-06-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c (verify): Friendlier error message.
+
+ * cert.c: Read in and use default trust anchors if they exists.
+
+ * hx_locl.h: Add concept of default_trust_anchors.
+
+ * ks_keychain.c: Remove err(), remove extra empty comment, fix
+ _iter function.
+
+ * error.c (hx509_get_error_string): if the error code is not the
+ one we expect, punt and use the default com_err/strerror string
+ instead.
+
+ * keyset.c (hx509_certs_merge): its ok to merge in the NULL set of
+ certs.
+
+ * test_windows.in: Fix status string.
+
+ * ks_p12.c (store_func): free whole CertBag, not just the data
+ part.
+
+ * print.c: Check that the self-signed cert is really self-signed.
+
+ * print.c: Use selfsigned for CRL DP whine, tell if its a
+ self-signed.
+
+ * print.c: Whine if its a non CA/proxy and doesn't have CRL DP.
+
+ * ca.c: Add cRLSign to CA certs.
+
+ * cert.c: Register NULL and KEYCHAIN.
+
+ * ks_null.c: register the NULL keystore.
+
+ * Makefile.am: Add ks_keychain.c and related libs.
+
+ * test_crypto.in: Print certificate with utf8.
+
+ * print.c: Leak less memory.
+
+ * hxtool.c: Leak less memory.
+
+ * print.c: Leak less memory, use functions that does same but
+ more.
+
+ * name.c (quote_string): don't sign extend the (signed) char to
+ avoid printing too much, add an assert to check that we didn't
+ overrun the buffer.
+
+ * name.c: Use right element out of the CHOICE for printableString
+ and utf8String
+
+ * ks_keychain.c: Certificate only KeyChain backend.
+
+ * name.c: Reset name before parsing it.
+
+2007-06-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * revoke.c (hx509_crl_*): fix sizeof() mistakes to fix memory
+ corruption.
+
+ * hxtool.c: Add lifetime to crls.
+
+ * hxtool-commands.in: Add lifetime to crls.
+
+ * revoke.c: Add lifetime to crls.
+
+ * test_ca.in: More crl checks.
+
+ * revoke.c: Add revoking certs.
+
+ * hxtool-commands.in: argument is certificates.. for crl-sign
+
+ * hxtool.c (certificate_copy): free lock
+
+ * revoke.c: Fix hx509_set_error_string calls, add
+ hx509_crl_add_revoked_certs(), implement hx509_crl_{alloc,free}.
+
+ * hxtool.c (crl_sign): free lock
+
+ * cert.c (hx509_context_free): free querystat
+
+2007-06-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_chain.in: test ocsp-verify
+
+ * revoke.c (hx509_ocsp_verify): explain what its useful for and
+ provide sane error message.
+
+ * hx509_err.et: New error code, CERT_NOT_IN_OCSP
+
+ * hxtool.c: New command ocsp-verify, check if ocsp contains all
+ certs and are valid (exist and non expired).
+
+ * hxtool-commands.in: New command ocsp-verify.
+
+2007-06-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ca.in: Create crl and verify that is works.
+
+ * hxtool.c: Sign CRL command.
+
+ * hx509.h: Add hx509_crl.
+
+ * hxtool-commands.in: Add crl-sign commands.
+
+ * revoke.c: Support to generate an empty CRL.
+
+ * tst-crypto-select2: Switched default types.
+
+ * tst-crypto-select1: Switched default types.
+
+ * ca.c: Use default AlgorithmIdentifier.
+
+ * cms.c: Use default AlgorithmIdentifier.
+
+ * crypto.c: Provide default AlgorithmIdentifier and use them.
+
+ * hx_locl.h: Provide default AlgorithmIdentifier.
+
+ * keyset.c (hx509_certs_find): collects stats for queries.
+
+ * cert.c: Sort and print more info.
+
+ * hx_locl.h: Add querystat to hx509_context.
+
+ * test_*.in: sprinle stat saveing
+
+ * Makefile.am: Add stat and objdir.
+
+ * collector.c (_hx509_collector_alloc): return error code instead
+ of pointer.
+
+ * hxtool.c: Add statistic hook.
+
+ * ks_file.c: Update _hx509_collector_alloc prototype.
+
+ * ks_p12.c: Update _hx509_collector_alloc prototype.
+
+ * ks_p11.c: Update _hx509_collector_alloc prototype.
+
+ * hxtool-commands.in: Add statistics hook.
+
+ * cert.c: Statistics printing.
+
+ * ks_p12.c: plug memory leak
+
+ * ca.c (hx509_ca_tbs_add_crl_dp_uri): plug memory leak
+
+2007-05-31 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * print.c: print utf8 type SAN's
+
+ * Makefile.am: Fix windows client cert name.
+
+ * test_windows.in: Add crl-uri for the ee certs.
+
+ * print.c: Printf formating.
+
+ * ca.c: Add glue for adding CRL dps.
+
+ * test_ca.in: Readd the crl adding code, it works (somewhat) now.
+
+ * print.c: Fix printing of CRL DPnames (I hate IMPLICIT encoded
+ structures).
+
+ * hxtool-commands.in: make ca and alias of certificate-sign
+
+2007-05-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * crypto.c (hx509_crypto_select): copy AI to the right place.
+
+ * hxtool-commands.in: Add ca --ms-upn.
+
+ * hxtool.c: add --ms-upn and add more EKU's for pk-init client.
+
+ * ca.c: Add hx509_ca_tbs_add_san_ms_upn and refactor code.
+
+ * test_crypto.in: Resurect killed e.
+
+ * test_crypto.in: check for aes256-cbc
+
+ * tst-crypto-select7: check for aes256-cbc
+
+ * test_windows.in: test windows stuff
+
+ * hxtool.c: add ca --domain-controller option, add secret key
+ option to avaible.
+
+ * ca.c: Add hx509_ca_tbs_set_domaincontroller.
+
+ * hxtool-commands.in: add ca --domain-controller
+
+ * hxtool.c: hook for testing secrety key algs
+
+ * crypto.c: Add selection code for secret key crypto.
+
+ * hx509.h: Add HX509_SELECT_SECRET_ENC.
+
+2007-05-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: add more mechtypes
+
+2007-05-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * print.c: Indent.
+
+ * hxtool-commands.in: add test-crypto command
+
+ * hxtool.c: test crypto command
+
+ * cms.c (hx509_cms_create_signed_1): if no eContentType is given,
+ use pkcs7-data.
+
+ * print.c: add Netscape cert comment
+
+ * crypto.c: Try both the empty password and the NULL
+ password (nothing vs the octet string \x00\x00).
+
+ * print.c: Add some US Fed PKI oids.
+
+ * ks_p11.c: Add some more hashes.
+
+2007-04-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c (crypto_select): stop memory leak
+
+2007-04-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * peer.c (hx509_peer_info_free): free memory used too
+
+ * hxtool.c (crypto_select): only free peer if it was used.
+
+2007-04-18 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: free template
+
+ * ks_mem.c (mem_free): free key array too
+
+ * hxtool.c: free private key and tbs
+
+ * hxtool.c (hxtool_ca): free signer
+
+ * hxtool.c (crypto_available): free peer too.
+
+ * ca.c (get_AuthorityKeyIdentifier): leak less memory
+
+ * hxtool.c (hxtool_ca): free SPKI
+
+ * hxtool.c (hxtool_ca): free cert
+
+ * ks_mem.c (mem_getkeys): allocate one more the we have elements
+ so its possible to store the NULL pointer at the end.
+
+2007-04-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: CLEANFILES += cert-null.pem cert-sub-ca2.pem
+
+2007-02-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ca.c: Disable CRLDistributionPoints for now, its IMPLICIT code
+ in the asn1 parser.
+
+ * print.c: Add some more \n's.
+
+2007-02-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * file.c: Allow mapping using heim_octet_string.
+
+ * hxtool.c: Add options to generate detached signatures.
+
+ * cms.c: Add flags to generate detached signatures.
+
+ * hx509.h: Flag to generate detached signatures.
+
+ * test_cms.in: Support detached sigatures.
+
+ * name.c (hx509_general_name_unparse): unparse the other
+ GeneralName nametypes.
+
+ * print.c: Use less printf. Use hx509_general_name_unparse.
+
+ * cert.c: Fix printing and plug leak-on-error.
+
+2007-01-31 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ca.in: Add test for ca --crl-uri.
+
+ * hxtool.c: Add ca --crl-uri.
+
+ * hxtool-commands.in: add ca --crl-uri
+
+ * ca.c: Code to set CRLDistributionPoints in certificates.
+
+ * print.c: Check CRLDistributionPointNames.
+
+ * name.c (hx509_general_name_unparse): function for unparsing
+ GeneralName, only supports GeneralName.URI
+
+ * cert.c (is_proxy_cert): free info if we wont return it.
+
+2007-01-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: Try to help how to use this command.
+
+2007-01-21 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * switch to sha256 as default digest for signing
+
+2007-01-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ca.in: Really test sub-ca code, add basic constraints tests
+
+2007-01-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Fix makefile problem.
+
+2007-01-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: Set num of bits before we generate the key.
+
+2007-01-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c (hx509_cms_create_signed_1): use hx509_cert_binary
+
+ * ks_p12.c (store_func): use hx509_cert_binary
+
+ * ks_file.c (store_func): use hx509_cert_binary
+
+ * cert.c (hx509_cert_binary): return binary encoded
+ certificate (DER format)
+
+2007-01-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ca.c (hx509_ca_tbs_subject_expand): new function.
+
+ * name.c (hx509_name_expand): if env is NULL, return directly
+
+ * test_ca.in: test template handling
+
+ * hx509.h: Add template flags.
+
+ * Makefile.am: clean out new files
+
+ * hxtool.c: Add certificate template processing, fix hx509_err
+ usage.
+
+ * hxtool-commands.in: Add certificate template processing.
+
+ * ca.c: Add certificate template processing. Fix return messages
+ from hx509_ca_tbs_add_eku.
+
+ * cert.c: Export more stuff from certificate.
+
+2007-01-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ca.c: update (c)
+
+ * ca.c: (hx509_ca_tbs_add_eku): filter out dups.
+
+ * hxtool.c: Add type email and add email eku when using option
+ --email.
+
+ * Makefile.am: add env.c
+
+ * name.c: Remove abort, add error handling.
+
+ * test_name.c: test name expansion
+
+ * name.c: add hx509_name_expand
+
+ * env.c: key-value pair help functions
+
+2007-01-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ca.c: Don't issue certs with subject DN that is NULL and have no
+ SANs
+
+ * print.c: Fix previous test.
+
+ * print.c: Check there is a SAN if subject DN is NULL.
+
+ * test_ca.in: test email, null subject dn
+
+ * hxtool.c: Allow setting parameters to private key generation.
+
+ * hx_locl.h: Allow setting parameters to private key generation.
+
+ * crypto.c: Allow setting parameters to private key generation.
+
+ * hxtool.c (eval_types): add jid if user gave one
+
+ * hxtool-commands.in (certificate-sign): add --jid
+
+ * ca.c (hx509_ca_tbs_add_san_jid): Allow adding
+ id-pkix-on-xmppAddr OtherName.
+
+ * print.c: Print id-pkix-on-xmppAddr OtherName.
+
+2007-01-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * no random, no RSA/DH tests
+
+ * hxtool.c (info): print status of random generator
+
+ * Makefile.am: remove files created by tests
+
+ * error.c: constify
+
+ * name.c: constify
+
+ * revoke.c: constify
+
+ * hx_locl.h: constify
+
+ * keyset.c: constify
+
+ * ks_p11.c: constify
+
+ * hx_locl.h: make printinfo char * argument const.
+
+ * cms.c: move _hx509_set_digest_alg from cms.c to crypto.c since
+ its only used there.
+
+ * crypto.c: remove no longer used stuff, move set_digest_alg here
+ from cms.c since its only used here.
+
+ * Makefile.am: add data/test-nopw.p12 to EXTRA_DIST
+
+2007-01-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * print.c: BasicConstraints vs criticality bit is complicated and
+ not really possible to evaluate on its own, silly RFC3280.
+
+ * ca.c: Make basicConstraints critical if this is a CA.
+
+ * print.c: fix the version vs extension test
+
+ * print.c: More validation checks.
+
+ * name.c (hx509_name_cmp): add
+
+2007-01-09 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c (collect_private_key): Missing CKA_MODULUS is ok
+ too (XXX why should these be fetched given they are not used).
+
+ * test_ca.in: rename all files to PEM files, since that is what
+ they are.
+
+ * hxtool.c: copy out the key with the self signed CA cert
+
+ * Factor out private key operation out of the signing, operations,
+ support import, export, and generation of private keys. Add
+ support for writing PEM and PKCS12 files with private keys in them.
+
+ * data/gen-req.sh: Generate a no password pkcs12 file.
+
+2007-01-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c: Check for internal ASN1 encoder error.
+
+2007-01-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Drop most of the pkcs11 files.
+
+ * test_ca.in: test reissueing ca certificate (xxx time
+ validAfter).
+
+ * hxtool.c: Allow setting serialNumber (needed for reissuing
+ certificates) Change --key argument to --out-key.
+
+ * hxtool-commands.in (issue-certificate): Allow setting
+ serialNumber (needed for reissuing certificates), Change --key
+ argument to --out-key.
+
+ * ref: Replace with Marcus Brinkmann of g10 Code GmbH pkcs11
+ headerfile that is compatible with GPL (file taken from scute)
+
+2007-01-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ca.in: Test to generate key and use them.
+
+ * hxtool.c: handle other keys the pkcs10 requested keys
+
+ * hxtool-commands.in: add generate key commands
+
+ * req.c (_hx509_request_to_pkcs10): PKCS10 needs to have a subject
+
+ * hxtool-commands.in: Spelling.
+
+ * ca.c (hx509_ca_tbs_set_proxy): allow negative pathLenConstraint
+ to signal no limit
+
+ * ks_file.c: Try all formats on the binary file before giving up,
+ this way we can handle binary rsa keys too.
+
+ * data/key2.der: new test key
+
+2007-01-04 David Love <fx@gnu.org>
+
+ * Makefile.am (hxtool_LDADD): Add libasn1.la
+
+ * hxtool.c (pcert_verify): Fix format string.
+
+2006-12-31 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: Allow setting path length
+
+ * cert.c: Fix test for proxy certs chain length, it was too
+ restrictive.
+
+ * data: regen
+
+ * data/openssl.cnf: (proxy_cert) make length 0
+
+ * test_ca.in: Issue a long living cert.
+
+ * hxtool.c: add --lifetime to ca command.
+
+ * hxtool-commands.in: add --lifetime to ca command.
+
+ * ca.c: allow setting notBefore and notAfter.
+
+ * test_ca.in: Test generation of proxy certificates.
+
+ * ca.c: Allow generation of proxy certificates, always include
+ BasicConstraints, fix error codes.
+
+ * hxtool.c: Allow generation of proxy certificates.
+
+ * test_name.c: make hx509_parse_name take a hx509_context.
+
+ * name.c: Split building RDN to a separate function.
+
+2006-12-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: clean test_ca files.
+
+ * test_ca.in: test issuing self-signed and CA certificates.
+
+ * hxtool.c: Add bits to allow issuing self-signed and CA
+ certificates.
+
+ * hxtool-commands.in: Add bits to allow issuing self-signed and CA
+ certificates.
+
+ * ca.c: Add bits to allow issuing CA certificates.
+
+ * revoke.c: use new OCSPSigning.
+
+ * ca.c: Add Subject Key Identifier.
+
+ * ca.c: Add Authority Key Identifier.
+
+ * cert.c: Locally export _hx509_find_extension_subject_key_id.
+ Handle AuthorityKeyIdentifier where only authorityCertSerialNumber
+ and authorityCertSerialNumber is set.
+
+ * hxtool-commands.in: Add dnsname and rfc822 SANs.
+
+ * test_ca.in: Test dnsname and rfc822 SANs.
+
+ * ca.c: Add dnsname and rfc822 SANs.
+
+ * hxtool.c: Add dnsname and rfc822 SANs.
+
+ * test_ca.in: test adding eku, ku and san to the
+ certificate (https and pk-init)
+
+ * hxtool.c: Add eku, ku and san to the certificate.
+
+ * ca.c: Add eku, ku and san to the certificate.
+
+ * hxtool-commands.in: Add --type and --pk-init-principal
+
+ * ocsp.asn1: remove id-kp-OCSPSigning, its in rfc2459.asn1 now
+
+2006-12-29 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ca.c: Add KeyUsage extension.
+
+ * Makefile.am: add ca.c, add sign-certificate tests.
+
+ * crypto.c: Add _hx509_create_signature_bitstring.
+
+ * hxtool-commands.in: Add the sign-certificate tool.
+
+ * hxtool.c: Add the sign-certificate tool.
+
+ * cert.c: Add HX509_QUERY_OPTION_KU_KEYCERTSIGN.
+
+ * hx509.h: Add hx509_ca_tbs and HX509_QUERY_OPTION_KU_KEYCERTSIGN.
+
+ * test_ca.in: Basic test of generating a pkcs10 request, signing
+ it and verifying the chain.
+
+ * ca.c: Naive certificate signer.
+
+2006-12-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: add hxtool_hex
+
+2006-12-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: use top_builddir for libasn1.la
+
+2006-12-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c (print_certificate): print serial number.
+
+ * name.c (no): add S=stateOrProvinceName
+
+2006-12-09 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * crypto.c (_hx509_private_key_assign_rsa): set a default sig alg
+
+ * ks_file.c (try_decrypt): pass down AlgorithmIdentifier that key
+ uses to do sigatures so there is no need to hardcode RSA into this
+ function.
+
+2006-12-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_file.c: Pass filename to the parse functions and use it in
+ the error messages
+
+ * test_chain.in: test proxy cert (third level)
+
+ * hx509_err.et: fix errorstring for PROXY_CERT_NAME_WRONG
+
+ * data: regen
+
+ * Makefile.am: EXTRA_DIST: add
+ data/proxy10-child-child-test.{key,crt}
+
+ * data/gen-req.sh: Fix names and restrictions on the proxy
+ certificates
+
+ * cert.c: Clairfy and make proxy cert handling work for multiple
+ levels, before it was too restrictive. More helpful error message.
+
+2006-12-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c (check_key_usage): tell what keyusages are missing
+
+ * print.c: Split OtherName printing code to a oid lookup and print
+ function.
+
+ * print.c (Time2string): print hour as hour not min
+
+ * Makefile.am: CLEANFILES += test
+
+2006-12-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am (EXTRA_DIST): add data/pkinit-proxy* files
+
+ * Makefile.am (EXTRA_DIST): add tst-crypto* files
+
+ * cert.c (hx509_query_match_issuer_serial): make a copy of the
+ data
+
+ * cert.c (hx509_query_match_issuer_serial): allow matching on
+ issuer and serial num
+
+ * cert.c (_hx509_calculate_path): add flag to allow leaving out
+ trust anchor
+
+ * cms.c (hx509_cms_create_signed_1): when building the path, omit
+ the trust anchors.
+
+ * crypto.c (rsa_create_signature): Abort when signature is longer,
+ not shorter.
+
+ * cms.c: Provide time to _hx509_calculate_path so we don't send no
+ longer valid certs to our peer.
+
+ * cert.c (find_parent): when checking for certs and its not a
+ trust anchor, require time be in range.
+ (_hx509_query_match_cert): Add time validity-testing to query mask
+
+ * hx_locl.h: add time validity-testing to query mask
+
+ * test_cms.in: Tests for CMS SignedData with incomplete chain from
+ the signer.
+
+2006-11-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c (hx509_cms_verify_signed): specify what signature we
+ failed to verify
+
+ * Makefile.am: Depend on LIB_com_err for AIX.
+
+ * keyset.c: Remove anther strndup that causes AIX to fall over.
+
+ * cert.c: Don't check the trust anchors expiration time since they
+ are transported out of band, from RFC3820.
+
+ * cms.c: sprinkle more error strings
+
+ * crypto.c: sprinkle more error strings
+
+ * hxtool.c: use unsigned int as counter to fit better with the
+ asn1 compiler
+
+ * crypto.c: use unsigned int as counter to fit better with the
+ asn1 compiler
+
+2006-11-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c: Remove trailing white space.
+
+ * crypto.c: rewrite comment to make more sense
+
+ * crypto.c (hx509_crypto_select): check sig_algs[j]->key_oid
+
+ * hxtool-commands.in (crypto-available): add --type
+
+ * crypto.c (hx509_crypto_available): let alg pass if its keyless
+
+ * hxtool-commands.in: Expand crypto-select
+
+ * cms.c: Rename hx509_select to hx509_crypto_select.
+
+ * hxtool-commands.in: Add crypto-select and crypto-available.
+
+ * hxtool.c: Add crypto-select and crypto-available.
+
+ * crypto.c (hx509_crypto_available): use right index.
+ (hx509_crypto_free_algs): new function
+
+ * crypto.c (hx509_crypto_select): improve
+ (hx509_crypto_available): new function
+
+2006-11-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c: Sprinkle more error string and hx509_contexts.
+
+ * cms.c: Sprinkle more error strings.
+
+ * crypto.c: Sprinkle error string and hx509_contexts.
+
+ * crypto.c: Add some more comments about how this works.
+
+ * crypto.c (hx509_select): new function.
+
+ * Makefile.am: add peer.c
+
+ * hxtool.c: Update hx509_cms_create_signed_1.
+
+ * hx_locl.h: add struct hx509_peer_info
+
+ * peer.c: Allow selection of digest/sig-alg
+
+ * cms.c: Allow selection of a better digest using hx509_peer_info.
+
+ * revoke.c: Handle that _hx509_verify_signature takes a context.
+
+ * cert.c: Handle that _hx509_verify_signature takes a context.
+
+2006-11-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c: Sprinkle error strings.
+
+ * crypto.c: Sprinkle context and error strings.
+
+2006-11-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * name.c: Handle printing and parsing raw oids in name.
+
+2006-11-23 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c (_hx509_calculate_path): allow to calculate optimistic
+ path when we don't know the trust anchors, just follow the chain
+ upward until we no longer find a parent or we hit the max limit.
+
+ * cms.c (hx509_cms_create_signed_1): provide a best effort path to
+ the trust anchors to be stored in the SignedData packet, if find
+ parents until trust anchor or max length.
+
+ * data: regen
+
+ * data/gen-req.sh: Build pk-init proxy cert.
+
+2006-11-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * error.c (hx509_get_error_string): Put ", " between strings in
+ error message.
+
+2006-11-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * data/openssl.cnf: Change realm to TEST.H5L.SE
+
+2006-11-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * revoke.c: Sprinkle error strings.
+
+2006-11-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hx_locl.h: add context variable to cmp function.
+
+ * cert.c (hx509_query_match_cmp_func): allow setting the match
+ function.
+
+2006-10-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: Return less EINVAL.
+
+ * hx509_err.et: add more pkcs11 errors
+
+ * hx509_err.et: more error-codes
+
+ * revoke.c: Return less EINVAL.
+
+ * ks_dir.c: sprinkel more hx509_set_error_string
+
+ * ks_file.c: Return less EINVAL.
+
+ * hxtool.c: Pass in context to _hx509_parse_private_key.
+
+ * ks_file.c: Sprinkle more hx509_context so we can return propper
+ errors.
+
+ * hx509_err.et: add HX509_PARSING_KEY_FAILED
+
+ * crypto.c: Sprinkle more hx509_context so we can return propper
+ errors.
+
+ * collector.c: No more EINVAL.
+
+ * hx509_err.et: add HX509_LOCAL_ATTRIBUTE_MISSING
+
+ * cert.c (hx509_cert_get_base_subject): one less EINVAL
+ (_hx509_cert_private_decrypt): one less EINVAL
+
+2006-10-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * collector.c: indent
+
+ * hxtool.c: Try to not leak memory.
+
+ * req.c: clean memory before free
+
+ * crypto.c (_hx509_private_key2SPKI): indent
+
+ * req.c: Try to not leak memory.
+
+2006-10-21 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_crypto.in: Read 50 kilobyte random data
+
+ * revoke.c: Try to not leak memory.
+
+ * hxtool.c: Try to not leak memory.
+
+ * crypto.c (hx509_crypto_destroy): free oid.
+
+ * error.c: Clean error string on failure just to make sure.
+
+ * cms.c: Try to not leak memory (again).
+
+ * hxtool.c: use a sensable content type
+
+ * cms.c: Try harder to free certificate.
+
+2006-10-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Add make check data.
+
+2006-10-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c (p11_list_keys): make element of search_data[0]
+ constants and set them later
+
+ * Makefile.am: Add more files.
+
+2006-10-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_file.c: set ret, remember to free ivdata
+
+2006-10-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hx_locl.h: Include <parse_bytes.h>.
+
+ * test_crypto.in: Test random-data.
+
+ * hxtool.c: RAND_bytes() return 1 for cryptographic strong data,
+ check for that.
+
+ * Makefile.am: clean random-data
+
+ * hxtool.c: Add random-data command, use sl_slc_help.
+
+ * hxtool-commands.in: Add random-data.
+
+ * ks_p12.c: Remember to release certs.
+
+ * ks_p11.c: Remember to release certs.
+
+2006-10-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * prefix der primitives with der_
+
+ * lock.c: Match the prompt type PROMPT exact.
+
+ * hx_locl.h: Drop heim_any.h
+
+2006-10-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c (p11_release_module): j needs to be used as inter loop
+ index. From Douglas Engert.
+
+ * ks_file.c (parse_rsa_private_key): try all passwords and
+ prompter.
+
+2006-10-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_*.in: Parameterise the invocation of hxtool, so we can make
+ it run under TESTS_ENVIRONMENT. From Andrew Bartlett
+
+2006-10-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_crypto.in: Put all test stuck at 2006-09-25 since all their
+ chains where valied then.
+
+ * hxtool.c: Implement --time= option.
+
+ * hxtool-commands.in: Add option time.
+
+ * Makefile.am: test_name is a PROGRAM_TESTS
+
+ * ks_p11.c: Return HX509_PKCS11_NO_SLOT when there are no slots
+ and HX509_PKCS11_NO_TOKEN when there are no token. For use in PAM
+ modules that want to detect when to use smartcard login and when
+ not to. Patched based on code from Douglas Engert.
+
+ * hx509_err.et: Add new pkcs11 related errors in a new section:
+ keystore related error. Patched based on code from Douglas
+ Engert.
+
+2006-10-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Make depenency for slc built files just like
+ everywhere else.
+
+ * cert.c: Add all openssl algs and init asn1 et
+
+2006-10-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_file.c (parse_rsa_private_key): free type earlier.
+
+ * ks_file.c (parse_rsa_private_key): free type after use
+
+ * name.c (_hx509_Name_to_string): remove dup const
+
+2006-10-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Add more libs to libhx509
+
+2006-10-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: Fix double free's, NULL ptr de-reference, and conform
+ better to pkcs11. From Douglas Engert.
+
+ * ref: remove ^M, it breaks solaris 10s cc. From Harald Barth
+
+2006-09-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_crypto.in: Bleichenbacher bad cert from Ralf-Philipp
+ Weinmann and Andrew Pyshkin, pad right.
+
+ * data: starfield test root cert and Ralf-Philipp and Andreis
+ correctly padded bad cert
+
+2006-09-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_crypto.in: Add test for yutaka certs.
+
+ * cert.c: Add a strict rfc3280 verification flag. rfc3280 requires
+ certificates to have KeyUsage.keyCertSign if they are to be used
+ for signing of certificates, but the step in the verifiation is
+ optional.
+
+ * hxtool.c: Improve printing and error reporting.
+
+2006-09-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_crypto.in,Makefile.am,data/bleichenbacher-{bad,good}.pem:
+ test bleichenbacher from eay
+
+2006-09-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: Make common function for all getarg_strings and
+ hx509_certs_append commonly used.
+
+ * cms.c: HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT is a negative
+ flag, treat it was such.
+
+2006-09-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * req.c: Use the new add_GeneralNames function.
+
+ * hx509.h: Add HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT.
+
+ * ks_p12.c: Adapt to new signature of hx509_cms_unenvelope.
+
+ * hxtool.c: Adapt to new signature of hx509_cms_unenvelope.
+
+ * cms.c: Allow passing in encryptedContent and flag. Add new flag
+ HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT.
+
+2006-09-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: cast void * to char * when using it for %s formating
+ in printf.
+
+ * name.c: New function _hx509_Name_to_string.
+
+2006-09-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_file.c: Sprinkle error messages.
+
+ * cms.c: Sprinkle even more error messages.
+
+ * cms.c: Sprinkle some error messages.
+
+ * cms.c (find_CMSIdentifier): only free string when we allocated
+ one.
+
+ * ks_p11.c: Don't build most of the pkcs11 module if there are no
+ dlopen().
+
+2006-09-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c (hx509_cms_unenvelope): try to save the error string from
+ find_CMSIdentifier so we have one more bit of information what
+ went wrong.
+
+ * hxtool.c: More pretty printing, make verify_signed return the
+ error string from the library.
+
+ * cms.c: Try returning what certificates failed to parse or be
+ found.
+
+ * ks_p11.c (p11_list_keys): fetch CKA_LABEL and use it to set the
+ friendlyname for the certificate.
+
+2006-09-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * crypto.c: check that there are no extra bytes in the checksum
+ and that the parameters are NULL or the NULL-type. All to avoid
+ having excess data that can be used to fake the signature.
+
+ * hxtool.c: print keyusage
+
+ * print.c: add hx509_cert_keyusage_print, simplify oid printing
+
+ * cert.c: add _hx509_cert_get_keyusage
+
+ * ks_p11.c: keep one session around for the whole life of the keyset
+
+ * test_query.in: tests more selection
+
+ * hxtool.c: improve pretty printing in print and query
+
+ * hxtool{.c,-commands.in}: add selection on KU and printing to query
+
+ * test_cms.in: Add cms test for digitalSignature and
+ keyEncipherment certs.
+
+ * name.c (no): Add serialNumber
+
+ * ks_p11.c (p11_get_session): return better error messages
+
+2006-09-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ref: update to pkcs11 reference files 2.20
+
+ * ks_p11.c: add more mechflags
+
+ * name.c (no): add OU and sort
+
+ * revoke.c: pass context to _hx509_create_signature
+
+ * ks_p11.c (p11_printinfo): print proper plural s
+
+ * ks_p11.c: save the mechs supported when initing the token, print
+ them in printinfo.
+
+ * hx_locl.h: Include <parse_units.h>.
+
+ * cms.c: pass context to _hx509_create_signature
+
+ * req.c: pass context to _hx509_create_signature
+
+ * keyset.c (hx509_certs_info): print information about the keyset.
+
+ * hxtool.c (pcert_print) print keystore info when --info flag is
+ given.
+
+ * hxtool-commands.in: Add hxtool print --info.
+
+ * test_query.in: Test hxtool print --info.
+
+ * hx_locl.h (hx509_keyset_ops): add printinfo
+
+ * crypto.c: Start to hang the private key operations of the
+ private key, pass hx509_context to create_checksum.
+
+2006-05-29 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: Iterate over all slots, not just the first/selected
+ one.
+
+2006-05-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c: Add release function for certifiates so backend knowns
+ when its no longer used.
+
+ * ks_p11.c: Add reference counting on certifiates, push out
+ CK_SESSION_HANDLE from slot.
+
+ * cms.c: sprinkle more hx509_clear_error_string
+
+2006-05-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: Sprinkle some hx509_set_error_strings
+
+2006-05-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: Avoid shadowing.
+
+ * revoke.c: Avoid shadowing.
+
+ * ks_file.c: Avoid shadowing.
+
+ * cert.c: Avoid shadowing.
+
+2006-05-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * lock.c (hx509_prompt_hidden): reshuffle to avoid gcc warning
+
+ * hx509.h: Reshuffle the prompter types, remove the hidden field.
+
+ * lock.c (hx509_prompt_hidden): return if the prompt should be
+ hidden or not
+
+ * revoke.c (hx509_revoke_free): allow free of NULL.
+
+2006-05-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_file.c (file_init): Avoid shadowing ret (and thus avoiding
+ crashing).
+
+ * ks_dir.c: Implement DIR: caches useing FILE: caches.
+
+ * ks_p11.c: Catch more errors.
+
+2006-05-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * crypto.c (hx509_crypto_encrypt): free correctly in error
+ path. From Andrew Bartlett.
+
+ * crypto.c: If RAND_bytes fails, then we will attempt to
+ double-free crypt->key.data. From Andrew Bartlett.
+
+2006-05-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * name.c: Rename u_intXX_t to uintXX_t
+
+2006-05-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * TODO: More to do about the about the PKCS11 code.
+
+ * ks_p11.c: Use the prompter from the lock function.
+
+ * lock.c: Deal with that hx509_prompt.reply is no longer a
+ pointer.
+
+ * hx509.h: Make hx509_prompt.reply not a pointer.
+
+2006-05-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * keyset.c: Sprinkle setting error strings.
+
+ * crypto.c: Sprinkle setting error strings.
+
+ * collector.c: Sprinkle setting error strings.
+
+ * cms.c: Sprinkle setting error strings.
+
+2006-05-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_name.c: renamed one error code
+
+ * name.c: renamed one error code
+
+ * ks_p11.c: _hx509_set_cert_attribute changed signature
+
+ * hxtool.c (pcert_print): use hx509_err so I can test it
+
+ * error.c (hx509_set_error_stringv): clear errors on malloc
+ failure
+
+ * hx509_err.et: Add some more errors
+
+ * cert.c: Sprinkle setting error strings.
+
+ * cms.c: _hx509_path_append changed signature.
+
+ * revoke.c: changed signature of _hx509_check_key_usage
+
+ * keyset.c: changed signature of _hx509_query_match_cert
+
+ * hx509.h: Add support for error strings.
+
+ * cms.c: changed signature of _hx509_check_key_usage
+
+ * Makefile.am: ibhx509_la_files += error.c
+
+ * ks_file.c: Sprinkel setting error strings.
+
+ * cert.c: Sprinkel setting error strings.
+
+ * hx_locl.h: Add support for error strings.
+
+ * error.c: Add string error handling functions.
+
+ * keyset.c (hx509_certs_init): pass the right error code back
+
+2006-04-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * revoke.c: Revert previous patch.
+ (hx509_ocsp_verify): new function that returns the expiration of
+ certificate in ocsp data-blob
+
+ * cert.c: Reverse previous patch, lets do it another way.
+
+ * cert.c (hx509_revoke_verify): update usage
+
+ * revoke.c: Make compile.
+
+ * revoke.c: Add the expiration time the crl/ocsp info expire
+
+ * name.c: Add hx509_name_is_null_p
+
+ * cert.c: remove _hx509_cert_private_sigature
+
+2006-04-29 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * name.c: Expose more of Name.
+
+ * hxtool.c (main): add missing argument to printf
+
+ * data/openssl.cnf: Add EKU for the KDC certificate
+
+ * cert.c (hx509_cert_get_base_subject): reject un-canon proxy
+ certs, not the reverse
+ (add_to_list): constify and fix argument order to
+ copy_octet_string
+ (hx509_cert_find_subjectAltName_otherName): make work
+
+2006-04-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * data/{pkinit,kdc}.{crt,key}: pkinit certificates
+
+ * data/gen-req.sh: Generate pkinit certificates.
+
+ * data/openssl.cnf: Add pkinit glue.
+
+ * cert.c (hx509_verify_hostname): implement stub function
+
+2006-04-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * TODO: CRL delta support
+
+2006-04-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * data/.cvsignore: ignore leftover from OpenSSL cert generation
+
+ * hx509_err.et: Add name malformated error
+
+ * name.c (hx509_parse_name): don't abort on error, rather return
+ error
+
+ * test_name.c: Test failure parsing name.
+
+ * cert.c: When verifying certificates, store subject basename for
+ later consumption.
+
+ * test_name.c: test to parse and print name and check that they
+ are the same.
+
+ * name.c (hx509_parse_name): fix length argument to printf string
+
+ * name.c (hx509_parse_name): fix length argument to stringtooid, 1
+ too short.
+
+ * cert.c: remove debug printf's
+
+ * name.c (hx509_parse_name): make compile pre c99
+
+ * data/gen-req.sh: OpenSSL have a serious issue of user confusion
+ -subj in -ca takes the arguments in LDAP order. -subj for x509
+ takes it in x509 order.
+
+ * cert.c (hx509_verify_path): handle the case where the where two
+ proxy certs in a chain.
+
+ * test_chain.in: enable two proxy certificates in a chain test
+
+ * test_chain.in: tests proxy certificates
+
+ * data: re-gen
+
+ * data/gen-req.sh: build proxy certificates
+
+ * data/openssl.cnf: add def for proxy10_cert
+
+ * hx509_err.et: Add another proxy certificate error.
+
+ * cert.c (hx509_verify_path): Need to mangle name to remove the CN
+ of the subject, copying issuer only works for one level but is
+ better then doing no checking at all.
+
+ * hxtool.c: Add verify --allow-proxy-certificate.
+
+ * hxtool-commands.in: add verify --allow-proxy-certificate
+
+ * hx509_err.et: Add proxy certificate errors.
+
+ * cert.c: Fix comment about subject name of proxy certificate.
+
+ * test_chain.in: tests for proxy certs
+
+ * data/gen-req.sh: gen proxy and non-proxy tests certificates
+
+ * data/openssl.cnf: Add definition for proxy certs
+
+ * data/*proxy-test.*: Add proxy certificates
+
+ * cert.c (hx509_verify_path): verify proxy certificate have no san
+ or ian
+
+ * cert.c (hx509_verify_set_proxy_certificate): Add
+ (*): rename policy cert to proxy cert
+
+ * cert.c: Initial support for proxy certificates.
+
+2006-04-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: some error checking
+
+ * name.c: Switch over to asn1 generaed oids.
+
+ * TODO: merge with old todo file
+
+2006-04-23 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_query.in: make quiet
+
+ * test_req.in: SKIP test if there is no RSA support.
+
+ * hxtool.c: print dh method too
+
+ * test_chain.in: SKIP test if there is no RSA support.
+
+ * test_cms.in: SKIP test if there is no RSA support.
+
+ * test_nist.in: SKIP test if there is no RSA support.
+
+2006-04-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool-commands.in: Allow passing in pool and anchor to
+ signedData
+
+ * hxtool.c: Allow passing in pool and anchor to signedData
+
+ * test_cms.in: Test that certs in signed data is picked up.
+
+ * hx_locl.h: Expose the path building function to internal
+ functions.
+
+ * cert.c: Expose the path building function to internal functions.
+
+ * hxtool-commands.in: cms-envelope: Add support for choosing the
+ encryption type
+
+ * hxtool.c (cms_create_enveloped): Add support for choosing the
+ encryption type
+
+ * test_cms.in: Test generating des-ede3 aes-128 aes-256 enveloped
+ data
+
+ * crypto.c: Add names to cipher types.
+
+ * cert.c (hx509_query_match_friendly_name): fix return value
+
+ * data/gen-req.sh: generate tests for enveloped data using
+ des-ede3 and aes256
+
+ * test_cms.in: add tests for enveloped data using des-ede3 and
+ aes256
+
+ * cert.c (hx509_query_match_friendly_name): New function.
+
+2006-04-21 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c: Add support for parsing slot-number.
+
+ * crypto.c (oid_private_rc2_40): simply
+
+ * crypto.c: Use oids from asn1 generator.
+
+ * ks_file.c (file_init): reset length when done with a part
+
+ * test_cms.in: check with test.combined.crt.
+
+ * data/gen-req.sh: Create test.combined.crt.
+
+ * test_cms.in: Test signed data using keyfile that is encrypted.
+
+ * ks_file.c: Remove (commented out) debug printf
+
+ * ks_file.c (parse_rsa_private_key): use EVP_get_cipherbyname
+
+ * ks_file.c (parse_rsa_private_key): make working for one
+ password.
+
+ * ks_file.c (parse_rsa_private_key): Implement enought for
+ testing.
+
+ * hx_locl.h: Add <ctype.h>
+
+ * ks_file.c: Add glue code for PEM encrypted password files.
+
+ * test_cms.in: Add commeted out password protected PEM file,
+ remove password for those tests that doesn't need it.
+
+ * test_cms.in: adapt test now that we can use any certificate and
+ trust anchor
+
+ * collector.c: handle PEM RSA PRIVATE KEY files
+
+ * cert.c: Remove unused function.
+
+ * ks_dir.c: move code here from ks_file.c now that its no longer
+ used.
+
+ * ks_file.c: Add support for parsing unencrypted RSA PRIVATE KEY
+
+ * crypto.c: Handle rsa private keys better.
+
+2006-04-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: Use hx509_cms_{,un}wrap_ContentInfo
+
+ * cms.c: Make hx509_cms_{,un}wrap_ContentInfo usable in asn1
+ un-aware code.
+
+ * cert.c (hx509_verify_path): if trust anchor is not self signed,
+ don't check sig From Douglas Engert.
+
+ * test_chain.in: test "sub-cert -> sub-ca"
+
+ * crypto.c: Use the right length for the sha256 checksums.
+
+2006-04-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * crypto.c: Fix breakage from sha256 code.
+
+ * crypto.c: Add SHA256 support, and symbols for the other new
+ SHA-2 types.
+
+2006-04-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_cms.in: test rc2-40 rc2-64 rc2-128 enveloped data
+
+ * data/test-enveloped-rc2-{40,64,128}: add tests cases for rc2
+
+ * cms.c: Update prototypes changes for hx509_crypto_[gs]et_params.
+
+ * crypto.c: Break out the parameter handling code for encrypting
+ data to handle RC2. Needed for Windows 2k pk-init support.
+
+2006-04-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Split libhx509_la_SOURCES into build file and
+ distributed files so we can avoid building prototypes for
+ build-files.
+
+2006-04-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * TODO: split certificate request into pkcs10 and CRMF
+
+ * hxtool-commands.in: Add nonce flag to ocsp-fetch
+
+ * hxtool.c: control sending nonce
+
+ * hxtool.c (request_create): store the request in a file, no in
+ bitbucket.
+
+ * cert.c: expose print_cert_subject internally
+
+ * hxtool.c: Add ocsp_print.
+
+ * hxtool-commands.in: New command "ocsp-print".
+
+ * hx_locl.h: Include <hex.h>.
+
+ * revoke.c (verify_ocsp): require issuer to match too.
+ (free_ocsp): new function
+ (hx509_revoke_ocsp_print): new function, print ocsp reply
+
+ * Makefile.am: build CRMF files
+
+ * data/key.der: needed for cert request test
+
+ * test_req.in: adapt to rename of pkcs10-create to request-create
+
+ * hxtool.c: adapt to rename of pkcs10-create to request-create
+
+ * hxtool-commands.in: Rename pkcs10-create to request-create
+
+ * crypto.c: (_hx509_parse_private_key): Avoid crashing on bad input.
+
+ * hxtool.c (pkcs10_create): use opt->subject_string
+
+ * hxtool-commands.in: Add pkcs10-create --subject
+
+ * Makefile.am: Add test_req to tests.
+
+ * test_req.in: Test for pkcs10 commands.
+
+ * name.c (hx509_parse_name): new function.
+
+ * hxtool.c (pkcs10_create): implement
+
+ * hxtool-commands.in (pkcs10-create): Add arguments
+
+ * crypto.c: Add _hx509_private_key2SPKI and support
+ functions (only support RSA for now).
+
+2006-04-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool-commands.in: Add pkcs10-create command.
+
+ * hx509.h: Add hx509_request.
+
+ * TODO: more stuff
+
+ * Makefile.am: Add req.c
+
+ * req.c: Create certificate requests, prototype converts the
+ request in a pkcs10 packet.
+
+ * hxtool.c: Add pkcs10_create
+
+ * name.c (hx509_name_copy): new function.
+
+2006-04-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * TODO: fill out what do
+
+ * hxtool-commands.in: add pkcs10-print
+
+ * hx_locl.h: Include <pkcs10_asn1.h>.
+
+ * pkcs10.asn1: PKCS#10
+
+ * hxtool.c (pkcs10_print): new function.
+
+ * test_chain.in: test ocsp keyhash
+
+ * data: generate ocsp keyhash version too
+
+ * revoke.c (load_ocsp): test that we got back a BasicReponse
+
+ * ocsp.asn1: Add asn1_id_pkix_ocsp*.
+
+ * Makefile.am: Add asn1_id_pkix_ocsp*.
+
+ * cert.c: Add HX509_QUERY_MATCH_KEY_HASH_SHA1
+
+ * hx_locl.h: Add HX509_QUERY_MATCH_KEY_HASH_SHA1
+
+ * revoke.c: Support OCSPResponderID.byKey, indent.
+
+ * revoke.c (hx509_ocsp_request): Add nonce to ocsp request.
+
+ * hxtool.c: Add nonce to ocsp request.
+
+ * test_chain.in: Added crl tests
+
+ * data/nist-data: rename missing-crl to missing-revoke
+
+ * data: make ca use openssl ca command so we can add ocsp tests,
+ and regen certs
+
+ * test_chain.in: Add revoked ocsp cert test
+
+ * cert.c: rename missing-crl to missing-revoke
+
+ * revoke.c: refactor code, fix a un-init-ed variable
+
+ * test_chain.in: rename missing-crl to missing-revoke add ocsp
+ tests
+
+ * test_cms.in: rename missing-crl to missing-revoke
+
+ * hxtool.c: rename missing-crl to missing-revoke
+
+ * hxtool-commands.in: rename missing-crl to missing-revoke
+
+ * revoke.c: Plug one memory leak.
+
+ * revoke.c: Renamed generic CRL related errors.
+
+ * hx509_err.et: Comments and renamed generic CRL related errors
+
+ * revoke.c: Add ocsp checker.
+
+ * ocsp.asn1: Add id-kp-OCSPSigning
+
+ * hxtool-commands.in: add url-path argument to ocsp-fetch
+
+ * hxtool.c: implement ocsp-fetch
+
+ * cert.c: Use HX509_DEFAULT_OCSP_TIME_DIFF.
+
+ * hx_locl.h: Add ocsp_time_diff to hx509_context
+
+ * crypto.c (_hx509_verify_signature_bitstring): new function,
+ commonly use when checking certificates
+
+ * cms.c (hx509_cms_envelope_1): check for internal ASN.1 encoder
+ error
+
+ * cert.c: Add ocsp glue, use new
+ _hx509_verify_signature_bitstring, add eku checking function.
+
+2006-03-31 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: add id_kp_OCSPSigning.x
+
+ * revoke.c: Pick out certs in ocsp response
+
+ * TODO: list of stuff to verify
+
+ * revoke.c: Add code to load OCSPBasicOCSPResponse files, reload
+ crl when its changed on disk.
+
+ * cert.c: Update for ocsp merge. handle building path w/o
+ subject (using subject key id)
+
+ * ks_p12.c: _hx509_map_file changed prototype.
+
+ * file.c: _hx509_map_file changed prototype, returns struct stat
+ if requested.
+
+ * ks_file.c: _hx509_map_file changed prototype.
+
+ * hxtool.c: Add stub for ocsp-fetch, _hx509_map_file changed
+ prototype, add ocsp parsing to verify command.
+
+ * hx_locl.h: rename HX509_CTX_CRL_MISSING_OK to
+ HX509_CTX_VERIFY_MISSING_OK now that we have OCSP glue
+
+2006-03-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hx_locl.h: Add <krb5-types.h> to make it compile on Solaris,
+ from Alex V. Labuta.
+
+2006-03-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * crypto.c (_hx509_pbe_decrypt): try all passwords, not just the
+ first one.
+
+2006-03-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * print.c (check_altName): Print the othername oid.
+
+ * crypto.c: Manual page claims RSA_public_decrypt will return -1
+ on error, lets check for that
+
+ * crypto.c (_hx509_pbe_decrypt): also try the empty password
+
+ * collector.c (match_localkeyid): no need to add back the cert to
+ the cert pool, its already there.
+
+ * crypto.c: Add REQUIRE_SIGNER
+
+ * cert.c (hx509_cert_free): ok to free NULL
+
+ * hx509_err.et: Add new error code SIGNATURE_WITHOUT_SIGNER.
+
+ * name.c (_hx509_name_ds_cmp): make DirectoryString case
+ insenstive
+ (hx509_name_to_string): less spacing
+
+ * cms.c: Check for signature error, check consitency of error
+
+2006-03-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * collector.c (_hx509_collector_alloc): handle errors
+
+ * cert.c (hx509_query_alloc): allocate slight more more then a
+ sizeof(pointer)
+
+ * crypto.c (_hx509_private_key_assign_key_file): ask for password
+ if nothing matches.
+
+ * cert.c: Expose more of the hx509_query interface.
+
+ * collector.c: hx509_certs_find is now exposed.
+
+ * cms.c: hx509_certs_find is now exposed.
+
+ * revoke.c: hx509_certs_find is now exposed.
+
+ * keyset.c (hx509_certs_free): allow free-ing NULL
+ (hx509_certs_find): expose
+ (hx509_get_one_cert): new function
+
+ * hxtool.c: hx509_certs_find is now exposed.
+
+ * hx_locl.h: Remove hx509_query, its exposed now.
+
+ * hx509.h: Add hx509_query.
+
+2006-02-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c: Add exceptions for null (empty) subjectNames
+
+ * data/nist-data: Add some more name constraints tests.
+
+ * data/nist-data: Add some of the test from 4.13 Name Constraints.
+
+ * cert.c: Name constraits needs to be evaluated in block as they
+ appear in the certificates, they can not be joined to one
+ list. One example of this is:
+
+ - cert is cn=foo,dc=bar,dc=baz
+ - subca is dc=foo,dc=baz with name restriction dc=kaka,dc=baz
+ - ca is dc=baz with name restriction dc=baz
+
+ If the name restrictions are merged to a list, the certificate
+ will pass this test.
+
+2006-02-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c: Handle more name constraints cases.
+
+ * crypto.c (dsa_verify_signature): if test if malloc failed
+
+2006-01-31 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c: Drop partial pkcs12 string2key implementation.
+
+2006-01-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * data/nist-data: Add commited out DSA tests (they fail).
+
+ * data/nist-data: Add 4.2 Validity Periods.
+
+ * test_nist.in: Make less verbose to use.
+
+ * Makefile.am: Add test_nist_cert.
+
+ * data/nist-data: Add some more CRL-tests.
+
+ * test_nist.in: Print $id instead of . when running the tests.
+
+ * test_nist.in: Drop verifying certifiates, its done in another
+ test now.
+
+ * data/nist-data: fixup kill-rectangle leftovers
+
+ * data/nist-data: Drop verifying certifiates, its done in another
+ test now. Add more crl tests. comment out all unused tests.
+
+ * test_nist_cert.in: test parse all nist certs
+
+2006-01-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hx509_err.et: Add HX509_CRL_UNKNOWN_EXTENSION.
+
+ * revoke.c: Check for unknown extentions in CRLs and CRLEntries.
+
+ * test_nist.in: Parse new format to handle CRL info.
+
+ * test_chain.in: Add --missing-crl.
+
+ * name.c (hx509_unparse_der_name): Rename from hx509_parse_name.
+ (_hx509_unparse_Name): Add.
+
+ * hxtool-commands.in: Add --missing-crl to verify commands.
+
+ * hx509_err.et: Add CRL errors.
+
+ * cert.c (hx509_context_set_missing_crl): new function Add CRL
+ handling.
+
+ * hx_locl.h: Add HX509_CTX_CRL_MISSING_OK.
+
+ * revoke.c: Parse and verify CRLs (simplistic).
+
+ * hxtool.c: Parse CRL info.
+
+ * data/nist-data: Change format so we can deal with CRLs, also
+ note the test-id from PKITS.
+
+ * data: regenerate test
+
+ * data/gen-req.sh: use static-file to generate tests
+
+ * data/static-file: new file to use for commited tests
+
+ * test_cms.in: Use static file, add --missing-crl.
+
+2006-01-18 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * print.c: Its cRLReason, not cRLReasons.
+
+ * hxtool.c: Attach revoke context to verify context.
+
+ * data/nist-data: change syntax to make match better with crl
+ checks
+
+ * cert.c: Verify no certificates has been revoked with the new
+ revoke interface.
+
+ * Makefile.am: libhx509_la_SOURCES += revoke.c
+
+ * revoke.c: Add framework for handling CRLs.
+
+ * hx509.h: Add hx509_revoke_ctx.
+
+2006-01-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * delete crypto_headers.h, use global file instead.
+
+ * crypto.c (PBE_string2key): libdes now supports PKCS12_key_gen
+
+2006-01-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * crypto_headers.h: Need BN_is_negative too.
+
+2006-01-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ks_p11.c (p11_rsa_public_decrypt): since is wrong, don't provide
+ it. PKCS11 can't do public_decrypt, it support verify though. All
+ this doesn't matter, since the code never go though this path.
+
+ * crypto_headers.h: Provide glue to compile with less warnings
+ with OpenSSL
+
+2006-01-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: Depend on LIB_des
+
+ * lock.c: Use "crypto_headers.h".
+
+ * crypto_headers.h: Include the two diffrent implementation of
+ crypto headers.
+
+ * cert.c: Use "crypto-headers.h". Load ENGINE configuration.
+
+ * crypto.c: Make compile with both OpenSSL and heimdal libdes.
+
+ * ks_p11.c: Add code for public key decryption (not supported yet)
+ and use "crypto-headers.h".
+
+
+2006-01-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * add a hx509_context where we can store configuration
+
+ * p11.c,Makefile.am: pkcs11 is now supported by library, remove
+ old files.
+
+ * ks_p11.c: more paranoid on refcount, set refcounter ealier,
+ reset pointers after free
+
+ * collector.c (struct private_key): remove temporary key data
+ storage, convert directly to a key
+ (match_localkeyid): match certificate and key using localkeyid
+ (match_keys): match certificate and key using _hx509_match_keys
+ (_hx509_collector_collect): rewrite to use match_keys and
+ match_localkeyid
+
+ * crypto.c (_hx509_match_keys): function that determins if a
+ private key matches a certificate, used when there is no
+ localkeyid.
+ (*) reset free pointer
+
+ * ks_file.c: Rewrite to use collector and mapping support
+ function.
+
+ * ks_p11.c (rsa_pkcs1_method): constify
+
+ * ks_p11.c: drop extra wrapping of p11_init
+
+ * crypto.c (_hx509_private_key_assign_key_file): use function to
+ extact rsa key
+
+ * cert.c: Revert previous, refcounter is unsigned, so it can never
+ be negative.
+
+ * cert.c (hx509_cert_ref): more refcount paranoia
+
+ * ks_p11.c: Implement rsa_private_decrypt and add stubs for public
+ ditto.
+
+ * ks_p11.c: Less printf, less memory leaks.
+
+ * ks_p11.c: Implement signing using pkcs11.
+
+ * ks_p11.c: Partly assign private key, enough to complete
+ collection, but not any crypto functionallity.
+
+ * collector.c: Use hx509_private_key to assign private keys.
+
+ * crypto.c: Remove most of the EVP_PKEY code, and use RSA
+ directly, this temporary removes DSA support.
+
+ * hxtool.c (print_f): print if there is a friendly name and if
+ there is a private key
+
+2006-01-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * name.c: Avoid warning from missing __attribute__((noreturn))
+
+ * lock.c (_hx509_lock_unlock_certs): return unlock certificates
+
+ * crypto.c (_hx509_private_key_assign_ptr): new function, exposes
+ EVP_PKEY
+ (_hx509_private_key_assign_key_file): remember to free private key
+ if there is one.
+
+ * cert.c (_hx509_abort): add newline to output and flush stdout
+
+ * Makefile.am: libhx509_la_SOURCES += collector.c
+
+ * hx_locl.h: forward type declaration of struct hx509_collector.
+
+ * collector.c: Support functions to collect certificates and
+ private keys and then match them.
+
+ * ks_p12.c: Use the new hx509_collector support functions.
+
+ * ks_p11.c: Add enough glue to support certificate iteration.
+
+ * test_nist_pkcs12.in: Less verbose.
+
+ * cert.c (hx509_cert_free): if there is a private key assosited
+ with this cert, free it
+
+ * print.c: Use _hx509_abort.
+
+ * ks_p12.c: Use _hx509_abort.
+
+ * hxtool.c: Use _hx509_abort.
+
+ * crypto.c: Use _hx509_abort.
+
+ * cms.c: Use _hx509_abort.
+
+ * cert.c: Use _hx509_abort.
+
+ * name.c: use _hx509_abort
+
+2006-01-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * name.c (hx509_name_to_string): don't cut bmpString in half.
+
+ * name.c (hx509_name_to_string): don't overwrite with 1 byte with
+ bmpString.
+
+ * ks_file.c (parse_certificate): avoid stomping before array
+
+ * name.c (oidtostring): avoid leaking memory
+
+ * keyset.c: Add _hx509_ks_dir_register.
+
+ * Makefile.am (libhx509_la_SOURCES): += ks_dir.c
+
+ * hxtool-commands.in: Remove pkcs11.
+
+ * hxtool.c: Remove pcert_pkcs11.
+
+ * ks_file.c: Factor out certificate parsing code.
+
+ * ks_dir.c: Add new keystore that treats all files in a directory
+ a keystore, useful for regression tests.
+
+2005-12-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_nist_pkcs12.in: Test parse PKCS12 files from NIST.
+
+ * data/nist-data: Can handle DSA certificate.
+
+ * hxtool.c: Print error code on failure.
+
+2005-10-29 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * crypto.c: Support DSA signature operations.
+
+2005-10-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * print.c: Validate that issuerAltName and subjectAltName isn't
+ empty.
+
+2005-09-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * p11.c: Cast to unsigned char to avoid warning.
+
+ * keyset.c: Register pkcs11 module.
+
+ * Makefile.am: Add ks_p11.c, install hxtool.
+
+ * ks_p11.c: Starting point of a pkcs11 module.
+
+2005-09-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * lock.c: Implement prompter.
+
+ * hxtool-commands.in: add --content to print
+
+ * hxtool.c: Split verify and print.
+
+ * cms.c: _hx509_pbe_decrypt now takes a hx509_lock.
+
+ * crypto.c: Make _hx509_pbe_decrypt take a hx509_lock, workaround
+ for empty password.
+
+ * name.c: Add DC, handle all Directory strings, fix signless
+ problems.
+
+2005-09-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_query.in: Pass in --pass to all commands.
+
+ * hxtool.c: Use option --pass.
+
+ * hxtool-commands.in: Add --pass to all commands.
+
+ * hx509_err.et: add UNKNOWN_LOCK_COMMAND and CRYPTO_NO_PROMPTER
+
+ * test_cms.in: pass in password to cms-create-sd
+
+ * crypto.c: Abstract out PBE_string2key so I can add PBE2 s2k
+ later. Avoid signess warnings with OpenSSL.
+
+ * cms.c: Use void * instead of char * for to avoid signedness
+ issues
+
+ * cert.c (hx509_cert_get_attribute): remove const, its not
+
+ * ks_p12.c: Cast size_t to unsigned long when print.
+
+ * name.c: Fix signedness warning.
+
+ * test_query.in: Use echo, the function check isn't defined here.
+
+2005-08-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool-commands.in: Add more options that was missing.
+
+2005-07-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_cms.in: Use --certificate= for enveloped/unenvelope.
+
+ * hxtool.c: Use --certificate= for enveloped/unenvelope. Clean
+ up.
+
+ * test_cms.in: add EnvelopeData tests
+
+ * hxtool.c: use id-envelopedData for ContentInfo
+
+ * hxtool-commands.in: add contentinfo wrapping for create/unwrap
+ enveloped data
+
+ * hxtool.c: add contentinfo wrapping for create/unwrap enveloped
+ data
+
+ * data/gen-req.sh: add enveloped data (aes128)
+
+ * crypto.c: add "new" RC2 oid
+
+2005-07-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hx_locl.h, cert.c: Add HX509_QUERY_MATCH_FUNCTION that allows
+ caller to match by function, note that this doesn't not work
+ directly for backends that implements ->query, they must do their
+ own processing. (I'm running out of flags, only 12 left now)
+
+ * test_cms.in: verify ContentInfo wrapping code in hxtool
+
+ * hxtool-commands.in (cms_create_sd): support wrapping in content
+ info spelling
+
+ * hxtool.c (cms_create_sd): support wrapping in content info
+
+ * test_cms.in: test more cms signeddata messages
+
+ * data/gen-req.sh: generate SignedData
+
+ * hxtool.c (cms_create_sd): support certificate store, add support
+ to unwrap a ContentInfo the SignedData inside.
+
+ * crypto.c: sprinkel rk_UNCONST
+
+ * crypto.c: add DER NULL to the digest oid's
+
+ * hxtool-commands.in: add --content-info to cms-verify-sd
+
+ * cms.c (hx509_cms_create_signed_1): pass in a full
+ AlgorithmIdentifier instead of heim_oid for digest_alg
+
+ * crypto.c: make digest_alg a digest_oid, it's not needed right
+ now
+
+ * hx509_err.et: add CERT_NOT_FOUND
+
+ * keyset.c (_hx509_certs_find): add error code for cert not
+ found
+
+ * cms.c (hx509_cms_verify_signed): add external store of
+ certificates, use the right digest algorithm identifier.
+
+ * cert.c: fix const warning
+
+ * ks_p12.c: slightly less verbose
+
+ * cert.c: add hx509_cert_find_subjectAltName_otherName, add
+ HX509_QUERY_MATCH_FRIENDLY_NAME
+
+ * hx509.h: add hx509_octet_string_list, remove bad comment
+
+ * hx_locl.h: add HX509_QUERY_MATCH_FRIENDLY_NAME
+
+ * keyset.c (hx509_certs_append): needs a hx509_lock, add one
+
+ * Makefile.am: add test cases tempfiles to CLEANFILES
+
+ * Makefile.am: add test_query to TESTS, fix dependency on hxtool
+ sources on hxtool-commands.h
+
+ * hxtool-commands.in: explain what signer is for create-sd
+
+ * hxtool.c: add query, add more options to verify-sd and create-sd
+
+ * test_cms.in: add more cms tests
+
+ * hxtool-commands.in: add query, add more options to verify-sd
+
+ * test_query.in: test query interface
+
+ * data: fix filenames for ds/ke files, add pkcs12 files, regen
+
+ * hxtool.c,Makefile.am,hxtool-commands.in: switch to slc
+
+2005-07-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cert.c (hx509_verify_destroy_ctx): add
+
+ * hxtool.c: free hx509_verify_ctx
+
+ * name.c (_hx509_name_ds_cmp): make sure all strings are not equal
+
+2005-07-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * hxtool.c: return error
+
+ * keyset.c: return errors from iterations
+
+ * test_chain.in: clean up checks
+
+ * ks_file.c (parse_certificate): return errno's not 1 in case of
+ error
+
+ * ks_file.c (file_iter): make sure endpointer is NULL
+
+ * ks_mem.c (mem_iter): follow conversion and return NULL when we
+ get to the end, not ENOENT.
+
+ * Makefile.am: test_chain depends on hxtool
+
+ * data: test certs that lasts 10 years
+
+ * data/gen-req.sh: script to generate test certs
+
+ * Makefile.am: Add regression tests.
+
+ * data: test certificate and keys
+
+ * test_chain.in: test chain
+
+ * hxtool.c (cms_create_sd): add KU digitalSigature as a
+ requirement to the query
+
+ * hx_locl.h: add KeyUsage query bits
+
+ * hx509_err.et: add KeyUsage error
+
+ * cms.c: add checks for KeyUsage
+
+ * cert.c: more checks on KeyUsage, allow to query on them too
+
+2005-07-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cms.c: Add missing break.
+
+ * hx_locl.h,cms.c,cert.c: allow matching on SubjectKeyId
+
+ * hxtool.c: Use _hx509_map_file, _hx509_unmap_file and
+ _hx509_write_file.
+
+ * file.c (_hx509_write_file): in case of write error, return errno
+
+ * file.c (_hx509_write_file): add a function that write a data
+ blob to disk too
+
+ * Fix id-tags
+
+ * Import mostly complete X.509 and CMS library. Handles, PEM, DER,
+ PKCS12 encoded certicates. Verificate RSA chains and handled
+ CMS's SignedData, and EnvelopedData.
+
+
diff --git a/third_party/heimdal/lib/hx509/Makefile.am b/third_party/heimdal/lib/hx509/Makefile.am
new file mode 100644
index 0000000..214dabf
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/Makefile.am
@@ -0,0 +1,398 @@
+include $(top_srcdir)/Makefile.am.common
+
+AM_CPPFLAGS += $(INCLUDE_openssl_crypto)
+
+lib_LTLIBRARIES = libhx509.la libhx509template.la
+libhx509_la_LDFLAGS = -version-info 5:0:0
+libhx509template_la_LDFLAGS = -version-info 5:0:0
+
+BUILT_SOURCES = \
+ sel-gram.h \
+ hx509_err.c \
+ hx509_err.h
+
+AM_YFLAGS = -o sel-gram.c
+
+dist_libhx509_la_SOURCES = \
+ ca.c \
+ cert.c \
+ char_map.h \
+ cms.c \
+ collector.c \
+ crypto.c \
+ crypto-ec.c \
+ doxygen.c \
+ error.c \
+ env.c \
+ file.c \
+ hx509.h \
+ hx_locl.h \
+ sel.c \
+ sel.h \
+ sel-gram.y \
+ sel-lex.l \
+ keyset.c \
+ ks_dir.c \
+ ks_file.c \
+ ks_mem.c \
+ ks_null.c \
+ ks_p11.c \
+ ks_p12.c \
+ ks_keychain.c \
+ lock.c \
+ name.c \
+ peer.c \
+ print.c \
+ softp11.c \
+ ref/pkcs11.h \
+ req.c \
+ revoke.c
+
+dist_libhx509template_la_SOURCES = $(dist_libhx509_la_SOURCES)
+
+sel-gram.h: sel-gram.c
+sel-lex.c: sel-gram.h
+
+libhx509_la_DEPENDENCIES = version-script.map
+libhx509template_la_DEPENDENCIES = version-script.map
+
+libhx509_la_LIBADD = \
+ $(LIB_com_err) \
+ $(LIB_hcrypto) \
+ $(LIB_openssl_crypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(top_builddir)/lib/wind/libwind.la \
+ $(top_builddir)/lib/base/libheimbase.la \
+ $(LIBADD_roken) \
+ $(LIB_dlopen)
+
+libhx509template_la_LIBADD = \
+ $(LIB_com_err) \
+ $(LIB_hcrypto) \
+ $(LIB_openssl_crypto) \
+ $(top_builddir)/lib/asn1/libasn1template.la \
+ $(top_builddir)/lib/wind/libwind.la \
+ $(top_builddir)/lib/base/libheimbase.la \
+ $(LIBADD_roken) \
+ $(LIB_dlopen)
+
+if FRAMEWORK_SECURITY
+libhx509_la_LDFLAGS += -framework Security -framework CoreFoundation
+libhx509template_la_LDFLAGS += -framework Security -framework CoreFoundation
+endif
+
+if versionscript
+libhx509_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+libhx509template_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+endif
+$(libhx509_la_OBJECTS): $(srcdir)/version-script.map $(nodist_include_HEADERS) $(priv_headers)
+$(libhx509template_la_OBJECTS): $(srcdir)/version-script.map $(nodist_include_HEADERS) $(priv_headers)
+
+nodist_libhx509_la_SOURCES = $(BUILT_SOURCES)
+nodist_libhx509template_la_SOURCES = $(BUILT_SOURCES)
+
+dist_include_HEADERS = hx509.h $(srcdir)/hx509-protos.h
+
+noinst_HEADERS = $(srcdir)/hx509-private.h
+
+nodist_include_HEADERS = hx509_err.h
+
+ALL_OBJECTS = $(libhx509_la_OBJECTS)
+ALL_OBJECTS += $(hxtool_OBJECTS)
+
+HX509_PROTOS = $(srcdir)/hx509-protos.h $(srcdir)/hx509-private.h
+
+$(ALL_OBJECTS): $(HX509_PROTOS)
+
+$(libhx509_la_OBJECTS): $(srcdir)/hx_locl.h
+$(libhx509template_la_OBJECTS): $(srcdir)/hx_locl.h
+
+$(srcdir)/hx509-protos.h: $(dist_libhx509_la_SOURCES)
+ $(heim_verbose)cd $(srcdir) && perl ../../cf/make-proto.pl -R '^(_|^C)' -E HX509_LIB -q -P comment -o hx509-protos.h $(dist_libhx509_la_SOURCES) || rm -f hx509-protos.h
+
+$(srcdir)/hx509-private.h: $(dist_libhx509_la_SOURCES)
+ $(heim_verbose)cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p hx509-private.h $(dist_libhx509_la_SOURCES) || rm -f hx509-private.h
+
+bin_PROGRAMS = hxtool
+
+hxtool-commands.c hxtool-commands.h: hxtool-commands.in $(SLC)
+ $(heim_verbose)$(SLC) $(srcdir)/hxtool-commands.in
+
+dist_hxtool_SOURCES = hxtool.c
+nodist_hxtool_SOURCES = hxtool-commands.c hxtool-commands.h
+
+$(hxtool_OBJECTS): hxtool-commands.h $(nodist_include_HEADERS)
+
+hxtool_LDADD = \
+ libhx509template.la \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_hcrypto) \
+ $(LIB_roken) \
+ $(top_builddir)/lib/sl/libsl.la
+
+CLEANFILES = $(BUILT_SOURCES) sel-gram.c sel-lex.c \
+ $(TESTS) \
+ hxtool-commands.c hxtool-commands.h *.tmp \
+ request.out \
+ out.pem out2.pem \
+ sd sd.pem \
+ sd.data sd.data.out \
+ ev.data ev.data.out \
+ cert-null.pem cert-sub-ca2.pem \
+ cert-ee.pem cert-ca.pem \
+ cert-sub-ee.pem cert-sub-ca.pem \
+ cert-proxy.der cert-ca.der cert-ee.der pkcs10-request.der \
+ wca.pem wuser.pem wdc.pem wcrl.crl \
+ random-data statfile crl.crl \
+ test p11dbg.log pkcs11.cfg \
+ test-rc-file.rc
+
+clean-local:
+ @echo "cleaning PKITS" ; rm -rf PKITS_data
+
+#
+# regression tests
+#
+
+check_SCRIPTS = $(SCRIPT_TESTS)
+check_PROGRAMS = $(PROGRAM_TESTS) test_soft_pkcs11
+
+LDADD = libhx509.la
+
+test_soft_pkcs11_LDADD = libhx509.la $(top_builddir)/lib/asn1/libasn1.la
+
+test_name_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la
+test_expr_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la
+
+TESTS = $(SCRIPT_TESTS) $(PROGRAM_TESTS)
+
+PROGRAM_TESTS = \
+ test_name \
+ test_expr
+
+SCRIPT_TESTS = \
+ test_ca \
+ test_cert \
+ test_chain \
+ test_cms \
+ test_crypto \
+ test_nist \
+ test_nist2 \
+ test_pkcs11 \
+ test_java_pkcs11 \
+ test_nist_cert \
+ test_nist_pkcs12 \
+ test_req \
+ test_windows \
+ test_query
+
+do_subst = $(heim_verbose)sed -e 's,[@]srcdir[@],$(srcdir),g' \
+ -e 's,[@]objdir[@],$(top_builddir)/lib/hx509,g' \
+ -e 's,[@]egrep[@],$(EGREP),g'
+
+test_ca: test_ca.in Makefile
+ $(do_subst) < $(srcdir)/test_ca.in > test_ca.tmp
+ $(heim_verbose)chmod +x test_ca.tmp
+ mv test_ca.tmp test_ca
+
+test_cert: test_cert.in Makefile
+ $(do_subst) < $(srcdir)/test_cert.in > test_cert.tmp
+ $(heim_verbose)chmod +x test_cert.tmp
+ mv test_cert.tmp test_cert
+
+test_chain: test_chain.in Makefile
+ $(do_subst) < $(srcdir)/test_chain.in > test_chain.tmp
+ $(heim_verbose)chmod +x test_chain.tmp
+ mv test_chain.tmp test_chain
+
+test_cms: test_cms.in Makefile
+ $(do_subst) < $(srcdir)/test_cms.in > test_cms.tmp
+ $(heim_verbose)chmod +x test_cms.tmp
+ mv test_cms.tmp test_cms
+
+test_crypto: test_crypto.in Makefile
+ $(do_subst) < $(srcdir)/test_crypto.in > test_crypto.tmp
+ $(heim_verbose)chmod +x test_crypto.tmp
+ mv test_crypto.tmp test_crypto
+
+test_nist: test_nist.in Makefile
+ $(do_subst) < $(srcdir)/test_nist.in > test_nist.tmp
+ $(heim_verbose)chmod +x test_nist.tmp
+ mv test_nist.tmp test_nist
+
+test_nist2: test_nist2.in Makefile
+ $(do_subst) < $(srcdir)/test_nist2.in > test_nist2.tmp
+ $(heim_verbose)chmod +x test_nist2.tmp
+ mv test_nist2.tmp test_nist2
+
+test_pkcs11: test_pkcs11.in Makefile
+ $(do_subst) < $(srcdir)/test_pkcs11.in > test_pkcs11.tmp
+ $(heim_verbose)chmod +x test_pkcs11.tmp
+ mv test_pkcs11.tmp test_pkcs11
+
+test_java_pkcs11: test_java_pkcs11.in Makefile
+ $(do_subst) < $(srcdir)/test_java_pkcs11.in > test_java_pkcs11.tmp
+ $(heim_verbose)chmod +x test_java_pkcs11.tmp
+ mv test_java_pkcs11.tmp test_java_pkcs11
+
+test_nist_cert: test_nist_cert.in Makefile
+ $(do_subst) < $(srcdir)/test_nist_cert.in > test_nist_cert.tmp
+ $(heim_verbose)chmod +x test_nist_cert.tmp
+ mv test_nist_cert.tmp test_nist_cert
+
+test_nist_pkcs12: test_nist_pkcs12.in Makefile
+ $(do_subst) < $(srcdir)/test_nist_pkcs12.in > test_nist_pkcs12.tmp
+ $(heim_verbose)chmod +x test_nist_pkcs12.tmp
+ mv test_nist_pkcs12.tmp test_nist_pkcs12
+
+test_req: test_req.in Makefile
+ $(do_subst) < $(srcdir)/test_req.in > test_req.tmp
+ $(heim_verbose)chmod +x test_req.tmp
+ mv test_req.tmp test_req
+
+test_windows: test_windows.in Makefile
+ $(do_subst) < $(srcdir)/test_windows.in > test_windows.tmp
+ $(heim_verbose)chmod +x test_windows.tmp
+ mv test_windows.tmp test_windows
+
+test_query: test_query.in Makefile
+ $(do_subst) < $(srcdir)/test_query.in > test_query.tmp
+ $(heim_verbose)chmod +x test_query.tmp
+ mv test_query.tmp test_query
+
+EXTRA_DIST = \
+ NTMakefile \
+ hxtool-version.rc \
+ libhx509-exports.def \
+ version-script.map \
+ hx509_err.et \
+ hxtool-commands.in \
+ quote.py \
+ test_ca.in \
+ test_chain.in \
+ test_cert.in \
+ test_cms.in \
+ test_crypto.in \
+ test_nist.in \
+ test_nist2.in \
+ test_nist_cert.in \
+ test_nist_pkcs12.in \
+ test_pkcs11.in \
+ test_java_pkcs11.in \
+ test_query.in \
+ test_req.in \
+ test_windows.in \
+ tst-crypto-available1 \
+ tst-crypto-available2 \
+ tst-crypto-available3 \
+ tst-crypto-select \
+ tst-crypto-select1 \
+ tst-crypto-select2 \
+ tst-crypto-select3 \
+ tst-crypto-select4 \
+ tst-crypto-select5 \
+ tst-crypto-select6 \
+ tst-crypto-select7 \
+ data/PKITS_data.zip \
+ data/eccurve.pem \
+ data/https.crt \
+ data/https.key \
+ data/mkcert.sh \
+ data/nist-result2 \
+ data/n0ll.pem \
+ data/secp256r1TestCA.cert.pem \
+ data/secp256r1TestCA.key.pem \
+ data/secp256r1TestCA.pem \
+ data/secp256r2TestClient.cert.pem \
+ data/secp256r2TestClient.key.pem \
+ data/secp256r2TestClient.pem \
+ data/secp256r2TestServer.cert.pem \
+ data/secp256r2TestServer.key.pem \
+ data/secp256r2TestServer.pem \
+ data/bleichenbacher-bad.pem \
+ data/bleichenbacher-good.pem \
+ data/bleichenbacher-sf-pad-correct.pem \
+ data/ca.crt \
+ data/ca.key \
+ data/crl1.crl \
+ data/crl1.der \
+ data/gen-req.sh \
+ data/j.pem \
+ data/kdc.crt \
+ data/kdc.key \
+ data/key.der \
+ data/key2.der \
+ data/nist-data \
+ data/nist-data2 \
+ data/no-proxy-test.crt \
+ data/no-proxy-test.key \
+ data/ocsp-req1.der \
+ data/ocsp-req2.der \
+ data/ocsp-resp1-2.der \
+ data/ocsp-resp1-3.der \
+ data/ocsp-resp1-ca.der \
+ data/ocsp-resp1-keyhash.der \
+ data/ocsp-resp1-ocsp-no-cert.der \
+ data/ocsp-resp1-ocsp.der \
+ data/ocsp-resp1.der \
+ data/ocsp-resp2.der \
+ data/ocsp-responder.crt \
+ data/ocsp-responder.key \
+ data/openssl.1.0.cnf \
+ data/openssl.1.1.cnf \
+ data/pkinit-proxy-chain.crt \
+ data/pkinit-proxy.crt \
+ data/pkinit-proxy.key \
+ data/pkinit-pw.key \
+ data/pkinit.crt \
+ data/pkinit.key \
+ data/pkinit-ec.crt \
+ data/pkinit-ec.key \
+ data/proxy-level-test.crt \
+ data/proxy-level-test.key \
+ data/proxy-test.crt \
+ data/proxy-test.key \
+ data/proxy10-child-test.crt \
+ data/proxy10-child-test.key \
+ data/proxy10-child-child-test.crt \
+ data/proxy10-child-child-test.key \
+ data/proxy10-test.crt \
+ data/proxy10-test.key \
+ data/revoke.crt \
+ data/revoke.key \
+ data/sf-class2-root.pem \
+ data/static-file \
+ data/sub-ca.crt \
+ data/sub-ca.key \
+ data/sub-cert.crt \
+ data/sub-cert.key \
+ data/sub-cert.p12 \
+ data/test-ds-only.crt \
+ data/test-ds-only.key \
+ data/test-enveloped-aes-128 \
+ data/test-enveloped-aes-256 \
+ data/test-enveloped-des \
+ data/test-enveloped-des-ede3 \
+ data/test-enveloped-rc2-128 \
+ data/test-enveloped-rc2-40 \
+ data/test-enveloped-rc2-64 \
+ data/test-ke-only.crt \
+ data/test-ke-only.key \
+ data/test-nopw.p12 \
+ data/test-pw.key \
+ data/test-signed-data \
+ data/test-signed-data-noattr \
+ data/test-signed-data-noattr-nocerts \
+ data/test-signed-sha-1 \
+ data/test-signed-sha-256 \
+ data/test-signed-sha-512 \
+ data/test.combined.crt \
+ data/test.crt \
+ data/test.key \
+ data/test.p12 \
+ data/win-u16-in-printablestring.der \
+ data/yutaka-pad-broken-ca.pem \
+ data/yutaka-pad-broken-cert.pem \
+ data/yutaka-pad-ok-ca.pem \
+ data/yutaka-pad-ok-cert.pem \
+ data/yutaka-pad.key
diff --git a/third_party/heimdal/lib/hx509/NTMakefile b/third_party/heimdal/lib/hx509/NTMakefile
new file mode 100644
index 0000000..4d5ff09
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/NTMakefile
@@ -0,0 +1,180 @@
+########################################################################
+#
+# Copyright (c) 2009-2017, Secure Endpoints Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# - Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+RELDIR=lib\hx509
+intcflags=-I$(OBJ)
+localcflags=-DASN1_LIB
+
+!include ../../windows/NTMakefile.w32
+
+libhx509_la_OBJS = \
+ $(OBJ)\ca.obj \
+ $(OBJ)\cert.obj \
+ $(OBJ)\cms.obj \
+ $(OBJ)\collector.obj \
+ $(OBJ)\crypto.obj \
+ $(OBJ)\crypto-ec.obj \
+ $(OBJ)\error.obj \
+ $(OBJ)\env.obj \
+ $(OBJ)\file.obj \
+ $(OBJ)\hx509_err.obj \
+ $(OBJ)\sel.obj \
+ $(OBJ)\sel-gram.obj \
+ $(OBJ)\sel-lex.obj \
+ $(OBJ)\keyset.obj \
+ $(OBJ)\ks_dir.obj \
+ $(OBJ)\ks_file.obj \
+ $(OBJ)\ks_mem.obj \
+ $(OBJ)\ks_null.obj \
+ $(OBJ)\ks_p11.obj \
+ $(OBJ)\ks_p12.obj \
+ $(OBJ)\ks_keychain.obj \
+ $(OBJ)\lock.obj \
+ $(OBJ)\name.obj \
+ $(OBJ)\peer.obj \
+ $(OBJ)\print.obj \
+ $(OBJ)\softp11.obj \
+ $(OBJ)\req.obj \
+ $(OBJ)\revoke.obj
+
+$(LIBHX509): $(libhx509_la_OBJS)
+ $(LIBCON)
+
+dist_libhx509_la_SOURCES = \
+ $(SRCDIR)\ca.c \
+ $(SRCDIR)\cert.c \
+ $(SRCDIR)\cms.c \
+ $(SRCDIR)\collector.c \
+ $(SRCDIR)\crypto.c \
+ $(SRCDIR)\crypto-ec.c \
+ $(SRCDIR)\doxygen.c \
+ $(SRCDIR)\error.c \
+ $(SRCDIR)\env.c \
+ $(SRCDIR)\file.c \
+ $(SRCDIR)\hx509.h \
+ $(SRCDIR)\hx_locl.h \
+ $(SRCDIR)\sel.c \
+ $(SRCDIR)\sel.h \
+ $(SRCDIR)\sel-gram.y \
+ $(SRCDIR)\sel-lex.l \
+ $(SRCDIR)\keyset.c \
+ $(SRCDIR)\ks_dir.c \
+ $(SRCDIR)\ks_file.c \
+ $(SRCDIR)\ks_mem.c \
+ $(SRCDIR)\ks_null.c \
+ $(SRCDIR)\ks_p11.c \
+ $(SRCDIR)\ks_p12.c \
+ $(SRCDIR)\ks_keychain.c \
+ $(SRCDIR)\lock.c \
+ $(SRCDIR)\name.c \
+ $(SRCDIR)\peer.c \
+ $(SRCDIR)\print.c \
+ $(SRCDIR)\softp11.c \
+ $(SRCDIR)\ref\pkcs11.h \
+ $(SRCDIR)\req.c \
+ $(SRCDIR)\revoke.c
+
+{}.c{$(OBJ)}.obj::
+ $(C2OBJ_P) -DBUILD_HX509_LIB -DASN1_LIB
+
+{$(OBJ)}.c{$(OBJ)}.obj::
+ $(C2OBJ_P) -DBUILD_HX509_LIB -DASN1_LIB
+
+asn1_compile=$(BINDIR)\asn1_compile.exe
+
+INCFILES= \
+ $(INCDIR)\hx509.h \
+ $(INCDIR)\hx509-protos.h \
+ $(INCDIR)\hx509-private.h \
+ $(INCDIR)\hx509_err.h
+
+hxtool.c: $(OBJ)\hxtool-commands.h
+
+SLC=$(BINDIR)\slc.exe
+
+$(OBJ)\hxtool-commands.c $(OBJ)\hxtool-commands.h: hxtool-commands.in $(SLC)
+ cd $(OBJ)
+ $(CP) $(SRCDIR)\hxtool-commands.in $(OBJ)\hxtool-commands.in
+ $(SLC) hxtool-commands.in
+ cd $(SRCDIR)
+
+$(BINDIR)\hxtool.exe: $(OBJ)\tool\hxtool.obj $(OBJ)\tool\hxtool-commands.obj $(LIBHEIMDAL) $(OBJ)\hxtool-version.res
+ $(EXECONLINK) $(LIBHEIMDAL) $(LIBROKEN) $(LIBSL) $(LIBVERS) $(LIBCOMERR) $(LIB_openssl_crypto)
+ $(EXEPREP)
+
+$(OBJ)\hx509-protos.h:
+ cd $(OBJ)
+ $(PERL) $(SRCDIR)\..\..\cf\make-proto.pl -R "^(_|^C)" -E HX509_LIB -q -P remove -o hx509-protos.h $(dist_libhx509_la_SOURCES) || $(RM) -f hx509-protos.h
+ cd $(SRCDIR)
+
+$(OBJ)\hx509-private.h:
+ cd $(OBJ)
+ $(PERL) $(SRCDIR)\..\..\cf\make-proto.pl -q -P remove -p hx509-private.h $(dist_libhx509_la_SOURCES) || $(RM) -f hx509-private.h
+ cd $(SRCDIR)
+
+$(OBJ)\hx509_err.c $(OBJ)\hx509_err.h: hx509_err.et
+ cd $(OBJ)
+ $(BINDIR)\compile_et.exe $(SRCDIR)\hx509_err.et
+ cd $(SRCDIR)
+
+$(OBJ)\sel-gram.obj: $(OBJ)\sel-gram.c
+ $(C2OBJ) -I$(SRCDIR)
+
+$(OBJ)\sel-lex.obj: $(OBJ)\sel-lex.c
+ $(C2OBJ) -I$(SRCDIR) -I$(OBJ) -DYY_NO_UNISTD_H
+
+$(OBJ)\sel-gram.c: sel-gram.y
+ $(YACC) -o $@ --defines=$(OBJ)\sel-gram.h sel-gram.y
+
+$(OBJ)\sel-lex.c: sel-lex.l
+ $(LEX) -P_hx509_sel_yy -o$@ sel-lex.l
+
+all:: $(INCFILES) $(LIBHX509)
+
+prep:: mktooldir
+
+mktooldir:
+! if !exist($(OBJ)\tool)
+ $(MKDIR) $(OBJ)\tool
+! endif
+
+all-tools:: $(BINDIR)\hxtool.exe
+
+clean::
+ -$(RM) $(BINDIR)\hxtool.*
+ -$(RM) $(OBJ)\tool\*.*
+
+{}.c{$(OBJ)\tool}.obj::
+ $(C2OBJ_C) /Fd$(OBJ)\tool\ /Fo$(OBJ)\tool\ $(MPOPT) /UASN1_LIB $<
+
+{$(OBJ)}.c{$(OBJ)\tool}.obj::
+ $(C2OBJ_C) /Fd$(OBJ)\tool\ /Fo$(OBJ)\tool\ $(MPOPT) /UASN1_LIB $<
+
diff --git a/third_party/heimdal/lib/hx509/TODO b/third_party/heimdal/lib/hx509/TODO
new file mode 100644
index 0000000..ecdfa8d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/TODO
@@ -0,0 +1,61 @@
+Handle private_key_ops better, esp wrt ->key_oid
+
+Better support for keyex negotiation, DH and ECDH.
+
+x501 name
+ parsing
+ comparing (ldap canonlisation rules)
+
+DSA support
+DSA2 support
+
+Rewrite the pkcs11 code to support the following:
+
+ * Reset the pin on card change.
+ * Ref count the lock structure to make sure we have a
+ prompter when we need it.
+ * Add support for CK_TOKEN_INFO.CKF_PROTECTED_AUTHENTICATION_PATH
+
+x509 policy mappings support
+
+CRL delta support
+
+Qualified statement
+ https://bugzilla.mozilla.org/show_bug.cgi?id=277797#c2
+
+
+Signed Receipts
+ http://www.faqs.org/rfcs/rfc2634.html
+ chapter 2
+
+tests
+ nist tests
+ name constrains
+ policy mappings
+ http://csrc.nist.gov/pki/testing/x509paths.html
+
+ building path using Subject/Issuer vs SubjKeyID vs AuthKeyID
+ negative tests
+ all checksums
+ conditions/branches
+
+pkcs7
+ handle pkcs7 support in CMS ?
+
+certificate request
+ generate pkcs10 request
+ from existing cert
+ generate CRMF request
+ pk-init KDC/client
+ web server/client
+ jabber server/client
+ email
+
+
+x509 issues:
+
+ OtherName is left unspecified, but it's used by other
+ specs. creating this hole where a application/CA can't specify
+ policy for SubjectAltName what covers whole space. For example, a
+ CA is trusted to provide authentication but not authorization.
+
diff --git a/third_party/heimdal/lib/hx509/ca.c b/third_party/heimdal/lib/hx509/ca.c
new file mode 100644
index 0000000..3d62b93
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ca.c
@@ -0,0 +1,3093 @@
+/*
+ * Copyright (c) 2006 - 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/**
+ * @page page_ca Hx509 CA functions
+ *
+ * See the library functions here: @ref hx509_ca
+ */
+
+struct hx509_ca_tbs {
+ hx509_name subject;
+ SubjectPublicKeyInfo spki;
+ KeyUsage ku;
+ ExtKeyUsage eku;
+ GeneralNames san;
+ CertificatePolicies cps;
+ PolicyMappings pms;
+ heim_integer serial;
+ struct {
+ unsigned int proxy:1;
+ unsigned int ca:1;
+ unsigned int key:1;
+ unsigned int serial:1;
+ unsigned int domaincontroller:1;
+ unsigned int xUniqueID:1;
+ } flags;
+ time_t notBefore;
+ time_t notAfter;
+ HeimPkinitPrincMaxLifeSecs pkinitTicketMaxLife;
+ int pathLenConstraint; /* both for CA and Proxy */
+ CRLDistributionPoints crldp;
+ heim_bit_string subjectUniqueID;
+ heim_bit_string issuerUniqueID;
+ AlgorithmIdentifier *sigalg;
+};
+
+/**
+ * Allocate an to-be-signed certificate object that will be converted
+ * into an certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs returned to-be-signed certicate object, free with
+ * hx509_ca_tbs_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_init(hx509_context context, hx509_ca_tbs *tbs)
+{
+ *tbs = calloc(1, sizeof(**tbs));
+ if (*tbs == NULL)
+ return ENOMEM;
+
+ return 0;
+}
+
+/**
+ * Free an To Be Signed object.
+ *
+ * @param tbs object to free.
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_ca_tbs_free(hx509_ca_tbs *tbs)
+{
+ if (tbs == NULL || *tbs == NULL)
+ return;
+
+ free_SubjectPublicKeyInfo(&(*tbs)->spki);
+ free_CertificatePolicies(&(*tbs)->cps);
+ free_PolicyMappings(&(*tbs)->pms);
+ free_GeneralNames(&(*tbs)->san);
+ free_ExtKeyUsage(&(*tbs)->eku);
+ der_free_heim_integer(&(*tbs)->serial);
+ free_CRLDistributionPoints(&(*tbs)->crldp);
+ der_free_bit_string(&(*tbs)->subjectUniqueID);
+ der_free_bit_string(&(*tbs)->issuerUniqueID);
+ if ((*tbs)->subject)
+ hx509_name_free(&(*tbs)->subject);
+ if ((*tbs)->sigalg) {
+ free_AlgorithmIdentifier((*tbs)->sigalg);
+ free((*tbs)->sigalg);
+ }
+
+ memset(*tbs, 0, sizeof(**tbs));
+ free(*tbs);
+ *tbs = NULL;
+}
+
+/**
+ * Set the absolute time when the certificate is valid from. If not
+ * set the current time will be used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param t time the certificated will start to be valid
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_notBefore(hx509_context context,
+ hx509_ca_tbs tbs,
+ time_t t)
+{
+ tbs->notBefore = t;
+ return 0;
+}
+
+/**
+ * Set the absolute time when the certificate is valid to.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param t time when the certificate will expire
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_notAfter(hx509_context context,
+ hx509_ca_tbs tbs,
+ time_t t)
+{
+ tbs->notAfter = t;
+ return 0;
+}
+
+/**
+ * Set the relative time when the certificiate is going to expire.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param delta seconds to the certificate is going to expire.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_notAfter_lifetime(hx509_context context,
+ hx509_ca_tbs tbs,
+ time_t delta)
+{
+ return hx509_ca_tbs_set_notAfter(context, tbs, time(NULL) + delta);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_pkinit_max_life(hx509_context context,
+ hx509_ca_tbs tbs,
+ time_t max_life)
+{
+ tbs->pkinitTicketMaxLife = max_life;
+ return 0;
+}
+
+static const struct units templatebits[] = {
+ { "ExtendedKeyUsage", HX509_CA_TEMPLATE_EKU },
+ { "KeyUsage", HX509_CA_TEMPLATE_KU },
+ { "SPKI", HX509_CA_TEMPLATE_SPKI },
+ { "notAfter", HX509_CA_TEMPLATE_NOTAFTER },
+ { "notBefore", HX509_CA_TEMPLATE_NOTBEFORE },
+ { "serial", HX509_CA_TEMPLATE_SERIAL },
+ { "subject", HX509_CA_TEMPLATE_SUBJECT },
+ { "pkinitMaxLife", HX509_CA_TEMPLATE_PKINIT_MAX_LIFE },
+ { NULL, 0 }
+};
+
+/**
+ * Make of template units, use to build flags argument to
+ * hx509_ca_tbs_set_template() with parse_units().
+ *
+ * @return an units structure.
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION const struct units * HX509_LIB_CALL
+hx509_ca_tbs_template_units(void)
+{
+ return templatebits;
+}
+
+/**
+ * Initialize the to-be-signed certificate object from a template certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param flags bit field selecting what to copy from the template
+ * certificate.
+ * @param cert template certificate.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_template(hx509_context context,
+ hx509_ca_tbs tbs,
+ int flags,
+ hx509_cert cert)
+{
+ int ret;
+
+ if (flags & HX509_CA_TEMPLATE_SUBJECT) {
+ if (tbs->subject)
+ hx509_name_free(&tbs->subject);
+ ret = hx509_cert_get_subject(cert, &tbs->subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to get subject from template");
+ return ret;
+ }
+ }
+ if (flags & HX509_CA_TEMPLATE_SERIAL) {
+ der_free_heim_integer(&tbs->serial);
+ ret = hx509_cert_get_serialnumber(cert, &tbs->serial);
+ tbs->flags.serial = !ret;
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy serial number");
+ return ret;
+ }
+ }
+ if (flags & HX509_CA_TEMPLATE_NOTBEFORE)
+ tbs->notBefore = hx509_cert_get_notBefore(cert);
+ if (flags & HX509_CA_TEMPLATE_NOTAFTER)
+ tbs->notAfter = hx509_cert_get_notAfter(cert);
+ if (flags & HX509_CA_TEMPLATE_SPKI) {
+ free_SubjectPublicKeyInfo(&tbs->spki);
+ ret = hx509_cert_get_SPKI(context, cert, &tbs->spki);
+ tbs->flags.key = !ret;
+ if (ret)
+ return ret;
+ }
+ if (flags & HX509_CA_TEMPLATE_KU) {
+ ret = _hx509_cert_get_keyusage(context, cert, &tbs->ku);
+ if (ret)
+ return ret;
+ }
+ if (flags & HX509_CA_TEMPLATE_EKU) {
+ ExtKeyUsage eku;
+ size_t i;
+ ret = _hx509_cert_get_eku(context, cert, &eku);
+ if (ret)
+ return ret;
+ for (i = 0; i < eku.len; i++) {
+ ret = hx509_ca_tbs_add_eku(context, tbs, &eku.val[i]);
+ if (ret) {
+ free_ExtKeyUsage(&eku);
+ return ret;
+ }
+ }
+ free_ExtKeyUsage(&eku);
+ }
+ if (flags & HX509_CA_TEMPLATE_PKINIT_MAX_LIFE) {
+ time_t max_life;
+
+ if ((max_life = hx509_cert_get_pkinit_max_life(context, cert, 0)) > 0)
+ hx509_ca_tbs_set_pkinit_max_life(context, tbs, max_life);
+ }
+ return 0;
+}
+
+/**
+ * Make the to-be-signed certificate object a CA certificate. If the
+ * pathLenConstraint is negative path length constraint is used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param pathLenConstraint path length constraint, negative, no
+ * constraint.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_ca(hx509_context context,
+ hx509_ca_tbs tbs,
+ int pathLenConstraint)
+{
+ tbs->flags.ca = 1;
+ tbs->pathLenConstraint = pathLenConstraint;
+ return 0;
+}
+
+/**
+ * Make the to-be-signed certificate object a proxy certificate. If the
+ * pathLenConstraint is negative path length constraint is used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param pathLenConstraint path length constraint, negative, no
+ * constraint.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_proxy(hx509_context context,
+ hx509_ca_tbs tbs,
+ int pathLenConstraint)
+{
+ tbs->flags.proxy = 1;
+ tbs->pathLenConstraint = pathLenConstraint;
+ return 0;
+}
+
+
+/**
+ * Make the to-be-signed certificate object a windows domain controller certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_domaincontroller(hx509_context context,
+ hx509_ca_tbs tbs)
+{
+ tbs->flags.domaincontroller = 1;
+ return 0;
+}
+
+/**
+ * Set the subject public key info (SPKI) in the to-be-signed certificate
+ * object. SPKI is the public key and key related parameters in the
+ * certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param spki subject public key info to use for the to-be-signed certificate object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_spki(hx509_context context,
+ hx509_ca_tbs tbs,
+ const SubjectPublicKeyInfo *spki)
+{
+ int ret;
+ free_SubjectPublicKeyInfo(&tbs->spki);
+ ret = copy_SubjectPublicKeyInfo(spki, &tbs->spki);
+ tbs->flags.key = !ret;
+ return ret;
+}
+
+/**
+ * Set the serial number to use for to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param serialNumber serial number to use for the to-be-signed
+ * certificate object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_serialnumber(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_integer *serialNumber)
+{
+ int ret;
+ der_free_heim_integer(&tbs->serial);
+ ret = der_copy_heim_integer(serialNumber, &tbs->serial);
+ tbs->flags.serial = !ret;
+ return ret;
+}
+
+/**
+ * Copy elements of a CSR into a TBS, but only if all of them are authorized.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param req CSR
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_from_csr(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_request req)
+{
+ hx509_san_type san_type;
+ heim_oid oid = { 0, 0 };
+ KeyUsage ku;
+ size_t i;
+ char *s = NULL;
+ int ret;
+
+ if (hx509_request_count_unauthorized(req)) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return EACCES;
+ }
+
+ ret = hx509_request_get_ku(context, req, &ku);
+ if (ret == 0 && KeyUsage2int(ku))
+ ret = hx509_ca_tbs_add_ku(context, tbs, ku);
+
+ for (i = 0; ret == 0; i++) {
+ free(s); s = NULL;
+ der_free_oid(&oid);
+ ret = hx509_request_get_eku(req, i, &s);
+ if (ret == 0)
+ ret = der_parse_heim_oid(s, ".", &oid);
+ if (ret == 0)
+ ret = hx509_ca_tbs_add_eku(context, tbs, &oid);
+ }
+ if (ret == HX509_NO_ITEM)
+ ret = 0;
+
+ for (i = 0; ret == 0; i++) {
+ free(s); s = NULL;
+ ret = hx509_request_get_san(req, i, &san_type, &s);
+ if (ret == 0)
+ ret = hx509_ca_tbs_add_san(context, tbs, san_type, s);
+ }
+ if (ret == HX509_NO_ITEM)
+ ret = 0;
+
+ der_free_oid(&oid);
+ free(s);
+ return ret;
+}
+
+/**
+ * An an extended key usage to the to-be-signed certificate object.
+ * Duplicates will detected and not added.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param oid extended key usage to add.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_ku(hx509_context context,
+ hx509_ca_tbs tbs,
+ KeyUsage ku)
+{
+ tbs->ku = ku;
+ return 0;
+}
+
+/**
+ * An an extended key usage to the to-be-signed certificate object.
+ * Duplicates will detected and not added.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param oid extended key usage to add.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_eku(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid)
+{
+ void *ptr;
+ int ret;
+ unsigned i;
+
+ /* search for duplicates */
+ for (i = 0; i < tbs->eku.len; i++) {
+ if (der_heim_oid_cmp(oid, &tbs->eku.val[i]) == 0)
+ return 0;
+ }
+
+ ptr = realloc(tbs->eku.val, sizeof(tbs->eku.val[0]) * (tbs->eku.len + 1));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ tbs->eku.val = ptr;
+ ret = der_copy_oid(oid, &tbs->eku.val[tbs->eku.len]);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+ tbs->eku.len += 1;
+ return 0;
+}
+
+/**
+ * Add a certificate policy to the to-be-signed certificate object. Duplicates
+ * will detected and not added.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param oid policy OID.
+ * @param cps_uri CPS URI to qualify policy with.
+ * @param user_notice user notice display text to qualify policy with.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_pol(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid,
+ const char *cps_uri,
+ const char *user_notice)
+{
+ PolicyQualifierInfos pqis;
+ PolicyQualifierInfo pqi;
+ PolicyInformation pi;
+ size_t i, size;
+ int ret = 0;
+
+ /* search for duplicates */
+ for (i = 0; i < tbs->cps.len; i++) {
+ if (der_heim_oid_cmp(oid, &tbs->cps.val[i].policyIdentifier) == 0)
+ return 0;
+ }
+
+ memset(&pi, 0, sizeof(pi));
+ memset(&pqi, 0, sizeof(pqi));
+ memset(&pqis, 0, sizeof(pqis));
+
+ pi.policyIdentifier = *oid;
+ if (cps_uri) {
+ CPSuri uri;
+
+ uri.length = strlen(cps_uri);
+ uri.data = (void *)(uintptr_t)cps_uri;
+ pqi.policyQualifierId = asn1_oid_id_pkix_qt_cps;
+
+ ASN1_MALLOC_ENCODE(CPSuri,
+ pqi.qualifier.data,
+ pqi.qualifier.length,
+ &uri, &size, ret);
+ if (ret == 0) {
+ ret = add_PolicyQualifierInfos(&pqis, &pqi);
+ free_heim_any(&pqi.qualifier);
+ }
+ }
+ if (ret == 0 && user_notice) {
+ DisplayText dt;
+ UserNotice un;
+
+ dt.element = choice_DisplayText_utf8String;
+ dt.u.utf8String = (void *)(uintptr_t)user_notice;
+ un.explicitText = &dt;
+ un.noticeRef = 0;
+
+ pqi.policyQualifierId = asn1_oid_id_pkix_qt_unotice;
+ ASN1_MALLOC_ENCODE(UserNotice,
+ pqi.qualifier.data,
+ pqi.qualifier.length,
+ &un, &size, ret);
+ if (ret == 0) {
+ ret = add_PolicyQualifierInfos(&pqis, &pqi);
+ free_heim_any(&pqi.qualifier);
+ }
+ }
+
+ pi.policyQualifiers = pqis.len ? &pqis : 0;
+
+ if (ret == 0)
+ ret = add_CertificatePolicies(&tbs->cps, &pi);
+
+ free_PolicyQualifierInfos(&pqis);
+ return ret;
+}
+
+/**
+ * Add a certificate policy mapping to the to-be-signed certificate object.
+ * Duplicates will detected and not added.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param issuer issuerDomainPolicy policy OID.
+ * @param subject subjectDomainPolicy policy OID.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_pol_mapping(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *issuer,
+ const heim_oid *subject)
+{
+ PolicyMapping pm;
+ size_t i;
+
+ /* search for duplicates */
+ for (i = 0; i < tbs->pms.len; i++) {
+ PolicyMapping *pmp = &tbs->pms.val[i];
+ if (der_heim_oid_cmp(issuer, &pmp->issuerDomainPolicy) == 0 &&
+ der_heim_oid_cmp(subject, &pmp->subjectDomainPolicy) == 0)
+ return 0;
+ }
+
+ memset(&pm, 0, sizeof(pm));
+ pm.issuerDomainPolicy = *issuer;
+ pm.subjectDomainPolicy = *subject;
+ return add_PolicyMappings(&tbs->pms, &pm);
+}
+
+/**
+ * Add CRL distribution point URI to the to-be-signed certificate
+ * object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param uri uri to the CRL.
+ * @param issuername name of the issuer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_crl_dp_uri(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *uri,
+ hx509_name issuername)
+{
+ DistributionPointName dpn;
+ DistributionPoint dp;
+ GeneralNames crlissuer;
+ GeneralName gn, ign;
+ Name in;
+ int ret;
+
+ memset(&dp, 0, sizeof(dp));
+ memset(&gn, 0, sizeof(gn));
+ memset(&ign, 0, sizeof(ign));
+ memset(&in, 0, sizeof(in));
+ gn.element = choice_GeneralName_uniformResourceIdentifier;
+ gn.u.uniformResourceIdentifier.data = rk_UNCONST(uri);
+ gn.u.uniformResourceIdentifier.length = strlen(uri);
+ dpn.element = choice_DistributionPointName_fullName;
+ dpn.u.fullName.len = 1;
+ dpn.u.fullName.val = &gn;
+ dp.distributionPoint = &dpn;
+
+ if (issuername) {
+ ign.element = choice_GeneralName_directoryName;
+ ret = hx509_name_to_Name(issuername, &ign.u.directoryName);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+ crlissuer.len = 1;
+ crlissuer.val = &ign;
+ dp.cRLIssuer = &crlissuer;
+ }
+
+ ret = add_CRLDistributionPoints(&tbs->crldp, &dp);
+ if (issuername)
+ free_Name(&ign.u.directoryName);
+
+ if (ret)
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+}
+
+/**
+ * Add Subject Alternative Name otherName to the to-be-signed
+ * certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param oid the oid of the OtherName.
+ * @param os data in the other name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_otherName(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid,
+ const heim_octet_string *os)
+{
+ GeneralName gn;
+
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_otherName;
+ gn.u.otherName.type_id = *oid;
+ gn.u.otherName.value = *os;
+
+ return add_GeneralNames(&tbs->san, &gn);
+}
+
+static
+int
+dequote_strndup(hx509_context context, const char *in, size_t len, char **out)
+{
+ size_t i, k;
+ char *s;
+
+ *out = NULL;
+ if ((s = malloc(len + 1)) == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ for (k = i = 0; i < len; i++) {
+ if (in[i] == '\\') {
+ switch (in[++i]) {
+ case 't': s[k++] = '\t'; break;
+ case 'b': s[k++] = '\b'; break;
+ case 'n': s[k++] = '\n'; break;
+ case '0':
+ for (i++; i < len; i++) {
+ if (in[i] == '\0')
+ break;
+ if (in[i++] == '\\' && in[i] == '0')
+ continue;
+ hx509_set_error_string(context, 0,
+ HX509_PARSING_NAME_FAILED,
+ "embedded NULs not supported in "
+ "PKINIT SANs");
+ free(s);
+ return HX509_PARSING_NAME_FAILED;
+ }
+ break;
+ case '\0':
+ hx509_set_error_string(context, 0,
+ HX509_PARSING_NAME_FAILED,
+ "trailing unquoted backslashes not "
+ "allowed in PKINIT SANs");
+ free(s);
+ return HX509_PARSING_NAME_FAILED;
+ default: s[k++] = in[i]; break;
+ }
+ } else {
+ s[k++] = in[i];
+ }
+ }
+ s[k] = '\0';
+
+ *out = s;
+ return 0;
+}
+
+int
+_hx509_make_pkinit_san(hx509_context context,
+ const char *principal,
+ heim_octet_string *os)
+{
+ KRB5PrincipalName p;
+ size_t size;
+ int ret;
+
+ os->data = NULL;
+ os->length = 0;
+ memset(&p, 0, sizeof(p));
+
+ /* Parse principal */
+ {
+ const char *str, *str_start;
+ size_t n, i;
+
+ /* Count number of components */
+ n = 1;
+ for (str = principal; *str != '\0' && *str != '@'; str++) {
+ if (*str == '\\') {
+ if (str[1] == '\0') {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "trailing \\ in principal name");
+ goto out;
+ }
+ str++;
+ } else if(*str == '/') {
+ n++;
+ } else if(*str == '@') {
+ break;
+ }
+ }
+ if (*str != '@') {
+ /* Note that we allow the realm to be empty */
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, "Missing @ in principal");
+ goto out;
+ };
+
+ p.principalName.name_string.val =
+ calloc(n, sizeof(*p.principalName.name_string.val));
+ if (p.principalName.name_string.val == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc: out of memory");
+ goto out;
+ }
+ p.principalName.name_string.len = n;
+ p.principalName.name_type = KRB5_NT_PRINCIPAL;
+
+ for (i = 0, str_start = str = principal; *str != '\0'; str++) {
+ if (*str=='\\') {
+ str++;
+ } else if(*str == '/') {
+ /* Note that we allow components to be empty */
+ ret = dequote_strndup(context, str_start, str - str_start,
+ &p.principalName.name_string.val[i++]);
+ if (ret)
+ goto out;
+ str_start = str + 1;
+ } else if(*str == '@') {
+ ret = dequote_strndup(context, str_start, str - str_start,
+ &p.principalName.name_string.val[i++]);
+ if (ret == 0)
+ ret = dequote_strndup(context, str + 1, strlen(str + 1), &p.realm);
+ if (ret)
+ goto out;
+ break;
+ }
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(KRB5PrincipalName, os->data, os->length, &p, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != os->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+out:
+ free_KRB5PrincipalName(&p);
+ return ret;
+}
+
+static int
+add_ia5string_san(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid,
+ const char *string)
+{
+ SRVName ustring;
+ heim_octet_string os;
+ size_t size;
+ int ret;
+
+ ustring.data = (void *)(uintptr_t)string;
+ ustring.length = strlen(string);
+
+ os.length = 0;
+ os.data = NULL;
+
+ ASN1_MALLOC_ENCODE(SRVName, os.data, os.length, &ustring, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ return ret;
+ }
+ if (size != os.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = hx509_ca_tbs_add_san_otherName(context, tbs, oid, &os);
+ free(os.data);
+ return ret;
+}
+
+/**
+ * Add DNSSRV Subject Alternative Name to the to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param dnssrv An ASCII string of the for _Service.Name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_dnssrv(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *dnssrv)
+{
+ size_t i, len;
+
+ /* Minimal DNSSRV input validation */
+ if (dnssrv == 0 || dnssrv[0] != '_') {
+ hx509_set_error_string(context, 0, EINVAL, "Invalid DNSSRV name");
+ return EINVAL;
+ }
+ for (i = 1, len = strlen(dnssrv); i < len; i++) {
+ if (dnssrv[i] == '.' && dnssrv[i + 1] != '\0')
+ break;
+ }
+ if (i == len) {
+ hx509_set_error_string(context, 0, EINVAL, "Invalid DNSSRV name");
+ return EINVAL;
+ }
+
+ return add_ia5string_san(context, tbs,
+ &asn1_oid_id_pkix_on_dnsSRV, dnssrv);
+}
+
+/**
+ * Add Kerberos Subject Alternative Name to the to-be-signed
+ * certificate object. The principal string is a UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param principal Kerberos principal to add to the certificate.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_pkinit(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *principal)
+{
+ heim_octet_string os;
+ int ret;
+
+ ret = _hx509_make_pkinit_san(context, principal, &os);
+ if (ret == 0)
+ ret = hx509_ca_tbs_add_san_otherName(context, tbs,
+ &asn1_oid_id_pkinit_san, &os);
+ free(os.data);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+add_utf8_san(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid,
+ const char *string)
+{
+ const PKIXXmppAddr ustring = (const PKIXXmppAddr)(uintptr_t)string;
+ heim_octet_string os;
+ size_t size;
+ int ret;
+
+ os.length = 0;
+ os.data = NULL;
+
+ ASN1_MALLOC_ENCODE(PKIXXmppAddr, os.data, os.length, &ustring, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ return ret;
+ }
+ if (size != os.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = hx509_ca_tbs_add_san_otherName(context, tbs, oid, &os);
+ free(os.data);
+ return ret;
+}
+
+/**
+ * Add Microsoft UPN Subject Alternative Name to the to-be-signed
+ * certificate object. The principal string is a UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param principal Microsoft UPN string.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_ms_upn(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *principal)
+{
+ return add_utf8_san(context, tbs, &asn1_oid_id_pkinit_ms_san, principal);
+}
+
+/**
+ * Add a Jabber/XMPP jid Subject Alternative Name to the to-be-signed
+ * certificate object. The jid is an UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param jid string of an a jabber id in UTF8.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_jid(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *jid)
+{
+ return add_utf8_san(context, tbs, &asn1_oid_id_pkix_on_xmppAddr, jid);
+}
+
+
+/**
+ * Add a Subject Alternative Name hostname to to-be-signed certificate
+ * object. A domain match starts with ., an exact match does not.
+ *
+ * Example of a an domain match: .domain.se matches the hostname
+ * host.domain.se.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param dnsname a hostame.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_hostname(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *dnsname)
+{
+ GeneralName gn;
+
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_dNSName;
+ gn.u.dNSName.data = rk_UNCONST(dnsname);
+ gn.u.dNSName.length = strlen(dnsname);
+
+ return add_GeneralNames(&tbs->san, &gn);
+}
+
+/**
+ * Add a Subject Alternative Name rfc822 (email address) to
+ * to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param rfc822Name a string to a email address.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_rfc822name(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *rfc822Name)
+{
+ GeneralName gn;
+
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_rfc822Name;
+ gn.u.rfc822Name.data = rk_UNCONST(rfc822Name);
+ gn.u.rfc822Name.length = strlen(rfc822Name);
+
+ return add_GeneralNames(&tbs->san, &gn);
+}
+
+/*
+ * PermanentIdentifier is one SAN for naming devices with TPMs after their
+ * endorsement keys or EK certificates. See TPM 2.0 Keys for Device Identity
+ * and Attestation, Version 1.00, Revision 2, 9/17/2020 (DRAFT).
+ *
+ * The text on the form of permanent identifiers for TPM endorsement keys sans
+ * certificates is clearly problematic, saying: "When the TPM does not have an
+ * EK certificate, the identifierValue is a digest of a concatenation of the
+ * UTF8 string “EkPubkey” (terminating NULL not included) with the binary EK
+ * public key", but since arbitrary binary is not necessarily valid UTF-8...
+ * and since NULs embedded in UTF-8 might be OK in some contexts but really
+ * isn't in C (and Heimdal's ASN.1 compiler does not allow NULs in the
+ * middle of strings)... That just cannot be correct. Since elsewhere the TCG
+ * specs use the hex encoding of the SHA-256 digest of the DER encoding of
+ * public keys, that's what we should support in Heimdal, and maybe send in a
+ * comment.
+ *
+ * Also, even where one should use hex encoding of the SHA-256 digest of the
+ * DER encoding of public keys, how should the public keys be represented?
+ * Presumably as SPKIs, with all the required parameters and no more.
+ */
+
+/**
+ * Add a Subject Alternative Name of PermanentIdentifier type to a to-be-signed
+ * certificate object. The permanent identifier form for TPM endorsement key
+ * certificates is the hex encoding of the SHA-256 digest of the DER encoding
+ * of the certificate. The permanent identifier form for TPM endorsement keys
+ * are of the form "EkPubkey<public-key>", where the form of <public-key> is
+ * not well specified at this point. It is the caller's responsibility to
+ * format the identifierValue.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param str permanent identifier name in the form "[<assigner-oid>]:[<id>]".
+ * @param assigner The OID of an assigner.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_permanentIdentifier_string(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *str)
+{
+ const heim_oid *found = NULL;
+ heim_oid oid;
+ const char *oidstr, *id;
+ char *freeme, *p;
+ int ret;
+
+ if ((freeme = strdup(str)) == NULL)
+ return hx509_enomem(context);
+
+ oidstr = freeme;
+ p = strchr(freeme, ':');
+ if (!p) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Invalid PermanentIdentifier string (should be \"[<oid>]:[<id>]\")",
+ oidstr);
+ free(freeme);
+ return EINVAL;
+ }
+ if (p) {
+ *(p++) = '\0';
+ id = p;
+ }
+ if (oidstr[0] != '\0') {
+ ret = der_find_heim_oid_by_name(oidstr, &found);
+ if (ret) {
+ ret = der_parse_heim_oid(oidstr, " .", &oid);
+ if (ret == 0)
+ found = &oid;
+ }
+ }
+ ret = hx509_ca_tbs_add_san_permanentIdentifier(context, tbs, id, found);
+ if (found == &oid)
+ der_free_oid(&oid);
+ free(freeme);
+ return ret;
+}
+
+/**
+ * Add a Subject Alternative Name of PermanentIdentifier type to a to-be-signed
+ * certificate object. The permanent identifier form for TPM endorsement key
+ * certificates is the hex encoding of the SHA-256 digest of the DER encoding
+ * of the certificate. The permanent identifier form for TPM endorsement keys
+ * are of the form "EkPubkey<public-key>", where the form of <public-key> is
+ * not well specified at this point. It is the caller's responsibility to
+ * format the identifierValue.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param identifierValue The permanent identifier name.
+ * @param assigner The OID of an assigner.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_permanentIdentifier(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *identifierValue,
+ const heim_oid *assigner)
+{
+ PermanentIdentifier pi;
+ heim_utf8_string s = (void *)(uintptr_t)identifierValue;
+ heim_octet_string os;
+ size_t size;
+ int ret;
+
+ pi.identifierValue = &s;
+ pi.assigner = (heim_oid*)(uintptr_t)assigner;
+ os.length = 0;
+ os.data = NULL;
+
+ ASN1_MALLOC_ENCODE(PermanentIdentifier, os.data, os.length, &pi, &size,
+ ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ return ret;
+ }
+ if (size != os.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = hx509_ca_tbs_add_san_otherName(context, tbs,
+ &asn1_oid_id_pkix_on_permanentIdentifier,
+ &os);
+ free(os.data);
+ return ret;
+}
+
+/**
+ * Add a Subject Alternative Name of HardwareModuleName type to a to-be-signed
+ * certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param str a string of the form "<oid>:<serial>".
+ * @param hwserial The serial number.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_hardwareModuleName_string(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *str)
+{
+ const heim_oid *found = NULL;
+ heim_oid oid;
+ const char *oidstr, *sno;
+ char *freeme, *p;
+ int ret;
+
+ if ((freeme = strdup(str)) == NULL)
+ return hx509_enomem(context);
+
+ oidstr = freeme;
+ p = strchr(freeme, ':');
+ if (!p) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Invalid HardwareModuleName string (should be "
+ "\"<oid>:<serial>\")",
+ oidstr);
+ free(freeme);
+ return EINVAL;
+ }
+ if (p) {
+ *(p++) = '\0';
+ sno = p;
+ }
+ if (oidstr[0] == '\0') {
+ found = &asn1_oid_tcg_tpm20;
+ } else {
+ ret = der_find_heim_oid_by_name(oidstr, &found);
+ if (ret) {
+ ret = der_parse_heim_oid(oidstr, " .", &oid);
+ if (ret == 0)
+ found = &oid;
+ }
+ }
+ if (!found) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Could not resolve or parse OID \"%s\"",
+ oidstr);
+ free(freeme);
+ return EINVAL;
+ }
+ ret = hx509_ca_tbs_add_san_hardwareModuleName(context, tbs, found, sno);
+ if (found == &oid)
+ der_free_oid(&oid);
+ free(freeme);
+ return ret;
+}
+
+/**
+ * Add a Subject Alternative Name of HardwareModuleName type to a to-be-signed
+ * certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param hwtype The hardwar module type (e.g., `&asn1_oid_tcg_tpm20').
+ * @param hwserial The serial number.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san_hardwareModuleName(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *hwtype,
+ const char *hwserial)
+{
+ HardwareModuleName hm;
+ heim_octet_string os;
+ size_t size;
+ int ret;
+
+ hm.hwType = *hwtype;
+ hm.hwSerialNum.data = (void *)(uintptr_t)hwserial;
+ hm.hwSerialNum.length = strlen(hwserial);
+ os.length = 0;
+ os.data = NULL;
+
+ ASN1_MALLOC_ENCODE(HardwareModuleName, os.data, os.length, &hm, &size,
+ ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ return ret;
+ }
+ if (size != os.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = hx509_ca_tbs_add_san_otherName(context, tbs,
+ &asn1_oid_id_on_hardwareModuleName,
+ &os);
+ free(os.data);
+ return ret;
+}
+
+/**
+ * Add a Subject Alternative Name of the given type to the
+ * to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param rfc822Name a string to a email address.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_add_san(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_san_type type,
+ const char *s)
+{
+ switch (type) {
+ case HX509_SAN_TYPE_EMAIL:
+ return hx509_ca_tbs_add_san_rfc822name(context, tbs, s);
+ case HX509_SAN_TYPE_DNSNAME:
+ return hx509_ca_tbs_add_san_hostname(context, tbs, s);
+ case HX509_SAN_TYPE_DN:
+ return ENOTSUP;
+ case HX509_SAN_TYPE_REGISTERED_ID:
+ return ENOTSUP;
+ case HX509_SAN_TYPE_XMPP:
+ return hx509_ca_tbs_add_san_jid(context, tbs, s);
+ case HX509_SAN_TYPE_PKINIT:
+ return hx509_ca_tbs_add_san_pkinit(context, tbs, s);
+ case HX509_SAN_TYPE_MS_UPN:
+ return hx509_ca_tbs_add_san_ms_upn(context, tbs, s);
+ default:
+ return ENOTSUP;
+ }
+}
+
+/**
+ * Set the subject name of a to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param subject the name to set a subject.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_subject(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_name subject)
+{
+ if (tbs->subject)
+ hx509_name_free(&tbs->subject);
+ return hx509_name_copy(context, subject, &tbs->subject);
+}
+
+/**
+ * Set the issuerUniqueID and subjectUniqueID
+ *
+ * These are only supposed to be used considered with version 2
+ * certificates, replaced by the two extensions SubjectKeyIdentifier
+ * and IssuerKeyIdentifier. This function is to allow application
+ * using legacy protocol to issue them.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param issuerUniqueID to be set
+ * @param subjectUniqueID to be set
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_unique(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_bit_string *subjectUniqueID,
+ const heim_bit_string *issuerUniqueID)
+{
+ int ret;
+
+ der_free_bit_string(&tbs->subjectUniqueID);
+ der_free_bit_string(&tbs->issuerUniqueID);
+
+ if (subjectUniqueID) {
+ ret = der_copy_bit_string(subjectUniqueID, &tbs->subjectUniqueID);
+ if (ret)
+ return ret;
+ }
+
+ if (issuerUniqueID) {
+ ret = der_copy_bit_string(issuerUniqueID, &tbs->issuerUniqueID);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * Expand the the subject name in the to-be-signed certificate object
+ * using hx509_name_expand().
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param env environment variable to expand variables in the subject
+ * name, see hx509_env_init().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_subject_expand(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_env env)
+{
+ return hx509_name_expand(context, tbs->subject, env);
+}
+
+/**
+ * Get the name of a to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ *
+ * @return An hx509 name.
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION hx509_name HX509_LIB_CALL
+hx509_ca_tbs_get_name(hx509_ca_tbs tbs)
+{
+ return tbs->subject;
+}
+
+/**
+ * Set signature algorithm on the to be signed certificate
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param sigalg signature algorithm to use
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_tbs_set_signature_algorithm(hx509_context context,
+ hx509_ca_tbs tbs,
+ const AlgorithmIdentifier *sigalg)
+{
+ int ret;
+
+ tbs->sigalg = calloc(1, sizeof(*tbs->sigalg));
+ if (tbs->sigalg == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
+ return ENOMEM;
+ }
+ ret = copy_AlgorithmIdentifier(sigalg, tbs->sigalg);
+ if (ret) {
+ free(tbs->sigalg);
+ tbs->sigalg = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+add_extension(hx509_context context,
+ TBSCertificate *tbsc,
+ int critical_flag,
+ const heim_oid *oid,
+ const heim_octet_string *data)
+{
+ Extension ext;
+ int ret;
+
+ memset(&ext, 0, sizeof(ext));
+
+ ext.critical = critical_flag;
+ ret = der_copy_oid(oid, &ext.extnID);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ret = der_copy_octet_string(data, &ext.extnValue);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ret = add_Extensions(tbsc->extensions, &ext);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+out:
+ free_Extension(&ext);
+ return ret;
+}
+
+static int
+build_proxy_prefix(hx509_context context, const Name *issuer, Name *subject)
+{
+ char *tstr;
+ time_t t;
+ int ret;
+
+ ret = copy_Name(issuer, subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy subject name");
+ return ret;
+ }
+
+ t = time(NULL);
+ ret = asprintf(&tstr, "ts-%lu", (unsigned long)t);
+ if (ret == -1 || tstr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Failed to copy subject name");
+ return ENOMEM;
+ }
+ /* prefix with CN=<ts>,...*/
+ ret = _hx509_name_modify(context, subject, 1, &asn1_oid_id_at_commonName, tstr);
+ free(tstr);
+ if (ret)
+ free_Name(subject);
+ return ret;
+}
+
+static int
+ca_sign(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_private_key signer,
+ const AuthorityKeyIdentifier *ai,
+ const Name *issuername,
+ hx509_cert *certificate)
+{
+ heim_error_t error = NULL;
+ heim_octet_string data;
+ Certificate c;
+ TBSCertificate *tbsc;
+ size_t size;
+ int ret;
+ const AlgorithmIdentifier *sigalg;
+ time_t notBefore;
+ time_t notAfter;
+
+ sigalg = tbs->sigalg;
+ if (sigalg == NULL)
+ sigalg = _hx509_crypto_default_sig_alg;
+
+ memset(&c, 0, sizeof(c));
+
+ /*
+ * Default values are: Valid since 24h ago, valid one year into
+ * the future, KeyUsage digitalSignature and keyEncipherment set,
+ * and keyCertSign for CA certificates.
+ */
+ notBefore = tbs->notBefore;
+ if (notBefore == 0)
+ notBefore = time(NULL) - 3600 * 24;
+ notAfter = tbs->notAfter;
+ if (notAfter == 0)
+ notAfter = time(NULL) + 3600 * 24 * 365;
+
+ if (tbs->flags.ca) {
+ tbs->ku.keyCertSign = 1;
+ tbs->ku.cRLSign = 1;
+ } else if (KeyUsage2int(tbs->ku) == 0) {
+ tbs->ku.digitalSignature = 1;
+ tbs->ku.keyEncipherment = 1;
+ }
+
+ /*
+ *
+ */
+
+ tbsc = &c.tbsCertificate;
+
+ /* Default subject Name to empty */
+ if (tbs->subject == NULL &&
+ (ret = hx509_empty_name(context, &tbs->subject)))
+ return ret;
+
+ /* Sanity checks */
+ if (tbs->flags.key == 0) {
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret, "No public key set");
+ return ret;
+ }
+ /*
+ * Don't put restrictions on proxy certificate's subject name, it
+ * will be generated below.
+ */
+ if (!tbs->flags.proxy) {
+ if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Empty subject and no SubjectAltNames");
+ return EINVAL;
+ }
+ }
+ if (tbs->flags.ca && tbs->flags.proxy) {
+ hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA "
+ "at the same time");
+ return EINVAL;
+ }
+ if (tbs->flags.proxy) {
+ if (tbs->san.len > 0) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Proxy certificate is not allowed "
+ "to have SubjectAltNames");
+ return EINVAL;
+ }
+ }
+
+ /* version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */
+ tbsc->version = calloc(1, sizeof(*tbsc->version));
+ if (tbsc->version == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ *tbsc->version = rfc3280_version_3;
+ /* serialNumber CertificateSerialNumber, */
+ if (tbs->flags.serial) {
+ ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ } else {
+ /*
+ * If no explicit serial number is specified, 20 random bytes should be
+ * sufficiently collision resistant. Since the serial number must be a
+ * positive integer, ensure minimal ASN.1 DER form by forcing the high
+ * bit off and the next bit on (thus avoiding an all zero first octet).
+ */
+ tbsc->serialNumber.length = 20;
+ tbsc->serialNumber.data = malloc(tbsc->serialNumber.length);
+ if (tbsc->serialNumber.data == NULL){
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length);
+ ((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f;
+ ((unsigned char *)tbsc->serialNumber.data)[0] |= 0x40;
+ }
+ /* signature AlgorithmIdentifier, */
+ ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy signature alg");
+ goto out;
+ }
+ /* issuer Name, */
+ if (issuername)
+ ret = copy_Name(issuername, &tbsc->issuer);
+ else
+ ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy issuer name");
+ goto out;
+ }
+ /* validity Validity, */
+ {
+ /*
+ * From RFC 5280, section 4.1.2.5:
+ *
+ * CAs conforming to this profile MUST always encode certificate
+ * validity dates through the year 2049 as UTCTime; certificate validity
+ * dates in 2050 or later MUST be encoded as GeneralizedTime.
+ * Conforming applications MUST be able to process validity dates that
+ * are encoded in either UTCTime or GeneralizedTime.
+ *
+ * 2524608000 is seconds since the epoch for 2050-01-01T00:00:00Z.
+ *
+ * Both, ...u.generalTime and ...u..utcTime are time_t.
+ */
+ if (notBefore < 1 || (int64_t)notBefore < 2524608000)
+ tbsc->validity.notBefore.element = choice_Time_utcTime;
+ else
+ tbsc->validity.notBefore.element = choice_Time_generalTime;
+ tbsc->validity.notBefore.u.generalTime = notBefore;
+
+ if (notAfter < 1 || (int64_t)notAfter < 2524608000)
+ tbsc->validity.notAfter.element = choice_Time_utcTime;
+ else
+ tbsc->validity.notAfter.element = choice_Time_generalTime;
+ tbsc->validity.notAfter.u.generalTime = notAfter;
+ }
+ /* subject Name, */
+ if (tbs->flags.proxy) {
+ ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject);
+ if (ret)
+ goto out;
+ } else {
+ ret = hx509_name_to_Name(tbs->subject, &tbsc->subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy subject name");
+ goto out;
+ }
+ }
+ /* subjectPublicKeyInfo SubjectPublicKeyInfo, */
+ ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy spki");
+ goto out;
+ }
+ /* issuerUniqueID [1] IMPLICIT BIT STRING OPTIONAL */
+ if (tbs->issuerUniqueID.length) {
+ tbsc->issuerUniqueID = calloc(1, sizeof(*tbsc->issuerUniqueID));
+ if (tbsc->issuerUniqueID == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ret = der_copy_bit_string(&tbs->issuerUniqueID, tbsc->issuerUniqueID);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ }
+ /* subjectUniqueID [2] IMPLICIT BIT STRING OPTIONAL */
+ if (tbs->subjectUniqueID.length) {
+ tbsc->subjectUniqueID = calloc(1, sizeof(*tbsc->subjectUniqueID));
+ if (tbsc->subjectUniqueID == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ ret = der_copy_bit_string(&tbs->subjectUniqueID, tbsc->subjectUniqueID);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ }
+
+ /* extensions [3] EXPLICIT Extensions OPTIONAL */
+ tbsc->extensions = calloc(1, sizeof(*tbsc->extensions));
+ if (tbsc->extensions == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ /* Add the text BMP string Domaincontroller to the cert */
+ if (tbs->flags.domaincontroller) {
+ data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d"
+ "\x00\x61\x00\x69\x00\x6e\x00\x43"
+ "\x00\x6f\x00\x6e\x00\x74\x00\x72"
+ "\x00\x6f\x00\x6c\x00\x6c\x00\x65"
+ "\x00\x72");
+ data.length = 34;
+
+ ret = add_extension(context, tbsc, 0,
+ &asn1_oid_id_ms_cert_enroll_domaincontroller,
+ &data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add KeyUsage */
+ if (KeyUsage2int(tbs->ku) > 0) {
+ ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length,
+ &tbs->ku, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 1,
+ &asn1_oid_id_x509_ce_keyUsage, &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add ExtendedKeyUsage */
+ if (tbs->eku.len > 0) {
+ ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length,
+ &tbs->eku, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 1,
+ &asn1_oid_id_x509_ce_extKeyUsage, &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add Subject Alternative Name */
+ if (tbs->san.len > 0) {
+ ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length,
+ &tbs->san, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ /* The SAN extension is critical if the subject Name is empty */
+ ret = add_extension(context, tbsc, hx509_name_is_null_p(tbs->subject),
+ &asn1_oid_id_x509_ce_subjectAltName, &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add Authority Key Identifier */
+ if (ai) {
+ ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length,
+ ai, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 0,
+ &asn1_oid_id_x509_ce_authorityKeyIdentifier,
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add Subject Key Identifier */
+ {
+ SubjectKeyIdentifier si;
+ unsigned char hash[SHA_DIGEST_LENGTH];
+
+ {
+ EVP_MD_CTX *ctx;
+
+ ctx = EVP_MD_CTX_create();
+ EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
+ EVP_DigestUpdate(ctx, tbs->spki.subjectPublicKey.data,
+ tbs->spki.subjectPublicKey.length / 8);
+ EVP_DigestFinal_ex(ctx, hash, NULL);
+ EVP_MD_CTX_destroy(ctx);
+ }
+
+ si.data = hash;
+ si.length = sizeof(hash);
+
+ ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length,
+ &si, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 0,
+ &asn1_oid_id_x509_ce_subjectKeyIdentifier,
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add BasicConstraints */
+ {
+ BasicConstraints bc;
+ unsigned int path;
+
+ memset(&bc, 0, sizeof(bc));
+
+ if (tbs->flags.ca) {
+ bc.cA = 1;
+ if (tbs->pathLenConstraint >= 0) {
+ path = tbs->pathLenConstraint;
+ bc.pathLenConstraint = &path;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length,
+ &bc, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ /* Critical if this is a CA */
+ ret = add_extension(context, tbsc, tbs->flags.ca,
+ &asn1_oid_id_x509_ce_basicConstraints,
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add Proxy */
+ if (tbs->flags.proxy) {
+ ProxyCertInfo info;
+
+ memset(&info, 0, sizeof(info));
+
+ if (tbs->pathLenConstraint >= 0) {
+ info.pCPathLenConstraint =
+ malloc(sizeof(*info.pCPathLenConstraint));
+ if (info.pCPathLenConstraint == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ *info.pCPathLenConstraint = tbs->pathLenConstraint;
+ }
+
+ ret = der_copy_oid(&asn1_oid_id_pkix_ppl_inheritAll,
+ &info.proxyPolicy.policyLanguage);
+ if (ret) {
+ free_ProxyCertInfo(&info);
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length,
+ &info, &size, ret);
+ free_ProxyCertInfo(&info);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 0,
+ &asn1_oid_id_pkix_pe_proxyCertInfo,
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add CRL distribution point */
+ if (tbs->crldp.len) {
+ ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length,
+ &tbs->crldp, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, FALSE,
+ &asn1_oid_id_x509_ce_cRLDistributionPoints,
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add CertificatePolicies */
+ if (tbs->cps.len) {
+ ASN1_MALLOC_ENCODE(CertificatePolicies, data.data, data.length,
+ &tbs->cps, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, FALSE,
+ &asn1_oid_id_x509_ce_certificatePolicies, &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add PolicyMappings */
+ if (tbs->cps.len) {
+ ASN1_MALLOC_ENCODE(PolicyMappings, data.data, data.length,
+ &tbs->pms, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, FALSE,
+ &asn1_oid_id_x509_ce_policyMappings, &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add Heimdal PKINIT ticket max life extension */
+ if (tbs->pkinitTicketMaxLife > 0) {
+ ASN1_MALLOC_ENCODE(HeimPkinitPrincMaxLifeSecs, data.data, data.length,
+ &tbs->pkinitTicketMaxLife, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, FALSE,
+ &asn1_oid_id_heim_ce_pkinit_princ_max_life, &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ goto out;
+ }
+ if (data.length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = _hx509_create_signature_bitstring(context,
+ signer,
+ sigalg,
+ &data,
+ &c.signatureAlgorithm,
+ &c.signatureValue);
+ free(data.data);
+ if (ret)
+ goto out;
+
+ *certificate = hx509_cert_init(context, &c, &error);
+ if (*certificate == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
+ goto out;
+ }
+
+ free_Certificate(&c);
+
+ return 0;
+
+out:
+ free_Certificate(&c);
+ return ret;
+}
+
+static int
+get_AuthorityKeyIdentifier(hx509_context context,
+ const Certificate *certificate,
+ AuthorityKeyIdentifier *ai)
+{
+ SubjectKeyIdentifier si;
+ int ret;
+
+ ret = _hx509_find_extension_subject_key_id(certificate, &si);
+ if (ret == 0) {
+ ai->keyIdentifier = calloc(1, sizeof(*ai->keyIdentifier));
+ if (ai->keyIdentifier == NULL) {
+ free_SubjectKeyIdentifier(&si);
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ret = der_copy_octet_string(&si, ai->keyIdentifier);
+ free_SubjectKeyIdentifier(&si);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ } else {
+ GeneralNames gns;
+ GeneralName gn;
+ Name name;
+
+ memset(&gn, 0, sizeof(gn));
+ memset(&gns, 0, sizeof(gns));
+ memset(&name, 0, sizeof(name));
+
+ ai->authorityCertIssuer =
+ calloc(1, sizeof(*ai->authorityCertIssuer));
+ if (ai->authorityCertIssuer == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ai->authorityCertSerialNumber =
+ calloc(1, sizeof(*ai->authorityCertSerialNumber));
+ if (ai->authorityCertSerialNumber == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ /*
+ * XXX unbreak when asn1 compiler handle IMPLICIT
+ *
+ * This is so horrible.
+ */
+
+ ret = copy_Name(&certificate->tbsCertificate.subject, &name);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_directoryName;
+ gn.u.directoryName.element = choice_Name_rdnSequence;
+ gn.u.directoryName.u.rdnSequence = name.u.rdnSequence;
+
+ ret = add_GeneralNames(&gns, &gn);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ ai->authorityCertIssuer->val = gns.val;
+ ai->authorityCertIssuer->len = gns.len;
+
+ ret = der_copy_heim_integer(&certificate->tbsCertificate.serialNumber,
+ ai->authorityCertSerialNumber);
+ if (ai->authorityCertSerialNumber == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ }
+out:
+ if (ret)
+ free_AuthorityKeyIdentifier(ai);
+ return ret;
+}
+
+
+/**
+ * Sign a to-be-signed certificate object with a issuer certificate.
+ *
+ * The caller needs to at least have called the following functions on the
+ * to-be-signed certificate object:
+ * - hx509_ca_tbs_init()
+ * - hx509_ca_tbs_set_subject()
+ * - hx509_ca_tbs_set_spki()
+ *
+ * When done the to-be-signed certificate object should be freed with
+ * hx509_ca_tbs_free().
+ *
+ * When creating self-signed certificate use hx509_ca_sign_self() instead.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param signer the CA certificate object to sign with (need private key).
+ * @param certificate return cerificate, free with hx509_cert_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_sign(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_cert signer,
+ hx509_cert *certificate)
+{
+ const Certificate *signer_cert;
+ AuthorityKeyIdentifier ai;
+ int ret;
+
+ memset(&ai, 0, sizeof(ai));
+
+ signer_cert = _hx509_get_cert(signer);
+
+ ret = get_AuthorityKeyIdentifier(context, signer_cert, &ai);
+ if (ret)
+ goto out;
+
+ ret = ca_sign(context,
+ tbs,
+ _hx509_cert_private_key(signer),
+ &ai,
+ &signer_cert->tbsCertificate.subject,
+ certificate);
+
+out:
+ free_AuthorityKeyIdentifier(&ai);
+
+ return ret;
+}
+
+/**
+ * Work just like hx509_ca_sign() but signs it-self.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param signer private key to sign with.
+ * @param certificate return cerificate, free with hx509_cert_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ca_sign_self(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_private_key signer,
+ hx509_cert *certificate)
+{
+ return ca_sign(context,
+ tbs,
+ signer,
+ NULL,
+ NULL,
+ certificate);
+}
+
+/*
+ * The following used to be `kdc_issue_certificate()', which was added for
+ * kx509 support in the kdc, then adapted for bx509d. It now has no
+ * kdc-specific code and very little krb5-specific code, and is named
+ * `hx509_ca_issue_certificate()'.
+ */
+
+/* From lib/krb5/principal.c */
+#define princ_num_comp(P) ((P)->principalName.name_string.len)
+#define princ_type(P) ((P)->principalName.name_type)
+#define princ_comp(P) ((P)->principalName.name_string.val)
+#define princ_ncomp(P, N) ((P)->principalName.name_string.val[(N)])
+#define princ_realm(P) ((P)->realm)
+
+static const char *
+princ_get_comp_string(KRB5PrincipalName *principal, unsigned int component)
+{
+ if (component >= princ_num_comp(principal))
+ return NULL;
+ return princ_ncomp(principal, component);
+}
+/* XXX Add unparse_name() */
+
+typedef enum {
+ CERT_NOTSUP = 0,
+ CERT_CLIENT = 1,
+ CERT_SERVER = 2,
+ CERT_MIXED = 3
+} cert_type;
+
+static void
+frees(char **s)
+{
+ free(*s);
+ *s = NULL;
+}
+
+static heim_error_code
+count_sans(hx509_request req, size_t *n)
+{
+ size_t i;
+ char *s = NULL;
+ int ret = 0;
+
+ *n = 0;
+ for (i = 0; ret == 0; i++) {
+ hx509_san_type san_type;
+
+ ret = hx509_request_get_san(req, i, &san_type, &s);
+ if (ret)
+ break;
+ switch (san_type) {
+ case HX509_SAN_TYPE_DNSNAME:
+ case HX509_SAN_TYPE_EMAIL:
+ case HX509_SAN_TYPE_XMPP:
+ case HX509_SAN_TYPE_PKINIT:
+ case HX509_SAN_TYPE_MS_UPN:
+ (*n)++;
+ break;
+ default:
+ ret = ENOTSUP;
+ }
+ frees(&s);
+ }
+ free(s);
+ return ret == HX509_NO_ITEM ? 0 : ret;
+}
+
+static int
+has_sans(hx509_request req)
+{
+ hx509_san_type san_type;
+ char *s = NULL;
+ int ret = hx509_request_get_san(req, 0, &san_type, &s);
+
+ frees(&s);
+ return ret == HX509_NO_ITEM ? 0 : 1;
+}
+
+static cert_type
+characterize_cprinc(hx509_context context,
+ KRB5PrincipalName *cprinc)
+{
+ unsigned int ncomp = princ_num_comp(cprinc);
+ const char *comp1 = princ_get_comp_string(cprinc, 1);
+
+ switch (ncomp) {
+ case 1:
+ return CERT_CLIENT;
+ case 2:
+ if (strchr(comp1, '.') == NULL)
+ return CERT_CLIENT;
+ return CERT_SERVER;
+ case 3:
+ if (strchr(comp1, '.'))
+ return CERT_SERVER;
+ return CERT_NOTSUP;
+ default:
+ return CERT_NOTSUP;
+ }
+}
+
+/* Characterize request as client or server cert req */
+static cert_type
+characterize(hx509_context context,
+ KRB5PrincipalName *cprinc,
+ hx509_request req)
+{
+ heim_error_code ret = 0;
+ cert_type res = CERT_NOTSUP;
+ size_t i;
+ char *s = NULL;
+ int want_ekus = 0;
+
+ if (!has_sans(req))
+ return characterize_cprinc(context, cprinc);
+
+ for (i = 0; ret == 0; i++) {
+ heim_oid oid;
+
+ frees(&s);
+ ret = hx509_request_get_eku(req, i, &s);
+ if (ret)
+ break;
+
+ want_ekus = 1;
+ ret = der_parse_heim_oid(s, ".", &oid);
+ if (ret)
+ break;
+ /*
+ * If the client wants only a server certificate, then we'll be
+ * willing to issue one that may be longer-lived than the client's
+ * ticket/token.
+ *
+ * There may be other server EKUs, but these are the ones we know
+ * of.
+ */
+ if (der_heim_oid_cmp(&asn1_oid_id_pkix_kp_serverAuth, &oid) &&
+ der_heim_oid_cmp(&asn1_oid_id_pkix_kp_OCSPSigning, &oid) &&
+ der_heim_oid_cmp(&asn1_oid_id_pkix_kp_secureShellServer, &oid))
+ res |= CERT_CLIENT;
+ else
+ res |= CERT_SERVER;
+ der_free_oid(&oid);
+ }
+ frees(&s);
+ if (ret == HX509_NO_ITEM)
+ ret = 0;
+
+ for (i = 0; ret == 0; i++) {
+ hx509_san_type san_type;
+
+ frees(&s);
+ ret = hx509_request_get_san(req, i, &san_type, &s);
+ if (ret)
+ break;
+ switch (san_type) {
+ case HX509_SAN_TYPE_DNSNAME:
+ if (!want_ekus)
+ res |= CERT_SERVER;
+ break;
+ case HX509_SAN_TYPE_EMAIL:
+ case HX509_SAN_TYPE_XMPP:
+ case HX509_SAN_TYPE_PKINIT:
+ case HX509_SAN_TYPE_MS_UPN:
+ if (!want_ekus)
+ res |= CERT_CLIENT;
+ break;
+ default:
+ ret = ENOTSUP;
+ }
+ if (ret)
+ break;
+ }
+ frees(&s);
+ if (ret == HX509_NO_ITEM)
+ ret = 0;
+ return ret ? CERT_NOTSUP : res;
+}
+
+/*
+ * Get a configuration sub-tree for kx509 based on what's being requested and
+ * by whom.
+ *
+ * We have a number of cases:
+ *
+ * - default certificate (no CSR used, or no certificate extensions requested)
+ * - for client principals
+ * - for service principals
+ * - client certificate requested (CSR used and client-y SANs/EKUs requested)
+ * - server certificate requested (CSR used and server-y SANs/EKUs requested)
+ * - mixed client/server certificate requested (...)
+ */
+static heim_error_code
+get_cf(hx509_context context,
+ const heim_config_binding *cf,
+ heim_log_facility *logf,
+ hx509_request req,
+ KRB5PrincipalName *cprinc,
+ const heim_config_binding **out)
+{
+ heim_error_code ret;
+ unsigned int ncomp = princ_num_comp(cprinc);
+ const char *realm = princ_realm(cprinc);
+ const char *comp0 = princ_get_comp_string(cprinc, 0);
+ const char *comp1 = princ_get_comp_string(cprinc, 1);
+ const char *label = NULL;
+ const char *svc = NULL;
+ const char *def = NULL;
+ cert_type certtype = CERT_NOTSUP;
+ size_t nsans = 0;
+
+ *out = NULL;
+ if (ncomp == 0) {
+ heim_log_msg(context->hcontext, logf, 5, NULL,
+ "Client principal has no components!");
+ hx509_set_error_string(context, 0, ret = ENOTSUP,
+ "Client principal has no components!");
+ return ret;
+ }
+
+ if ((ret = count_sans(req, &nsans)) ||
+ (certtype = characterize(context, cprinc, req)) == CERT_NOTSUP) {
+ heim_log_msg(context->hcontext, logf, 5, NULL,
+ "Could not characterize CSR");
+ hx509_set_error_string(context, 0, ret, "Could not characterize CSR");
+ return ret;
+ }
+
+ if (nsans) {
+ def = "custom";
+ /* Client requested some certificate extension, a SAN or EKU */
+ switch (certtype) {
+ case CERT_MIXED: label = "mixed"; break;
+ case CERT_CLIENT: label = "client"; break;
+ case CERT_SERVER: label = "server"; break;
+ default:
+ hx509_set_error_string(context, 0, ret = ENOTSUP,
+ "Requested SAN/EKU combination not "
+ "supported");
+ return ret;
+ }
+ } else {
+ def = "default";
+ /* Default certificate desired */
+ if (ncomp == 1) {
+ label = "user";
+ } else if (ncomp == 2 && strcmp(comp1, "root") == 0) {
+ label = "root_user";
+ } else if (ncomp == 2 && strcmp(comp1, "admin") == 0) {
+ label = "admin_user";
+ } else if (strchr(comp1, '.')) {
+ label = "hostbased_service";
+ svc = comp0;
+ } else {
+ label = "other";
+ }
+ }
+
+ *out = heim_config_get_list(context->hcontext, cf, label, svc, NULL);
+ if (*out) {
+ ret = 0;
+ } else {
+ heim_log_msg(context->hcontext, logf, 3, NULL,
+ "No configuration for %s %s certificate's realm "
+ "-> %s -> kx509 -> %s%s%s", def, label, realm, label,
+ svc ? " -> " : "", svc ? svc : "");
+ hx509_set_error_string(context, 0, EACCES,
+ "No configuration for %s %s certificate's realm "
+ "-> %s -> kx509 -> %s%s%s", def, label, realm, label,
+ svc ? " -> " : "", svc ? svc : "");
+ }
+ return ret;
+}
+
+
+/*
+ * Find and set a certificate template using a configuration sub-tree
+ * appropriate to the requesting principal.
+ *
+ * This allows for the specification of the following in configuration:
+ *
+ * - certificates as templates, with ${var} tokens in subjectName attribute
+ * values that will be expanded later
+ * - a plain string with ${var} tokens to use as the subjectName
+ * - EKUs
+ * - whether to include a PKINIT SAN
+ */
+static heim_error_code
+set_template(hx509_context context,
+ heim_log_facility *logf,
+ const heim_config_binding *cf,
+ hx509_ca_tbs tbs)
+{
+ heim_error_code ret = 0;
+ const char *cert_template = NULL;
+ const char *subj_name = NULL;
+ char **ekus = NULL;
+
+ if (cf == NULL)
+ return EACCES; /* Can't happen */
+
+ cert_template = heim_config_get_string(context->hcontext, cf,
+ "template_cert", NULL);
+ subj_name = heim_config_get_string(context->hcontext, cf, "subject_name",
+ NULL);
+ ekus = heim_config_get_strings(context->hcontext, cf, "ekus", NULL);
+
+ if (cert_template) {
+ hx509_certs certs;
+ hx509_cert template;
+
+ ret = hx509_certs_init(context, cert_template, 0, NULL, &certs);
+ if (ret == 0)
+ ret = hx509_get_one_cert(context, certs, &template);
+ hx509_certs_free(&certs);
+ if (ret) {
+ heim_log_msg(context->hcontext, logf, 1, NULL,
+ "Failed to load certificate template from %s",
+ cert_template);
+ hx509_set_error_string(context, 0, EACCES,
+ "Failed to load certificate template from "
+ "%s", cert_template);
+ return ret;
+ }
+
+ /*
+ * Only take the subjectName, the keyUsage, and EKUs from the template
+ * certificate.
+ */
+ ret = hx509_ca_tbs_set_template(context, tbs,
+ HX509_CA_TEMPLATE_SUBJECT |
+ HX509_CA_TEMPLATE_KU |
+ HX509_CA_TEMPLATE_EKU,
+ template);
+ hx509_cert_free(template);
+ if (ret)
+ return ret;
+ }
+
+ if (subj_name) {
+ hx509_name dn = NULL;
+
+ ret = hx509_parse_name(context, subj_name, &dn);
+ if (ret == 0)
+ ret = hx509_ca_tbs_set_subject(context, tbs, dn);
+ hx509_name_free(&dn);
+ if (ret)
+ return ret;
+ }
+
+ if (cert_template == NULL && subj_name == NULL) {
+ hx509_name dn = NULL;
+
+ ret = hx509_empty_name(context, &dn);
+ if (ret == 0)
+ ret = hx509_ca_tbs_set_subject(context, tbs, dn);
+ hx509_name_free(&dn);
+ if (ret)
+ return ret;
+ }
+
+ if (ekus) {
+ size_t i;
+
+ for (i = 0; ret == 0 && ekus[i]; i++) {
+ heim_oid oid = { 0, 0 };
+
+ if ((ret = der_find_or_parse_heim_oid(ekus[i], ".", &oid)) == 0)
+ ret = hx509_ca_tbs_add_eku(context, tbs, &oid);
+ der_free_oid(&oid);
+ }
+ heim_config_free_strings(ekus);
+ }
+
+ /*
+ * XXX A KeyUsage template would be nice, but it needs some smarts to
+ * remove, e.g., encipherOnly, decipherOnly, keyEncipherment, if the SPKI
+ * algorithm does not support encryption. The same logic should be added
+ * to hx509_ca_tbs_set_template()'s HX509_CA_TEMPLATE_KU functionality.
+ */
+ return ret;
+}
+
+/*
+ * Find and set a certificate template, set "variables" in `env', and add add
+ * default SANs/EKUs as appropriate.
+ *
+ * TODO:
+ * - lookup a template for the client principal in its HDB entry
+ * - lookup subjectName, SANs for a principal in its HDB entry
+ * - lookup a host-based client principal's HDB entry and add its canonical
+ * name / aliases as dNSName SANs
+ * (this would have to be if requested by the client, perhaps)
+ */
+static heim_error_code
+set_tbs(hx509_context context,
+ heim_log_facility *logf,
+ const heim_config_binding *cf,
+ hx509_request req,
+ KRB5PrincipalName *cprinc,
+ hx509_env *env,
+ hx509_ca_tbs tbs)
+{
+ KRB5PrincipalName cprinc_no_realm = *cprinc;
+ heim_error_code ret;
+ unsigned int ncomp = princ_num_comp(cprinc);
+ const char *realm = princ_realm(cprinc);
+ const char *comp0 = princ_get_comp_string(cprinc, 0);
+ const char *comp1 = princ_get_comp_string(cprinc, 1);
+ const char *comp2 = princ_get_comp_string(cprinc, 2);
+ struct rk_strpool *strpool;
+ char *princ_no_realm = NULL;
+ char *princ = NULL;
+
+ strpool = _hx509_unparse_kerberos_name(NULL, cprinc);
+ if (strpool)
+ princ = rk_strpoolcollect(strpool);
+ cprinc_no_realm.realm = NULL;
+ strpool = _hx509_unparse_kerberos_name(NULL, &cprinc_no_realm);
+ if (strpool)
+ princ_no_realm = rk_strpoolcollect(strpool);
+ if (princ == NULL || princ_no_realm == NULL) {
+ free(princ);
+ return hx509_enomem(context);
+ }
+ strpool = NULL;
+ ret = hx509_env_add(context, env, "principal-name-without-realm",
+ princ_no_realm);
+ if (ret == 0)
+ ret = hx509_env_add(context, env, "principal-name", princ);
+ if (ret == 0)
+ ret = hx509_env_add(context, env, "principal-name-realm",
+ realm);
+
+ /* Populate requested certificate extensions from CSR/CSRPlus if allowed */
+ if (ret == 0)
+ ret = hx509_ca_tbs_set_from_csr(context, tbs, req);
+ if (ret == 0)
+ ret = set_template(context, logf, cf, tbs);
+
+ /*
+ * Optionally add PKINIT SAN.
+ *
+ * Adding an id-pkinit-san means the client can use the certificate to
+ * initiate PKINIT. That might seem odd, but it enables a sort of PKIX
+ * credential delegation by allowing forwarded Kerberos tickets to be
+ * used to acquire PKIX credentials. Thus this can work:
+ *
+ * PKIX (w/ HW token) -> Kerberos ->
+ * PKIX (w/ softtoken) -> Kerberos ->
+ * PKIX (w/ softtoken) -> Kerberos ->
+ * ...
+ *
+ * Note that we may not have added the PKINIT EKU -- that depends on the
+ * template, and host-based service templates might well not include it.
+ */
+ if (ret == 0 && !has_sans(req) &&
+ heim_config_get_bool_default(context->hcontext, cf, FALSE,
+ "include_pkinit_san", NULL)) {
+ ret = hx509_ca_tbs_add_san_pkinit(context, tbs, princ);
+ }
+
+ if (ret)
+ goto out;
+
+ if (ncomp == 1) {
+ const char *email_domain;
+
+ ret = hx509_env_add(context, env, "principal-component0",
+ princ_no_realm);
+
+ /*
+ * If configured, include an rfc822Name that's just the client's
+ * principal name sans realm @ configured email domain.
+ */
+ if (ret == 0 && !has_sans(req) &&
+ (email_domain = heim_config_get_string(context->hcontext, cf,
+ "email_domain", NULL))) {
+ char *email;
+
+ if (asprintf(&email, "%s@%s", princ_no_realm, email_domain) == -1 ||
+ email == NULL)
+ goto enomem;
+ ret = hx509_ca_tbs_add_san_rfc822name(context, tbs, email);
+ free(email);
+ }
+ } else if (ncomp == 2 || ncomp == 3) {
+ /*
+ * 2- and 3-component principal name.
+ *
+ * We do not have a reliable name-type indicator. If the second
+ * component has a '.' in it then we'll assume that the name is a
+ * host-based (2-component) or domain-based (3-component) service
+ * principal name. Else we'll assume it's a two-component admin-style
+ * username.
+ */
+
+ ret = hx509_env_add(context, env, "principal-component0", comp0);
+ if (ret == 0)
+ ret = hx509_env_add(context, env, "principal-component1", comp1);
+ if (ret == 0 && ncomp == 3)
+ ret = hx509_env_add(context, env, "principal-component2", comp2);
+ if (ret == 0 && strchr(comp1, '.')) {
+ /* Looks like host-based or domain-based service */
+ ret = hx509_env_add(context, env, "principal-service-name", comp0);
+ if (ret == 0)
+ ret = hx509_env_add(context, env, "principal-host-name",
+ comp1);
+ if (ret == 0 && ncomp == 3)
+ ret = hx509_env_add(context, env, "principal-domain-name",
+ comp2);
+ if (ret == 0 && !has_sans(req) &&
+ heim_config_get_bool_default(context->hcontext, cf, FALSE,
+ "include_dnsname_san", NULL)) {
+ ret = hx509_ca_tbs_add_san_hostname(context, tbs, comp1);
+ }
+ }
+ } else {
+ heim_log_msg(context->hcontext, logf, 5, NULL,
+ "kx509/bx509 client %s has too many components!", princ);
+ hx509_set_error_string(context, 0, ret = EACCES,
+ "kx509/bx509 client %s has too many "
+ "components!", princ);
+ }
+
+out:
+ if (ret == ENOMEM)
+ goto enomem;
+ free(princ_no_realm);
+ free(princ);
+ return ret;
+
+enomem:
+ heim_log_msg(context->hcontext, logf, 0, NULL,
+ "Could not set up TBSCertificate: Out of memory");
+ ret = hx509_enomem(context);
+ goto out;
+}
+
+/*
+ * Set the notBefore/notAfter for the certificate to be issued.
+ *
+ * Here `starttime' is the supplicant's credentials' notBefore equivalent,
+ * while `endtime' is the supplicant's credentials' notAfter equivalent.
+ *
+ * `req_life' is the lifetime requested by the supplicant.
+ *
+ * `endtime' must be larger than the current time.
+ *
+ * `starttime' can be zero or negative, in which case the notBefore will be the
+ * current time minus five minutes.
+ *
+ * `endtime', `req_life' and configuration parameters will be used to compute
+ * the actual notAfter.
+ */
+static heim_error_code
+tbs_set_times(hx509_context context,
+ const heim_config_binding *cf,
+ heim_log_facility *logf,
+ time_t starttime,
+ time_t endtime,
+ time_t req_life,
+ hx509_ca_tbs tbs)
+{
+ time_t now = time(NULL);
+ time_t force = heim_config_get_time_default(context->hcontext,
+ cf, 5 * 24 * 3600,
+ "force_cert_lifetime", NULL);
+ time_t clamp = heim_config_get_time_default(context->hcontext, cf, 0,
+ "max_cert_lifetime", NULL);
+ int allow_more = heim_config_get_bool_default(context->hcontext, cf, FALSE,
+ "allow_extra_lifetime",
+ NULL);
+ starttime = starttime > 0 ? starttime : now - 5 * 60;
+
+ if (endtime < now) {
+ heim_log_msg(context->hcontext, logf, 3, NULL,
+ "Endtime is in the past");
+ hx509_set_error_string(context, 0, ERANGE, "Endtime is in the past");
+ return ERANGE;
+ }
+
+ /* Apply requested lifetime if shorter or if allowed more */
+ if (req_life > 0 && req_life <= endtime - now)
+ endtime = now + req_life;
+ else if (req_life > 0 && allow_more)
+ endtime = now + req_life;
+
+ /* Apply floor */
+ if (force > 0 && force > endtime - now)
+ endtime = now + force;
+
+ /* Apply ceiling */
+ if (clamp > 0 && clamp < endtime - now)
+ endtime = now + clamp;
+
+ hx509_ca_tbs_set_notAfter(context, tbs, endtime);
+ hx509_ca_tbs_set_notBefore(context, tbs, starttime);
+ return 0;
+}
+
+/*
+ * Build a certifate for `principal' and its CSR.
+ *
+ * XXX Make `cprinc' a GeneralName! That's why this is private for now.
+ */
+heim_error_code
+_hx509_ca_issue_certificate(hx509_context context,
+ const heim_config_binding *cf,
+ heim_log_facility *logf,
+ hx509_request req,
+ KRB5PrincipalName *cprinc,
+ time_t starttime,
+ time_t endtime,
+ time_t req_life,
+ int send_chain,
+ hx509_certs *out)
+{
+ heim_error_code ret;
+ const char *ca;
+ hx509_ca_tbs tbs = NULL;
+ hx509_certs chain = NULL;
+ hx509_cert signer = NULL;
+ hx509_cert cert = NULL;
+ hx509_env env = NULL;
+ KeyUsage ku;
+
+ *out = NULL;
+ /* Force KU */
+ ku = int2KeyUsage(0);
+ ku.digitalSignature = 1;
+ hx509_request_authorize_ku(req, ku);
+
+ ret = get_cf(context, cf, logf, req, cprinc, &cf);
+ if (ret)
+ return ret;
+
+ if ((ca = heim_config_get_string(context->hcontext, cf,
+ "ca", NULL)) == NULL) {
+ heim_log_msg(context->hcontext, logf, 3, NULL,
+ "No kx509 CA issuer credential specified");
+ hx509_set_error_string(context, 0, ret = EACCES,
+ "No kx509 CA issuer credential specified");
+ return ret;
+ }
+
+ ret = hx509_ca_tbs_init(context, &tbs);
+ if (ret) {
+ heim_log_msg(context->hcontext, logf, 0, NULL,
+ "Failed to create certificate: Out of memory");
+ return ret;
+ }
+
+ /* Lookup a template and set things in `env' and `tbs' as appropriate */
+ if (ret == 0)
+ ret = set_tbs(context, logf, cf, req, cprinc, &env, tbs);
+
+ /* Populate generic template "env" variables */
+
+ /*
+ * The `tbs' and `env' are now complete as to naming and EKUs.
+ *
+ * We check that the `tbs' is not name-less, after which all remaining
+ * failures here will not be policy failures. So we also log the intent to
+ * issue a certificate now.
+ */
+ if (ret == 0 && hx509_name_is_null_p(hx509_ca_tbs_get_name(tbs)) &&
+ !has_sans(req)) {
+ heim_log_msg(context->hcontext, logf, 3, NULL,
+ "Not issuing certificate because it would have no names");
+ hx509_set_error_string(context, 0, ret = EACCES,
+ "Not issuing certificate because it "
+ "would have no names");
+ }
+ if (ret)
+ goto out;
+
+ /*
+ * Still to be done below:
+ *
+ * - set certificate spki
+ * - set certificate validity
+ * - expand variables in certificate subject name template
+ * - sign certificate
+ * - encode certificate and chain
+ */
+
+ /* Load the issuer certificate and private key */
+ {
+ hx509_certs certs;
+ hx509_query *q;
+
+ ret = hx509_certs_init(context, ca, 0, NULL, &certs);
+ if (ret) {
+ heim_log_msg(context->hcontext, logf, 1, NULL,
+ "Failed to load CA certificate and private key %s",
+ ca);
+ hx509_set_error_string(context, 0, ret, "Failed to load "
+ "CA certificate and private key %s", ca);
+ goto out;
+ }
+ ret = hx509_query_alloc(context, &q);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN);
+
+ ret = hx509_certs_find(context, certs, q, &signer);
+ hx509_query_free(context, q);
+ hx509_certs_free(&certs);
+ if (ret) {
+ heim_log_msg(context->hcontext, logf, 1, NULL,
+ "Failed to find a CA certificate in %s", ca);
+ hx509_set_error_string(context, 0, ret,
+ "Failed to find a CA certificate in %s",
+ ca);
+ goto out;
+ }
+ }
+
+ /* Populate the subject public key in the TBS context */
+ {
+ SubjectPublicKeyInfo spki;
+
+ ret = hx509_request_get_SubjectPublicKeyInfo(context,
+ req, &spki);
+ if (ret == 0)
+ ret = hx509_ca_tbs_set_spki(context, tbs, &spki);
+ free_SubjectPublicKeyInfo(&spki);
+ if (ret)
+ goto out;
+ }
+
+ /* Work out cert expiration */
+ if (ret == 0)
+ ret = tbs_set_times(context, cf, logf, starttime, endtime, req_life,
+ tbs);
+
+ /* Expand the subjectName template in the TBS using the env */
+ if (ret == 0)
+ ret = hx509_ca_tbs_subject_expand(context, tbs, env);
+ hx509_env_free(&env);
+
+ /* All done with the TBS, sign/issue the certificate */
+ if (ret == 0)
+ ret = hx509_ca_sign(context, tbs, signer, &cert);
+
+ /*
+ * Gather the certificate and chain into a MEMORY store, being careful not
+ * to include private keys in the chain.
+ *
+ * We could have specified a separate configuration parameter for an hx509
+ * store meant to have only the chain and no private keys, but expecting
+ * the full chain in the issuer credential store and copying only the certs
+ * (but not the private keys) is safer and easier to configure.
+ */
+ if (ret == 0)
+ ret = hx509_certs_init(context, "MEMORY:certs",
+ HX509_CERTS_NO_PRIVATE_KEYS, NULL, out);
+ if (ret == 0)
+ ret = hx509_certs_add(context, *out, cert);
+ if (ret == 0 && send_chain) {
+ ret = hx509_certs_init(context, ca,
+ HX509_CERTS_NO_PRIVATE_KEYS, NULL, &chain);
+ if (ret == 0)
+ ret = hx509_certs_merge(context, *out, chain);
+ }
+
+out:
+ hx509_certs_free(&chain);
+ if (env)
+ hx509_env_free(&env);
+ if (tbs)
+ hx509_ca_tbs_free(&tbs);
+ if (cert)
+ hx509_cert_free(cert);
+ if (signer)
+ hx509_cert_free(signer);
+ if (ret)
+ hx509_certs_free(out);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/hx509/cert.c b/third_party/heimdal/lib/hx509/cert.c
new file mode 100644
index 0000000..33805b8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/cert.c
@@ -0,0 +1,3892 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include "crypto-headers.h"
+#include <rtbl.h>
+
+/**
+ * @page page_cert The basic certificate
+ *
+ * The basic hx509 cerificate object in hx509 is hx509_cert. The
+ * hx509_cert object is representing one X509/PKIX certificate and
+ * associated attributes; like private key, friendly name, etc.
+ *
+ * A hx509_cert object is usully found via the keyset interfaces (@ref
+ * page_keyset), but its also possible to create a certificate
+ * directly from a parsed object with hx509_cert_init() and
+ * hx509_cert_init_data().
+ *
+ * See the library functions here: @ref hx509_cert
+ */
+
+struct hx509_verify_ctx_data {
+ hx509_certs trust_anchors;
+ int flags;
+#define HX509_VERIFY_CTX_F_TIME_SET 1
+#define HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE 2
+#define HX509_VERIFY_CTX_F_REQUIRE_RFC3280 4
+#define HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS 8
+#define HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS 16
+#define HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK 32
+ time_t time_now;
+ unsigned int max_depth;
+#define HX509_VERIFY_MAX_DEPTH 30
+ hx509_revoke_ctx revoke_ctx;
+};
+
+#define REQUIRE_RFC3280(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_REQUIRE_RFC3280)
+#define CHECK_TA(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS)
+#define ALLOW_DEF_TA(ctx) (((ctx)->flags & HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS) == 0)
+
+struct _hx509_cert_attrs {
+ size_t len;
+ hx509_cert_attribute *val;
+};
+
+struct hx509_cert_data {
+ unsigned int ref;
+ char *friendlyname;
+ Certificate *data;
+ hx509_private_key private_key;
+ struct _hx509_cert_attrs attrs;
+ hx509_name basename;
+ _hx509_cert_release_func release;
+ void *ctx;
+};
+
+typedef struct hx509_name_constraints {
+ NameConstraints *val;
+ size_t len;
+} hx509_name_constraints;
+
+#define GeneralSubtrees_SET(g,var) \
+ (g)->len = (var)->len, (g)->val = (var)->val;
+
+static void
+init_context_once(void *ignored)
+{
+
+ ENGINE_add_conf_module();
+ OpenSSL_add_all_algorithms();
+}
+
+/**
+ * Return a cookie identifying this instance of a library.
+ *
+ * Inputs:
+ *
+ * @context A krb5_context
+ * @module Our library name or a library we depend on
+ *
+ * Outputs: The instance cookie
+ *
+ * @ingroup krb5_support
+ */
+
+HX509_LIB_FUNCTION uintptr_t HX509_LIB_CALL
+hx509_get_instance(const char *libname)
+{
+ static const char *instance = "libhx509";
+
+ if (strcmp(libname, "hx509") == 0)
+ return (uintptr_t)instance;
+
+ return 0;
+}
+
+#ifndef PATH_SEP
+# define PATH_SEP ":"
+#endif
+static const char *hx509_config_file =
+"~/.hx509/config" PATH_SEP
+SYSCONFDIR "/hx509.conf" PATH_SEP
+#ifdef _WIN32
+"%{COMMON_APPDATA}/Heimdal/hx509.conf" PATH_SEP
+"%{WINDOWS}/hx509.ini"
+#else /* _WIN32 */
+"/etc/hx509.conf"
+#endif /* _WIN32 */
+;
+
+/**
+ * Creates a hx509 context that most functions in the library
+ * uses. The context is only allowed to be used by one thread at each
+ * moment. Free the context with hx509_context_free().
+ *
+ * @param context Returns a pointer to new hx509 context.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_context_init(hx509_context *contextp)
+{
+ static heim_base_once_t init_context = HEIM_BASE_ONCE_INIT;
+ heim_error_code ret;
+ hx509_context context;
+ const char *anchors;
+ char **files = NULL;
+
+ *contextp = NULL;
+ context = calloc(1, sizeof(*context));
+ if (context == NULL)
+ return ENOMEM;
+
+ heim_base_once_f(&init_context, NULL, init_context_once);
+
+ if ((context->hcontext = heim_context_init()) == NULL) {
+ free(context);
+ return ENOMEM;
+ }
+
+ if ((ret = heim_get_default_config_files(hx509_config_file,
+ "HX509_CONFIG",
+ &files))) {
+ heim_context_free(&context->hcontext);
+ free(context);
+ return ret;
+ }
+
+ /* If there's no hx509 config, we continue, as we never needed it before */
+ if (files)
+ (void) heim_set_config_files(context->hcontext, files, &context->cf);
+ heim_free_config_files(files);
+
+ _hx509_ks_null_register(context);
+ _hx509_ks_mem_register(context);
+ _hx509_ks_file_register(context);
+ _hx509_ks_pkcs12_register(context);
+ _hx509_ks_pkcs11_register(context);
+ _hx509_ks_dir_register(context);
+ _hx509_ks_keychain_register(context);
+
+ context->ocsp_time_diff =
+ heim_config_get_time_default(context->hcontext, context->cf,
+ HX509_DEFAULT_OCSP_TIME_DIFF,
+ "libdefaults", "ocsp_time_dif", NULL);
+
+ initialize_hx_error_table_r(&context->et_list);
+ initialize_asn1_error_table_r(&context->et_list);
+
+#ifdef HX509_DEFAULT_ANCHORS
+ anchors = heim_config_get_string_default(context->hcontext, context->cf,
+ HX509_DEFAULT_ANCHORS,
+ "libdefaults", "anchors", NULL);
+#else
+ anchors = heim_config_get_string(context->hcontext, context->cf,
+ "libdefaults", "anchors", NULL);
+#endif
+ if (anchors)
+ (void)hx509_certs_init(context, anchors, 0, NULL,
+ &context->default_trust_anchors);
+
+ *contextp = context;
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_set_log_dest(hx509_context context, heim_log_facility *fac)
+{
+ return heim_set_log_dest(context->hcontext, fac);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_set_debug_dest(hx509_context context, heim_log_facility *fac)
+{
+ return heim_set_debug_dest(context->hcontext, fac);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_set_warn_dest(hx509_context context, heim_log_facility *fac)
+{
+ return heim_set_warn_dest(context->hcontext, fac);
+}
+
+/**
+ * Selects if the hx509_revoke_verify() function is going to require
+ * the existans of a revokation method (OCSP, CRL) or not. Note that
+ * hx509_verify_path(), hx509_cms_verify_signed(), and other function
+ * call hx509_revoke_verify().
+ *
+ * @param context hx509 context to change the flag for.
+ * @param flag zero, revokation method required, non zero missing
+ * revokation method ok
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_context_set_missing_revoke(hx509_context context, int flag)
+{
+ if (flag)
+ context->flags |= HX509_CTX_VERIFY_MISSING_OK;
+ else
+ context->flags &= ~HX509_CTX_VERIFY_MISSING_OK;
+}
+
+/**
+ * Free the context allocated by hx509_context_init().
+ *
+ * @param context context to be freed.
+ *
+ * @ingroup hx509
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_context_free(hx509_context *context)
+{
+ if (!*context)
+ return;
+
+ hx509_clear_error_string(*context);
+ if ((*context)->ks_ops) {
+ free((*context)->ks_ops);
+ (*context)->ks_ops = NULL;
+ }
+ (*context)->ks_num_ops = 0;
+ free_error_table ((*context)->et_list);
+ if ((*context)->querystat)
+ free((*context)->querystat);
+ hx509_certs_free(&(*context)->default_trust_anchors);
+ heim_config_file_free((*context)->hcontext, (*context)->cf);
+ heim_context_free(&(*context)->hcontext);
+ memset(*context, 0, sizeof(**context));
+ free(*context);
+ *context = NULL;
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION Certificate * HX509_LIB_CALL
+_hx509_get_cert(hx509_cert cert)
+{
+ return cert->data;
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_cert_get_version(const Certificate *t)
+{
+ return t->tbsCertificate.version ? *t->tbsCertificate.version + 1 : 1;
+}
+
+static hx509_cert
+cert_init(hx509_context context, heim_error_t *error)
+{
+ hx509_cert cert;
+
+ cert = malloc(sizeof(*cert));
+ if (cert == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ cert->ref = 1;
+ cert->friendlyname = NULL;
+ cert->attrs.len = 0;
+ cert->attrs.val = NULL;
+ cert->private_key = NULL;
+ cert->basename = NULL;
+ cert->release = NULL;
+ cert->ctx = NULL;
+ cert->data= NULL;
+ return cert;
+}
+
+/**
+ * Allocate and init an hx509 certificate object from the decoded
+ * certificate `c´.
+ *
+ * @param context A hx509 context.
+ * @param c
+ * @param error
+ *
+ * @return Returns an hx509 certificate
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION hx509_cert HX509_LIB_CALL
+hx509_cert_init(hx509_context context, const Certificate *c, heim_error_t *error)
+{
+ hx509_cert cert;
+ int ret;
+
+ if ((cert = cert_init(context, error)) == NULL)
+ return NULL;
+
+ cert->data = calloc(1, sizeof(*(cert->data)));
+ if (cert->data == NULL) {
+ free(cert);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ ret = copy_Certificate(c, cert->data);
+ if (ret) {
+ free(cert->data);
+ free(cert);
+ cert = NULL;
+ }
+ return cert;
+}
+
+/**
+ * Copy a certificate object, but drop any private key assignment.
+ *
+ * @param context A hx509 context.
+ * @param src Certificate object
+ * @param error
+ *
+ * @return Returns an hx509 certificate
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION hx509_cert HX509_LIB_CALL
+hx509_cert_copy_no_private_key(hx509_context context,
+ hx509_cert src,
+ heim_error_t *error)
+{
+ return hx509_cert_init(context, src->data, error);
+}
+
+/**
+ * Allocate and init an hx509 certificate object containing only a private key
+ * (but no Certificate).
+ *
+ * @param context A hx509 context.
+ * @param key
+ * @param error
+ *
+ * @return Returns an hx509 certificate
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION hx509_cert HX509_LIB_CALL
+hx509_cert_init_private_key(hx509_context context,
+ hx509_private_key key,
+ heim_error_t *error)
+{
+ hx509_cert cert;
+
+ if ((cert = cert_init(context, error)))
+ (void) _hx509_cert_assign_key(cert, key);
+ return cert;
+}
+
+/**
+ * Just like hx509_cert_init(), but instead of a decode certificate
+ * takes an pointer and length to a memory region that contains a
+ * DER/BER encoded certificate.
+ *
+ * If the memory region doesn't contain just the certificate and
+ * nothing more the function will fail with
+ * HX509_EXTRA_DATA_AFTER_STRUCTURE.
+ *
+ * @param context A hx509 context.
+ * @param ptr pointer to memory region containing encoded certificate.
+ * @param len length of memory region.
+ * @param error possibly returns an error
+ *
+ * @return An hx509 certificate
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION hx509_cert HX509_LIB_CALL
+hx509_cert_init_data(hx509_context context,
+ const void *ptr,
+ size_t len,
+ heim_error_t *error)
+{
+ hx509_cert cert;
+ Certificate t;
+ size_t size;
+ int ret;
+
+ ret = decode_Certificate(ptr, len, &t, &size);
+ if (ret) {
+ if (error)
+ *error = heim_error_create(ret, "Failed to decode certificate");
+ errno = ret;
+ return NULL;
+ }
+ if (size != len) {
+ free_Certificate(&t);
+ if (error)
+ *error = heim_error_create(HX509_EXTRA_DATA_AFTER_STRUCTURE,
+ "Extra data after certificate");
+ errno = HX509_EXTRA_DATA_AFTER_STRUCTURE;
+ return NULL;
+ }
+
+ cert = hx509_cert_init(context, &t, error);
+ free_Certificate(&t);
+ return cert;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_cert_set_release(hx509_cert cert,
+ _hx509_cert_release_func release,
+ void *ctx)
+{
+ cert->release = release;
+ cert->ctx = ctx;
+}
+
+
+/* Doesn't make a copy of `private_key'. */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key)
+{
+ if (cert->private_key)
+ hx509_private_key_free(&cert->private_key);
+ cert->private_key = _hx509_private_key_ref(private_key);
+ return 0;
+}
+
+/**
+ * Free reference to the hx509 certificate object, if the refcounter
+ * reaches 0, the object if freed. Its allowed to pass in NULL.
+ *
+ * @param cert the cert to free.
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_cert_free(hx509_cert cert)
+{
+ size_t i;
+
+ if (cert == NULL)
+ return;
+
+ if (cert->ref <= 0)
+ _hx509_abort("cert refcount <= 0 on free");
+ if (--cert->ref > 0)
+ return;
+
+ if (cert->release)
+ (cert->release)(cert, cert->ctx);
+
+ if (cert->private_key)
+ hx509_private_key_free(&cert->private_key);
+
+ if (cert->data)
+ free_Certificate(cert->data);
+ free(cert->data);
+
+ for (i = 0; i < cert->attrs.len; i++) {
+ der_free_octet_string(&cert->attrs.val[i]->data);
+ der_free_oid(&cert->attrs.val[i]->oid);
+ free(cert->attrs.val[i]);
+ }
+ free(cert->attrs.val);
+ free(cert->friendlyname);
+ if (cert->basename)
+ hx509_name_free(&cert->basename);
+ memset(cert, 0, sizeof(*cert));
+ free(cert);
+}
+
+/**
+ * Add a reference to a hx509 certificate object.
+ *
+ * @param cert a pointer to an hx509 certificate object.
+ *
+ * @return the same object as is passed in.
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION hx509_cert HX509_LIB_CALL
+hx509_cert_ref(hx509_cert cert)
+{
+ if (cert == NULL)
+ return NULL;
+ if (cert->ref <= 0)
+ _hx509_abort("cert refcount <= 0");
+ cert->ref++;
+ if (cert->ref == 0)
+ _hx509_abort("cert refcount == 0");
+ return cert;
+}
+
+/**
+ * Allocate an verification context that is used fo control the
+ * verification process.
+ *
+ * @param context A hx509 context.
+ * @param ctx returns a pointer to a hx509_verify_ctx object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_verify_init_ctx(hx509_context context, hx509_verify_ctx *ctx)
+{
+ hx509_verify_ctx c;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL)
+ return ENOMEM;
+
+ c->max_depth = HX509_VERIFY_MAX_DEPTH;
+
+ *ctx = c;
+
+ return 0;
+}
+
+/**
+ * Free an hx509 verification context.
+ *
+ * @param ctx the context to be freed.
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_destroy_ctx(hx509_verify_ctx ctx)
+{
+ if (ctx) {
+ hx509_certs_free(&ctx->trust_anchors);
+ hx509_revoke_free(&ctx->revoke_ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ }
+ free(ctx);
+}
+
+/**
+ * Set the trust anchors in the verification context, makes an
+ * reference to the keyset, so the consumer can free the keyset
+ * independent of the destruction of the verification context (ctx).
+ * If there already is a keyset attached, it's released.
+ *
+ * @param ctx a verification context
+ * @param set a keyset containing the trust anchors.
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_attach_anchors(hx509_verify_ctx ctx, hx509_certs set)
+{
+ if (ctx->trust_anchors)
+ hx509_certs_free(&ctx->trust_anchors);
+ ctx->trust_anchors = hx509_certs_ref(set);
+}
+
+/**
+ * Attach an revocation context to the verfication context, , makes an
+ * reference to the revoke context, so the consumer can free the
+ * revoke context independent of the destruction of the verification
+ * context. If there is no revoke context, the verification process is
+ * NOT going to check any verification status.
+ *
+ * @param ctx a verification context.
+ * @param revoke_ctx a revoke context.
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_attach_revoke(hx509_verify_ctx ctx, hx509_revoke_ctx revoke_ctx)
+{
+ if (ctx->revoke_ctx)
+ hx509_revoke_free(&ctx->revoke_ctx);
+ ctx->revoke_ctx = _hx509_revoke_ref(revoke_ctx);
+}
+
+/**
+ * Set the clock time the the verification process is going to
+ * use. Used to check certificate in the past and future time. If not
+ * set the current time will be used.
+ *
+ * @param ctx a verification context.
+ * @param t the time the verifiation is using.
+ *
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_set_time(hx509_verify_ctx ctx, time_t t)
+{
+ ctx->flags |= HX509_VERIFY_CTX_F_TIME_SET;
+ ctx->time_now = t;
+}
+
+HX509_LIB_FUNCTION time_t HX509_LIB_CALL
+_hx509_verify_get_time(hx509_verify_ctx ctx)
+{
+ return ctx->time_now;
+}
+
+/**
+ * Set the maximum depth of the certificate chain that the path
+ * builder is going to try.
+ *
+ * @param ctx a verification context
+ * @param max_depth maxium depth of the certificate chain, include
+ * trust anchor.
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_set_max_depth(hx509_verify_ctx ctx, unsigned int max_depth)
+{
+ ctx->max_depth = max_depth;
+}
+
+/**
+ * Allow or deny the use of proxy certificates
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, allow proxy certificates.
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_set_proxy_certificate(hx509_verify_ctx ctx, int boolean)
+{
+ if (boolean)
+ ctx->flags |= HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE;
+ else
+ ctx->flags &= ~HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE;
+}
+
+/**
+ * Select strict RFC3280 verification of certificiates. This means
+ * checking key usage on CA certificates, this will make version 1
+ * certificiates unuseable.
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, use strict verification.
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_set_strict_rfc3280_verification(hx509_verify_ctx ctx, int boolean)
+{
+ if (boolean)
+ ctx->flags |= HX509_VERIFY_CTX_F_REQUIRE_RFC3280;
+ else
+ ctx->flags &= ~HX509_VERIFY_CTX_F_REQUIRE_RFC3280;
+}
+
+/**
+ * Allow using the operating system builtin trust anchors if no other
+ * trust anchors are configured.
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, useing the operating systems builtin
+ * trust anchors.
+ *
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_ctx_f_allow_default_trustanchors(hx509_verify_ctx ctx, int boolean)
+{
+ if (boolean)
+ ctx->flags &= ~HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS;
+ else
+ ctx->flags |= HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_verify_ctx_f_allow_best_before_signature_algs(hx509_context ctx,
+ int boolean)
+{
+ if (boolean)
+ ctx->flags &= ~HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK;
+ else
+ ctx->flags |= HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK;
+}
+
+static const Extension *
+find_extension(const Certificate *cert, const heim_oid *oid, size_t *idx)
+{
+ const TBSCertificate *c = &cert->tbsCertificate;
+
+ if (c->version == NULL || *c->version < 2 || c->extensions == NULL)
+ return NULL;
+
+ for (;*idx < c->extensions->len; (*idx)++) {
+ if (der_heim_oid_cmp(&c->extensions->val[*idx].extnID, oid) == 0)
+ return &c->extensions->val[(*idx)++];
+ }
+ return NULL;
+}
+
+static int
+find_extension_auth_key_id(const Certificate *subject,
+ AuthorityKeyIdentifier *ai)
+{
+ const Extension *e;
+ size_t size;
+ size_t i = 0;
+
+ memset(ai, 0, sizeof(*ai));
+
+ e = find_extension(subject, &asn1_oid_id_x509_ce_authorityKeyIdentifier, &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_AuthorityKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ ai, &size);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_find_extension_subject_key_id(const Certificate *issuer,
+ SubjectKeyIdentifier *si)
+{
+ const Extension *e;
+ size_t size;
+ size_t i = 0;
+
+ memset(si, 0, sizeof(*si));
+
+ e = find_extension(issuer, &asn1_oid_id_x509_ce_subjectKeyIdentifier, &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_SubjectKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ si, &size);
+}
+
+static int
+find_extension_name_constraints(const Certificate *subject,
+ NameConstraints *nc)
+{
+ const Extension *e;
+ size_t size;
+ size_t i = 0;
+
+ memset(nc, 0, sizeof(*nc));
+
+ e = find_extension(subject, &asn1_oid_id_x509_ce_nameConstraints, &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_NameConstraints(e->extnValue.data,
+ e->extnValue.length,
+ nc, &size);
+}
+
+static int
+find_extension_subject_alt_name(const Certificate *cert, size_t *i,
+ GeneralNames *sa)
+{
+ const Extension *e;
+ size_t size;
+
+ memset(sa, 0, sizeof(*sa));
+
+ e = find_extension(cert, &asn1_oid_id_x509_ce_subjectAltName, i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_GeneralNames(e->extnValue.data,
+ e->extnValue.length,
+ sa, &size);
+}
+
+static int
+find_extension_eku(const Certificate *cert, ExtKeyUsage *eku)
+{
+ const Extension *e;
+ size_t size;
+ size_t i = 0;
+
+ memset(eku, 0, sizeof(*eku));
+
+ e = find_extension(cert, &asn1_oid_id_x509_ce_extKeyUsage, &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_ExtKeyUsage(e->extnValue.data,
+ e->extnValue.length,
+ eku, &size);
+}
+
+static int
+add_to_list(hx509_octet_string_list *list, const heim_octet_string *entry)
+{
+ void *p;
+ int ret;
+
+ p = realloc(list->val, (list->len + 1) * sizeof(list->val[0]));
+ if (p == NULL)
+ return ENOMEM;
+ list->val = p;
+ ret = der_copy_octet_string(entry, &list->val[list->len]);
+ if (ret)
+ return ret;
+ list->len++;
+ return 0;
+}
+
+/**
+ * Free a list of octet strings returned by another hx509 library
+ * function.
+ *
+ * @param list list to be freed.
+ *
+ * @ingroup hx509_misc
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_free_octet_string_list(hx509_octet_string_list *list)
+{
+ size_t i;
+
+ if (list->val) {
+ for (i = 0; i < list->len; i++)
+ der_free_octet_string(&list->val[i]);
+ free(list->val);
+ }
+ list->val = NULL;
+ list->len = 0;
+}
+
+/**
+ * Return a list of subjectAltNames specified by oid in the
+ * certificate. On error the
+ *
+ * The returned list of octet string should be freed with
+ * hx509_free_octet_string_list().
+ *
+ * @param context A hx509 context.
+ * @param cert a hx509 certificate object.
+ * @param oid an oid to for SubjectAltName.
+ * @param list list of matching SubjectAltName.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_find_subjectAltName_otherName(hx509_context context,
+ hx509_cert cert,
+ const heim_oid *oid,
+ hx509_octet_string_list *list)
+{
+ GeneralNames sa;
+ int ret;
+ size_t i, j;
+
+ list->val = NULL;
+ list->len = 0;
+
+ i = 0;
+ while (1) {
+ ret = find_extension_subject_alt_name(_hx509_get_cert(cert), &i, &sa);
+ i++;
+ if (ret == HX509_EXTENSION_NOT_FOUND) {
+ return 0;
+ } else if (ret != 0) {
+ hx509_set_error_string(context, 0, ret, "Error searching for SAN");
+ hx509_free_octet_string_list(list);
+ return ret;
+ }
+
+ for (j = 0; j < sa.len; j++) {
+ if (sa.val[j].element == choice_GeneralName_otherName &&
+ der_heim_oid_cmp(&sa.val[j].u.otherName.type_id, oid) == 0)
+ {
+ ret = add_to_list(list, &sa.val[j].u.otherName.value);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Error adding an exra SAN to "
+ "return list");
+ hx509_free_octet_string_list(list);
+ free_GeneralNames(&sa);
+ return ret;
+ }
+ }
+ }
+ free_GeneralNames(&sa);
+ }
+}
+
+
+static int
+check_key_usage(hx509_context context, const Certificate *cert,
+ unsigned flags, int req_present)
+{
+ const Extension *e;
+ KeyUsage ku;
+ size_t size;
+ int ret;
+ size_t i = 0;
+ uint64_t ku_flags;
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, &asn1_oid_id_x509_ce_keyUsage, &i);
+ if (e == NULL) {
+ if (req_present) {
+ hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
+ "Required extension key "
+ "usage missing from certificate");
+ return HX509_KU_CERT_MISSING;
+ }
+ return 0;
+ }
+
+ ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, &ku, &size);
+ if (ret)
+ return ret;
+ ku_flags = KeyUsage2int(ku);
+ if ((ku_flags & flags) != flags) {
+ uint64_t missing = (~ku_flags) & flags;
+ char buf[256], *name;
+
+ int result = unparse_flags(missing, asn1_KeyUsage_units(),
+ buf, sizeof(buf));
+ _hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
+ hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
+ "Key usage %s required but missing "
+ "from certificate %s",
+ (result > 0) ? buf : "<unknown>",
+ name ? name : "<unknown>");
+ free(name);
+ return HX509_KU_CERT_MISSING;
+ }
+ return 0;
+}
+
+/*
+ * Return 0 on matching key usage 'flags' for 'cert', otherwise return
+ * an error code. If 'req_present' the existence is required of the
+ * KeyUsage extension.
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_check_key_usage(hx509_context context, hx509_cert cert,
+ unsigned flags, int req_present)
+{
+ return check_key_usage(context, _hx509_get_cert(cert), flags, req_present);
+}
+
+enum certtype { PROXY_CERT, EE_CERT, CA_CERT };
+
+static int
+check_basic_constraints(hx509_context context, const Certificate *cert,
+ enum certtype type, size_t depth)
+{
+ BasicConstraints bc;
+ const Extension *e;
+ size_t size;
+ int ret;
+ size_t i = 0;
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, &asn1_oid_id_x509_ce_basicConstraints, &i);
+ if (e == NULL) {
+ switch(type) {
+ case PROXY_CERT:
+ case EE_CERT:
+ return 0;
+ case CA_CERT: {
+ char *name;
+ ret = _hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
+ assert(ret == 0);
+ hx509_set_error_string(context, 0, HX509_EXTENSION_NOT_FOUND,
+ "basicConstraints missing from "
+ "CA certifiacte %s", name);
+ free(name);
+ return HX509_EXTENSION_NOT_FOUND;
+ }
+ }
+ }
+
+ ret = decode_BasicConstraints(e->extnValue.data,
+ e->extnValue.length, &bc,
+ &size);
+ if (ret)
+ return ret;
+ switch(type) {
+ case PROXY_CERT:
+ if (bc.cA)
+ ret = HX509_PARENT_IS_CA;
+ break;
+ case EE_CERT:
+ ret = 0;
+ break;
+ case CA_CERT:
+ if (!bc.cA)
+ ret = HX509_PARENT_NOT_CA;
+ else if (bc.pathLenConstraint)
+ if (depth - 1 > *bc.pathLenConstraint)
+ ret = HX509_CA_PATH_TOO_DEEP;
+ break;
+ }
+ free_BasicConstraints(&bc);
+ return ret;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_cert_is_parent_cmp(const Certificate *subject,
+ const Certificate *issuer,
+ int allow_self_signed)
+{
+ int diff;
+ AuthorityKeyIdentifier ai;
+ SubjectKeyIdentifier si;
+ int ret_ai, ret_si, ret;
+
+ ret = _hx509_name_cmp(&issuer->tbsCertificate.subject,
+ &subject->tbsCertificate.issuer,
+ &diff);
+ if (ret)
+ return ret;
+ if (diff)
+ return diff;
+
+ memset(&ai, 0, sizeof(ai));
+ memset(&si, 0, sizeof(si));
+
+ /*
+ * Try to find AuthorityKeyIdentifier, if it's not present in the
+ * subject certificate nor the parent.
+ */
+
+ ret_ai = find_extension_auth_key_id(subject, &ai);
+ if (ret_ai && ret_ai != HX509_EXTENSION_NOT_FOUND)
+ return 1;
+ ret_si = _hx509_find_extension_subject_key_id(issuer, &si);
+ if (ret_si && ret_si != HX509_EXTENSION_NOT_FOUND)
+ return -1;
+
+ if (ret_si && ret_ai)
+ goto out;
+ if (ret_ai)
+ goto out;
+ if (ret_si) {
+ if (allow_self_signed) {
+ diff = 0;
+ goto out;
+ } else if (ai.keyIdentifier) {
+ diff = -1;
+ goto out;
+ }
+ }
+
+ if (ai.keyIdentifier == NULL) {
+ Name name;
+
+ if (ai.authorityCertIssuer == NULL)
+ return -1;
+ if (ai.authorityCertSerialNumber == NULL)
+ return -1;
+
+ diff = der_heim_integer_cmp(ai.authorityCertSerialNumber,
+ &issuer->tbsCertificate.serialNumber);
+ if (diff)
+ return diff;
+ if (ai.authorityCertIssuer->len != 1)
+ return -1;
+ if (ai.authorityCertIssuer->val[0].element != choice_GeneralName_directoryName)
+ return -1;
+
+ name.element = (enum Name_enum)
+ ai.authorityCertIssuer->val[0].u.directoryName.element;
+ name.u.rdnSequence =
+ ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence;
+
+ ret = _hx509_name_cmp(&issuer->tbsCertificate.subject,
+ &name,
+ &diff);
+ if (ret)
+ return ret;
+ if (diff)
+ return diff;
+ diff = 0;
+ } else
+ diff = der_heim_octet_string_cmp(ai.keyIdentifier, &si);
+ if (diff)
+ goto out;
+
+ out:
+ free_AuthorityKeyIdentifier(&ai);
+ free_SubjectKeyIdentifier(&si);
+ return diff;
+}
+
+static int
+certificate_is_anchor(hx509_context context,
+ hx509_certs trust_anchors,
+ const hx509_cert cert)
+{
+ hx509_query q;
+ hx509_cert c;
+ int ret;
+
+ if (trust_anchors == NULL)
+ return 0;
+
+ _hx509_query_clear(&q);
+
+ q.match = HX509_QUERY_MATCH_CERTIFICATE;
+ q.certificate = _hx509_get_cert(cert);
+
+ ret = hx509_certs_find(context, trust_anchors, &q, &c);
+ if (ret == 0)
+ hx509_cert_free(c);
+ return ret == 0;
+}
+
+static int
+certificate_is_self_signed(hx509_context context,
+ const Certificate *cert,
+ int *self_signed)
+{
+ int ret, diff;
+ ret = _hx509_name_cmp(&cert->tbsCertificate.subject,
+ &cert->tbsCertificate.issuer, &diff);
+ *self_signed = (diff == 0);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to check if self signed");
+ } else
+ ret = _hx509_self_signed_valid(context, &cert->signatureAlgorithm);
+
+ return ret;
+}
+
+/*
+ * The subjectName is "null" when it's empty set of relative DBs.
+ */
+
+static int
+subject_null_p(const Certificate *c)
+{
+ return c->tbsCertificate.subject.u.rdnSequence.len == 0;
+}
+
+
+static int
+find_parent(hx509_context context,
+ time_t time_now,
+ hx509_certs trust_anchors,
+ hx509_path *path,
+ hx509_certs pool,
+ hx509_cert current,
+ hx509_cert *parent)
+{
+ AuthorityKeyIdentifier ai;
+ hx509_query q;
+ int ret;
+
+ *parent = NULL;
+ memset(&ai, 0, sizeof(ai));
+
+ _hx509_query_clear(&q);
+
+ if (!subject_null_p(current->data)) {
+ q.match |= HX509_QUERY_FIND_ISSUER_CERT;
+ q.subject = _hx509_get_cert(current);
+ } else {
+ ret = find_extension_auth_key_id(current->data, &ai);
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED,
+ "Subjectless certificate missing AuthKeyID");
+ return HX509_CERTIFICATE_MALFORMED;
+ }
+
+ if (ai.keyIdentifier == NULL) {
+ free_AuthorityKeyIdentifier(&ai);
+ hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED,
+ "Subjectless certificate missing keyIdentifier "
+ "inside AuthKeyID");
+ return HX509_CERTIFICATE_MALFORMED;
+ }
+
+ q.subject_id = ai.keyIdentifier;
+ q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID;
+ }
+
+ q.path = path;
+ q.match |= HX509_QUERY_NO_MATCH_PATH;
+
+ if (pool) {
+ q.timenow = time_now;
+ q.match |= HX509_QUERY_MATCH_TIME;
+
+ ret = hx509_certs_find(context, pool, &q, parent);
+ if (ret == 0) {
+ free_AuthorityKeyIdentifier(&ai);
+ return 0;
+ }
+ q.match &= ~HX509_QUERY_MATCH_TIME;
+ }
+
+ if (trust_anchors) {
+ ret = hx509_certs_find(context, trust_anchors, &q, parent);
+ if (ret == 0) {
+ free_AuthorityKeyIdentifier(&ai);
+ return ret;
+ }
+ }
+ free_AuthorityKeyIdentifier(&ai);
+
+ {
+ hx509_name name;
+ char *str;
+
+ ret = hx509_cert_get_subject(current, &name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return HX509_ISSUER_NOT_FOUND;
+ }
+ ret = hx509_name_to_string(name, &str);
+ hx509_name_free(&name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return HX509_ISSUER_NOT_FOUND;
+ }
+
+ hx509_set_error_string(context, 0, HX509_ISSUER_NOT_FOUND,
+ "Failed to find issuer for "
+ "certificate with subject: '%s'", str);
+ free(str);
+ }
+ return HX509_ISSUER_NOT_FOUND;
+}
+
+/*
+ *
+ */
+
+static int
+is_proxy_cert(hx509_context context,
+ const Certificate *cert,
+ ProxyCertInfo *rinfo)
+{
+ ProxyCertInfo info;
+ const Extension *e;
+ size_t size;
+ int ret;
+ size_t i = 0;
+
+ if (rinfo)
+ memset(rinfo, 0, sizeof(*rinfo));
+
+ e = find_extension(cert, &asn1_oid_id_pkix_pe_proxyCertInfo, &i);
+ if (e == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_EXTENSION_NOT_FOUND;
+ }
+
+ ret = decode_ProxyCertInfo(e->extnValue.data,
+ e->extnValue.length,
+ &info,
+ &size);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ if (size != e->extnValue.length) {
+ free_ProxyCertInfo(&info);
+ hx509_clear_error_string(context);
+ return HX509_EXTRA_DATA_AFTER_STRUCTURE;
+ }
+ if (rinfo == NULL)
+ free_ProxyCertInfo(&info);
+ else
+ *rinfo = info;
+
+ return 0;
+}
+
+/*
+ * Path operations are like MEMORY based keyset, but with exposed
+ * internal so we can do easy searches.
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_path_append(hx509_context context, hx509_path *path, hx509_cert cert)
+{
+ hx509_cert *val;
+ val = realloc(path->val, (path->len + 1) * sizeof(path->val[0]));
+ if (val == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ path->val = val;
+ path->val[path->len] = hx509_cert_ref(cert);
+ path->len++;
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_path_free(hx509_path *path)
+{
+ unsigned i;
+
+ for (i = 0; i < path->len; i++)
+ hx509_cert_free(path->val[i]);
+ free(path->val);
+ path->val = NULL;
+ path->len = 0;
+}
+
+/*
+ * Find path by looking up issuer for the top certificate and continue
+ * until an anchor certificate is found or max limit is found. A
+ * certificate never included twice in the path.
+ *
+ * If the trust anchors are not given, calculate optimistic path, just
+ * follow the chain upward until we no longer find a parent or we hit
+ * the max path limit. In this case, a failure will always be returned
+ * depending on what error condition is hit first.
+ *
+ * The path includes a path from the top certificate to the anchor
+ * certificate.
+ *
+ * The caller needs to free `path´ both on successful built path and
+ * failure.
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_calculate_path(hx509_context context,
+ int flags,
+ time_t time_now,
+ hx509_certs anchors,
+ unsigned int max_depth,
+ hx509_cert cert,
+ hx509_certs pool,
+ hx509_path *path)
+{
+ hx509_cert parent, current;
+ int ret;
+
+ if (max_depth == 0)
+ max_depth = HX509_VERIFY_MAX_DEPTH;
+
+ ret = _hx509_path_append(context, path, cert);
+ if (ret)
+ return ret;
+
+ current = hx509_cert_ref(cert);
+
+ while (!certificate_is_anchor(context, anchors, current)) {
+
+ ret = find_parent(context, time_now, anchors, path,
+ pool, current, &parent);
+ hx509_cert_free(current);
+ if (ret)
+ return ret;
+
+ ret = _hx509_path_append(context, path, parent);
+ if (ret)
+ return ret;
+ current = parent;
+
+ if (path->len > max_depth) {
+ hx509_cert_free(current);
+ hx509_set_error_string(context, 0, HX509_PATH_TOO_LONG,
+ "Path too long while bulding "
+ "certificate chain");
+ return HX509_PATH_TOO_LONG;
+ }
+ }
+
+ if ((flags & HX509_CALCULATE_PATH_NO_ANCHOR) &&
+ path->len > 0 &&
+ certificate_is_anchor(context, anchors, path->val[path->len - 1]))
+ {
+ hx509_cert_free(path->val[path->len - 1]);
+ path->len--;
+ }
+
+ hx509_cert_free(current);
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p,
+ const AlgorithmIdentifier *q)
+{
+ int diff;
+ diff = der_heim_oid_cmp(&p->algorithm, &q->algorithm);
+ if (diff)
+ return diff;
+ if (p->parameters) {
+ if (q->parameters)
+ return heim_any_cmp(p->parameters,
+ q->parameters);
+ else
+ return 1;
+ } else {
+ if (q->parameters)
+ return -1;
+ else
+ return 0;
+ }
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_Certificate_cmp(const Certificate *p, const Certificate *q)
+{
+ int diff;
+ diff = der_heim_bit_string_cmp(&p->signatureValue, &q->signatureValue);
+ if (diff)
+ return diff;
+ diff = _hx509_AlgorithmIdentifier_cmp(&p->signatureAlgorithm,
+ &q->signatureAlgorithm);
+ if (diff)
+ return diff;
+ diff = der_heim_octet_string_cmp(&p->tbsCertificate._save,
+ &q->tbsCertificate._save);
+ return diff;
+}
+
+/**
+ * Compare to hx509 certificate object, useful for sorting.
+ *
+ * @param p a hx509 certificate object.
+ * @param q a hx509 certificate object.
+ *
+ * @return 0 the objects are the same, returns > 0 is p is "larger"
+ * then q, < 0 if p is "smaller" then q.
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_cmp(hx509_cert p, hx509_cert q)
+{
+ return _hx509_Certificate_cmp(p->data, q->data);
+}
+
+/**
+ * Return the name of the issuer of the hx509 certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_get_issuer(hx509_cert p, hx509_name *name)
+{
+ return _hx509_name_from_Name(&p->data->tbsCertificate.issuer, name);
+}
+
+/**
+ * Return the name of the subject of the hx509 certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free(). See also hx509_cert_get_base_subject().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_get_subject(hx509_cert p, hx509_name *name)
+{
+ return _hx509_name_from_Name(&p->data->tbsCertificate.subject, name);
+}
+
+/**
+ * Return the name of the base subject of the hx509 certificate. If
+ * the certiicate is a verified proxy certificate, the this function
+ * return the base certificate (root of the proxy chain). If the proxy
+ * certificate is not verified with the base certificate
+ * HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED is returned.
+ *
+ * @param context a hx509 context.
+ * @param c a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free(). See also hx509_cert_get_subject().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_get_base_subject(hx509_context context, hx509_cert c,
+ hx509_name *name)
+{
+ if (c->basename)
+ return hx509_name_copy(context, c->basename, name);
+ if (is_proxy_cert(context, c->data, NULL) == 0) {
+ int ret = HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate has not been "
+ "canonicalized yet: no base name");
+ return ret;
+ }
+ return _hx509_name_from_Name(&c->data->tbsCertificate.subject, name);
+}
+
+/**
+ * Get serial number of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param i serial number, should be freed ith der_free_heim_integer().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_get_serialnumber(hx509_cert p, heim_integer *i)
+{
+ return der_copy_heim_integer(&p->data->tbsCertificate.serialNumber, i);
+}
+
+/**
+ * Get notBefore time of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ *
+ * @return return not before time
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION time_t HX509_LIB_CALL
+hx509_cert_get_notBefore(hx509_cert p)
+{
+ return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notBefore);
+}
+
+/**
+ * Get notAfter time of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ *
+ * @return return not after time.
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION time_t HX509_LIB_CALL
+hx509_cert_get_notAfter(hx509_cert p)
+{
+ return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notAfter);
+}
+
+/**
+ * Get a maximum Kerberos credential lifetime from a Heimdal certificate
+ * extension.
+ *
+ * @param context hx509 context.
+ * @param cert Certificate.
+ * @param bound If larger than zero, return no more than this.
+ *
+ * @return maximum ticket lifetime.
+ */
+HX509_LIB_FUNCTION time_t HX509_LIB_CALL
+hx509_cert_get_pkinit_max_life(hx509_context context,
+ hx509_cert cert,
+ time_t bound)
+{
+ HeimPkinitPrincMaxLifeSecs r = 0;
+ size_t sz, i;
+ time_t b, e;
+ int ret;
+
+ for (i = 0; i < cert->data->tbsCertificate.extensions->len; i++) {
+ Extension *ext = &cert->data->tbsCertificate.extensions->val[i];
+
+ if (ext->_ioschoice_extnValue.element !=
+ choice_Extension_iosnumunknown &&
+ ext->_ioschoice_extnValue.element !=
+ choice_Extension_iosnum_id_heim_ce_pkinit_princ_max_life)
+ continue;
+ if (ext->_ioschoice_extnValue.element == choice_Extension_iosnumunknown &&
+ der_heim_oid_cmp(&asn1_oid_id_heim_ce_pkinit_princ_max_life, &ext->extnID))
+ continue;
+ if (ext->_ioschoice_extnValue.u.ext_HeimPkinitPrincMaxLife) {
+ r = *ext->_ioschoice_extnValue.u.ext_HeimPkinitPrincMaxLife;
+ } else {
+ ret = decode_HeimPkinitPrincMaxLifeSecs(ext->extnValue.data,
+ ext->extnValue.length,
+ &r, &sz);
+ /* No need to free_HeimPkinitPrincMaxLifeSecs(); it's an int */
+ if (ret || r < 1)
+ return 0;
+ }
+ if (bound > 0 && r > bound)
+ return bound;
+ return r;
+ }
+ if (hx509_cert_check_eku(context, cert,
+ &asn1_oid_id_heim_eku_pkinit_certlife_is_max_life, 0))
+ return 0;
+ b = hx509_cert_get_notBefore(cert);
+ e = hx509_cert_get_notAfter(cert);
+ if (e > b)
+ r = e - b;
+ if (bound > 0 && r > bound)
+ return bound;
+ return r;
+}
+
+/**
+ * Get the SubjectPublicKeyInfo structure from the hx509 certificate.
+ *
+ * @param context a hx509 context.
+ * @param p a hx509 certificate object.
+ * @param spki SubjectPublicKeyInfo, should be freed with
+ * free_SubjectPublicKeyInfo().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_get_SPKI(hx509_context context, hx509_cert p, SubjectPublicKeyInfo *spki)
+{
+ int ret;
+
+ ret = copy_SubjectPublicKeyInfo(&p->data->tbsCertificate.subjectPublicKeyInfo, spki);
+ if (ret)
+ hx509_set_error_string(context, 0, ret, "Failed to copy SPKI");
+ return ret;
+}
+
+/**
+ * Get the AlgorithmIdentifier from the hx509 certificate.
+ *
+ * @param context a hx509 context.
+ * @param p a hx509 certificate object.
+ * @param alg AlgorithmIdentifier, should be freed with
+ * free_AlgorithmIdentifier(). The algorithmidentifier is
+ * typicly rsaEncryption, or id-ecPublicKey, or some other
+ * public key mechanism.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_get_SPKI_AlgorithmIdentifier(hx509_context context,
+ hx509_cert p,
+ AlgorithmIdentifier *alg)
+{
+ int ret;
+
+ ret = copy_AlgorithmIdentifier(&p->data->tbsCertificate.subjectPublicKeyInfo.algorithm, alg);
+ if (ret)
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy SPKI AlgorithmIdentifier");
+ return ret;
+}
+
+static int
+get_x_unique_id(hx509_context context, const char *name,
+ const heim_bit_string *cert, heim_bit_string *subject)
+{
+ int ret;
+
+ if (cert == NULL) {
+ ret = HX509_EXTENSION_NOT_FOUND;
+ hx509_set_error_string(context, 0, ret, "%s unique id doesn't exist", name);
+ return ret;
+ }
+ ret = der_copy_bit_string(cert, subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "malloc out of memory", name);
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * Get a copy of the Issuer Unique ID
+ *
+ * @param context a hx509_context
+ * @param p a hx509 certificate
+ * @param issuer the issuer id returned, free with der_free_bit_string()
+ *
+ * @return An hx509 error code, see hx509_get_error_string(). The
+ * error code HX509_EXTENSION_NOT_FOUND is returned if the certificate
+ * doesn't have a issuerUniqueID
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_get_issuer_unique_id(hx509_context context, hx509_cert p, heim_bit_string *issuer)
+{
+ return get_x_unique_id(context, "issuer", p->data->tbsCertificate.issuerUniqueID, issuer);
+}
+
+/**
+ * Get a copy of the Subect Unique ID
+ *
+ * @param context a hx509_context
+ * @param p a hx509 certificate
+ * @param subject the subject id returned, free with der_free_bit_string()
+ *
+ * @return An hx509 error code, see hx509_get_error_string(). The
+ * error code HX509_EXTENSION_NOT_FOUND is returned if the certificate
+ * doesn't have a subjectUniqueID
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_get_subject_unique_id(hx509_context context, hx509_cert p, heim_bit_string *subject)
+{
+ return get_x_unique_id(context, "subject", p->data->tbsCertificate.subjectUniqueID, subject);
+}
+
+
+HX509_LIB_FUNCTION hx509_private_key HX509_LIB_CALL
+_hx509_cert_private_key(hx509_cert p)
+{
+ return p->private_key;
+}
+
+/**
+ * Indicate whether a hx509_cert has a private key.
+ *
+ * @param p a hx509 certificate
+ *
+ * @return 1 if p has a private key, 0 otherwise.
+ *
+ * @ingroup hx509_cert
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_have_private_key(hx509_cert p)
+{
+ return p->private_key ? 1 : 0;
+}
+
+/**
+ * Indicate whether a hx509_cert has a private key only (no certificate).
+ *
+ * @param p a hx509 certificate
+ *
+ * @return 1 if p has a private key only (no certificate), 0 otherwise.
+ *
+ * @ingroup hx509_cert
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_have_private_key_only(hx509_cert p)
+{
+ return p->private_key && !p->data ? 1 : 0;
+}
+
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_cert_private_key_exportable(hx509_cert p)
+{
+ if (p->private_key == NULL)
+ return 0;
+ return _hx509_private_key_exportable(p->private_key);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_cert_private_decrypt(hx509_context context,
+ const heim_octet_string *ciphertext,
+ const heim_oid *encryption_oid,
+ hx509_cert p,
+ heim_octet_string *cleartext)
+{
+ cleartext->data = NULL;
+ cleartext->length = 0;
+
+ if (p->private_key == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private key missing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ return hx509_private_key_private_decrypt(context,
+ ciphertext,
+ encryption_oid,
+ p->private_key,
+ cleartext);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_public_encrypt(hx509_context context,
+ const heim_octet_string *cleartext,
+ const hx509_cert p,
+ heim_oid *encryption_oid,
+ heim_octet_string *ciphertext)
+{
+ return _hx509_public_encrypt(context,
+ cleartext, p->data,
+ encryption_oid, ciphertext);
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION time_t HX509_LIB_CALL
+_hx509_Time2time_t(const Time *t)
+{
+ switch(t->element) {
+ case choice_Time_utcTime:
+ return t->u.utcTime;
+ case choice_Time_generalTime:
+ return t->u.generalTime;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+init_name_constraints(hx509_name_constraints *nc)
+{
+ memset(nc, 0, sizeof(*nc));
+ return 0;
+}
+
+static int
+add_name_constraints(hx509_context context, const Certificate *c, int not_ca,
+ hx509_name_constraints *nc)
+{
+ NameConstraints tnc;
+ int ret;
+
+ ret = find_extension_name_constraints(c, &tnc);
+ if (ret == HX509_EXTENSION_NOT_FOUND)
+ return 0;
+ else if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed getting NameConstraints");
+ return ret;
+ } else if (not_ca) {
+ ret = HX509_VERIFY_CONSTRAINTS;
+ hx509_set_error_string(context, 0, ret, "Not a CA and "
+ "have NameConstraints");
+ } else {
+ NameConstraints *val;
+ val = realloc(nc->val, sizeof(nc->val[0]) * (nc->len + 1));
+ if (val == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ nc->val = val;
+ ret = copy_NameConstraints(&tnc, &nc->val[nc->len]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ nc->len += 1;
+ }
+out:
+ free_NameConstraints(&tnc);
+ return ret;
+}
+
+static int
+match_RDN(const RelativeDistinguishedName *c,
+ const RelativeDistinguishedName *n)
+{
+ size_t i;
+
+ if (c->len != n->len)
+ return HX509_NAME_CONSTRAINT_ERROR;
+
+ for (i = 0; i < n->len; i++) {
+ int diff, ret;
+
+ if (der_heim_oid_cmp(&c->val[i].type, &n->val[i].type) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ ret = _hx509_name_ds_cmp(&c->val[i].value, &n->val[i].value, &diff);
+ if (ret)
+ return ret;
+ if (diff != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+ return 0;
+}
+
+static int
+match_X501Name(const Name *c, const Name *n)
+{
+ size_t i;
+ int ret;
+
+ if (c->element != choice_Name_rdnSequence
+ || n->element != choice_Name_rdnSequence)
+ return 0;
+ if (c->u.rdnSequence.len > n->u.rdnSequence.len)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ for (i = 0; i < c->u.rdnSequence.len; i++) {
+ ret = match_RDN(&c->u.rdnSequence.val[i], &n->u.rdnSequence.val[i]);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+
+static int
+match_general_name(const GeneralName *c, const GeneralName *n, int *match)
+{
+ /*
+ * Name constraints only apply to the same name type, see RFC3280,
+ * 4.2.1.11.
+ */
+ assert(c->element == n->element);
+
+ switch(c->element) {
+ case choice_GeneralName_otherName:
+ if (der_heim_oid_cmp(&c->u.otherName.type_id,
+ &n->u.otherName.type_id) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (heim_any_cmp(&c->u.otherName.value,
+ &n->u.otherName.value) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ *match = 1;
+ return 0;
+ case choice_GeneralName_rfc822Name: {
+ const char *s;
+ size_t len1, len2;
+ s = memchr(c->u.rfc822Name.data, '@', c->u.rfc822Name.length);
+ if (s) {
+ if (der_printable_string_cmp(&c->u.rfc822Name, &n->u.rfc822Name) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ } else {
+ s = memchr(n->u.rfc822Name.data, '@', n->u.rfc822Name.length);
+ if (s == NULL)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ len1 = c->u.rfc822Name.length;
+ len2 = n->u.rfc822Name.length -
+ (s - ((char *)n->u.rfc822Name.data));
+ if (len1 > len2)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (memcmp(s + 1 + len2 - len1, c->u.rfc822Name.data, len1) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (len1 < len2 && s[len2 - len1 + 1] != '.')
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+ *match = 1;
+ return 0;
+ }
+ case choice_GeneralName_dNSName: {
+ size_t lenc, lenn;
+ char *ptr;
+
+ lenc = c->u.dNSName.length;
+ lenn = n->u.dNSName.length;
+ if (lenc > lenn)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ ptr = n->u.dNSName.data;
+ if (memcmp(&ptr[lenn - lenc], c->u.dNSName.data, lenc) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (lenn != lenc && ptr[lenn - lenc - 1] != '.')
+ return HX509_NAME_CONSTRAINT_ERROR;
+ *match = 1;
+ return 0;
+ }
+ case choice_GeneralName_directoryName: {
+ Name c_name, n_name;
+ int ret;
+
+ c_name._save.data = NULL;
+ c_name._save.length = 0;
+ c_name.element = (enum Name_enum)c->u.directoryName.element;
+ c_name.u.rdnSequence = c->u.directoryName.u.rdnSequence;
+
+ n_name._save.data = NULL;
+ n_name._save.length = 0;
+ n_name.element = (enum Name_enum)n->u.directoryName.element;
+ n_name.u.rdnSequence = n->u.directoryName.u.rdnSequence;
+
+ ret = match_X501Name(&c_name, &n_name);
+ if (ret == 0)
+ *match = 1;
+ return ret;
+ }
+ case choice_GeneralName_uniformResourceIdentifier:
+ case choice_GeneralName_iPAddress:
+ case choice_GeneralName_registeredID:
+ default:
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+}
+
+static int
+match_alt_name(const GeneralName *n, const Certificate *c,
+ int *same, int *match)
+{
+ GeneralNames sa;
+ int ret = 0;
+ size_t i, j;
+
+ i = 0;
+ do {
+ ret = find_extension_subject_alt_name(c, &i, &sa);
+ if (ret == HX509_EXTENSION_NOT_FOUND) {
+ ret = 0;
+ break;
+ } else if (ret != 0)
+ break;
+
+ for (j = 0; j < sa.len; j++) {
+ if (n->element == sa.val[j].element) {
+ *same = 1;
+ match_general_name(n, &sa.val[j], match);
+ }
+ }
+ free_GeneralNames(&sa);
+ } while (1);
+ return ret;
+}
+
+
+static int
+match_tree(const GeneralSubtrees *t, const Certificate *c, int *match)
+{
+ int name, alt_name, same;
+ unsigned int i;
+ int ret = 0;
+
+ name = alt_name = same = *match = 0;
+ for (i = 0; i < t->len; i++) {
+ if (t->val[i].minimum && t->val[i].maximum)
+ return HX509_RANGE;
+
+ /*
+ * If the constraint apply to directoryNames, test is with
+ * subjectName of the certificate if the certificate have a
+ * non-null (empty) subjectName.
+ */
+
+ if (t->val[i].base.element == choice_GeneralName_directoryName
+ && !subject_null_p(c))
+ {
+ GeneralName certname;
+
+ memset(&certname, 0, sizeof(certname));
+ certname.element = choice_GeneralName_directoryName;
+ certname.u.directoryName.element = (enum Name_enum)
+ c->tbsCertificate.subject.element;
+ certname.u.directoryName.u.rdnSequence =
+ c->tbsCertificate.subject.u.rdnSequence;
+
+ match_general_name(&t->val[i].base, &certname, &name);
+ }
+
+ /* Handle subjectAltNames, this is icky since they
+ * restrictions only apply if the subjectAltName is of the
+ * same type. So if there have been a match of type, require
+ * altname to be set.
+ */
+ match_alt_name(&t->val[i].base, c, &same, &alt_name);
+ }
+ if (name && (!same || alt_name))
+ *match = 1;
+ return ret;
+}
+
+static int
+check_name_constraints(hx509_context context,
+ const hx509_name_constraints *nc,
+ const Certificate *c)
+{
+ int match, ret;
+ size_t i;
+
+ for (i = 0 ; i < nc->len; i++) {
+ GeneralSubtrees gs;
+
+ if (nc->val[i].permittedSubtrees) {
+ GeneralSubtrees_SET(&gs, nc->val[i].permittedSubtrees);
+ ret = match_tree(&gs, c, &match);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ /* allow null subjectNames, they wont matches anything */
+ if (match == 0 && !subject_null_p(c)) {
+ hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS,
+ "Error verifying constraints: "
+ "certificate didn't match any "
+ "permitted subtree");
+ return HX509_VERIFY_CONSTRAINTS;
+ }
+ }
+ if (nc->val[i].excludedSubtrees) {
+ GeneralSubtrees_SET(&gs, nc->val[i].excludedSubtrees);
+ ret = match_tree(&gs, c, &match);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ if (match) {
+ hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS,
+ "Error verifying constraints: "
+ "certificate included in excluded "
+ "subtree");
+ return HX509_VERIFY_CONSTRAINTS;
+ }
+ }
+ }
+ return 0;
+}
+
+static void
+free_name_constraints(hx509_name_constraints *nc)
+{
+ size_t i;
+
+ for (i = 0 ; i < nc->len; i++)
+ free_NameConstraints(&nc->val[i]);
+ free(nc->val);
+}
+
+/**
+ * Build and verify the path for the certificate to the trust anchor
+ * specified in the verify context. The path is constructed from the
+ * certificate, the pool and the trust anchors.
+ *
+ * @param context A hx509 context.
+ * @param ctx A hx509 verification context.
+ * @param cert the certificate to build the path from.
+ * @param pool A keyset of certificates to build the chain from.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_verify_path(hx509_context context,
+ hx509_verify_ctx ctx,
+ hx509_cert cert,
+ hx509_certs pool)
+{
+ hx509_name_constraints nc;
+ hx509_path path;
+ int ret, proxy_cert_depth, selfsigned_depth, diff;
+ size_t i, k;
+ enum certtype type;
+ Name proxy_issuer;
+ hx509_certs anchors = NULL;
+
+ memset(&proxy_issuer, 0, sizeof(proxy_issuer));
+
+ if ((ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE) == 0 &&
+ is_proxy_cert(context, cert->data, NULL) == 0)
+ {
+ ret = HX509_PROXY_CERT_INVALID;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate is not allowed as an EE "
+ "certificate if proxy certificate is disabled");
+ return ret;
+ }
+
+ ret = init_name_constraints(&nc);
+ if (ret)
+ return ret;
+
+ path.val = NULL;
+ path.len = 0;
+
+ if ((ctx->flags & HX509_VERIFY_CTX_F_TIME_SET) == 0)
+ ctx->time_now = time(NULL);
+
+ /*
+ *
+ */
+ if (ctx->trust_anchors)
+ anchors = hx509_certs_ref(ctx->trust_anchors);
+ else if (context->default_trust_anchors && ALLOW_DEF_TA(ctx))
+ anchors = hx509_certs_ref(context->default_trust_anchors);
+ else {
+ ret = hx509_certs_init(context, "MEMORY:no-TA", 0, NULL, &anchors);
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * Calculate the path from the certificate user presented to the
+ * to an anchor.
+ */
+ ret = _hx509_calculate_path(context, 0, ctx->time_now,
+ anchors, ctx->max_depth,
+ cert, pool, &path);
+ if (ret)
+ goto out;
+
+ /*
+ * Check CA and proxy certificate chain from the top of the
+ * certificate chain. Also check certificate is valid with respect
+ * to the current time.
+ *
+ */
+
+ proxy_cert_depth = 0;
+ selfsigned_depth = 0;
+
+ if (ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE)
+ type = PROXY_CERT;
+ else
+ type = EE_CERT;
+
+ for (i = 0; i < path.len; i++) {
+ Certificate *c;
+ time_t t;
+
+ c = _hx509_get_cert(path.val[i]);
+
+ /*
+ * Lets do some basic check on issuer like
+ * keyUsage.keyCertSign and basicConstraints.cA bit depending
+ * on what type of certificate this is.
+ */
+
+ switch (type) {
+ case CA_CERT:
+
+ /* XXX make constants for keyusage */
+ ret = check_key_usage(context, c, 1 << 5,
+ REQUIRE_RFC3280(ctx) ? TRUE : FALSE);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Key usage missing from CA certificate");
+ goto out;
+ }
+
+ /* self signed cert doesn't add to path length */
+ if (i + 1 != path.len) {
+ int selfsigned;
+
+ ret = certificate_is_self_signed(context, c, &selfsigned);
+ if (ret)
+ goto out;
+ if (selfsigned)
+ selfsigned_depth++;
+ }
+
+ break;
+ case PROXY_CERT: {
+ ProxyCertInfo info;
+
+ if (is_proxy_cert(context, c, &info) == 0) {
+ size_t j;
+
+ if (info.pCPathLenConstraint != NULL &&
+ *info.pCPathLenConstraint < i)
+ {
+ free_ProxyCertInfo(&info);
+ ret = HX509_PATH_TOO_LONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate chain "
+ "longer than allowed");
+ goto out;
+ }
+ /* XXX MUST check info.proxyPolicy */
+ free_ProxyCertInfo(&info);
+
+ j = 0;
+ if (find_extension(c, &asn1_oid_id_x509_ce_subjectAltName, &j)) {
+ ret = HX509_PROXY_CERT_INVALID;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate has explicitly "
+ "forbidden subjectAltName");
+ goto out;
+ }
+
+ j = 0;
+ if (find_extension(c, &asn1_oid_id_x509_ce_issuerAltName, &j)) {
+ ret = HX509_PROXY_CERT_INVALID;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate has explicitly "
+ "forbidden issuerAltName");
+ goto out;
+ }
+
+ /*
+ * The subject name of the proxy certificate should be
+ * CN=XXX,<proxy issuer>, prune of CN and check if its
+ * the same over the whole chain of proxy certs and
+ * then check with the EE cert when we get to it.
+ */
+
+ if (proxy_cert_depth) {
+ ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject, &diff);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (diff) {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Base proxy name not right");
+ goto out;
+ }
+ }
+
+ free_Name(&proxy_issuer);
+
+ ret = copy_Name(&c->tbsCertificate.subject, &proxy_issuer);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ j = proxy_issuer.u.rdnSequence.len;
+ if (proxy_issuer.u.rdnSequence.len < 2
+ || proxy_issuer.u.rdnSequence.val[j - 1].len > 1
+ || der_heim_oid_cmp(&proxy_issuer.u.rdnSequence.val[j - 1].val[0].type,
+ &asn1_oid_id_at_commonName))
+ {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy name too short or "
+ "does not have Common name "
+ "at the top");
+ goto out;
+ }
+
+ free_RelativeDistinguishedName(&proxy_issuer.u.rdnSequence.val[j - 1]);
+ proxy_issuer.u.rdnSequence.len -= 1;
+
+ ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.issuer, &diff);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (diff != 0) {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy issuer name not as expected");
+ goto out;
+ }
+
+ break;
+ } else {
+ /*
+ * Now we are done with the proxy certificates, this
+ * cert was an EE cert and we we will fall though to
+ * EE checking below.
+ */
+ type = EE_CERT;
+ }
+ }
+ fallthrough;
+ case EE_CERT:
+ /*
+ * If there where any proxy certificates in the chain
+ * (proxy_cert_depth > 0), check that the proxy issuer
+ * matched proxy certificates "base" subject.
+ */
+ if (proxy_cert_depth) {
+
+ ret = _hx509_name_cmp(&proxy_issuer,
+ &c->tbsCertificate.subject, &diff);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+ if (diff) {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (cert->basename)
+ hx509_name_free(&cert->basename);
+
+ ret = _hx509_name_from_Name(&proxy_issuer, &cert->basename);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ break;
+ }
+
+ ret = check_basic_constraints(context, c, type,
+ i - proxy_cert_depth - selfsigned_depth);
+ if (ret)
+ goto out;
+
+ /*
+ * Don't check the trust anchors expiration time since they
+ * are transported out of band, from RFC3820.
+ */
+ if (i + 1 != path.len || CHECK_TA(ctx)) {
+
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
+ if (t > ctx->time_now) {
+ ret = HX509_CERT_USED_BEFORE_TIME;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter);
+ if (t < ctx->time_now) {
+ ret = HX509_CERT_USED_AFTER_TIME;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ if (type == EE_CERT)
+ type = CA_CERT;
+ else if (type == PROXY_CERT)
+ proxy_cert_depth++;
+ }
+
+ /*
+ * Verify constraints, do this backward so path constraints are
+ * checked in the right order.
+ */
+
+ for (ret = 0, k = path.len; k > 0; k--) {
+ Certificate *c;
+ int selfsigned;
+ i = k - 1;
+
+ c = _hx509_get_cert(path.val[i]);
+
+ ret = certificate_is_self_signed(context, c, &selfsigned);
+ if (ret)
+ goto out;
+
+ /* verify name constraints, not for selfsigned and anchor */
+ if (!selfsigned || i + 1 != path.len) {
+ ret = check_name_constraints(context, &nc, c);
+ if (ret) {
+ goto out;
+ }
+ }
+ ret = add_name_constraints(context, c, i == 0, &nc);
+ if (ret)
+ goto out;
+
+ /* XXX verify all other silly constraints */
+
+ }
+
+ /*
+ * Verify that no certificates has been revoked.
+ */
+
+ if (ctx->revoke_ctx) {
+ hx509_certs certs;
+
+ ret = hx509_certs_init(context, "MEMORY:revoke-certs", 0,
+ NULL, &certs);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < path.len; i++) {
+ ret = hx509_certs_add(context, certs, path.val[i]);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+ }
+ ret = hx509_certs_merge(context, certs, pool);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+
+ for (i = 0; i < path.len - 1; i++) {
+ size_t parent = (i < path.len - 1) ? i + 1 : i;
+
+ ret = hx509_revoke_verify(context,
+ ctx->revoke_ctx,
+ certs,
+ ctx->time_now,
+ path.val[i],
+ path.val[parent]);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+ }
+ hx509_certs_free(&certs);
+ }
+
+ /*
+ * Verify signatures, do this backward so public key working
+ * parameter is passed up from the anchor up though the chain.
+ */
+
+ for (k = path.len; k > 0; k--) {
+ hx509_cert signer;
+ Certificate *c;
+ i = k - 1;
+
+ c = _hx509_get_cert(path.val[i]);
+
+ /* is last in chain (trust anchor) */
+ if (i + 1 == path.len) {
+ int selfsigned;
+
+ signer = path.val[i];
+
+ ret = certificate_is_self_signed(context, signer->data, &selfsigned);
+ if (ret)
+ goto out;
+
+ /* if trust anchor is not self signed, don't check sig */
+ if (!selfsigned)
+ continue;
+ } else {
+ /* take next certificate in chain */
+ signer = path.val[i + 1];
+ }
+
+ /* verify signatureValue */
+ ret = _hx509_verify_signature_bitstring(context,
+ signer,
+ &c->signatureAlgorithm,
+ &c->tbsCertificate._save,
+ &c->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify signature of certificate");
+ goto out;
+ }
+ /*
+ * Verify that the sigature algorithm is not weak. Ignore
+ * trust anchors since they are provisioned by the user.
+ */
+
+ if (i + 1 != path.len && (ctx->flags & HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK) == 0) {
+ ret = _hx509_signature_is_weak(context, &c->signatureAlgorithm);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ hx509_certs_free(&anchors);
+ free_Name(&proxy_issuer);
+ free_name_constraints(&nc);
+ _hx509_path_free(&path);
+
+ return ret;
+}
+
+/**
+ * Verify a signature made using the private key of an certificate.
+ *
+ * @param context A hx509 context.
+ * @param signer the certificate that made the signature.
+ * @param alg algorthm that was used to sign the data.
+ * @param data the data that was signed.
+ * @param sig the sigature to verify.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_crypto
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_verify_signature(hx509_context context,
+ const hx509_cert signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ return _hx509_verify_signature(context, signer, alg, data, sig);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_verify_signature_bitstring(hx509_context context,
+ const hx509_cert signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_bit_string *sig)
+{
+ heim_octet_string os;
+
+ if (sig->length & 7) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "signature not multiple of 8 bits");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ os.data = sig->data;
+ os.length = sig->length / 8;
+
+ return _hx509_verify_signature(context, signer, alg, data, &os);
+}
+
+
+
+/**
+ * Verify that the certificate is allowed to be used for the hostname
+ * and address.
+ *
+ * @param context A hx509 context.
+ * @param cert the certificate to match with
+ * @param flags Flags to modify the behavior:
+ * - HX509_VHN_F_ALLOW_NO_MATCH no match is ok
+ * @param type type of hostname:
+ * - HX509_HN_HOSTNAME for plain hostname.
+ * - HX509_HN_DNSSRV for DNS SRV names.
+ * @param hostname the hostname to check
+ * @param sa address of the host
+ * @param sa_size length of address
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_verify_hostname(hx509_context context,
+ const hx509_cert cert,
+ int flags,
+ hx509_hostname_type type,
+ const char *hostname,
+ const struct sockaddr *sa,
+ /* XXX krb5_socklen_t */ int sa_size)
+{
+ GeneralNames san;
+ const Name *name;
+ int ret;
+ size_t i, j, k;
+
+ if (sa && sa_size <= 0)
+ return EINVAL;
+
+ memset(&san, 0, sizeof(san));
+
+ i = 0;
+ do {
+ ret = find_extension_subject_alt_name(cert->data, &i, &san);
+ if (ret == HX509_EXTENSION_NOT_FOUND)
+ break;
+ else if (ret != 0)
+ return HX509_PARSING_NAME_FAILED;
+
+ for (j = 0; j < san.len; j++) {
+ switch (san.val[j].element) {
+ case choice_GeneralName_dNSName: {
+ heim_printable_string hn;
+ hn.data = rk_UNCONST(hostname);
+ hn.length = strlen(hostname);
+
+ if (der_printable_string_cmp(&san.val[j].u.dNSName, &hn) == 0) {
+ free_GeneralNames(&san);
+ return 0;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ free_GeneralNames(&san);
+ } while (1);
+
+ name = &cert->data->tbsCertificate.subject;
+
+ /* Find first CN= in the name, and try to match the hostname on that */
+ for (ret = 0, k = name->u.rdnSequence.len; ret == 0 && k > 0; k--) {
+ i = k - 1;
+ for (j = 0; ret == 0 && j < name->u.rdnSequence.val[i].len; j++) {
+ AttributeTypeAndValue *n = &name->u.rdnSequence.val[i].val[j];
+
+ if (der_heim_oid_cmp(&n->type, &asn1_oid_id_at_commonName) == 0) {
+ DirectoryString *ds = &n->value;
+ switch (ds->element) {
+ case choice_DirectoryString_printableString: {
+ heim_printable_string hn;
+ hn.data = rk_UNCONST(hostname);
+ hn.length = strlen(hostname);
+
+ if (der_printable_string_cmp(&ds->u.printableString, &hn) == 0)
+ return 0;
+ break;
+ }
+ case choice_DirectoryString_ia5String: {
+ heim_ia5_string hn;
+ hn.data = rk_UNCONST(hostname);
+ hn.length = strlen(hostname);
+
+ if (der_ia5_string_cmp(&ds->u.ia5String, &hn) == 0)
+ return 0;
+ break;
+ }
+ case choice_DirectoryString_utf8String:
+ if (strcasecmp(ds->u.utf8String, hostname) == 0)
+ return 0;
+ default:
+ break;
+ }
+ ret = HX509_NAME_CONSTRAINT_ERROR;
+ }
+ }
+ }
+
+ if ((flags & HX509_VHN_F_ALLOW_NO_MATCH) == 0)
+ ret = HX509_NAME_CONSTRAINT_ERROR;
+
+ return ret;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_set_cert_attribute(hx509_context context,
+ hx509_cert cert,
+ const heim_oid *oid,
+ const heim_octet_string *attr)
+{
+ hx509_cert_attribute a;
+ void *d;
+ int ret;
+
+ /*
+ * TODO: Rewrite this (and hx509_cert_attribute, and _hx509_cert_attrs) to
+ * use the add_AttributeValues() util generated by asn1_compile.
+ */
+
+ if (hx509_cert_get_attribute(cert, oid) != NULL)
+ return 0;
+
+ d = realloc(cert->attrs.val,
+ sizeof(cert->attrs.val[0]) * (cert->attrs.len + 1));
+ if (d == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ cert->attrs.val = d;
+
+ a = malloc(sizeof(*a));
+ if (a == NULL)
+ return ENOMEM;
+
+ ret = der_copy_octet_string(attr, &a->data);
+ if (ret == 0)
+ ret = der_copy_oid(oid, &a->oid);
+ if (ret == 0) {
+ cert->attrs.val[cert->attrs.len] = a;
+ cert->attrs.len++;
+ } else {
+ der_free_octet_string(&a->data);
+ free(a);
+ }
+
+ return ret;
+}
+
+/**
+ * Get an external attribute for the certificate, examples are
+ * friendly name and id.
+ *
+ * @param cert hx509 certificate object to search
+ * @param oid an oid to search for.
+ *
+ * @return an hx509_cert_attribute, only valid as long as the
+ * certificate is referenced.
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION hx509_cert_attribute HX509_LIB_CALL
+hx509_cert_get_attribute(hx509_cert cert, const heim_oid *oid)
+{
+ size_t i;
+ for (i = 0; i < cert->attrs.len; i++)
+ if (der_heim_oid_cmp(oid, &cert->attrs.val[i]->oid) == 0)
+ return cert->attrs.val[i];
+ return NULL;
+}
+
+/**
+ * Set the friendly name on the certificate.
+ *
+ * @param cert The certificate to set the friendly name on
+ * @param name Friendly name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_set_friendly_name(hx509_cert cert, const char *name)
+{
+ if (cert->friendlyname)
+ free(cert->friendlyname);
+ cert->friendlyname = strdup(name);
+ if (cert->friendlyname == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+/**
+ * Get friendly name of the certificate.
+ *
+ * @param cert cert to get the friendly name from.
+ *
+ * @return an friendly name or NULL if there is. The friendly name is
+ * only valid as long as the certificate is referenced.
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION const char * HX509_LIB_CALL
+hx509_cert_get_friendly_name(hx509_cert cert)
+{
+ hx509_cert_attribute a;
+ PKCS9_friendlyName n;
+ size_t sz;
+ int ret;
+ size_t i;
+
+ if (cert->friendlyname)
+ return cert->friendlyname;
+
+ a = hx509_cert_get_attribute(cert, &asn1_oid_id_pkcs_9_at_friendlyName);
+ if (a == NULL) {
+ hx509_name name;
+
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret)
+ return NULL;
+ ret = hx509_name_to_string(name, &cert->friendlyname);
+ hx509_name_free(&name);
+ if (ret)
+ return NULL;
+ return cert->friendlyname;
+ }
+
+ ret = decode_PKCS9_friendlyName(a->data.data, a->data.length, &n, &sz);
+ if (ret)
+ return NULL;
+
+ if (n.len != 1) {
+ free_PKCS9_friendlyName(&n);
+ return NULL;
+ }
+
+ cert->friendlyname = malloc(n.val[0].length + 1);
+ if (cert->friendlyname == NULL) {
+ free_PKCS9_friendlyName(&n);
+ return NULL;
+ }
+
+ for (i = 0; i < n.val[0].length; i++) {
+ if (n.val[0].data[i] <= 0xff)
+ cert->friendlyname[i] = n.val[0].data[i] & 0xff;
+ else
+ cert->friendlyname[i] = 'X';
+ }
+ cert->friendlyname[i] = '\0';
+ free_PKCS9_friendlyName(&n);
+
+ return cert->friendlyname;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_query_clear(hx509_query *q)
+{
+ memset(q, 0, sizeof(*q));
+}
+
+/**
+ * Allocate an query controller. Free using hx509_query_free().
+ *
+ * @param context A hx509 context.
+ * @param q return pointer to a hx509_query.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_query_alloc(hx509_context context, hx509_query **q)
+{
+ *q = calloc(1, sizeof(**q));
+ if (*q == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+
+/**
+ * Set match options for the hx509 query controller.
+ *
+ * @param q query controller.
+ * @param option options to control the query controller.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_query_match_option(hx509_query *q, hx509_query_option option)
+{
+ switch(option) {
+ case HX509_QUERY_OPTION_PRIVATE_KEY:
+ q->match |= HX509_QUERY_PRIVATE_KEY;
+ break;
+ case HX509_QUERY_OPTION_KU_ENCIPHERMENT:
+ q->match |= HX509_QUERY_KU_ENCIPHERMENT;
+ break;
+ case HX509_QUERY_OPTION_KU_DIGITALSIGNATURE:
+ q->match |= HX509_QUERY_KU_DIGITALSIGNATURE;
+ break;
+ case HX509_QUERY_OPTION_KU_KEYCERTSIGN:
+ q->match |= HX509_QUERY_KU_KEYCERTSIGN;
+ break;
+ case HX509_QUERY_OPTION_END:
+ default:
+ break;
+ }
+}
+
+/**
+ * Set the issuer and serial number of match in the query
+ * controller. The function make copies of the isser and serial number.
+ *
+ * @param q a hx509 query controller
+ * @param issuer issuer to search for
+ * @param serialNumber the serialNumber of the issuer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_query_match_issuer_serial(hx509_query *q,
+ const Name *issuer,
+ const heim_integer *serialNumber)
+{
+ int ret;
+ if (q->serial) {
+ der_free_heim_integer(q->serial);
+ free(q->serial);
+ }
+ q->serial = malloc(sizeof(*q->serial));
+ if (q->serial == NULL)
+ return ENOMEM;
+ ret = der_copy_heim_integer(serialNumber, q->serial);
+ if (ret) {
+ free(q->serial);
+ q->serial = NULL;
+ return ret;
+ }
+ if (q->issuer_name) {
+ free_Name(q->issuer_name);
+ free(q->issuer_name);
+ }
+ q->issuer_name = malloc(sizeof(*q->issuer_name));
+ if (q->issuer_name == NULL)
+ return ENOMEM;
+ ret = copy_Name(issuer, q->issuer_name);
+ if (ret) {
+ free(q->issuer_name);
+ q->issuer_name = NULL;
+ return ret;
+ }
+ q->match |= HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME;
+ return 0;
+}
+
+/**
+ * Set the query controller to match on a friendly name
+ *
+ * @param q a hx509 query controller.
+ * @param name a friendly name to match on
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_query_match_friendly_name(hx509_query *q, const char *name)
+{
+ if (q->friendlyname)
+ free(q->friendlyname);
+ q->friendlyname = strdup(name);
+ if (q->friendlyname == NULL)
+ return ENOMEM;
+ q->match |= HX509_QUERY_MATCH_FRIENDLY_NAME;
+ return 0;
+}
+
+/**
+ * Set the query controller to require an one specific EKU (extended
+ * key usage). Any previous EKU matching is overwitten. If NULL is
+ * passed in as the eku, the EKU requirement is reset.
+ *
+ * @param q a hx509 query controller.
+ * @param eku an EKU to match on.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_query_match_eku(hx509_query *q, const heim_oid *eku)
+{
+ int ret;
+
+ if (eku == NULL) {
+ if (q->eku) {
+ der_free_oid(q->eku);
+ free(q->eku);
+ q->eku = NULL;
+ }
+ q->match &= ~HX509_QUERY_MATCH_EKU;
+ } else {
+ if (q->eku) {
+ der_free_oid(q->eku);
+ } else {
+ q->eku = calloc(1, sizeof(*q->eku));
+ if (q->eku == NULL)
+ return ENOMEM;
+ }
+ ret = der_copy_oid(eku, q->eku);
+ if (ret) {
+ free(q->eku);
+ q->eku = NULL;
+ return ret;
+ }
+ q->match |= HX509_QUERY_MATCH_EKU;
+ }
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_query_match_expr(hx509_context context, hx509_query *q, const char *expr)
+{
+ if (q->expr) {
+ _hx509_expr_free(q->expr);
+ q->expr = NULL;
+ }
+
+ if (expr == NULL) {
+ q->match &= ~HX509_QUERY_MATCH_EXPR;
+ return 0;
+ }
+
+ q->expr = _hx509_expr_parse(expr);
+ if (q->expr == NULL) {
+ const char *reason = _hx509_expr_parse_error();
+
+ hx509_set_error_string(context, 0, EINVAL,
+ "Invalid certificate query match expression: "
+ "%s (%s)", expr,
+ reason ? reason : "syntax error");
+ return EINVAL;
+ }
+
+ q->match |= HX509_QUERY_MATCH_EXPR;
+ return 0;
+}
+
+/**
+ * Set the query controller to match using a specific match function.
+ *
+ * @param q a hx509 query controller.
+ * @param func function to use for matching, if the argument is NULL,
+ * the match function is removed.
+ * @param ctx context passed to the function.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_query_match_cmp_func(hx509_query *q,
+ int (*func)(hx509_context, hx509_cert, void *),
+ void *ctx)
+{
+ if (func)
+ q->match |= HX509_QUERY_MATCH_FUNCTION;
+ else
+ q->match &= ~HX509_QUERY_MATCH_FUNCTION;
+ q->cmp_func = func;
+ q->cmp_func_ctx = ctx;
+ return 0;
+}
+
+/**
+ * Free the query controller.
+ *
+ * @param context A hx509 context.
+ * @param q a pointer to the query controller.
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_query_free(hx509_context context, hx509_query *q)
+{
+ if (q == NULL)
+ return;
+
+ if (q->serial) {
+ der_free_heim_integer(q->serial);
+ free(q->serial);
+ }
+ if (q->issuer_name) {
+ free_Name(q->issuer_name);
+ free(q->issuer_name);
+ }
+ if (q->eku) {
+ der_free_oid(q->eku);
+ free(q->eku);
+ }
+ if (q->friendlyname)
+ free(q->friendlyname);
+ if (q->expr)
+ _hx509_expr_free(q->expr);
+
+ memset(q, 0, sizeof(*q));
+ free(q);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert cert)
+{
+ Certificate *c = _hx509_get_cert(cert);
+ int ret, diff;
+
+ _hx509_query_statistic(context, 1, q);
+
+ if ((q->match & HX509_QUERY_FIND_ISSUER_CERT) &&
+ _hx509_cert_is_parent_cmp(q->subject, c, 0) != 0)
+ return 0;
+
+ if ((q->match & HX509_QUERY_MATCH_CERTIFICATE) &&
+ _hx509_Certificate_cmp(q->certificate, c) != 0)
+ return 0;
+
+ if ((q->match & HX509_QUERY_MATCH_SERIALNUMBER)
+ && der_heim_integer_cmp(&c->tbsCertificate.serialNumber, q->serial) != 0)
+ return 0;
+
+ if (q->match & HX509_QUERY_MATCH_ISSUER_NAME) {
+ ret = _hx509_name_cmp(&c->tbsCertificate.issuer, q->issuer_name, &diff);
+ if (ret || diff)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_SUBJECT_NAME) {
+ ret = _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name, &diff);
+ if (ret || diff)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_SUBJECT_KEY_ID) {
+ SubjectKeyIdentifier si;
+
+ ret = _hx509_find_extension_subject_key_id(c, &si);
+ if (ret == 0) {
+ if (der_heim_octet_string_cmp(&si, q->subject_id) != 0)
+ ret = 1;
+ free_SubjectKeyIdentifier(&si);
+ }
+ if (ret)
+ return 0;
+ }
+ if ((q->match & HX509_QUERY_MATCH_ISSUER_ID))
+ return 0;
+ if ((q->match & HX509_QUERY_PRIVATE_KEY) &&
+ _hx509_cert_private_key(cert) == NULL)
+ return 0;
+
+ {
+ unsigned ku = 0;
+ if (q->match & HX509_QUERY_KU_DIGITALSIGNATURE)
+ ku |= (1 << 0);
+ if (q->match & HX509_QUERY_KU_NONREPUDIATION)
+ ku |= (1 << 1);
+ if (q->match & HX509_QUERY_KU_ENCIPHERMENT)
+ ku |= (1 << 2);
+ if (q->match & HX509_QUERY_KU_DATAENCIPHERMENT)
+ ku |= (1 << 3);
+ if (q->match & HX509_QUERY_KU_KEYAGREEMENT)
+ ku |= (1 << 4);
+ if (q->match & HX509_QUERY_KU_KEYCERTSIGN)
+ ku |= (1 << 5);
+ if (q->match & HX509_QUERY_KU_CRLSIGN)
+ ku |= (1 << 6);
+ if (ku && check_key_usage(context, c, ku, TRUE))
+ return 0;
+ }
+ if ((q->match & HX509_QUERY_ANCHOR))
+ return 0;
+
+ if (q->match & HX509_QUERY_MATCH_LOCAL_KEY_ID) {
+ hx509_cert_attribute a;
+
+ a = hx509_cert_get_attribute(cert, &asn1_oid_id_pkcs_9_at_localKeyId);
+ if (a == NULL)
+ return 0;
+ if (der_heim_octet_string_cmp(&a->data, q->local_key_id) != 0)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_NO_MATCH_PATH) {
+ size_t i;
+
+ for (i = 0; i < q->path->len; i++)
+ if (hx509_cert_cmp(q->path->val[i], cert) == 0)
+ return 0;
+ }
+ if (q->match & HX509_QUERY_MATCH_FRIENDLY_NAME) {
+ const char *name = hx509_cert_get_friendly_name(cert);
+ if (name == NULL)
+ return 0;
+ if (strcasecmp(q->friendlyname, name) != 0)
+ return 0;
+ }
+ if (q->match & HX509_QUERY_MATCH_FUNCTION) {
+ ret = (*q->cmp_func)(context, cert, q->cmp_func_ctx);
+ if (ret != 0)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_KEY_HASH_SHA1) {
+ heim_octet_string os;
+
+ os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length =
+ c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ hx509_signature_sha1(),
+ &os,
+ q->keyhash_sha1);
+ if (ret != 0)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_TIME) {
+ time_t t;
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
+ if (t > q->timenow)
+ return 0;
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter);
+ if (t < q->timenow)
+ return 0;
+ }
+
+ /* If an EKU is required, check the cert for it. */
+ if ((q->match & HX509_QUERY_MATCH_EKU) &&
+ hx509_cert_check_eku(context, cert, q->eku, 0))
+ return 0;
+
+ if ((q->match & HX509_QUERY_MATCH_EXPR)) {
+ hx509_env env = NULL;
+
+ ret = _hx509_cert_to_env(context, cert, &env);
+ if (ret)
+ return 0;
+
+ ret = _hx509_expr_eval(context, env, q->expr);
+ hx509_env_free(&env);
+ if (ret == 0)
+ return 0;
+ }
+
+ if (q->match & ~HX509_QUERY_MASK)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * Set a statistic file for the query statistics.
+ *
+ * @param context A hx509 context.
+ * @param fn statistics file name
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_query_statistic_file(hx509_context context, const char *fn)
+{
+ if (context->querystat)
+ free(context->querystat);
+ context->querystat = strdup(fn);
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_query_statistic(hx509_context context, int type, const hx509_query *q)
+{
+ FILE *f;
+ if (context->querystat == NULL)
+ return;
+ f = fopen(context->querystat, "a");
+ if (f == NULL)
+ return;
+ rk_cloexec_file(f);
+ fprintf(f, "%d %d\n", type, q->match);
+ fclose(f);
+}
+
+static const char *statname[] = {
+ "find issuer cert",
+ "match serialnumber",
+ "match issuer name",
+ "match subject name",
+ "match subject key id",
+ "match issuer id",
+ "private key",
+ "ku encipherment",
+ "ku digitalsignature",
+ "ku keycertsign",
+ "ku crlsign",
+ "ku nonrepudiation",
+ "ku keyagreement",
+ "ku dataencipherment",
+ "anchor",
+ "match certificate",
+ "match local key id",
+ "no match path",
+ "match friendly name",
+ "match function",
+ "match key hash sha1",
+ "match time"
+};
+
+struct stat_el {
+ unsigned long stats;
+ unsigned int index;
+};
+
+
+static int
+stat_sort(const void *a, const void *b)
+{
+ const struct stat_el *ae = a;
+ const struct stat_el *be = b;
+ return be->stats - ae->stats;
+}
+
+/**
+ * Unparse the statistics file and print the result on a FILE descriptor.
+ *
+ * @param context A hx509 context.
+ * @param printtype tyep to print
+ * @param out the FILE to write the data on.
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out)
+{
+ rtbl_t t;
+ FILE *f;
+ int type, mask, num;
+ size_t i;
+ unsigned long multiqueries = 0, totalqueries = 0;
+ struct stat_el stats[32];
+
+ if (context->querystat == NULL)
+ return;
+ f = fopen(context->querystat, "r");
+ if (f == NULL) {
+ fprintf(out, "No statistics file %s: %s.\n",
+ context->querystat, strerror(errno));
+ return;
+ }
+ rk_cloexec_file(f);
+
+ for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) {
+ stats[i].index = i;
+ stats[i].stats = 0;
+ }
+
+ while (fscanf(f, "%d %d\n", &type, &mask) == 2) {
+ if (type != printtype)
+ continue;
+ num = i = 0;
+ while (mask && i < sizeof(stats)/sizeof(stats[0])) {
+ if (mask & 1) {
+ stats[i].stats++;
+ num++;
+ }
+ mask = mask >>1 ;
+ i++;
+ }
+ if (num > 1)
+ multiqueries++;
+ totalqueries++;
+ }
+ fclose(f);
+
+ qsort(stats, sizeof(stats)/sizeof(stats[0]), sizeof(stats[0]), stat_sort);
+
+ t = rtbl_create();
+ if (t == NULL)
+ errx(1, "out of memory");
+
+ rtbl_set_separator (t, " ");
+
+ rtbl_add_column_by_id (t, 0, "Name", 0);
+ rtbl_add_column_by_id (t, 1, "Counter", 0);
+
+
+ for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) {
+ char str[10];
+
+ if (stats[i].index < sizeof(statname)/sizeof(statname[0]))
+ rtbl_add_column_entry_by_id (t, 0, statname[stats[i].index]);
+ else {
+ snprintf(str, sizeof(str), "%d", stats[i].index);
+ rtbl_add_column_entry_by_id (t, 0, str);
+ }
+ snprintf(str, sizeof(str), "%lu", stats[i].stats);
+ rtbl_add_column_entry_by_id (t, 1, str);
+ }
+
+ rtbl_format(t, out);
+ rtbl_destroy(t);
+
+ fprintf(out, "\nQueries: multi %lu total %lu\n",
+ multiqueries, totalqueries);
+}
+
+/**
+ * Check the extended key usage on the hx509 certificate.
+ *
+ * @param context A hx509 context.
+ * @param cert A hx509 context.
+ * @param eku the EKU to check for
+ * @param allow_any_eku if the any EKU is set, allow that to be a
+ * substitute.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_check_eku(hx509_context context, hx509_cert cert,
+ const heim_oid *eku, int allow_any_eku)
+{
+ ExtKeyUsage e;
+ int ret;
+ size_t i;
+
+ ret = find_extension_eku(_hx509_get_cert(cert), &e);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+
+ for (i = 0; i < e.len; i++) {
+ if (der_heim_oid_cmp(eku, &e.val[i]) == 0) {
+ free_ExtKeyUsage(&e);
+ return 0;
+ }
+ if (allow_any_eku) {
+ if (der_heim_oid_cmp(&asn1_oid_id_x509_ce_anyExtendedKeyUsage,
+ &e.val[i]) == 0) {
+ free_ExtKeyUsage(&e);
+ return 0;
+ }
+ }
+ }
+ free_ExtKeyUsage(&e);
+ hx509_clear_error_string(context);
+ return HX509_CERTIFICATE_MISSING_EKU;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_cert_get_keyusage(hx509_context context,
+ hx509_cert c,
+ KeyUsage *ku)
+{
+ Certificate *cert;
+ const Extension *e;
+ size_t size;
+ int ret;
+ size_t i = 0;
+
+ memset(ku, 0, sizeof(*ku));
+
+ cert = _hx509_get_cert(c);
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, &asn1_oid_id_x509_ce_keyUsage, &i);
+ if (e == NULL)
+ return HX509_KU_CERT_MISSING;
+
+ ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, ku, &size);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_cert_get_eku(hx509_context context,
+ hx509_cert cert,
+ ExtKeyUsage *e)
+{
+ int ret;
+
+ memset(e, 0, sizeof(*e));
+
+ ret = find_extension_eku(_hx509_get_cert(cert), e);
+ if (ret && ret != HX509_EXTENSION_NOT_FOUND) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * Encodes the hx509 certificate as a DER encode binary.
+ *
+ * @param context A hx509 context.
+ * @param c the certificate to encode.
+ * @param os the encode certificate, set to NULL, 0 on case of
+ * error. Free the os->data with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os)
+{
+ size_t size;
+ int ret;
+
+ os->data = NULL;
+ os->length = 0;
+
+ ASN1_MALLOC_ENCODE(Certificate, os->data, os->length,
+ _hx509_get_cert(c), &size, ret);
+ if (ret) {
+ os->data = NULL;
+ os->length = 0;
+ return ret;
+ }
+ if (os->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+ return ret;
+}
+
+/*
+ * Last to avoid lost __attribute__s due to #undef.
+ */
+
+#undef __attribute__
+#define __attribute__(X)
+
+HX509_LIB_NORETURN_FUNCTION void HX509_LIB_CALL
+_hx509_abort(const char *fmt, ...)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 1, 2)))
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+ fflush(stdout);
+ abort();
+}
+
+/**
+ * Free a data element allocated in the library.
+ *
+ * @param ptr data to be freed.
+ *
+ * @ingroup hx509_misc
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_xfree(void *ptr)
+{
+ free(ptr);
+}
+
+/**
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_cert_to_env(hx509_context context, hx509_cert cert, hx509_env *env)
+{
+ ExtKeyUsage eku;
+ hx509_name name;
+ char *buf;
+ int ret;
+ hx509_env envcert = NULL;
+
+ *env = NULL;
+
+ /* version */
+ ret = asprintf(&buf, "%d", _hx509_cert_get_version(_hx509_get_cert(cert)));
+ if (ret == -1)
+ goto out;
+ ret = hx509_env_add(context, &envcert, "version", buf);
+ free(buf);
+ if (ret)
+ goto out;
+
+ /* subject */
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret)
+ goto out;
+
+ ret = hx509_name_to_string(name, &buf);
+ hx509_name_free(&name);
+ if (ret)
+ goto out;
+
+ ret = hx509_env_add(context, &envcert, "subject", buf);
+ hx509_xfree(buf);
+ if (ret)
+ goto out;
+
+ /* issuer */
+ ret = hx509_cert_get_issuer(cert, &name);
+ if (ret)
+ goto out;
+
+ ret = hx509_name_to_string(name, &buf);
+ hx509_name_free(&name);
+ if (ret)
+ goto out;
+
+ ret = hx509_env_add(context, &envcert, "issuer", buf);
+ hx509_xfree(buf);
+ if (ret)
+ goto out;
+
+ /* eku */
+
+ ret = _hx509_cert_get_eku(context, cert, &eku);
+ if (ret == HX509_EXTENSION_NOT_FOUND)
+ ;
+ else if (ret != 0)
+ goto out;
+ else {
+ size_t i;
+ hx509_env enveku = NULL;
+
+ for (i = 0; i < eku.len; i++) {
+
+ ret = der_print_heim_oid(&eku.val[i], '.', &buf);
+ if (ret) {
+ free_ExtKeyUsage(&eku);
+ hx509_env_free(&enveku);
+ goto out;
+ }
+ ret = hx509_env_add(context, &enveku, buf, "oid-name-here");
+ free(buf);
+ if (ret) {
+ free_ExtKeyUsage(&eku);
+ hx509_env_free(&enveku);
+ goto out;
+ }
+ }
+ free_ExtKeyUsage(&eku);
+
+ ret = hx509_env_add_binding(context, &envcert, "eku", enveku);
+ if (ret) {
+ hx509_env_free(&enveku);
+ goto out;
+ }
+ }
+
+ {
+ Certificate *c = _hx509_get_cert(cert);
+ heim_octet_string os, sig;
+ hx509_env envhash = NULL;
+
+ os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length =
+ c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ hx509_signature_sha1(),
+ &os,
+ NULL,
+ &sig);
+ if (ret != 0)
+ goto out;
+
+ ret = hex_encode(sig.data, sig.length, &buf);
+ der_free_octet_string(&sig);
+ if (ret < 0) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret,
+ "Out of memory");
+ goto out;
+ }
+
+ ret = hx509_env_add(context, &envhash, "sha1", buf);
+ free(buf);
+ if (ret)
+ goto out;
+
+ ret = hx509_env_add_binding(context, &envcert, "hash", envhash);
+ if (ret) {
+ hx509_env_free(&envhash);
+ goto out;
+ }
+ }
+
+ ret = hx509_env_add_binding(context, env, "certificate", envcert);
+ if (ret)
+ goto out;
+
+ return 0;
+
+out:
+ hx509_env_free(&envcert);
+ return ret;
+}
+
+/**
+ * Print a simple representation of a certificate
+ *
+ * @param context A hx509 context, can be NULL
+ * @param cert certificate to print
+ * @param out the stdio output stream, if NULL, stdout is used
+ *
+ * @return An hx509 error code
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_print_cert(hx509_context context, hx509_cert cert, FILE *out)
+{
+ hx509_name name;
+ char *str;
+ int ret;
+
+ if (out == NULL)
+ out = stderr;
+
+ ret = hx509_cert_get_issuer(cert, &name);
+ if (ret)
+ return ret;
+ hx509_name_to_string(name, &str);
+ hx509_name_free(&name);
+ fprintf(out, " issuer: \"%s\"\n", str);
+ free(str);
+
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret)
+ return ret;
+ hx509_name_to_string(name, &str);
+ hx509_name_free(&name);
+ fprintf(out, " subject: \"%s\"\n", str);
+ free(str);
+
+ {
+ heim_integer serialNumber;
+
+ ret = hx509_cert_get_serialnumber(cert, &serialNumber);
+ if (ret)
+ return ret;
+ ret = der_print_hex_heim_integer(&serialNumber, &str);
+ if (ret)
+ return ret;
+ der_free_heim_integer(&serialNumber);
+ fprintf(out, " serial: %s\n", str);
+ free(str);
+ }
+
+ fprintf(out, " keyusage: ");
+ ret = hx509_cert_keyusage_print(context, cert, &str);
+ if (ret == 0) {
+ fprintf(out, "%s\n", str);
+ free(str);
+ } else
+ fprintf(out, "no");
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/hx509/char_map.h b/third_party/heimdal/lib/hx509/char_map.h
new file mode 100644
index 0000000..8a3026c
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/char_map.h
@@ -0,0 +1,45 @@
+#define Q_CONTROL_CHAR 1
+#define Q_PRINTABLE 2
+#define Q_RFC2253_QUOTE_FIRST 4
+#define Q_RFC2253_QUOTE_LAST 8
+#define Q_RFC2253_QUOTE 16
+#define Q_RFC2253_HEX 32
+
+#define Q_RFC2253 (Q_RFC2253_QUOTE_FIRST|Q_RFC2253_QUOTE_LAST|Q_RFC2253_QUOTE|Q_RFC2253_HEX)
+
+
+
+unsigned char char_map[] = {
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x06 , 0x00 , 0x00 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 ,
+ 0x00 , 0x00 , 0x00 , 0x12 , 0x12 , 0x02 , 0x02 , 0x02 ,
+ 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 ,
+ 0x02 , 0x02 , 0x02 , 0x10 , 0x10 , 0x12 , 0x10 , 0x02 ,
+ 0x00 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 ,
+ 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 ,
+ 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 ,
+ 0x02 , 0x02 , 0x02 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
+ 0x00 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 ,
+ 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 ,
+ 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 , 0x02 ,
+ 0x02 , 0x02 , 0x02 , 0x00 , 0x00 , 0x00 , 0x00 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 ,
+ 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21 , 0x21
+};
diff --git a/third_party/heimdal/lib/hx509/cms.c b/third_party/heimdal/lib/hx509/cms.c
new file mode 100644
index 0000000..d2728a3
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/cms.c
@@ -0,0 +1,1718 @@
+/*
+ * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/**
+ * @page page_cms CMS/PKCS7 message functions.
+ *
+ * CMS is defined in RFC 3369 and is an continuation of the RSA Labs
+ * standard PKCS7. The basic messages in CMS is
+ *
+ * - SignedData
+ * Data signed with private key (RSA, DSA, ECDSA) or secret
+ * (symmetric) key
+ * - EnvelopedData
+ * Data encrypted with private key (RSA)
+ * - EncryptedData
+ * Data encrypted with secret (symmetric) key.
+ * - ContentInfo
+ * Wrapper structure including type and data.
+ *
+ *
+ * See the library functions here: @ref hx509_cms
+ */
+
+#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
+#define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0)
+
+/**
+ * Wrap data and oid in a ContentInfo and encode it.
+ *
+ * @param oid type of the content.
+ * @param buf data to be wrapped. If a NULL pointer is passed in, the
+ * optional content field in the ContentInfo is not going be filled
+ * in.
+ * @param res the encoded buffer, the result should be freed with
+ * der_free_octet_string().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_wrap_ContentInfo(const heim_oid *oid,
+ const heim_octet_string *buf,
+ heim_octet_string *res)
+{
+ ContentInfo ci;
+ size_t size;
+ int ret;
+
+ memset(res, 0, sizeof(*res));
+ memset(&ci, 0, sizeof(ci));
+
+ ret = der_copy_oid(oid, &ci.contentType);
+ if (ret)
+ return ret;
+ if (buf) {
+ ALLOC(ci.content, 1);
+ if (ci.content == NULL) {
+ free_ContentInfo(&ci);
+ return ENOMEM;
+ }
+ ci.content->data = malloc(buf->length);
+ if (ci.content->data == NULL) {
+ free_ContentInfo(&ci);
+ return ENOMEM;
+ }
+ memcpy(ci.content->data, buf->data, buf->length);
+ ci.content->length = buf->length;
+ }
+
+ ASN1_MALLOC_ENCODE(ContentInfo, res->data, res->length, &ci, &size, ret);
+ free_ContentInfo(&ci);
+ if (ret)
+ return ret;
+ if (res->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return 0;
+}
+
+/**
+ * Decode an ContentInfo and unwrap data and oid it.
+ *
+ * @param in the encoded buffer.
+ * @param oid type of the content.
+ * @param out data to be wrapped.
+ * @param have_data since the data is optional, this flags show dthe
+ * diffrence between no data and the zero length data.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_unwrap_ContentInfo(const heim_octet_string *in,
+ heim_oid *oid,
+ heim_octet_string *out,
+ int *have_data)
+{
+ ContentInfo ci;
+ size_t size;
+ int ret;
+
+ memset(oid, 0, sizeof(*oid));
+ memset(out, 0, sizeof(*out));
+
+ ret = decode_ContentInfo(in->data, in->length, &ci, &size);
+ if (ret)
+ return ret;
+
+ ret = der_copy_oid(&ci.contentType, oid);
+ if (ret) {
+ free_ContentInfo(&ci);
+ return ret;
+ }
+ if (ci.content) {
+ ret = der_copy_octet_string(ci.content, out);
+ if (ret) {
+ der_free_oid(oid);
+ free_ContentInfo(&ci);
+ return ret;
+ }
+ } else
+ memset(out, 0, sizeof(*out));
+
+ if (have_data)
+ *have_data = (ci.content != NULL) ? 1 : 0;
+
+ free_ContentInfo(&ci);
+
+ return 0;
+}
+
+#define CMS_ID_SKI 0
+#define CMS_ID_NAME 1
+
+static int
+fill_CMSIdentifier(const hx509_cert cert,
+ int type,
+ CMSIdentifier *id)
+{
+ int ret;
+
+ switch (type) {
+ case CMS_ID_SKI:
+ id->element = choice_CMSIdentifier_subjectKeyIdentifier;
+ ret = _hx509_find_extension_subject_key_id(_hx509_get_cert(cert),
+ &id->u.subjectKeyIdentifier);
+ if (ret == 0)
+ break;
+ fallthrough;
+ case CMS_ID_NAME: {
+ hx509_name name;
+
+ id->element = choice_CMSIdentifier_issuerAndSerialNumber;
+ ret = hx509_cert_get_issuer(cert, &name);
+ if (ret)
+ return ret;
+ ret = hx509_name_to_Name(name, &id->u.issuerAndSerialNumber.issuer);
+ hx509_name_free(&name);
+ if (ret)
+ return ret;
+
+ ret = hx509_cert_get_serialnumber(cert, &id->u.issuerAndSerialNumber.serialNumber);
+ break;
+ }
+ default:
+ _hx509_abort("CMS fill identifier with unknown type");
+ }
+ return ret;
+}
+
+static int
+unparse_CMSIdentifier(hx509_context context,
+ CMSIdentifier *id,
+ char **str)
+{
+ int ret = -1;
+
+ *str = NULL;
+ switch (id->element) {
+ case choice_CMSIdentifier_issuerAndSerialNumber: {
+ IssuerAndSerialNumber *iasn;
+ char *serial, *name;
+
+ iasn = &id->u.issuerAndSerialNumber;
+
+ ret = _hx509_Name_to_string(&iasn->issuer, &name);
+ if(ret)
+ return ret;
+ ret = der_print_hex_heim_integer(&iasn->serialNumber, &serial);
+ if (ret) {
+ free(name);
+ return ret;
+ }
+ ret = asprintf(str, "certificate issued by %s with serial number %s",
+ name, serial);
+ free(name);
+ free(serial);
+ break;
+ }
+ case choice_CMSIdentifier_subjectKeyIdentifier: {
+ KeyIdentifier *ki = &id->u.subjectKeyIdentifier;
+ char *keyid;
+ ssize_t len;
+
+ len = hex_encode(ki->data, ki->length, &keyid);
+ if (len < 0)
+ return ENOMEM;
+
+ ret = asprintf(str, "certificate with id %s", keyid);
+ free(keyid);
+ break;
+ }
+ default:
+ ret = asprintf(str, "certificate have unknown CMSidentifier type");
+ break;
+ }
+ /*
+ * In the following if, we check ret and *str which should be returned/set
+ * by asprintf(3) in every branch of the switch statement.
+ */
+ if (ret == -1 || *str == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+static int
+find_CMSIdentifier(hx509_context context,
+ CMSIdentifier *client,
+ hx509_certs certs,
+ time_t time_now,
+ hx509_cert *signer_cert,
+ int match)
+{
+ hx509_query q;
+ hx509_cert cert;
+ Certificate c;
+ int ret;
+
+ memset(&c, 0, sizeof(c));
+ _hx509_query_clear(&q);
+
+ *signer_cert = NULL;
+
+ switch (client->element) {
+ case choice_CMSIdentifier_issuerAndSerialNumber:
+ q.serial = &client->u.issuerAndSerialNumber.serialNumber;
+ q.issuer_name = &client->u.issuerAndSerialNumber.issuer;
+ q.match = HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME;
+ break;
+ case choice_CMSIdentifier_subjectKeyIdentifier:
+ q.subject_id = &client->u.subjectKeyIdentifier;
+ q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID;
+ break;
+ default:
+ hx509_set_error_string(context, 0, HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "unknown CMS identifier element");
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ }
+
+ q.match |= match;
+
+ q.match |= HX509_QUERY_MATCH_TIME;
+ if (time_now)
+ q.timenow = time_now;
+ else
+ q.timenow = time(NULL);
+
+ ret = hx509_certs_find(context, certs, &q, &cert);
+ if (ret == HX509_CERT_NOT_FOUND) {
+ char *str;
+
+ ret = unparse_CMSIdentifier(context, client, &str);
+ if (ret == 0) {
+ hx509_set_error_string(context, 0,
+ HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "Failed to find %s", str);
+ } else
+ hx509_clear_error_string(context);
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ } else if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND,
+ HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "Failed to find CMS id in cert store");
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ }
+
+ *signer_cert = cert;
+
+ return 0;
+}
+
+/**
+ * Decode and unencrypt EnvelopedData.
+ *
+ * Extract data and parameteres from from the EnvelopedData. Also
+ * supports using detached EnvelopedData.
+ *
+ * @param context A hx509 context.
+ * @param certs Certificate that can decrypt the EnvelopedData
+ * encryption key.
+ * @param flags HX509_CMS_UE flags to control the behavior.
+ * @param data pointer the structure the contains the DER/BER encoded
+ * EnvelopedData stucture.
+ * @param length length of the data that data point to.
+ * @param encryptedContent in case of detached signature, this
+ * contains the actual encrypted data, othersize its should be NULL.
+ * @param time_now set the current time, if zero the library uses now as the date.
+ * @param contentType output type oid, should be freed with der_free_oid().
+ * @param content the data, free with der_free_octet_string().
+ *
+ * @return an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_unenvelope(hx509_context context,
+ hx509_certs certs,
+ int flags,
+ const void *data,
+ size_t length,
+ const heim_octet_string *encryptedContent,
+ time_t time_now,
+ heim_oid *contentType,
+ heim_octet_string *content)
+{
+ heim_octet_string key;
+ EnvelopedData ed;
+ hx509_cert cert;
+ AlgorithmIdentifier *ai;
+ const heim_octet_string *enccontent;
+ heim_octet_string *params, params_data;
+ heim_octet_string ivec;
+ size_t size;
+ int ret, matched = 0, findflags = 0;
+ size_t i;
+
+
+ memset(&key, 0, sizeof(key));
+ memset(&ed, 0, sizeof(ed));
+ memset(&ivec, 0, sizeof(ivec));
+ memset(content, 0, sizeof(*content));
+ memset(contentType, 0, sizeof(*contentType));
+
+ if ((flags & HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT) == 0)
+ findflags |= HX509_QUERY_KU_ENCIPHERMENT;
+
+ ret = decode_EnvelopedData(data, length, &ed, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode EnvelopedData");
+ return ret;
+ }
+
+ if (ed.recipientInfos.len == 0) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, 0, ret,
+ "No recipient info in enveloped data");
+ goto out;
+ }
+
+ enccontent = ed.encryptedContentInfo.encryptedContent;
+ if (enccontent == NULL) {
+ if (encryptedContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "Content missing from encrypted data");
+ goto out;
+ }
+ enccontent = encryptedContent;
+ } else if (encryptedContent != NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "Both internal and external encrypted data");
+ goto out;
+ }
+
+ cert = NULL;
+ for (i = 0; i < ed.recipientInfos.len; i++) {
+ KeyTransRecipientInfo *ri;
+ char *str;
+ int ret2;
+
+ ri = &ed.recipientInfos.val[i];
+
+ ret = find_CMSIdentifier(context, &ri->rid, certs,
+ time_now, &cert,
+ HX509_QUERY_PRIVATE_KEY|findflags);
+ if (ret)
+ continue;
+
+ matched = 1; /* found a matching certificate, let decrypt */
+
+ ret = _hx509_cert_private_decrypt(context,
+ &ri->encryptedKey,
+ &ri->keyEncryptionAlgorithm.algorithm,
+ cert, &key);
+
+ hx509_cert_free(cert);
+ if (ret == 0)
+ break; /* succuessfully decrypted cert */
+ cert = NULL;
+ ret2 = unparse_CMSIdentifier(context, &ri->rid, &str);
+ if (ret2 == 0) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to decrypt with %s", str);
+ free(str);
+ }
+ }
+
+ if (!matched) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, 0, ret,
+ "No private key matched any certificate");
+ goto out;
+ }
+
+ if (cert == NULL) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "No private key decrypted the transfer key");
+ goto out;
+ }
+
+ ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy EnvelopedData content oid");
+ goto out;
+ }
+
+ ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ if (ai->parameters) {
+ params_data.data = ai->parameters->data;
+ params_data.length = ai->parameters->length;
+ params = &params_data;
+ } else
+ params = NULL;
+
+ {
+ hx509_crypto crypto;
+
+ ret = hx509_crypto_init(context, NULL, &ai->algorithm, &crypto);
+ if (ret)
+ goto out;
+
+ if (flags & HX509_CMS_UE_ALLOW_WEAK)
+ hx509_crypto_allow_weak(crypto);
+
+ if (params) {
+ ret = hx509_crypto_set_params(context, crypto, params, &ivec);
+ if (ret) {
+ hx509_crypto_destroy(crypto);
+ goto out;
+ }
+ }
+
+ ret = hx509_crypto_set_key_data(crypto, key.data, key.length);
+ if (ret) {
+ hx509_crypto_destroy(crypto);
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set key for decryption "
+ "of EnvelopedData");
+ goto out;
+ }
+
+ ret = hx509_crypto_decrypt(crypto,
+ enccontent->data,
+ enccontent->length,
+ ivec.length ? &ivec : NULL,
+ content);
+ hx509_crypto_destroy(crypto);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decrypt EnvelopedData");
+ goto out;
+ }
+ }
+
+out:
+
+ free_EnvelopedData(&ed);
+ der_free_octet_string(&key);
+ if (ivec.length)
+ der_free_octet_string(&ivec);
+ if (ret) {
+ der_free_oid(contentType);
+ der_free_octet_string(content);
+ }
+
+ return ret;
+}
+
+/**
+ * Encrypt end encode EnvelopedData.
+ *
+ * Encrypt and encode EnvelopedData. The data is encrypted with a
+ * random key and the the random key is encrypted with the
+ * certificates private key. This limits what private key type can be
+ * used to RSA.
+ *
+ * @param context A hx509 context.
+ * @param flags flags to control the behavior.
+ * - HX509_CMS_EV_NO_KU_CHECK - Don't check KU on certificate
+ * - HX509_CMS_EV_ALLOW_WEAK - Allow weak crytpo
+ * - HX509_CMS_EV_ID_NAME - prefer issuer name and serial number
+ * @param cert Certificate to encrypt the EnvelopedData encryption key
+ * with.
+ * @param data pointer the data to encrypt.
+ * @param length length of the data that data point to.
+ * @param encryption_type Encryption cipher to use for the bulk data,
+ * use NULL to get default.
+ * @param contentType type of the data that is encrypted
+ * @param content the output of the function,
+ * free with der_free_octet_string().
+ *
+ * @return an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_envelope_1(hx509_context context,
+ int flags,
+ hx509_cert cert,
+ const void *data,
+ size_t length,
+ const heim_oid *encryption_type,
+ const heim_oid *contentType,
+ heim_octet_string *content)
+{
+ KeyTransRecipientInfo *ri;
+ heim_octet_string ivec;
+ heim_octet_string key;
+ hx509_crypto crypto = NULL;
+ int ret, cmsidflag;
+ EnvelopedData ed;
+ size_t size;
+
+ memset(&ivec, 0, sizeof(ivec));
+ memset(&key, 0, sizeof(key));
+ memset(&ed, 0, sizeof(ed));
+ memset(content, 0, sizeof(*content));
+
+ if (encryption_type == NULL)
+ encryption_type = &asn1_oid_id_aes_256_cbc;
+
+ if ((flags & HX509_CMS_EV_NO_KU_CHECK) == 0) {
+ ret = _hx509_check_key_usage(context, cert, 1 << 2, TRUE);
+ if (ret)
+ goto out;
+ }
+
+ ret = hx509_crypto_init(context, NULL, encryption_type, &crypto);
+ if (ret)
+ goto out;
+
+ if (flags & HX509_CMS_EV_ALLOW_WEAK)
+ hx509_crypto_allow_weak(crypto);
+
+ ret = hx509_crypto_set_random_key(crypto, &key);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Create random key for EnvelopedData content");
+ goto out;
+ }
+
+ ret = hx509_crypto_random_iv(crypto, &ivec);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to create a random iv");
+ goto out;
+ }
+
+ ret = hx509_crypto_encrypt(crypto,
+ data,
+ length,
+ &ivec,
+ &ed.encryptedContentInfo.encryptedContent);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to encrypt EnvelopedData content");
+ goto out;
+ }
+
+ {
+ AlgorithmIdentifier *enc_alg;
+ enc_alg = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ ret = der_copy_oid(encryption_type, &enc_alg->algorithm);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set crypto oid "
+ "for EnvelopedData");
+ goto out;
+ }
+ ALLOC(enc_alg->parameters, 1);
+ if (enc_alg->parameters == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to allocate crypto parameters "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ret = hx509_crypto_get_params(context,
+ crypto,
+ &ivec,
+ enc_alg->parameters);
+ if (ret) {
+ goto out;
+ }
+ }
+
+ ALLOC_SEQ(&ed.recipientInfos, 1);
+ if (ed.recipientInfos.val == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to allocate recipients info "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ri = &ed.recipientInfos.val[0];
+
+ if (flags & HX509_CMS_EV_ID_NAME) {
+ ri->version = 0;
+ cmsidflag = CMS_ID_NAME;
+ } else {
+ ri->version = 2;
+ cmsidflag = CMS_ID_SKI;
+ }
+
+ ret = fill_CMSIdentifier(cert, cmsidflag, &ri->rid);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set CMS identifier info "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ret = hx509_cert_public_encrypt(context,
+ &key, cert,
+ &ri->keyEncryptionAlgorithm.algorithm,
+ &ri->encryptedKey);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to encrypt transport key for "
+ "EnvelopedData");
+ goto out;
+ }
+
+ /*
+ *
+ */
+
+ ed.version = 0;
+ ed.originatorInfo = NULL;
+
+ ret = der_copy_oid(contentType, &ed.encryptedContentInfo.contentType);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy content oid for "
+ "EnvelopedData");
+ goto out;
+ }
+
+ ed.unprotectedAttrs = NULL;
+
+ ASN1_MALLOC_ENCODE(EnvelopedData, content->data, content->length,
+ &ed, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to encode EnvelopedData");
+ goto out;
+ }
+ if (size != content->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+out:
+ if (crypto)
+ hx509_crypto_destroy(crypto);
+ if (ret)
+ der_free_octet_string(content);
+ der_free_octet_string(&key);
+ der_free_octet_string(&ivec);
+ free_EnvelopedData(&ed);
+
+ return ret;
+}
+
+static int
+any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs)
+{
+ int ret;
+ size_t i;
+
+ if (sd->certificates == NULL)
+ return 0;
+
+ for (i = 0; i < sd->certificates->len; i++) {
+ heim_error_t error;
+ hx509_cert c;
+
+ c = hx509_cert_init_data(context,
+ sd->certificates->val[i].data,
+ sd->certificates->val[i].length,
+ &error);
+ if (c == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
+ return ret;
+ }
+ ret = hx509_certs_add(context, certs, c);
+ hx509_cert_free(c);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const Attribute *
+find_attribute(const CMSAttributes *attr, const heim_oid *oid)
+{
+ size_t i;
+ for (i = 0; i < attr->len; i++)
+ if (der_heim_oid_cmp(&attr->val[i].type, oid) == 0)
+ return &attr->val[i];
+ return NULL;
+}
+
+/**
+ * Decode SignedData and verify that the signature is correct.
+ *
+ * @param context A hx509 context.
+ * @param ctx a hx509 verify context.
+ * @param flags to control the behaivor of the function.
+ * - HX509_CMS_VS_NO_KU_CHECK - Don't check KeyUsage
+ * - HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH - allow oid mismatch
+ * - HX509_CMS_VS_ALLOW_ZERO_SIGNER - no signer, see below.
+ * @param data pointer to CMS SignedData encoded data.
+ * @param length length of the data that data point to.
+ * @param signedContent external data used for signature.
+ * @param pool certificate pool to build certificates paths.
+ * @param contentType free with der_free_oid().
+ * @param content the output of the function, free with
+ * der_free_octet_string().
+ * @param signer_certs list of the cerficates used to sign this
+ * request, free with hx509_certs_free().
+ *
+ * @return an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_verify_signed(hx509_context context,
+ hx509_verify_ctx ctx,
+ unsigned int flags,
+ const void *data,
+ size_t length,
+ const heim_octet_string *signedContent,
+ hx509_certs pool,
+ heim_oid *contentType,
+ heim_octet_string *content,
+ hx509_certs *signer_certs)
+{
+ unsigned int verify_flags;
+
+ return hx509_cms_verify_signed_ext(context,
+ ctx,
+ flags,
+ data,
+ length,
+ signedContent,
+ pool,
+ contentType,
+ content,
+ signer_certs,
+ &verify_flags);
+}
+
+/**
+ * Decode SignedData and verify that the signature is correct.
+ *
+ * @param context A hx509 context.
+ * @param ctx a hx509 verify context.
+ * @param flags to control the behaivor of the function.
+ * - HX509_CMS_VS_NO_KU_CHECK - Don't check KeyUsage
+ * - HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH - allow oid mismatch
+ * - HX509_CMS_VS_ALLOW_ZERO_SIGNER - no signer, see below.
+ * @param data pointer to CMS SignedData encoded data.
+ * @param length length of the data that data point to.
+ * @param signedContent external data used for signature.
+ * @param pool certificate pool to build certificates paths.
+ * @param contentType free with der_free_oid().
+ * @param content the output of the function, free with
+ * der_free_octet_string().
+ * @param signer_certs list of the cerficates used to sign this
+ * request, free with hx509_certs_free().
+ * @param verify_flags flags indicating whether the certificate
+ * was verified or not
+ *
+ * @return an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_verify_signed_ext(hx509_context context,
+ hx509_verify_ctx ctx,
+ unsigned int flags,
+ const void *data,
+ size_t length,
+ const heim_octet_string *signedContent,
+ hx509_certs pool,
+ heim_oid *contentType,
+ heim_octet_string *content,
+ hx509_certs *signer_certs,
+ unsigned int *verify_flags)
+{
+ SignerInfo *signer_info;
+ hx509_cert cert = NULL;
+ hx509_certs certs = NULL;
+ SignedData sd;
+ size_t size;
+ int ret, found_valid_sig;
+ size_t i;
+
+ *signer_certs = NULL;
+ *verify_flags = 0;
+
+ content->data = NULL;
+ content->length = 0;
+ contentType->length = 0;
+ contentType->components = NULL;
+
+ memset(&sd, 0, sizeof(sd));
+
+ ret = decode_SignedData(data, length, &sd, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode SignedData");
+ goto out;
+ }
+
+ if (sd.encapContentInfo.eContent == NULL && signedContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "No content data in SignedData");
+ goto out;
+ }
+ if (sd.encapContentInfo.eContent && signedContent) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "Both external and internal SignedData");
+ goto out;
+ }
+
+ if (sd.encapContentInfo.eContent)
+ ret = der_copy_octet_string(sd.encapContentInfo.eContent, content);
+ else
+ ret = der_copy_octet_string(signedContent, content);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ret = hx509_certs_init(context, "MEMORY:cms-cert-buffer",
+ 0, NULL, &certs);
+ if (ret)
+ goto out;
+
+ ret = hx509_certs_init(context, "MEMORY:cms-signer-certs",
+ 0, NULL, signer_certs);
+ if (ret)
+ goto out;
+
+ /* XXX Check CMS version */
+
+ ret = any_to_certs(context, &sd, certs);
+ if (ret)
+ goto out;
+
+ if (pool) {
+ ret = hx509_certs_merge(context, certs, pool);
+ if (ret)
+ goto out;
+ }
+
+ for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) {
+ heim_octet_string signed_data = { 0, 0 };
+ const heim_oid *match_oid;
+ heim_oid decode_oid;
+
+ signer_info = &sd.signerInfos.val[i];
+ match_oid = NULL;
+
+ if (signer_info->signature.length == 0) {
+ ret = HX509_CMS_MISSING_SIGNER_DATA;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo %d in SignedData "
+ "missing sigature", i);
+ continue;
+ }
+
+ ret = find_CMSIdentifier(context, &signer_info->sid, certs,
+ _hx509_verify_get_time(ctx), &cert,
+ HX509_QUERY_KU_DIGITALSIGNATURE);
+ if (ret) {
+ /**
+ * If HX509_CMS_VS_NO_KU_CHECK is set, allow more liberal
+ * search for matching certificates by not considering
+ * KeyUsage bits on the certificates.
+ */
+ if ((flags & HX509_CMS_VS_NO_KU_CHECK) == 0)
+ continue;
+
+ ret = find_CMSIdentifier(context, &signer_info->sid, certs,
+ _hx509_verify_get_time(ctx), &cert,
+ 0);
+ if (ret)
+ continue;
+
+ }
+
+ if (signer_info->signedAttrs) {
+ const Attribute *attr;
+
+ CMSAttributes sa;
+ heim_octet_string os;
+
+ sa.val = signer_info->signedAttrs->val;
+ sa.len = signer_info->signedAttrs->len;
+
+ /* verify that sigature exists */
+ attr = find_attribute(&sa, &asn1_oid_id_pkcs9_messageDigest);
+ if (attr == NULL) {
+ ret = HX509_CRYPTO_SIGNATURE_MISSING;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo have signed attributes "
+ "but messageDigest (signature) "
+ "is missing");
+ goto next_sigature;
+ }
+ if (attr->value.len != 1) {
+ ret = HX509_CRYPTO_SIGNATURE_MISSING;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo have more then one "
+ "messageDigest (signature)");
+ goto next_sigature;
+ }
+
+ ret = decode_MessageDigest(attr->value.val[0].data,
+ attr->value.val[0].length,
+ &os,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode "
+ "messageDigest (signature)");
+ goto next_sigature;
+ }
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &signer_info->digestAlgorithm,
+ content,
+ &os);
+ der_free_octet_string(&os);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify messageDigest");
+ goto next_sigature;
+ }
+
+ /*
+ * Fetch content oid inside signedAttrs or set it to
+ * id-pkcs7-data.
+ */
+ attr = find_attribute(&sa, &asn1_oid_id_pkcs9_contentType);
+ if (attr == NULL) {
+ match_oid = &asn1_oid_id_pkcs7_data;
+ } else {
+ if (attr->value.len != 1) {
+ ret = HX509_CMS_DATA_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret,
+ "More then one oid in signedAttrs");
+ goto next_sigature;
+
+ }
+ ret = decode_ContentType(attr->value.val[0].data,
+ attr->value.val[0].length,
+ &decode_oid,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode "
+ "oid in signedAttrs");
+ goto next_sigature;
+ }
+ match_oid = &decode_oid;
+ }
+
+ ASN1_MALLOC_ENCODE(CMSAttributes,
+ signed_data.data,
+ signed_data.length,
+ &sa,
+ &size, ret);
+ if (ret) {
+ if (match_oid == &decode_oid)
+ der_free_oid(&decode_oid);
+ hx509_clear_error_string(context);
+ goto next_sigature;
+ }
+ if (size != signed_data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ } else {
+ signed_data.data = content->data;
+ signed_data.length = content->length;
+ match_oid = &asn1_oid_id_pkcs7_data;
+ }
+
+ /**
+ * If HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH, allow
+ * encapContentInfo mismatch with the oid in signedAttributes
+ * (or if no signedAttributes where use, pkcs7-data oid).
+ * This is only needed to work with broken CMS implementations
+ * that doesn't follow CMS signedAttributes rules.
+ */
+
+ if (der_heim_oid_cmp(match_oid, &sd.encapContentInfo.eContentType) &&
+ (flags & HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH) == 0) {
+ ret = HX509_CMS_DATA_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret,
+ "Oid in message mismatch from the expected");
+ }
+ if (match_oid == &decode_oid)
+ der_free_oid(&decode_oid);
+
+ if (ret == 0) {
+ ret = hx509_verify_signature(context,
+ cert,
+ &signer_info->signatureAlgorithm,
+ &signed_data,
+ &signer_info->signature);
+ if (ret)
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify signature in "
+ "CMS SignedData");
+ }
+ if (signed_data.data != NULL && content->data != signed_data.data) {
+ free(signed_data.data);
+ signed_data.data = NULL;
+ }
+ if (ret)
+ goto next_sigature;
+
+ /**
+ * If HX509_CMS_VS_NO_VALIDATE flags is set, return the signer
+ * certificate unconditionally but do not set HX509_CMS_VSE_VALIDATED.
+ */
+ ret = hx509_verify_path(context, ctx, cert, certs);
+ if (ret == 0 || (flags & HX509_CMS_VS_NO_VALIDATE)) {
+ if (ret == 0)
+ *verify_flags |= HX509_CMS_VSE_VALIDATED;
+
+ ret = hx509_certs_add(context, *signer_certs, cert);
+ if (ret == 0)
+ found_valid_sig++;
+ }
+
+ next_sigature:
+ if (cert)
+ hx509_cert_free(cert);
+ cert = NULL;
+ }
+ /**
+ * If HX509_CMS_VS_ALLOW_ZERO_SIGNER is set, allow empty
+ * SignerInfo (no signatures). If SignedData have no signatures,
+ * the function will return 0 with signer_certs set to NULL. Zero
+ * signers is allowed by the standard, but since its only useful
+ * in corner cases, it make into a flag that the caller have to
+ * turn on.
+ */
+ if (sd.signerInfos.len == 0 && (flags & HX509_CMS_VS_ALLOW_ZERO_SIGNER)) {
+ if (*signer_certs)
+ hx509_certs_free(signer_certs);
+ } else if (found_valid_sig == 0) {
+ if (ret == 0) {
+ ret = HX509_CMS_SIGNER_NOT_FOUND;
+ hx509_set_error_string(context, 0, ret,
+ "No signers where found");
+ }
+ goto out;
+ }
+
+ ret = der_copy_oid(&sd.encapContentInfo.eContentType, contentType);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+out:
+ free_SignedData(&sd);
+ if (certs)
+ hx509_certs_free(&certs);
+ if (ret) {
+ if (content->data)
+ der_free_octet_string(content);
+ if (*signer_certs)
+ hx509_certs_free(signer_certs);
+ der_free_oid(contentType);
+ der_free_octet_string(content);
+ }
+
+ return ret;
+}
+
+static int
+add_one_attribute(Attribute **attr,
+ unsigned int *len,
+ const heim_oid *oid,
+ heim_octet_string *data)
+{
+ void *d;
+ int ret;
+
+ d = realloc(*attr, sizeof((*attr)[0]) * (*len + 1));
+ if (d == NULL)
+ return ENOMEM;
+ (*attr) = d;
+
+ ret = der_copy_oid(oid, &(*attr)[*len].type);
+ if (ret)
+ return ret;
+
+ ALLOC_SEQ(&(*attr)[*len].value, 1);
+ if ((*attr)[*len].value.val == NULL) {
+ der_free_oid(&(*attr)[*len].type);
+ return ENOMEM;
+ }
+
+ (*attr)[*len].value.val[0].data = data->data;
+ (*attr)[*len].value.val[0].length = data->length;
+
+ *len += 1;
+
+ return 0;
+}
+
+/**
+ * Decode SignedData and verify that the signature is correct.
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * @param eContentType the type of the data.
+ * @param data data to sign
+ * @param length length of the data that data point to.
+ * @param digest_alg digest algorithm to use, use NULL to get the
+ * default or the peer determined algorithm.
+ * @param cert certificate to use for sign the data.
+ * @param peer info about the peer the message to send the message to,
+ * like what digest algorithm to use.
+ * @param anchors trust anchors that the client will use, used to
+ * polulate the certificates included in the message
+ * @param pool certificates to use in try to build the path to the
+ * trust anchors.
+ * @param signed_data the output of the function, free with
+ * der_free_octet_string().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_create_signed_1(hx509_context context,
+ int flags,
+ const heim_oid *eContentType,
+ const void *data, size_t length,
+ const AlgorithmIdentifier *digest_alg,
+ hx509_cert cert,
+ hx509_peer_info peer,
+ hx509_certs anchors,
+ hx509_certs pool,
+ heim_octet_string *signed_data)
+{
+ hx509_certs certs;
+ int ret = 0;
+
+ signed_data->data = NULL;
+ signed_data->length = 0;
+
+ ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &certs);
+ if (ret)
+ return ret;
+ ret = hx509_certs_add(context, certs, cert);
+ if (ret)
+ goto out;
+
+ ret = hx509_cms_create_signed(context, flags, eContentType, data, length,
+ digest_alg, certs, peer, anchors, pool,
+ signed_data);
+
+ out:
+ hx509_certs_free(&certs);
+ return ret;
+}
+
+struct sigctx {
+ SignedData sd;
+ const AlgorithmIdentifier *digest_alg;
+ const heim_oid *eContentType;
+ heim_octet_string content;
+ hx509_peer_info peer;
+ int cmsidflag;
+ int leafonly;
+ hx509_certs certs;
+ hx509_certs anchors;
+ hx509_certs pool;
+};
+
+static int HX509_LIB_CALL
+sig_process(hx509_context context, void *ctx, hx509_cert cert)
+{
+ struct sigctx *sigctx = ctx;
+ heim_octet_string buf, sigdata = { 0, NULL };
+ SignerInfo *signer_info = NULL;
+ AlgorithmIdentifier digest;
+ size_t size;
+ void *ptr;
+ int ret;
+ SignedData *sd = &sigctx->sd;
+ hx509_path path;
+
+ memset(&digest, 0, sizeof(digest));
+ memset(&path, 0, sizeof(path));
+
+ if (_hx509_cert_private_key(cert) == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private key missing for signing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ if (sigctx->digest_alg) {
+ ret = copy_AlgorithmIdentifier(sigctx->digest_alg, &digest);
+ if (ret)
+ hx509_clear_error_string(context);
+ } else {
+ ret = hx509_crypto_select(context, HX509_SELECT_DIGEST,
+ _hx509_cert_private_key(cert),
+ sigctx->peer, &digest);
+ }
+ if (ret)
+ goto out;
+
+ /*
+ * Allocate on more signerInfo and do the signature processing
+ */
+
+ ptr = realloc(sd->signerInfos.val,
+ (sd->signerInfos.len + 1) * sizeof(sd->signerInfos.val[0]));
+ if (ptr == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ sd->signerInfos.val = ptr;
+
+ signer_info = &sd->signerInfos.val[sd->signerInfos.len];
+
+ memset(signer_info, 0, sizeof(*signer_info));
+
+ signer_info->version = 1;
+
+ ret = fill_CMSIdentifier(cert, sigctx->cmsidflag, &signer_info->sid);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ signer_info->signedAttrs = NULL;
+ signer_info->unsignedAttrs = NULL;
+
+ ret = copy_AlgorithmIdentifier(&digest, &signer_info->digestAlgorithm);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ /*
+ * If it isn't pkcs7-data send signedAttributes
+ */
+
+ if (der_heim_oid_cmp(sigctx->eContentType, &asn1_oid_id_pkcs7_data) != 0) {
+ CMSAttributes sa;
+ heim_octet_string sig;
+
+ ALLOC(signer_info->signedAttrs, 1);
+ if (signer_info->signedAttrs == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &digest,
+ &sigctx->content,
+ NULL,
+ &sig);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(MessageDigest,
+ buf.data,
+ buf.length,
+ &sig,
+ &size,
+ ret);
+ der_free_octet_string(&sig);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (size != buf.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = add_one_attribute(&signer_info->signedAttrs->val,
+ &signer_info->signedAttrs->len,
+ &asn1_oid_id_pkcs9_messageDigest,
+ &buf);
+ if (ret) {
+ free(buf.data);
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+
+ ASN1_MALLOC_ENCODE(ContentType,
+ buf.data,
+ buf.length,
+ sigctx->eContentType,
+ &size,
+ ret);
+ if (ret)
+ goto out;
+ if (size != buf.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = add_one_attribute(&signer_info->signedAttrs->val,
+ &signer_info->signedAttrs->len,
+ &asn1_oid_id_pkcs9_contentType,
+ &buf);
+ if (ret) {
+ free(buf.data);
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ sa.val = signer_info->signedAttrs->val;
+ sa.len = signer_info->signedAttrs->len;
+
+ ASN1_MALLOC_ENCODE(CMSAttributes,
+ sigdata.data,
+ sigdata.length,
+ &sa,
+ &size,
+ ret);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (size != sigdata.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ } else {
+ sigdata.data = sigctx->content.data;
+ sigdata.length = sigctx->content.length;
+ }
+
+ {
+ AlgorithmIdentifier sigalg;
+
+ ret = hx509_crypto_select(context, HX509_SELECT_PUBLIC_SIG,
+ _hx509_cert_private_key(cert), sigctx->peer,
+ &sigalg);
+ if (ret)
+ goto out;
+
+ ret = _hx509_create_signature(context,
+ _hx509_cert_private_key(cert),
+ &sigalg,
+ &sigdata,
+ &signer_info->signatureAlgorithm,
+ &signer_info->signature);
+ free_AlgorithmIdentifier(&sigalg);
+ if (ret)
+ goto out;
+ }
+
+ sigctx->sd.signerInfos.len++;
+ signer_info = NULL;
+
+ /*
+ * Provide best effort path
+ */
+ if (sigctx->certs) {
+ unsigned int i;
+
+ if (sigctx->pool && sigctx->leafonly == 0) {
+ _hx509_calculate_path(context,
+ HX509_CALCULATE_PATH_NO_ANCHOR,
+ time(NULL),
+ sigctx->anchors,
+ 0,
+ cert,
+ sigctx->pool,
+ &path);
+ } else
+ _hx509_path_append(context, &path, cert);
+
+ for (i = 0; i < path.len; i++) {
+ /* XXX remove dups */
+ ret = hx509_certs_add(context, sigctx->certs, path.val[i]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+ }
+
+ out:
+ if (signer_info)
+ free_SignerInfo(signer_info);
+ if (sigdata.data != sigctx->content.data)
+ der_free_octet_string(&sigdata);
+ _hx509_path_free(&path);
+ free_AlgorithmIdentifier(&digest);
+
+ return ret;
+}
+
+static int HX509_LIB_CALL
+cert_process(hx509_context context, void *ctx, hx509_cert cert)
+{
+ struct sigctx *sigctx = ctx;
+ const unsigned int i = sigctx->sd.certificates->len;
+ void *ptr;
+ int ret;
+
+ ptr = realloc(sigctx->sd.certificates->val,
+ (i + 1) * sizeof(sigctx->sd.certificates->val[0]));
+ if (ptr == NULL)
+ return ENOMEM;
+ sigctx->sd.certificates->val = ptr;
+
+ ret = hx509_cert_binary(context, cert,
+ &sigctx->sd.certificates->val[i]);
+ if (ret == 0)
+ sigctx->sd.certificates->len++;
+
+ return ret;
+}
+
+static int
+cmp_AlgorithmIdentifier(const AlgorithmIdentifier *p, const AlgorithmIdentifier *q)
+{
+ return der_heim_oid_cmp(&p->algorithm, &q->algorithm);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_create_signed(hx509_context context,
+ int flags,
+ const heim_oid *eContentType,
+ const void *data, size_t length,
+ const AlgorithmIdentifier *digest_alg,
+ hx509_certs certs,
+ hx509_peer_info peer,
+ hx509_certs anchors,
+ hx509_certs pool,
+ heim_octet_string *signed_data)
+{
+ unsigned int i, j;
+ hx509_name name;
+ int ret;
+ size_t size;
+ struct sigctx sigctx;
+
+ memset(&sigctx, 0, sizeof(sigctx));
+ memset(&name, 0, sizeof(name));
+
+ if (eContentType == NULL)
+ eContentType = &asn1_oid_id_pkcs7_data;
+
+ sigctx.digest_alg = digest_alg;
+ sigctx.content.data = rk_UNCONST(data);
+ sigctx.content.length = length;
+ sigctx.eContentType = eContentType;
+ sigctx.peer = peer;
+ /**
+ * Use HX509_CMS_SIGNATURE_ID_NAME to preferred use of issuer name
+ * and serial number if possible. Otherwise subject key identifier
+ * will preferred.
+ */
+ if (flags & HX509_CMS_SIGNATURE_ID_NAME)
+ sigctx.cmsidflag = CMS_ID_NAME;
+ else
+ sigctx.cmsidflag = CMS_ID_SKI;
+
+ /**
+ * Use HX509_CMS_SIGNATURE_LEAF_ONLY to only request leaf
+ * certificates to be added to the SignedData.
+ */
+ sigctx.leafonly = (flags & HX509_CMS_SIGNATURE_LEAF_ONLY) ? 1 : 0;
+
+ /**
+ * Use HX509_CMS_NO_CERTS to make the SignedData contain no
+ * certificates, overrides HX509_CMS_SIGNATURE_LEAF_ONLY.
+ */
+
+ if ((flags & HX509_CMS_SIGNATURE_NO_CERTS) == 0) {
+ ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &sigctx.certs);
+ if (ret)
+ return ret;
+ }
+
+ sigctx.anchors = anchors;
+ sigctx.pool = pool;
+
+ sigctx.sd.version = cMSVersion_v3;
+
+ ret = der_copy_oid(eContentType, &sigctx.sd.encapContentInfo.eContentType);
+ if (ret)
+ goto out;
+
+ /**
+ * Use HX509_CMS_SIGNATURE_DETACHED to create detached signatures.
+ */
+ if ((flags & HX509_CMS_SIGNATURE_DETACHED) == 0) {
+ ALLOC(sigctx.sd.encapContentInfo.eContent, 1);
+ if (sigctx.sd.encapContentInfo.eContent == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ sigctx.sd.encapContentInfo.eContent->data = malloc(length);
+ if (sigctx.sd.encapContentInfo.eContent->data == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ memcpy(sigctx.sd.encapContentInfo.eContent->data, data, length);
+ sigctx.sd.encapContentInfo.eContent->length = length;
+ }
+
+ /**
+ * Use HX509_CMS_SIGNATURE_NO_SIGNER to create no sigInfo (no
+ * signatures).
+ */
+ if ((flags & HX509_CMS_SIGNATURE_NO_SIGNER) == 0) {
+ ret = hx509_certs_iter_f(context, certs, sig_process, &sigctx);
+ if (ret)
+ goto out;
+ }
+
+ if (sigctx.sd.signerInfos.len) {
+
+ /*
+ * For each signerInfo, collect all different digest types.
+ */
+ for (i = 0; i < sigctx.sd.signerInfos.len; i++) {
+ AlgorithmIdentifier *di =
+ &sigctx.sd.signerInfos.val[i].digestAlgorithm;
+
+ for (j = 0; j < sigctx.sd.digestAlgorithms.len; j++)
+ if (cmp_AlgorithmIdentifier(di, &sigctx.sd.digestAlgorithms.val[j]) == 0)
+ break;
+ if (j == sigctx.sd.digestAlgorithms.len) {
+ ret = add_DigestAlgorithmIdentifiers(&sigctx.sd.digestAlgorithms, di);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+ }
+ }
+
+ /*
+ * Add certs we think are needed, build as part of sig_process
+ */
+ if (sigctx.certs) {
+ ALLOC(sigctx.sd.certificates, 1);
+ if (sigctx.sd.certificates == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = hx509_certs_iter_f(context, sigctx.certs, cert_process, &sigctx);
+ if (ret)
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(SignedData,
+ signed_data->data, signed_data->length,
+ &sigctx.sd, &size, ret);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (signed_data->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+out:
+ hx509_certs_free(&sigctx.certs);
+ free_SignedData(&sigctx.sd);
+
+ return ret;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cms_decrypt_encrypted(hx509_context context,
+ hx509_lock lock,
+ const void *data,
+ size_t length,
+ heim_oid *contentType,
+ heim_octet_string *content)
+{
+ heim_octet_string cont;
+ CMSEncryptedData ed;
+ AlgorithmIdentifier *ai;
+ int ret;
+
+ memset(content, 0, sizeof(*content));
+ memset(&cont, 0, sizeof(cont));
+
+ ret = decode_CMSEncryptedData(data, length, &ed, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode CMSEncryptedData");
+ return ret;
+ }
+
+ if (ed.encryptedContentInfo.encryptedContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "No content in EncryptedData");
+ goto out;
+ }
+
+ ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ if (ai->parameters == NULL) {
+ ret = HX509_ALG_NOT_SUPP;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = _hx509_pbe_decrypt(context,
+ lock,
+ ai,
+ ed.encryptedContentInfo.encryptedContent,
+ &cont);
+ if (ret)
+ goto out;
+
+ *content = cont;
+
+out:
+ if (ret) {
+ if (cont.data)
+ free(cont.data);
+ }
+ free_CMSEncryptedData(&ed);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/hx509/collector.c b/third_party/heimdal/lib/hx509/collector.c
new file mode 100644
index 0000000..7b46809
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/collector.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+struct private_key {
+ AlgorithmIdentifier alg;
+ hx509_private_key private_key;
+ heim_octet_string localKeyId;
+};
+
+struct hx509_collector {
+ hx509_lock lock;
+ hx509_certs unenvelop_certs;
+ hx509_certs certs;
+ struct {
+ struct private_key **data;
+ size_t len;
+ } val;
+};
+
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_collector_alloc(hx509_context context, hx509_lock lock, struct hx509_collector **collector)
+{
+ struct hx509_collector *c;
+ int ret;
+
+ *collector = NULL;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ c->lock = lock;
+
+ ret = hx509_certs_init(context, "MEMORY:collector-unenvelop-cert",
+ 0,NULL, &c->unenvelop_certs);
+ if (ret) {
+ free(c);
+ return ret;
+ }
+ c->val.data = NULL;
+ c->val.len = 0;
+ ret = hx509_certs_init(context, "MEMORY:collector-tmp-store",
+ 0, NULL, &c->certs);
+ if (ret) {
+ hx509_certs_free(&c->unenvelop_certs);
+ free(c);
+ return ret;
+ }
+
+ *collector = c;
+ return 0;
+}
+
+HX509_LIB_FUNCTION hx509_lock HX509_LIB_CALL
+_hx509_collector_get_lock(struct hx509_collector *c)
+{
+ return c->lock;
+}
+
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_collector_certs_add(hx509_context context,
+ struct hx509_collector *c,
+ hx509_cert cert)
+{
+ return hx509_certs_add(context, c->certs, cert);
+}
+
+static void
+free_private_key(struct private_key *key)
+{
+ free_AlgorithmIdentifier(&key->alg);
+ if (key->private_key)
+ hx509_private_key_free(&key->private_key);
+ der_free_octet_string(&key->localKeyId);
+ free(key);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_collector_private_key_add(hx509_context context,
+ struct hx509_collector *c,
+ const AlgorithmIdentifier *alg,
+ hx509_private_key private_key,
+ const heim_octet_string *key_data,
+ const heim_octet_string *localKeyId)
+{
+ struct private_key *key;
+ void *d;
+ int ret;
+
+ key = calloc(1, sizeof(*key));
+ if (key == NULL)
+ return ENOMEM;
+
+ d = realloc(c->val.data, (c->val.len + 1) * sizeof(c->val.data[0]));
+ if (d == NULL) {
+ free(key);
+ hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
+ return ENOMEM;
+ }
+ c->val.data = d;
+
+ ret = copy_AlgorithmIdentifier(alg, &key->alg);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy "
+ "AlgorithmIdentifier");
+ goto out;
+ }
+ if (private_key) {
+ key->private_key = private_key;
+ } else {
+ ret = hx509_parse_private_key(context, alg,
+ key_data->data, key_data->length,
+ HX509_KEY_FORMAT_DER,
+ &key->private_key);
+ if (ret)
+ goto out;
+ }
+ if (localKeyId) {
+ ret = der_copy_octet_string(localKeyId, &key->localKeyId);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy localKeyId");
+ goto out;
+ }
+ } else
+ memset(&key->localKeyId, 0, sizeof(key->localKeyId));
+
+ c->val.data[c->val.len] = key;
+ c->val.len++;
+
+out:
+ if (ret)
+ free_private_key(key);
+
+ return ret;
+}
+
+static int
+match_localkeyid(hx509_context context,
+ struct private_key *value,
+ hx509_certs certs)
+{
+ hx509_cert cert;
+ hx509_query q;
+ int ret;
+
+ if (value->localKeyId.length == 0) {
+ hx509_set_error_string(context, 0, HX509_LOCAL_ATTRIBUTE_MISSING,
+ "No local key attribute on private key");
+ return HX509_LOCAL_ATTRIBUTE_MISSING;
+ }
+
+ _hx509_query_clear(&q);
+ q.match |= HX509_QUERY_MATCH_LOCAL_KEY_ID;
+
+ q.local_key_id = &value->localKeyId;
+
+ ret = hx509_certs_find(context, certs, &q, &cert);
+ if (ret == 0 && cert == NULL)
+ ret = HX509_CERT_NOT_FOUND;
+ if (ret == 0) {
+ if (value->private_key)
+ _hx509_cert_assign_key(cert, value->private_key);
+ hx509_cert_free(cert);
+ }
+ return ret;
+}
+
+static int
+match_keys(hx509_context context, struct private_key *value, hx509_certs certs)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret, found = HX509_CERT_NOT_FOUND;
+
+ if (value->private_key == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "No private key to compare with");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ c = NULL;
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL)
+ break;
+ if (_hx509_cert_private_key(c)) {
+ hx509_cert_free(c);
+ continue;
+ }
+
+ ret = _hx509_match_keys(c, value->private_key);
+ if (ret) {
+ _hx509_cert_assign_key(c, value->private_key);
+ hx509_cert_free(c);
+ found = 0;
+ break;
+ }
+ hx509_cert_free(c);
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+
+ if (found)
+ hx509_clear_error_string(context);
+
+ return found;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_collector_collect_certs(hx509_context context,
+ struct hx509_collector *c,
+ hx509_certs *ret_certs)
+{
+ hx509_certs certs;
+ int ret;
+ size_t i;
+
+ *ret_certs = NULL;
+
+ ret = hx509_certs_init(context, "MEMORY:collector-store", 0, NULL, &certs);
+ if (ret)
+ return ret;
+
+ ret = hx509_certs_merge(context, certs, c->certs);
+ if (ret) {
+ hx509_certs_free(&certs);
+ return ret;
+ }
+
+ for (i = 0; i < c->val.len; i++) {
+ ret = match_localkeyid(context, c->val.data[i], certs);
+ if (ret == 0)
+ continue;
+ ret = match_keys(context, c->val.data[i], certs);
+ if (ret == 0)
+ continue;
+ }
+
+ *ret_certs = certs;
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_collector_collect_private_keys(hx509_context context,
+ struct hx509_collector *c,
+ hx509_private_key **keys)
+{
+ size_t i, nkeys;
+
+ *keys = NULL;
+
+ for (i = 0, nkeys = 0; i < c->val.len; i++)
+ if (c->val.data[i]->private_key)
+ nkeys++;
+
+ *keys = calloc(nkeys + 1, sizeof(**keys));
+ if (*keys == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory");
+ return ENOMEM;
+ }
+
+ for (i = 0, nkeys = 0; i < c->val.len; i++) {
+ if (c->val.data[i]->private_key) {
+ (*keys)[nkeys++] = c->val.data[i]->private_key;
+ c->val.data[i]->private_key = NULL;
+ }
+ }
+ (*keys)[nkeys] = NULL;
+
+ return 0;
+}
+
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_collector_free(struct hx509_collector *c)
+{
+ size_t i;
+
+ if (c->unenvelop_certs)
+ hx509_certs_free(&c->unenvelop_certs);
+ if (c->certs)
+ hx509_certs_free(&c->certs);
+ for (i = 0; i < c->val.len; i++)
+ free_private_key(c->val.data[i]);
+ if (c->val.data)
+ free(c->val.data);
+ free(c);
+}
diff --git a/third_party/heimdal/lib/hx509/crypto-ec.c b/third_party/heimdal/lib/hx509/crypto-ec.c
new file mode 100644
index 0000000..46e6cd8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/crypto-ec.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/rsa.h>
+#include <openssl/bn.h>
+#include <openssl/objects.h>
+#define HEIM_NO_CRYPTO_HDRS
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+
+#include "hx_locl.h"
+
+extern const AlgorithmIdentifier _hx509_signature_sha512_data;
+extern const AlgorithmIdentifier _hx509_signature_sha384_data;
+extern const AlgorithmIdentifier _hx509_signature_sha256_data;
+extern const AlgorithmIdentifier _hx509_signature_sha1_data;
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_private_eckey_free(void *eckey)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ EC_KEY_free(eckey);
+#endif
+}
+
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+static int
+heim_oid2ecnid(heim_oid *oid)
+{
+ /*
+ * Now map to openssl OID fun
+ */
+
+ if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP256R1) == 0)
+ return NID_X9_62_prime256v1;
+#ifdef NID_secp521r1
+ else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP521R1) == 0)
+ return NID_secp521r1;
+#endif
+#ifdef NID_secp384r1
+ else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP384R1) == 0)
+ return NID_secp384r1;
+#endif
+#ifdef NID_secp160r1
+ else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R1) == 0)
+ return NID_secp160r1;
+#endif
+#ifdef NID_secp160r2
+ else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R2) == 0)
+ return NID_secp160r2;
+#endif
+
+ return NID_undef;
+}
+
+static int
+parse_ECParameters(hx509_context context,
+ heim_octet_string *parameters, int *nid)
+{
+ ECParameters ecparam;
+ size_t size;
+ int ret;
+
+ if (parameters == NULL) {
+ ret = HX509_PARSING_KEY_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "EC parameters missing");
+ return ret;
+ }
+
+ ret = decode_ECParameters(parameters->data, parameters->length,
+ &ecparam, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode EC parameters");
+ return ret;
+ }
+
+ if (ecparam.element != choice_ECParameters_namedCurve) {
+ free_ECParameters(&ecparam);
+ hx509_set_error_string(context, 0, ret,
+ "EC parameters is not a named curve");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ *nid = heim_oid2ecnid(&ecparam.u.namedCurve);
+ free_ECParameters(&ecparam);
+ if (*nid == NID_undef) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to find matcing NID for EC curve");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+static int
+ecdsa_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const AlgorithmIdentifier *digest_alg;
+ const SubjectPublicKeyInfo *spi;
+ heim_octet_string digest;
+ int ret;
+ EC_KEY *key = NULL;
+ int groupnid;
+ EC_GROUP *group;
+ const unsigned char *p;
+ long len;
+
+ digest_alg = sig_alg->digest_alg;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ digest_alg,
+ data,
+ NULL,
+ &digest);
+ if (ret)
+ return ret;
+
+ /* set up EC KEY */
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ if (der_heim_oid_cmp(&spi->algorithm.algorithm, ASN1_OID_ID_ECPUBLICKEY) != 0)
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+
+ /*
+ * Find the group id
+ */
+
+ ret = parse_ECParameters(context, spi->algorithm.parameters, &groupnid);
+ if (ret) {
+ der_free_octet_string(&digest);
+ return ret;
+ }
+
+ /*
+ * Create group, key, parse key
+ */
+
+ key = EC_KEY_new();
+ group = EC_GROUP_new_by_curve_name(groupnid);
+ EC_KEY_set_group(key, group);
+ EC_GROUP_free(group);
+
+ p = spi->subjectPublicKey.data;
+ len = spi->subjectPublicKey.length / 8;
+
+ if (o2i_ECPublicKey(&key, &p, len) == NULL) {
+ EC_KEY_free(key);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ ret = ECDSA_verify(-1, digest.data, digest.length,
+ sig->data, sig->length, key);
+ der_free_octet_string(&digest);
+ EC_KEY_free(key);
+ if (ret != 1) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+ecdsa_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ const AlgorithmIdentifier *digest_alg;
+ heim_octet_string indata;
+ const heim_oid *sig_oid;
+ unsigned int siglen;
+ int ret;
+
+ if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0)
+ _hx509_abort("internal error passing private key to wrong ops");
+
+ sig_oid = sig_alg->sig_oid;
+ digest_alg = sig_alg->digest_alg;
+
+ if (signatureAlgorithm) {
+ ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid,
+ "\x05\x00", 2);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ }
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ digest_alg,
+ data,
+ NULL,
+ &indata);
+ if (ret)
+ goto error;
+
+ sig->length = ECDSA_size(signer->private_key.ecdsa);
+ sig->data = malloc(sig->length);
+ if (sig->data == NULL) {
+ der_free_octet_string(&indata);
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto error;
+ }
+
+ siglen = sig->length;
+
+ ret = ECDSA_sign(-1, indata.data, indata.length,
+ sig->data, &siglen, signer->private_key.ecdsa);
+ der_free_octet_string(&indata);
+ if (ret != 1) {
+ ret = HX509_CMS_FAILED_CREATE_SIGATURE;
+ hx509_set_error_string(context, 0, ret,
+ "ECDSA sign failed: %d", ret);
+ goto error;
+ }
+ if (siglen > sig->length)
+ _hx509_abort("ECDSA signature prelen longer the output len");
+
+ sig->length = siglen;
+
+ return 0;
+ error:
+ if (signatureAlgorithm)
+ free_AlgorithmIdentifier(signatureAlgorithm);
+ return ret;
+}
+
+static int
+ecdsa_available(const hx509_private_key signer,
+ const AlgorithmIdentifier *sig_alg)
+{
+ const struct signature_alg *sig;
+ const EC_GROUP *group;
+ BN_CTX *bnctx = NULL;
+ BIGNUM *order = NULL;
+ int ret = 0;
+
+ if (der_heim_oid_cmp(signer->ops->key_oid, &asn1_oid_id_ecPublicKey) != 0)
+ _hx509_abort("internal error passing private key to wrong ops");
+
+ sig = _hx509_find_sig_alg(&sig_alg->algorithm);
+
+ if (sig == NULL || sig->digest_size == 0)
+ return 0;
+
+ group = EC_KEY_get0_group(signer->private_key.ecdsa);
+ if (group == NULL)
+ return 0;
+
+ bnctx = BN_CTX_new();
+ order = BN_new();
+ if (order == NULL)
+ goto err;
+
+ if (EC_GROUP_get_order(group, order, bnctx) != 1)
+ goto err;
+
+#if 0
+ /* If anything, require a digest at least as wide as the EC key size */
+ if (BN_num_bytes(order) > sig->digest_size)
+#endif
+ ret = 1;
+ err:
+ if (bnctx)
+ BN_CTX_free(bnctx);
+ if (order)
+ BN_clear_free(order);
+
+ return ret;
+}
+
+static int
+ecdsa_private_key2SPKI(hx509_context context,
+ hx509_private_key private_key,
+ SubjectPublicKeyInfo *spki)
+{
+ memset(spki, 0, sizeof(*spki));
+ return ENOMEM;
+}
+
+static int
+ecdsa_private_key_export(hx509_context context,
+ const hx509_private_key key,
+ hx509_key_format_t format,
+ heim_octet_string *data)
+{
+ return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED;
+}
+
+static int
+ecdsa_private_key_import(hx509_context context,
+ const AlgorithmIdentifier *keyai,
+ const void *data,
+ size_t len,
+ hx509_key_format_t format,
+ hx509_private_key private_key)
+{
+ const unsigned char *p = data;
+ EC_KEY **pkey = NULL;
+ EC_KEY *key;
+
+ if (keyai->parameters) {
+ EC_GROUP *group;
+ int groupnid;
+ int ret;
+
+ ret = parse_ECParameters(context, keyai->parameters, &groupnid);
+ if (ret)
+ return ret;
+
+ key = EC_KEY_new();
+ if (key == NULL)
+ return ENOMEM;
+
+ group = EC_GROUP_new_by_curve_name(groupnid);
+ if (group == NULL) {
+ EC_KEY_free(key);
+ return ENOMEM;
+ }
+ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+ if (EC_KEY_set_group(key, group) == 0) {
+ EC_KEY_free(key);
+ EC_GROUP_free(group);
+ return ENOMEM;
+ }
+ EC_GROUP_free(group);
+ pkey = &key;
+ }
+
+ switch (format) {
+ case HX509_KEY_FORMAT_DER:
+
+ private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len);
+ if (private_key->private_key.ecdsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to parse EC private key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+ private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256;
+ break;
+
+ default:
+ return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED;
+ }
+
+ return 0;
+}
+
+static int
+ecdsa_generate_private_key(hx509_context context,
+ struct hx509_generate_private_context *ctx,
+ hx509_private_key private_key)
+{
+ return ENOMEM;
+}
+
+static BIGNUM *
+ecdsa_get_internal(hx509_context context,
+ hx509_private_key key,
+ const char *type)
+{
+ return NULL;
+}
+
+static const unsigned ecPublicKey[] ={ 1, 2, 840, 10045, 2, 1 };
+const AlgorithmIdentifier _hx509_signature_ecPublicKey = {
+ { 6, rk_UNCONST(ecPublicKey) }, NULL
+};
+
+static const unsigned ecdsa_with_sha256_oid[] ={ 1, 2, 840, 10045, 4, 3, 2 };
+const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha256_data = {
+ { 7, rk_UNCONST(ecdsa_with_sha256_oid) }, NULL
+};
+
+static const unsigned ecdsa_with_sha384_oid[] ={ 1, 2, 840, 10045, 4, 3, 3 };
+const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha384_data = {
+ { 7, rk_UNCONST(ecdsa_with_sha384_oid) }, NULL
+};
+
+static const unsigned ecdsa_with_sha512_oid[] ={ 1, 2, 840, 10045, 4, 3, 4 };
+const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha512_data = {
+ { 7, rk_UNCONST(ecdsa_with_sha512_oid) }, NULL
+};
+
+static const unsigned ecdsa_with_sha1_oid[] ={ 1, 2, 840, 10045, 4, 1 };
+const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha1_data = {
+ { 6, rk_UNCONST(ecdsa_with_sha1_oid) }, NULL
+};
+
+hx509_private_key_ops ecdsa_private_key_ops = {
+ "EC PRIVATE KEY",
+ ASN1_OID_ID_ECPUBLICKEY,
+ ecdsa_available,
+ ecdsa_private_key2SPKI,
+ ecdsa_private_key_export,
+ ecdsa_private_key_import,
+ ecdsa_generate_private_key,
+ ecdsa_get_internal
+};
+
+const struct signature_alg ecdsa_with_sha512_alg = {
+ "ecdsa-with-sha512",
+ ASN1_OID_ID_ECDSA_WITH_SHA512,
+ &_hx509_signature_ecdsa_with_sha512_data,
+ ASN1_OID_ID_ECPUBLICKEY,
+ &_hx509_signature_sha512_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|
+ SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ ecdsa_verify_signature,
+ ecdsa_create_signature,
+ 64
+};
+
+const struct signature_alg ecdsa_with_sha384_alg = {
+ "ecdsa-with-sha384",
+ ASN1_OID_ID_ECDSA_WITH_SHA384,
+ &_hx509_signature_ecdsa_with_sha384_data,
+ ASN1_OID_ID_ECPUBLICKEY,
+ &_hx509_signature_sha384_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|
+ SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ ecdsa_verify_signature,
+ ecdsa_create_signature,
+ 48
+};
+
+const struct signature_alg ecdsa_with_sha256_alg = {
+ "ecdsa-with-sha256",
+ ASN1_OID_ID_ECDSA_WITH_SHA256,
+ &_hx509_signature_ecdsa_with_sha256_data,
+ ASN1_OID_ID_ECPUBLICKEY,
+ &_hx509_signature_sha256_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|
+ SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ ecdsa_verify_signature,
+ ecdsa_create_signature,
+ 32
+};
+
+const struct signature_alg ecdsa_with_sha1_alg = {
+ "ecdsa-with-sha1",
+ ASN1_OID_ID_ECDSA_WITH_SHA1,
+ &_hx509_signature_ecdsa_with_sha1_data,
+ ASN1_OID_ID_ECPUBLICKEY,
+ &_hx509_signature_sha1_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|
+ SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ ecdsa_verify_signature,
+ ecdsa_create_signature,
+ 20
+};
+
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+
+HX509_LIB_FUNCTION const AlgorithmIdentifier * HX509_LIB_CALL
+hx509_signature_ecPublicKey(void)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ return &_hx509_signature_ecPublicKey;
+#else
+ return NULL;
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+}
+
+HX509_LIB_FUNCTION const AlgorithmIdentifier * HX509_LIB_CALL
+hx509_signature_ecdsa_with_sha256(void)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ return &_hx509_signature_ecdsa_with_sha256_data;
+#else
+ return NULL;
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+}
diff --git a/third_party/heimdal/lib/hx509/crypto.c b/third_party/heimdal/lib/hx509/crypto.c
new file mode 100644
index 0000000..8d368ed
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/crypto.c
@@ -0,0 +1,2804 @@
+/*
+ * Copyright (c) 2004 - 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/*-
+ * RFC5758 specifies no parameters for ecdsa-with-SHA<N> signatures
+ * RFC5754 specifies NULL parameters for sha<N>WithRSAEncryption signatures
+ *
+ * XXX: Make sure that the parameters are either NULL in both the tbs and the
+ * signature, or absent from both the tbs and the signature.
+ */
+
+static const heim_octet_string null_entry_oid = { 2, rk_UNCONST("\x05\x00") };
+
+static const unsigned sha512_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 3 };
+const AlgorithmIdentifier _hx509_signature_sha512_data = {
+ { 9, rk_UNCONST(sha512_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha384_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 2 };
+const AlgorithmIdentifier _hx509_signature_sha384_data = {
+ { 9, rk_UNCONST(sha384_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha256_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 1 };
+const AlgorithmIdentifier _hx509_signature_sha256_data = {
+ { 9, rk_UNCONST(sha256_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha1_oid_tree[] = { 1, 3, 14, 3, 2, 26 };
+const AlgorithmIdentifier _hx509_signature_sha1_data = {
+ { 6, rk_UNCONST(sha1_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned md5_oid_tree[] = { 1, 2, 840, 113549, 2, 5 };
+const AlgorithmIdentifier _hx509_signature_md5_data = {
+ { 6, rk_UNCONST(md5_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned rsa_with_sha512_oid[] ={ 1, 2, 840, 113549, 1, 1, 13 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha512_data = {
+ { 7, rk_UNCONST(rsa_with_sha512_oid) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned rsa_with_sha384_oid[] ={ 1, 2, 840, 113549, 1, 1, 12 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha384_data = {
+ { 7, rk_UNCONST(rsa_with_sha384_oid) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned rsa_with_sha256_oid[] ={ 1, 2, 840, 113549, 1, 1, 11 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha256_data = {
+ { 7, rk_UNCONST(rsa_with_sha256_oid) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned rsa_with_sha1_oid[] ={ 1, 2, 840, 113549, 1, 1, 5 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha1_data = {
+ { 7, rk_UNCONST(rsa_with_sha1_oid) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned rsa_with_md5_oid[] ={ 1, 2, 840, 113549, 1, 1, 4 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_md5_data = {
+ { 7, rk_UNCONST(rsa_with_md5_oid) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned rsa_oid[] ={ 1, 2, 840, 113549, 1, 1, 1 };
+const AlgorithmIdentifier _hx509_signature_rsa_data = {
+ { 7, rk_UNCONST(rsa_oid) }, NULL
+};
+
+static const unsigned rsa_pkcs1_x509_oid[] ={ 1, 2, 752, 43, 16, 1 };
+const AlgorithmIdentifier _hx509_signature_rsa_pkcs1_x509_data = {
+ { 6, rk_UNCONST(rsa_pkcs1_x509_oid) }, NULL
+};
+
+static const unsigned des_rsdi_ede3_cbc_oid[] ={ 1, 2, 840, 113549, 3, 7 };
+const AlgorithmIdentifier _hx509_des_rsdi_ede3_cbc_oid = {
+ { 6, rk_UNCONST(des_rsdi_ede3_cbc_oid) }, NULL
+};
+
+static const unsigned aes128_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 2 };
+const AlgorithmIdentifier _hx509_crypto_aes128_cbc_data = {
+ { 9, rk_UNCONST(aes128_cbc_oid) }, NULL
+};
+
+static const unsigned aes256_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 42 };
+const AlgorithmIdentifier _hx509_crypto_aes256_cbc_data = {
+ { 9, rk_UNCONST(aes256_cbc_oid) }, NULL
+};
+
+/*
+ *
+ */
+
+static BIGNUM *
+heim_int2BN(const heim_integer *i)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn(i->data, i->length, NULL);
+ BN_set_negative(bn, i->negative);
+ return bn;
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_set_digest_alg(DigestAlgorithmIdentifier *id,
+ const heim_oid *oid,
+ const void *param, size_t length)
+{
+ int ret;
+ if (param) {
+ id->parameters = malloc(sizeof(*id->parameters));
+ if (id->parameters == NULL)
+ return ENOMEM;
+ id->parameters->data = malloc(length);
+ if (id->parameters->data == NULL) {
+ free(id->parameters);
+ id->parameters = NULL;
+ return ENOMEM;
+ }
+ memcpy(id->parameters->data, param, length);
+ id->parameters->length = length;
+ } else
+ id->parameters = NULL;
+ ret = der_copy_oid(oid, &id->algorithm);
+ if (ret) {
+ if (id->parameters) {
+ free(id->parameters->data);
+ free(id->parameters);
+ id->parameters = NULL;
+ }
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+rsa_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const SubjectPublicKeyInfo *spi;
+ DigestInfo di;
+ unsigned char *to;
+ int tosize, retsize;
+ int ret;
+ RSA *rsa;
+ size_t size;
+ const unsigned char *p;
+
+ memset(&di, 0, sizeof(di));
+
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ p = spi->subjectPublicKey.data;
+ size = spi->subjectPublicKey.length / 8;
+
+ rsa = d2i_RSAPublicKey(NULL, &p, size);
+ if (rsa == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ tosize = RSA_size(rsa);
+ to = malloc(tosize);
+ if (to == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ retsize = RSA_public_decrypt(sig->length, (unsigned char *)sig->data,
+ to, rsa, RSA_PKCS1_PADDING);
+ if (retsize <= 0) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret,
+ "RSA public decrypt failed: %d", retsize);
+ free(to);
+ goto out;
+ }
+ if (retsize > tosize)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+
+ if (sig_alg->flags & RA_RSA_USES_DIGEST_INFO) {
+
+ ret = decode_DigestInfo(to, retsize, &di, &size);
+ free(to);
+ if (ret) {
+ goto out;
+ }
+
+ /* Check for extra data inside the sigature */
+ if (size != (size_t)retsize) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "size from decryption mismatch");
+ goto out;
+ }
+
+ if (sig_alg->digest_alg &&
+ der_heim_oid_cmp(&di.digestAlgorithm.algorithm,
+ &sig_alg->digest_alg->algorithm) != 0)
+ {
+ ret = HX509_CRYPTO_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch");
+ goto out;
+ }
+
+ /* verify that the parameters are NULL or the NULL-type */
+ if (di.digestAlgorithm.parameters != NULL &&
+ (di.digestAlgorithm.parameters->length != 2 ||
+ memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0))
+ {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature");
+ goto out;
+ }
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &di.digestAlgorithm,
+ data,
+ &di.digest);
+ if (ret)
+ goto out;
+
+ } else {
+ if ((size_t)retsize != data->length ||
+ ct_memcmp(to, data->data, retsize) != 0)
+ {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "RSA Signature incorrect");
+ goto out;
+ }
+ free(to);
+ ret = 0;
+ }
+
+ out:
+ free_DigestInfo(&di);
+ if (rsa)
+ RSA_free(rsa);
+ return ret;
+}
+
+static int
+rsa_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ const AlgorithmIdentifier *digest_alg;
+ heim_octet_string indata;
+ const heim_oid *sig_oid;
+ size_t size;
+ int ret;
+
+ if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) != 0)
+ return HX509_ALG_NOT_SUPP;
+
+ if (alg)
+ sig_oid = &alg->algorithm;
+ else
+ sig_oid = signer->signature_alg;
+
+ if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA512WITHRSAENCRYPTION) == 0) {
+ digest_alg = hx509_signature_sha512();
+ } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA384WITHRSAENCRYPTION) == 0) {
+ digest_alg = hx509_signature_sha384();
+ } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA256WITHRSAENCRYPTION) == 0) {
+ digest_alg = hx509_signature_sha256();
+ } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION) == 0) {
+ digest_alg = hx509_signature_sha1();
+ } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION) == 0) {
+ digest_alg = hx509_signature_md5();
+ } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION) == 0) {
+ digest_alg = hx509_signature_md5();
+ } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_DSA_WITH_SHA1) == 0) {
+ digest_alg = hx509_signature_sha1();
+ } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) {
+ digest_alg = hx509_signature_sha1();
+ } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_HEIM_RSA_PKCS1_X509) == 0) {
+ digest_alg = NULL;
+ } else
+ return HX509_ALG_NOT_SUPP;
+
+ if (signatureAlgorithm) {
+ ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid,
+ "\x05\x00", 2);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ }
+
+ if (digest_alg) {
+ DigestInfo di;
+ memset(&di, 0, sizeof(di));
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ digest_alg,
+ data,
+ &di.digestAlgorithm,
+ &di.digest);
+ if (ret)
+ return ret;
+ ASN1_MALLOC_ENCODE(DigestInfo,
+ indata.data,
+ indata.length,
+ &di,
+ &size,
+ ret);
+ free_DigestInfo(&di);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+ if (indata.length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+ } else {
+ indata = *data;
+ }
+
+ sig->length = RSA_size(signer->private_key.rsa);
+ sig->data = malloc(sig->length);
+ if (sig->data == NULL) {
+ der_free_octet_string(&indata);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = RSA_private_encrypt(indata.length, indata.data,
+ sig->data,
+ signer->private_key.rsa,
+ RSA_PKCS1_PADDING);
+ if (indata.data != data->data)
+ der_free_octet_string(&indata);
+ if (ret <= 0) {
+ ret = HX509_CMS_FAILED_CREATE_SIGATURE;
+ hx509_set_error_string(context, 0, ret,
+ "RSA private encrypt failed: %d", ret);
+ return ret;
+ }
+ if (sig->length > (size_t)ret) {
+ size = sig->length - ret;
+ memmove((uint8_t *)sig->data + size, sig->data, ret);
+ memset(sig->data, 0, size);
+ } else if (sig->length < (size_t)ret)
+ _hx509_abort("RSA signature prelen longer the output len");
+
+ return 0;
+}
+
+static int
+rsa_private_key_import(hx509_context context,
+ const AlgorithmIdentifier *keyai,
+ const void *data,
+ size_t len,
+ hx509_key_format_t format,
+ hx509_private_key private_key)
+{
+ switch (format) {
+ case HX509_KEY_FORMAT_DER: {
+ const unsigned char *p = data;
+
+ private_key->private_key.rsa =
+ d2i_RSAPrivateKey(NULL, &p, len);
+ if (private_key->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to parse RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+ private_key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION;
+ break;
+
+ }
+ default:
+ return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED;
+ }
+
+ return 0;
+}
+
+static int
+rsa_private_key2SPKI(hx509_context context,
+ hx509_private_key private_key,
+ SubjectPublicKeyInfo *spki)
+{
+ int len, ret;
+
+ memset(spki, 0, sizeof(*spki));
+
+ len = i2d_RSAPublicKey(private_key->private_key.rsa, NULL);
+ if (len < 0)
+ return -1;
+
+ spki->subjectPublicKey.data = malloc(len);
+ if (spki->subjectPublicKey.data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory");
+ return ENOMEM;
+ }
+ spki->subjectPublicKey.length = len * 8;
+
+ ret = _hx509_set_digest_alg(&spki->algorithm,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ "\x05\x00", 2);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "malloc - out of memory");
+ free(spki->subjectPublicKey.data);
+ spki->subjectPublicKey.data = NULL;
+ spki->subjectPublicKey.length = 0;
+ return ret;
+ }
+
+ {
+ unsigned char *pp = spki->subjectPublicKey.data;
+ i2d_RSAPublicKey(private_key->private_key.rsa, &pp);
+ }
+
+ return 0;
+}
+
+static int
+rsa_generate_private_key(hx509_context context,
+ struct hx509_generate_private_context *ctx,
+ hx509_private_key private_key)
+{
+ BIGNUM *e;
+ int ret;
+ unsigned long bits;
+
+ static const int default_rsa_e = 65537;
+ static const int default_rsa_bits = 2048;
+
+ private_key->private_key.rsa = RSA_new();
+ if (private_key->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to generate RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ e = BN_new();
+ BN_set_word(e, default_rsa_e);
+
+ bits = default_rsa_bits;
+
+ if (ctx->num_bits)
+ bits = ctx->num_bits;
+
+ ret = RSA_generate_key_ex(private_key->private_key.rsa, bits, e, NULL);
+ BN_free(e);
+ if (ret != 1) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to generate RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+ private_key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION;
+
+ return 0;
+}
+
+static int
+rsa_private_key_export(hx509_context context,
+ const hx509_private_key key,
+ hx509_key_format_t format,
+ heim_octet_string *data)
+{
+ int ret;
+
+ data->data = NULL;
+ data->length = 0;
+
+ switch (format) {
+ case HX509_KEY_FORMAT_DER:
+
+ ret = i2d_RSAPrivateKey(key->private_key.rsa, NULL);
+ if (ret <= 0) {
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "Private key is not exportable");
+ return ret;
+ }
+
+ data->data = malloc(ret);
+ if (data->data == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+ }
+ data->length = ret;
+
+ {
+ unsigned char *p = data->data;
+ i2d_RSAPrivateKey(key->private_key.rsa, &p);
+ }
+ break;
+ default:
+ return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED;
+ }
+
+ return 0;
+}
+
+static BIGNUM *
+rsa_get_internal(hx509_context context,
+ hx509_private_key key,
+ const char *type)
+{
+ if (strcasecmp(type, "rsa-modulus") == 0) {
+ return BN_dup(key->private_key.rsa->n);
+ } else if (strcasecmp(type, "rsa-exponent") == 0) {
+ return BN_dup(key->private_key.rsa->e);
+ } else
+ return NULL;
+}
+
+
+
+static hx509_private_key_ops rsa_private_key_ops = {
+ "RSA PRIVATE KEY",
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ NULL,
+ rsa_private_key2SPKI,
+ rsa_private_key_export,
+ rsa_private_key_import,
+ rsa_generate_private_key,
+ rsa_get_internal
+};
+
+/*
+ *
+ */
+
+static int
+dsa_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const SubjectPublicKeyInfo *spi;
+ DSAPublicKey pk;
+ DSAParams param;
+ size_t size;
+ DSA *dsa;
+ int ret;
+
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ dsa = DSA_new();
+ if (dsa == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = decode_DSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret)
+ goto out;
+
+ dsa->pub_key = heim_int2BN(&pk);
+
+ free_DSAPublicKey(&pk);
+
+ if (dsa->pub_key == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ if (spi->algorithm.parameters == NULL) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "DSA parameters missing");
+ goto out;
+ }
+
+ ret = decode_DSAParams(spi->algorithm.parameters->data,
+ spi->algorithm.parameters->length,
+ &param,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "DSA parameters failed to decode");
+ goto out;
+ }
+
+ dsa->p = heim_int2BN(&param.p);
+ dsa->q = heim_int2BN(&param.q);
+ dsa->g = heim_int2BN(&param.g);
+
+ free_DSAParams(&param);
+
+ if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ ret = DSA_verify(-1, data->data, data->length,
+ (unsigned char*)sig->data, sig->length,
+ dsa);
+ if (ret == 1)
+ ret = 0;
+ else if (ret == 0 || ret == -1) {
+ ret = HX509_CRYPTO_BAD_SIGNATURE;
+ hx509_set_error_string(context, 0, ret, "BAD DSA sigature");
+ } else {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "Invalid format of DSA sigature");
+ }
+
+ out:
+ DSA_free(dsa);
+
+ return ret;
+}
+
+#if 0
+static int
+dsa_parse_private_key(hx509_context context,
+ const void *data,
+ size_t len,
+ hx509_private_key private_key)
+{
+ const unsigned char *p = data;
+
+ private_key->private_key.dsa =
+ d2i_DSAPrivateKey(NULL, &p, len);
+ if (private_key->private_key.dsa == NULL)
+ return EINVAL;
+ private_key->signature_alg = ASN1_OID_ID_DSA_WITH_SHA1;
+
+ return 0;
+/* else */
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "No support to parse DSA keys");
+ return HX509_PARSING_KEY_FAILED;
+}
+#endif
+
+static int
+evp_md_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ size_t sigsize = EVP_MD_size(sig_alg->evp_md());
+ EVP_MD_CTX *ctx;
+
+ memset(sig, 0, sizeof(*sig));
+
+ if (signatureAlgorithm) {
+ int ret;
+ ret = _hx509_set_digest_alg(signatureAlgorithm,
+ sig_alg->sig_oid, "\x05\x00", 2);
+ if (ret)
+ return ret;
+ }
+
+
+ sig->data = malloc(sigsize);
+ if (sig->data == NULL) {
+ sig->length = 0;
+ return ENOMEM;
+ }
+ sig->length = sigsize;
+
+ ctx = EVP_MD_CTX_create();
+ EVP_DigestInit_ex(ctx, sig_alg->evp_md(), NULL);
+ EVP_DigestUpdate(ctx, data->data, data->length);
+ EVP_DigestFinal_ex(ctx, sig->data, NULL);
+ EVP_MD_CTX_destroy(ctx);
+
+
+ return 0;
+}
+
+static int
+evp_md_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ EVP_MD_CTX *ctx;
+ size_t sigsize = EVP_MD_size(sig_alg->evp_md());
+
+ if (sig->length != sigsize || sigsize > sizeof(digest)) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "SHA256 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ ctx = EVP_MD_CTX_create();
+ EVP_DigestInit_ex(ctx, sig_alg->evp_md(), NULL);
+ EVP_DigestUpdate(ctx, data->data, data->length);
+ EVP_DigestFinal_ex(ctx, digest, NULL);
+ EVP_MD_CTX_destroy(ctx);
+
+ if (ct_memcmp(digest, sig->data, sigsize) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad %s sigature", sig_alg->name);
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+extern const struct signature_alg ecdsa_with_sha512_alg;
+extern const struct signature_alg ecdsa_with_sha384_alg;
+extern const struct signature_alg ecdsa_with_sha256_alg;
+extern const struct signature_alg ecdsa_with_sha1_alg;
+#endif
+
+static const struct signature_alg heim_rsa_pkcs1_x509 = {
+ "rsa-pkcs1-x509",
+ ASN1_OID_ID_HEIM_RSA_PKCS1_X509,
+ &_hx509_signature_rsa_pkcs1_x509_data,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ NULL,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ 0,
+ NULL,
+ rsa_verify_signature,
+ rsa_create_signature,
+ 0
+};
+
+static const struct signature_alg pkcs1_rsa_sha1_alg = {
+ "rsa",
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &_hx509_signature_rsa_with_sha1_data,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ NULL,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ rsa_verify_signature,
+ rsa_create_signature,
+ 0
+};
+
+static const struct signature_alg rsa_with_sha512_alg = {
+ "rsa-with-sha512",
+ ASN1_OID_ID_PKCS1_SHA512WITHRSAENCRYPTION,
+ &_hx509_signature_rsa_with_sha512_data,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &_hx509_signature_sha512_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ rsa_verify_signature,
+ rsa_create_signature,
+ 0
+};
+
+static const struct signature_alg rsa_with_sha384_alg = {
+ "rsa-with-sha384",
+ ASN1_OID_ID_PKCS1_SHA384WITHRSAENCRYPTION,
+ &_hx509_signature_rsa_with_sha384_data,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &_hx509_signature_sha384_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ rsa_verify_signature,
+ rsa_create_signature,
+ 0
+};
+
+static const struct signature_alg rsa_with_sha256_alg = {
+ "rsa-with-sha256",
+ ASN1_OID_ID_PKCS1_SHA256WITHRSAENCRYPTION,
+ &_hx509_signature_rsa_with_sha256_data,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &_hx509_signature_sha256_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ rsa_verify_signature,
+ rsa_create_signature,
+ 0
+};
+
+static const struct signature_alg rsa_with_sha1_alg = {
+ "rsa-with-sha1",
+ ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION,
+ &_hx509_signature_rsa_with_sha1_data,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &_hx509_signature_sha1_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ rsa_verify_signature,
+ rsa_create_signature,
+ 0
+};
+
+static const struct signature_alg rsa_with_sha1_alg_secsig = {
+ "rsa-with-sha1",
+ ASN1_OID_ID_SECSIG_SHA_1WITHRSAENCRYPTION,
+ &_hx509_signature_rsa_with_sha1_data,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &_hx509_signature_sha1_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ rsa_verify_signature,
+ rsa_create_signature,
+ 0
+};
+
+static const struct signature_alg rsa_with_md5_alg = {
+ "rsa-with-md5",
+ ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION,
+ &_hx509_signature_rsa_with_md5_data,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &_hx509_signature_md5_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|WEAK_SIG_ALG,
+ 1230739889,
+ NULL,
+ rsa_verify_signature,
+ rsa_create_signature,
+ 0
+};
+
+static const struct signature_alg dsa_sha1_alg = {
+ "dsa-with-sha1",
+ ASN1_OID_ID_DSA_WITH_SHA1,
+ NULL,
+ ASN1_OID_ID_DSA,
+ &_hx509_signature_sha1_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ 0,
+ NULL,
+ dsa_verify_signature,
+ /* create_signature */ NULL,
+ 0
+};
+
+static const struct signature_alg sha512_alg = {
+ "sha-512",
+ ASN1_OID_ID_SHA512,
+ &_hx509_signature_sha512_data,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ 0,
+ EVP_sha512,
+ evp_md_verify_signature,
+ evp_md_create_signature,
+ 0
+};
+
+static const struct signature_alg sha384_alg = {
+ "sha-384",
+ ASN1_OID_ID_SHA384,
+ &_hx509_signature_sha384_data,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ 0,
+ EVP_sha384,
+ evp_md_verify_signature,
+ evp_md_create_signature,
+ 0
+};
+
+static const struct signature_alg sha256_alg = {
+ "sha-256",
+ ASN1_OID_ID_SHA256,
+ &_hx509_signature_sha256_data,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ 0,
+ EVP_sha256,
+ evp_md_verify_signature,
+ evp_md_create_signature,
+ 0
+};
+
+static const struct signature_alg sha1_alg = {
+ "sha1",
+ ASN1_OID_ID_SECSIG_SHA_1,
+ &_hx509_signature_sha1_data,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ 0,
+ EVP_sha1,
+ evp_md_verify_signature,
+ evp_md_create_signature,
+ 0
+};
+
+static const struct signature_alg md5_alg = {
+ "rsa-md5",
+ ASN1_OID_ID_RSA_DIGEST_MD5,
+ &_hx509_signature_md5_data,
+ NULL,
+ NULL,
+ SIG_DIGEST|WEAK_SIG_ALG,
+ 0,
+ EVP_md5,
+ evp_md_verify_signature,
+ NULL,
+ 0
+};
+
+/*
+ * Order matter in this structure, "best" first for each "key
+ * compatible" type (type is ECDSA, RSA, DSA, none, etc)
+ */
+
+static const struct signature_alg *sig_algs[] = {
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ &ecdsa_with_sha512_alg,
+ &ecdsa_with_sha384_alg,
+ &ecdsa_with_sha256_alg,
+ &ecdsa_with_sha1_alg,
+#endif
+ &rsa_with_sha512_alg,
+ &rsa_with_sha384_alg,
+ &rsa_with_sha256_alg,
+ &rsa_with_sha1_alg,
+ &rsa_with_sha1_alg_secsig,
+ &pkcs1_rsa_sha1_alg,
+ &rsa_with_md5_alg,
+ &heim_rsa_pkcs1_x509,
+ &dsa_sha1_alg,
+ &sha512_alg,
+ &sha384_alg,
+ &sha256_alg,
+ &sha1_alg,
+ &md5_alg,
+ NULL
+};
+
+const struct signature_alg *
+_hx509_find_sig_alg(const heim_oid *oid)
+{
+ unsigned int i;
+ for (i = 0; sig_algs[i]; i++)
+ if (der_heim_oid_cmp(sig_algs[i]->sig_oid, oid) == 0)
+ return sig_algs[i];
+ return NULL;
+}
+
+static const AlgorithmIdentifier *
+alg_for_privatekey(const hx509_private_key pk, int type)
+{
+ const heim_oid *keytype;
+ unsigned int i;
+
+ if (pk->ops == NULL)
+ return NULL;
+
+ keytype = pk->ops->key_oid;
+
+ for (i = 0; sig_algs[i]; i++) {
+ if (sig_algs[i]->key_oid == NULL)
+ continue;
+ if (der_heim_oid_cmp(sig_algs[i]->key_oid, keytype) != 0)
+ continue;
+ if (pk->ops->available &&
+ pk->ops->available(pk, sig_algs[i]->sig_alg) == 0)
+ continue;
+ if (type == HX509_SELECT_PUBLIC_SIG)
+ return sig_algs[i]->sig_alg;
+ if (type == HX509_SELECT_DIGEST)
+ return sig_algs[i]->digest_alg;
+
+ return NULL;
+ }
+ return NULL;
+}
+
+/*
+ *
+ */
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+extern hx509_private_key_ops ecdsa_private_key_ops;
+#endif
+
+static struct hx509_private_key_ops *private_algs[] = {
+ &rsa_private_key_ops,
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ &ecdsa_private_key_ops,
+#endif
+ NULL
+};
+
+HX509_LIB_FUNCTION hx509_private_key_ops * HX509_LIB_CALL
+hx509_find_private_alg(const heim_oid *oid)
+{
+ int i;
+ for (i = 0; private_algs[i]; i++) {
+ if (private_algs[i]->key_oid == NULL)
+ continue;
+ if (der_heim_oid_cmp(private_algs[i]->key_oid, oid) == 0)
+ return private_algs[i];
+ }
+ return NULL;
+}
+
+/*
+ * Check if the algorithm `alg' have a best before date, and if it
+ * des, make sure the its before the time `t'.
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_signature_is_weak(hx509_context context, const AlgorithmIdentifier *alg)
+{
+ const struct signature_alg *md;
+
+ md = _hx509_find_sig_alg(&alg->algorithm);
+ if (md == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+ if (md->flags & WEAK_SIG_ALG) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_ALGORITHM_BEST_BEFORE,
+ "Algorithm %s is weak", md->name);
+ return HX509_CRYPTO_ALGORITHM_BEST_BEFORE;
+ }
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_self_signed_valid(hx509_context context,
+ const AlgorithmIdentifier *alg)
+{
+ const struct signature_alg *md;
+
+ md = _hx509_find_sig_alg(&alg->algorithm);
+ if (md == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+ if ((md->flags & SELF_SIGNED_OK) == 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_ALGORITHM_BEST_BEFORE,
+ "Algorithm %s not trusted for self signatures",
+ md->name);
+ return HX509_CRYPTO_ALGORITHM_BEST_BEFORE;
+ }
+ return 0;
+}
+
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_verify_signature(hx509_context context,
+ const hx509_cert cert,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const struct signature_alg *md;
+ const Certificate *signer = NULL;
+
+ if (cert)
+ signer = _hx509_get_cert(cert);
+
+ md = _hx509_find_sig_alg(&alg->algorithm);
+ if (md == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+ if (signer && (md->flags & PROVIDE_CONF) == 0) {
+ hx509_clear_error_string(context);
+ return HX509_CRYPTO_SIG_NO_CONF;
+ }
+ if (signer == NULL && (md->flags & REQUIRE_SIGNER)) {
+ hx509_clear_error_string(context);
+ return HX509_CRYPTO_SIGNATURE_WITHOUT_SIGNER;
+ }
+ if (md->key_oid && signer) {
+ const SubjectPublicKeyInfo *spi;
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ if (der_heim_oid_cmp(&spi->algorithm.algorithm, md->key_oid) != 0) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_DONT_MATCH_KEY_ALG;
+ }
+ }
+ return (*md->verify_signature)(context, md, signer, alg, data, sig);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_create_signature(hx509_context context,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ const struct signature_alg *md;
+
+ md = _hx509_find_sig_alg(&alg->algorithm);
+ if (md == NULL) {
+ hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED,
+ "algorithm no supported");
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ if (signer && (md->flags & PROVIDE_CONF) == 0) {
+ hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED,
+ "algorithm provides no conf");
+ return HX509_CRYPTO_SIG_NO_CONF;
+ }
+
+ return (*md->create_signature)(context, md, signer, alg, data,
+ signatureAlgorithm, sig);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_create_signature_bitstring(hx509_context context,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_bit_string *sig)
+{
+ heim_octet_string os;
+ int ret;
+
+ ret = _hx509_create_signature(context, signer, alg,
+ data, signatureAlgorithm, &os);
+ if (ret)
+ return ret;
+ sig->data = os.data;
+ sig->length = os.length * 8;
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_public_encrypt(hx509_context context,
+ const heim_octet_string *cleartext,
+ const Certificate *cert,
+ heim_oid *encryption_oid,
+ heim_octet_string *ciphertext)
+{
+ const SubjectPublicKeyInfo *spi;
+ unsigned char *to;
+ int tosize;
+ int ret;
+ RSA *rsa;
+ size_t size;
+ const unsigned char *p;
+
+ ciphertext->data = NULL;
+ ciphertext->length = 0;
+
+ spi = &cert->tbsCertificate.subjectPublicKeyInfo;
+
+ p = spi->subjectPublicKey.data;
+ size = spi->subjectPublicKey.length / 8;
+
+ rsa = d2i_RSAPublicKey(NULL, &p, size);
+ if (rsa == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ tosize = RSA_size(rsa);
+ to = malloc(tosize);
+ if (to == NULL) {
+ RSA_free(rsa);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = RSA_public_encrypt(cleartext->length,
+ (unsigned char *)cleartext->data,
+ to, rsa, RSA_PKCS1_PADDING);
+ RSA_free(rsa);
+ if (ret <= 0) {
+ free(to);
+ hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PUBLIC_ENCRYPT,
+ "RSA public encrypt failed with %d", ret);
+ return HX509_CRYPTO_RSA_PUBLIC_ENCRYPT;
+ }
+ if (ret > tosize)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+
+ ciphertext->length = ret;
+ ciphertext->data = to;
+
+ ret = der_copy_oid(ASN1_OID_ID_PKCS1_RSAENCRYPTION, encryption_oid);
+ if (ret) {
+ der_free_octet_string(ciphertext);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_private_key_private_decrypt(hx509_context context,
+ const heim_octet_string *ciphertext,
+ const heim_oid *encryption_oid,
+ hx509_private_key p,
+ heim_octet_string *cleartext)
+{
+ int ret;
+
+ cleartext->data = NULL;
+ cleartext->length = 0;
+
+ if (p->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private RSA key missing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ cleartext->length = RSA_size(p->private_key.rsa);
+ cleartext->data = malloc(cleartext->length);
+ if (cleartext->data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ ret = RSA_private_decrypt(ciphertext->length, ciphertext->data,
+ cleartext->data,
+ p->private_key.rsa,
+ RSA_PKCS1_PADDING);
+ if (ret <= 0) {
+ der_free_octet_string(cleartext);
+ hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PRIVATE_DECRYPT,
+ "Failed to decrypt using private key: %d", ret);
+ return HX509_CRYPTO_RSA_PRIVATE_DECRYPT;
+ }
+ if (cleartext->length < (size_t)ret)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+
+ cleartext->length = ret;
+
+ return 0;
+}
+
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_parse_private_key(hx509_context context,
+ const AlgorithmIdentifier *keyai,
+ const void *data,
+ size_t len,
+ hx509_key_format_t format,
+ hx509_private_key *private_key)
+{
+ struct hx509_private_key_ops *ops;
+ int ret;
+
+ *private_key = NULL;
+
+ if (format == HX509_KEY_FORMAT_PKCS8) {
+ PKCS8PrivateKeyInfo ki;
+ hx509_private_key key;
+
+ ret = decode_PKCS8PrivateKeyInfo(data, len, &ki, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to parse PKCS#8-encoded private "
+ "key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ /* Re-enter to parse DER-encoded key from PKCS#8 envelope */
+ ret = hx509_parse_private_key(context, &ki.privateKeyAlgorithm,
+ ki.privateKey.data, ki.privateKey.length,
+ HX509_KEY_FORMAT_DER, &key);
+ free_PKCS8PrivateKeyInfo(&ki);
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to parse RSA key from PKCS#8 "
+ "envelope");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ *private_key = key;
+ return ret;
+ }
+
+ ops = hx509_find_private_alg(&keyai->algorithm);
+ if (ops == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ ret = hx509_private_key_init(private_key, ops, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+
+ ret = (*ops->import)(context, keyai, data, len, format, *private_key);
+ if (ret)
+ hx509_private_key_free(private_key);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_private_key2SPKI(hx509_context context,
+ hx509_private_key private_key,
+ SubjectPublicKeyInfo *spki)
+{
+ const struct hx509_private_key_ops *ops = private_key->ops;
+ if (ops == NULL || ops->get_spki == NULL) {
+ hx509_set_error_string(context, 0, HX509_UNIMPLEMENTED_OPERATION,
+ "Private key have no key2SPKI function");
+ return HX509_UNIMPLEMENTED_OPERATION;
+ }
+ return (*ops->get_spki)(context, private_key, spki);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_generate_private_key_init(hx509_context context,
+ const heim_oid *oid,
+ struct hx509_generate_private_context **ctx)
+{
+ *ctx = NULL;
+
+ if (der_heim_oid_cmp(oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) != 0) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "private key not an RSA key");
+ return EINVAL;
+ }
+
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ (*ctx)->key_oid = oid;
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_generate_private_key_is_ca(hx509_context context,
+ struct hx509_generate_private_context *ctx)
+{
+ ctx->isCA = 1;
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_generate_private_key_bits(hx509_context context,
+ struct hx509_generate_private_context *ctx,
+ unsigned long bits)
+{
+ ctx->num_bits = bits;
+ return 0;
+}
+
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_generate_private_key_free(struct hx509_generate_private_context **ctx)
+{
+ free(*ctx);
+ *ctx = NULL;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_generate_private_key(hx509_context context,
+ struct hx509_generate_private_context *ctx,
+ hx509_private_key *private_key)
+{
+ struct hx509_private_key_ops *ops;
+ int ret;
+
+ *private_key = NULL;
+
+ ops = hx509_find_private_alg(ctx->key_oid);
+ if (ops == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ ret = hx509_private_key_init(private_key, ops, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+
+ ret = (*ops->generate_private_key)(context, ctx, *private_key);
+ if (ret)
+ hx509_private_key_free(private_key);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+const AlgorithmIdentifier *
+hx509_signature_sha512(void)
+{ return &_hx509_signature_sha512_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha384(void)
+{ return &_hx509_signature_sha384_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha256(void)
+{ return &_hx509_signature_sha256_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha1(void)
+{ return &_hx509_signature_sha1_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_md5(void)
+{ return &_hx509_signature_md5_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha512(void)
+{ return &_hx509_signature_rsa_with_sha512_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha384(void)
+{ return &_hx509_signature_rsa_with_sha384_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha256(void)
+{ return &_hx509_signature_rsa_with_sha256_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha1(void)
+{ return &_hx509_signature_rsa_with_sha1_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_md5(void)
+{ return &_hx509_signature_rsa_with_md5_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa(void)
+{ return &_hx509_signature_rsa_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_pkcs1_x509(void)
+{ return &_hx509_signature_rsa_pkcs1_x509_data; }
+
+const AlgorithmIdentifier *
+hx509_crypto_des_rsdi_ede3_cbc(void)
+{ return &_hx509_des_rsdi_ede3_cbc_oid; }
+
+const AlgorithmIdentifier *
+hx509_crypto_aes128_cbc(void)
+{ return &_hx509_crypto_aes128_cbc_data; }
+
+const AlgorithmIdentifier *
+hx509_crypto_aes256_cbc(void)
+{ return &_hx509_crypto_aes256_cbc_data; }
+
+/*
+ *
+ */
+
+const AlgorithmIdentifier * _hx509_crypto_default_sig_alg =
+ &_hx509_signature_rsa_with_sha256_data;
+const AlgorithmIdentifier * _hx509_crypto_default_digest_alg =
+ &_hx509_signature_sha256_data;
+const AlgorithmIdentifier * _hx509_crypto_default_secret_alg =
+ &_hx509_crypto_aes128_cbc_data;
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_private_key_init(hx509_private_key *key,
+ hx509_private_key_ops *ops,
+ void *keydata)
+{
+ *key = calloc(1, sizeof(**key));
+ if (*key == NULL)
+ return ENOMEM;
+ (*key)->ref = 1;
+ (*key)->ops = ops;
+ (*key)->private_key.keydata = keydata;
+ return 0;
+}
+
+HX509_LIB_FUNCTION hx509_private_key HX509_LIB_CALL
+_hx509_private_key_ref(hx509_private_key key)
+{
+ if (key->ref == 0)
+ _hx509_abort("key refcount <= 0 on ref");
+ key->ref++;
+ if (key->ref == UINT_MAX)
+ _hx509_abort("key refcount == UINT_MAX on ref");
+ return key;
+}
+
+HX509_LIB_FUNCTION const char * HX509_LIB_CALL
+_hx509_private_pem_name(hx509_private_key key)
+{
+ return key->ops->pemtype;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_private_key_free(hx509_private_key *key)
+{
+ if (key == NULL || *key == NULL)
+ return 0;
+
+ if ((*key)->ref == 0)
+ _hx509_abort("key refcount == 0 on free");
+ if (--(*key)->ref > 0)
+ return 0;
+
+ if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) {
+ if ((*key)->private_key.rsa)
+ RSA_free((*key)->private_key.rsa);
+ } else if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid,
+ ASN1_OID_ID_ECPUBLICKEY) == 0 &&
+ (*key)->private_key.ecdsa != NULL) {
+ _hx509_private_eckey_free((*key)->private_key.ecdsa);
+ }
+ (*key)->private_key.rsa = NULL;
+ free(*key);
+ *key = NULL;
+ return 0;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_private_key_assign_rsa(hx509_private_key key, void *ptr)
+{
+ if (key->private_key.rsa)
+ RSA_free(key->private_key.rsa);
+ key->private_key.rsa = ptr;
+ key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION;
+ key->md = &pkcs1_rsa_sha1_alg;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_private_key_oid(hx509_context context,
+ const hx509_private_key key,
+ heim_oid *data)
+{
+ int ret;
+ ret = der_copy_oid(key->ops->key_oid, data);
+ if (ret)
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_private_key_exportable(hx509_private_key key)
+{
+ if (key->ops->export == NULL)
+ return 0;
+ return 1;
+}
+
+HX509_LIB_FUNCTION BIGNUM * HX509_LIB_CALL
+_hx509_private_key_get_internal(hx509_context context,
+ hx509_private_key key,
+ const char *type)
+{
+ if (key->ops->get_internal == NULL)
+ return NULL;
+ return (*key->ops->get_internal)(context, key, type);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_private_key_export(hx509_context context,
+ const hx509_private_key key,
+ hx509_key_format_t format,
+ heim_octet_string *data)
+{
+ data->length = 0;
+ data->data = NULL;
+ if (key->ops->export == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_UNIMPLEMENTED_OPERATION;
+ }
+ if (format == HX509_KEY_FORMAT_PKCS8) {
+ PKCS8PrivateKeyInfo ki;
+ size_t size;
+ int ret;
+
+ memset(&ki, 0, sizeof(ki));
+ ki.attributes = NULL; /* No localKeyId needed */
+ ki.privateKey.data = NULL;
+ ki.privateKeyAlgorithm.algorithm.components = NULL;
+ ret = der_parse_hex_heim_integer("00", &ki.version);
+ if (ret == 0)
+ ret = _hx509_private_key_oid(context, key,
+ &ki.privateKeyAlgorithm.algorithm);
+ if (ret == 0)
+ /* Re-enter */
+ ret = _hx509_private_key_export(context, key, HX509_KEY_FORMAT_DER,
+ &ki.privateKey);
+
+ /*
+ * XXX To set ki.privateKeyAlgorithm.parameters we'll need to either
+ * move this code into the *key->ops->export() functions, or expand
+ * their signature to allow them to set it for us, or add a method to
+ * hx509_private_key_ops that allows us to get the parameters from the
+ * backend.
+ */
+ ki.privateKeyAlgorithm.parameters = NULL;
+
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, data->data, data->length,
+ &ki, &size, ret);
+ free_PKCS8PrivateKeyInfo(&ki);
+ if (ret == 0 && size != data->length)
+ ret = EINVAL;
+ if (ret)
+ hx509_set_error_string(context, 0, ret,
+ "Private key PKCS#8 encoding failed");
+ return ret;
+ }
+ return (*key->ops->export)(context, key, format, data);
+}
+
+/*
+ *
+ */
+
+struct hx509cipher {
+ const char *name;
+ int flags;
+#define CIPHER_WEAK 1
+ const heim_oid *oid;
+ const AlgorithmIdentifier *(*ai_func)(void);
+ const EVP_CIPHER *(*evp_func)(void);
+ int (*get_params)(hx509_context, const hx509_crypto,
+ const heim_octet_string *, heim_octet_string *);
+ int (*set_params)(hx509_context, const heim_octet_string *,
+ hx509_crypto, heim_octet_string *);
+};
+
+struct hx509_crypto_data {
+ char *name;
+ int flags;
+#define ALLOW_WEAK 1
+
+#define PADDING_NONE 2
+#define PADDING_PKCS7 4
+#define PADDING_FLAGS (2|4)
+ const struct hx509cipher *cipher;
+ const EVP_CIPHER *c;
+ heim_octet_string key;
+ heim_oid oid;
+ void *param;
+};
+
+/*
+ *
+ */
+
+static unsigned private_rc2_40_oid_data[] = { 127, 1 };
+
+static heim_oid asn1_oid_private_rc2_40 =
+ { 2, private_rc2_40_oid_data };
+
+/*
+ *
+ */
+
+static int
+CMSCBCParam_get(hx509_context context, const hx509_crypto crypto,
+ const heim_octet_string *ivec, heim_octet_string *param)
+{
+ size_t size;
+ int ret;
+
+ assert(crypto->param == NULL);
+ if (ivec == NULL)
+ return 0;
+
+ ASN1_MALLOC_ENCODE(CMSCBCParameter, param->data, param->length,
+ ivec, &size, ret);
+ if (ret == 0 && size != param->length)
+ _hx509_abort("Internal asn1 encoder failure");
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+}
+
+static int
+CMSCBCParam_set(hx509_context context, const heim_octet_string *param,
+ hx509_crypto crypto, heim_octet_string *ivec)
+{
+ int ret;
+ if (ivec == NULL)
+ return 0;
+
+ ret = decode_CMSCBCParameter(param->data, param->length, ivec, NULL);
+ if (ret)
+ hx509_clear_error_string(context);
+
+ return ret;
+}
+
+struct _RC2_params {
+ int maximum_effective_key;
+};
+
+static int
+CMSRC2CBCParam_get(hx509_context context, const hx509_crypto crypto,
+ const heim_octet_string *ivec, heim_octet_string *param)
+{
+ CMSRC2CBCParameter rc2params;
+ const struct _RC2_params *p = crypto->param;
+ int maximum_effective_key = 128;
+ size_t size;
+ int ret;
+
+ memset(&rc2params, 0, sizeof(rc2params));
+
+ if (p)
+ maximum_effective_key = p->maximum_effective_key;
+
+ switch(maximum_effective_key) {
+ case 40:
+ rc2params.rc2ParameterVersion = 160;
+ break;
+ case 64:
+ rc2params.rc2ParameterVersion = 120;
+ break;
+ case 128:
+ rc2params.rc2ParameterVersion = 58;
+ break;
+ }
+ rc2params.iv = *ivec;
+
+ ASN1_MALLOC_ENCODE(CMSRC2CBCParameter, param->data, param->length,
+ &rc2params, &size, ret);
+ if (ret == 0 && size != param->length)
+ _hx509_abort("Internal asn1 encoder failure");
+
+ return ret;
+}
+
+static int
+CMSRC2CBCParam_set(hx509_context context, const heim_octet_string *param,
+ hx509_crypto crypto, heim_octet_string *ivec)
+{
+ CMSRC2CBCParameter rc2param;
+ struct _RC2_params *p;
+ size_t size;
+ int ret;
+
+ ret = decode_CMSRC2CBCParameter(param->data, param->length,
+ &rc2param, &size);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ free_CMSRC2CBCParameter(&rc2param);
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ switch(rc2param.rc2ParameterVersion) {
+ case 160:
+ crypto->c = EVP_rc2_40_cbc();
+ p->maximum_effective_key = 40;
+ break;
+ case 120:
+ crypto->c = EVP_rc2_64_cbc();
+ p->maximum_effective_key = 64;
+ break;
+ case 58:
+ crypto->c = EVP_rc2_cbc();
+ p->maximum_effective_key = 128;
+ break;
+ default:
+ free(p);
+ free_CMSRC2CBCParameter(&rc2param);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+ if (ivec)
+ ret = der_copy_octet_string(&rc2param.iv, ivec);
+ free_CMSRC2CBCParameter(&rc2param);
+ if (ret) {
+ free(p);
+ hx509_clear_error_string(context);
+ } else
+ crypto->param = p;
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static const struct hx509cipher ciphers[] = {
+ {
+ "rc2-cbc",
+ CIPHER_WEAK,
+ ASN1_OID_ID_PKCS3_RC2_CBC,
+ NULL,
+ EVP_rc2_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "rc2-cbc",
+ CIPHER_WEAK,
+ ASN1_OID_ID_RSADSI_RC2_CBC,
+ NULL,
+ EVP_rc2_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "rc2-40-cbc",
+ CIPHER_WEAK,
+ &asn1_oid_private_rc2_40,
+ NULL,
+ EVP_rc2_40_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "des-ede3-cbc",
+ 0,
+ ASN1_OID_ID_PKCS3_DES_EDE3_CBC,
+ NULL,
+ EVP_des_ede3_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "des-ede3-cbc",
+ 0,
+ ASN1_OID_ID_RSADSI_DES_EDE3_CBC,
+ hx509_crypto_des_rsdi_ede3_cbc,
+ EVP_des_ede3_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-128-cbc",
+ 0,
+ ASN1_OID_ID_AES_128_CBC,
+ hx509_crypto_aes128_cbc,
+ EVP_aes_128_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-192-cbc",
+ 0,
+ ASN1_OID_ID_AES_192_CBC,
+ NULL,
+ EVP_aes_192_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-256-cbc",
+ 0,
+ ASN1_OID_ID_AES_256_CBC,
+ hx509_crypto_aes256_cbc,
+ EVP_aes_256_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ }
+};
+
+static const struct hx509cipher *
+find_cipher_by_oid(const heim_oid *oid)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++)
+ if (der_heim_oid_cmp(oid, ciphers[i].oid) == 0)
+ return &ciphers[i];
+
+ return NULL;
+}
+
+static const struct hx509cipher *
+find_cipher_by_name(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++)
+ if (strcasecmp(name, ciphers[i].name) == 0)
+ return &ciphers[i];
+
+ return NULL;
+}
+
+
+HX509_LIB_FUNCTION const heim_oid * HX509_LIB_CALL
+hx509_crypto_enctype_by_name(const char *name)
+{
+ const struct hx509cipher *cipher;
+
+ cipher = find_cipher_by_name(name);
+ if (cipher == NULL)
+ return NULL;
+ return cipher->oid;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_init(hx509_context context,
+ const char *provider,
+ const heim_oid *enctype,
+ hx509_crypto *crypto)
+{
+ const struct hx509cipher *cipher;
+
+ *crypto = NULL;
+
+ cipher = find_cipher_by_oid(enctype);
+ if (cipher == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "Algorithm not supported");
+ return HX509_ALG_NOT_SUPP;
+ }
+
+ *crypto = calloc(1, sizeof(**crypto));
+ if (*crypto == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ (*crypto)->flags = PADDING_PKCS7;
+ (*crypto)->cipher = cipher;
+ (*crypto)->c = (*cipher->evp_func)();
+
+ if (der_copy_oid(enctype, &(*crypto)->oid)) {
+ hx509_crypto_destroy(*crypto);
+ *crypto = NULL;
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION const char * HX509_LIB_CALL
+hx509_crypto_provider(hx509_crypto crypto)
+{
+ return "unknown";
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_crypto_destroy(hx509_crypto crypto)
+{
+ if (crypto->name)
+ free(crypto->name);
+ if (crypto->key.data)
+ free(crypto->key.data);
+ if (crypto->param)
+ free(crypto->param);
+ der_free_oid(&crypto->oid);
+ memset(crypto, 0, sizeof(*crypto));
+ free(crypto);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_set_key_name(hx509_crypto crypto, const char *name)
+{
+ return 0;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_crypto_allow_weak(hx509_crypto crypto)
+{
+ crypto->flags |= ALLOW_WEAK;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_crypto_set_padding(hx509_crypto crypto, int padding_type)
+{
+ switch (padding_type) {
+ case HX509_CRYPTO_PADDING_PKCS7:
+ crypto->flags &= ~PADDING_FLAGS;
+ crypto->flags |= PADDING_PKCS7;
+ break;
+ case HX509_CRYPTO_PADDING_NONE:
+ crypto->flags &= ~PADDING_FLAGS;
+ crypto->flags |= PADDING_NONE;
+ break;
+ default:
+ _hx509_abort("Invalid padding");
+ }
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_set_key_data(hx509_crypto crypto, const void *data, size_t length)
+{
+ if (EVP_CIPHER_key_length(crypto->c) > (int)length)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (crypto->key.data) {
+ free(crypto->key.data);
+ crypto->key.data = NULL;
+ crypto->key.length = 0;
+ }
+ crypto->key.data = malloc(length);
+ if (crypto->key.data == NULL)
+ return ENOMEM;
+ memcpy(crypto->key.data, data, length);
+ crypto->key.length = length;
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_set_random_key(hx509_crypto crypto, heim_octet_string *key)
+{
+ if (crypto->key.data) {
+ free(crypto->key.data);
+ crypto->key.length = 0;
+ }
+
+ crypto->key.length = EVP_CIPHER_key_length(crypto->c);
+ crypto->key.data = malloc(crypto->key.length);
+ if (crypto->key.data == NULL) {
+ crypto->key.length = 0;
+ return ENOMEM;
+ }
+ if (RAND_bytes(crypto->key.data, crypto->key.length) <= 0) {
+ free(crypto->key.data);
+ crypto->key.data = NULL;
+ crypto->key.length = 0;
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ if (key)
+ return der_copy_octet_string(&crypto->key, key);
+ else
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_set_params(hx509_context context,
+ hx509_crypto crypto,
+ const heim_octet_string *param,
+ heim_octet_string *ivec)
+{
+ return (*crypto->cipher->set_params)(context, param, crypto, ivec);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_get_params(hx509_context context,
+ hx509_crypto crypto,
+ const heim_octet_string *ivec,
+ heim_octet_string *param)
+{
+ return (*crypto->cipher->get_params)(context, crypto, ivec, param);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_random_iv(hx509_crypto crypto, heim_octet_string *ivec)
+{
+ ivec->length = EVP_CIPHER_iv_length(crypto->c);
+ ivec->data = malloc(ivec->length);
+ if (ivec->data == NULL) {
+ ivec->length = 0;
+ return ENOMEM;
+ }
+
+ if (RAND_bytes(ivec->data, ivec->length) <= 0) {
+ free(ivec->data);
+ ivec->data = NULL;
+ ivec->length = 0;
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_encrypt(hx509_crypto crypto,
+ const void *data,
+ const size_t length,
+ const heim_octet_string *ivec,
+ heim_octet_string **ciphertext)
+{
+ EVP_CIPHER_CTX evp;
+ size_t padsize, bsize;
+ int ret;
+
+ *ciphertext = NULL;
+
+ if ((crypto->cipher->flags & CIPHER_WEAK) &&
+ (crypto->flags & ALLOW_WEAK) == 0)
+ return HX509_CRYPTO_ALGORITHM_BEST_BEFORE;
+
+ assert(EVP_CIPHER_iv_length(crypto->c) == (int)ivec->length);
+
+ EVP_CIPHER_CTX_init(&evp);
+
+ ret = EVP_CipherInit_ex(&evp, crypto->c, NULL,
+ crypto->key.data, ivec->data, 1);
+ if (ret != 1) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ *ciphertext = calloc(1, sizeof(**ciphertext));
+ if (*ciphertext == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ assert(crypto->flags & PADDING_FLAGS);
+
+ bsize = EVP_CIPHER_block_size(crypto->c);
+ padsize = 0;
+
+ if (crypto->flags & PADDING_NONE) {
+ if (bsize != 1 && (length % bsize) != 0)
+ return HX509_CMS_PADDING_ERROR;
+ } else if (crypto->flags & PADDING_PKCS7) {
+ if (bsize != 1)
+ padsize = bsize - (length % bsize);
+ }
+
+ (*ciphertext)->length = length + padsize;
+ (*ciphertext)->data = malloc(length + padsize);
+ if ((*ciphertext)->data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ memcpy((*ciphertext)->data, data, length);
+ if (padsize) {
+ size_t i;
+ unsigned char *p = (*ciphertext)->data;
+ p += length;
+ for (i = 0; i < padsize; i++)
+ *p++ = padsize;
+ }
+
+ ret = EVP_Cipher(&evp, (*ciphertext)->data,
+ (*ciphertext)->data,
+ length + padsize);
+ if (ret != 1) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+ ret = 0;
+
+ out:
+ if (ret) {
+ if (*ciphertext) {
+ if ((*ciphertext)->data) {
+ free((*ciphertext)->data);
+ }
+ free(*ciphertext);
+ *ciphertext = NULL;
+ }
+ }
+ EVP_CIPHER_CTX_cleanup(&evp);
+
+ return ret;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_decrypt(hx509_crypto crypto,
+ const void *data,
+ const size_t length,
+ heim_octet_string *ivec,
+ heim_octet_string *clear)
+{
+ EVP_CIPHER_CTX evp;
+ void *idata = NULL;
+ int ret;
+
+ clear->data = NULL;
+ clear->length = 0;
+
+ if ((crypto->cipher->flags & CIPHER_WEAK) &&
+ (crypto->flags & ALLOW_WEAK) == 0)
+ return HX509_CRYPTO_ALGORITHM_BEST_BEFORE;
+
+ if (ivec && EVP_CIPHER_iv_length(crypto->c) < (int)ivec->length)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (crypto->key.data == NULL)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (ivec)
+ idata = ivec->data;
+
+ EVP_CIPHER_CTX_init(&evp);
+
+ ret = EVP_CipherInit_ex(&evp, crypto->c, NULL,
+ crypto->key.data, idata, 0);
+ if (ret != 1) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+
+ clear->length = length;
+ clear->data = malloc(length);
+ if (clear->data == NULL) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ clear->length = 0;
+ return ENOMEM;
+ }
+
+ if (EVP_Cipher(&evp, clear->data, data, length) != 1) {
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ EVP_CIPHER_CTX_cleanup(&evp);
+
+ if ((crypto->flags & PADDING_PKCS7) && EVP_CIPHER_block_size(crypto->c) > 1) {
+ int padsize;
+ unsigned char *p;
+ int j, bsize = EVP_CIPHER_block_size(crypto->c);
+
+ if ((int)clear->length < bsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+
+ p = clear->data;
+ p += clear->length - 1;
+ padsize = *p;
+ if (padsize > bsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+ clear->length -= padsize;
+ for (j = 0; j < padsize; j++) {
+ if (*p-- != padsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+ }
+ }
+
+ return 0;
+
+ out:
+ if (clear->data)
+ free(clear->data);
+ clear->data = NULL;
+ clear->length = 0;
+ return ret;
+}
+
+typedef int (*PBE_string2key_func)(hx509_context,
+ const char *,
+ const heim_octet_string *,
+ hx509_crypto *, heim_octet_string *,
+ heim_octet_string *,
+ const heim_oid *, const EVP_MD *);
+
+static int
+PBE_string2key(hx509_context context,
+ const char *password,
+ const heim_octet_string *parameters,
+ hx509_crypto *crypto,
+ heim_octet_string *key, heim_octet_string *iv,
+ const heim_oid *enc_oid,
+ const EVP_MD *md)
+{
+ PKCS12_PBEParams p12params;
+ int passwordlen;
+ hx509_crypto c;
+ int iter, saltlen, ret;
+ unsigned char *salt;
+
+ passwordlen = password ? strlen(password) : 0;
+
+ if (parameters == NULL)
+ return HX509_ALG_NOT_SUPP;
+
+ ret = decode_PKCS12_PBEParams(parameters->data,
+ parameters->length,
+ &p12params, NULL);
+ if (ret)
+ goto out;
+
+ if (p12params.iterations)
+ iter = *p12params.iterations;
+ else
+ iter = 1;
+ salt = p12params.salt.data;
+ saltlen = p12params.salt.length;
+
+ if (!PKCS12_key_gen (password, passwordlen, salt, saltlen,
+ PKCS12_KEY_ID, iter, key->length, key->data, md)) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ if (!PKCS12_key_gen (password, passwordlen, salt, saltlen,
+ PKCS12_IV_ID, iter, iv->length, iv->data, md)) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ ret = hx509_crypto_init(context, NULL, enc_oid, &c);
+ if (ret)
+ goto out;
+
+ hx509_crypto_allow_weak(c);
+
+ ret = hx509_crypto_set_key_data(c, key->data, key->length);
+ if (ret) {
+ hx509_crypto_destroy(c);
+ goto out;
+ }
+
+ *crypto = c;
+out:
+ free_PKCS12_PBEParams(&p12params);
+ return ret;
+}
+
+static const heim_oid *
+find_string2key(const heim_oid *oid,
+ const EVP_CIPHER **c,
+ const EVP_MD **md,
+ PBE_string2key_func *s2k)
+{
+ if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC2_CBC) == 0) {
+ *c = EVP_rc2_40_cbc();
+ if (*c == NULL)
+ return NULL;
+ *md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
+ *s2k = PBE_string2key;
+ return &asn1_oid_private_rc2_40;
+ } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC2_CBC) == 0) {
+ *c = EVP_rc2_cbc();
+ if (*c == NULL)
+ return NULL;
+ *md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
+ *s2k = PBE_string2key;
+ return ASN1_OID_ID_PKCS3_RC2_CBC;
+#if 0
+ } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC4) == 0) {
+ *c = EVP_rc4_40();
+ if (*c == NULL)
+ return NULL;
+ *md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
+ *s2k = PBE_string2key;
+ return NULL;
+ } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC4) == 0) {
+ *c = EVP_rc4();
+ if (*c == NULL)
+ return NULL;
+ *md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
+ *s2k = PBE_string2key;
+ return ASN1_OID_ID_PKCS3_RC4;
+#endif
+ } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND3_KEYTRIPLEDES_CBC) == 0) {
+ *c = EVP_des_ede3_cbc();
+ if (*c == NULL)
+ return NULL;
+ *md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
+ *s2k = PBE_string2key;
+ return ASN1_OID_ID_PKCS3_DES_EDE3_CBC;
+ }
+
+ return NULL;
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_pbe_encrypt(hx509_context context,
+ hx509_lock lock,
+ const AlgorithmIdentifier *ai,
+ const heim_octet_string *content,
+ heim_octet_string *econtent)
+{
+ hx509_clear_error_string(context);
+ return EINVAL;
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_pbe_decrypt(hx509_context context,
+ hx509_lock lock,
+ const AlgorithmIdentifier *ai,
+ const heim_octet_string *econtent,
+ heim_octet_string *content)
+{
+ const struct _hx509_password *pw;
+ heim_octet_string key, iv;
+ const heim_oid *enc_oid;
+ const EVP_CIPHER *c;
+ const EVP_MD *md;
+ PBE_string2key_func s2k;
+ int ret = 0;
+ size_t i;
+
+ memset(&key, 0, sizeof(key));
+ memset(&iv, 0, sizeof(iv));
+
+ memset(content, 0, sizeof(*content));
+
+ enc_oid = find_string2key(&ai->algorithm, &c, &md, &s2k);
+ if (enc_oid == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "String to key algorithm not supported");
+ ret = HX509_ALG_NOT_SUPP;
+ goto out;
+ }
+
+ key.length = EVP_CIPHER_key_length(c);
+ key.data = malloc(key.length);
+ if (key.data == NULL) {
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ iv.length = EVP_CIPHER_iv_length(c);
+ iv.data = malloc(iv.length);
+ if (iv.data == NULL) {
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ pw = _hx509_lock_get_passwords(lock);
+
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ for (i = 0; i < pw->len + 1; i++) {
+ hx509_crypto crypto;
+ const char *password;
+
+ if (i < pw->len)
+ password = pw->val[i];
+ else if (i < pw->len + 1)
+ password = "";
+ else
+ password = NULL;
+
+ ret = (*s2k)(context, password, ai->parameters, &crypto,
+ &key, &iv, enc_oid, md);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_decrypt(crypto,
+ econtent->data,
+ econtent->length,
+ &iv,
+ content);
+ hx509_crypto_destroy(crypto);
+ if (ret == 0)
+ goto out;
+
+ }
+out:
+ if (key.data)
+ der_free_octet_string(&key);
+ if (iv.data)
+ der_free_octet_string(&iv);
+ return ret;
+}
+
+/*
+ *
+ */
+
+
+static int
+match_keys_rsa(hx509_cert c, hx509_private_key private_key)
+{
+ const Certificate *cert;
+ const SubjectPublicKeyInfo *spi;
+ RSAPublicKey pk;
+ RSA *rsa;
+ size_t size;
+ int ret;
+
+ if (private_key->private_key.rsa == NULL)
+ return 0;
+
+ rsa = private_key->private_key.rsa;
+ if (rsa->d == NULL || rsa->p == NULL || rsa->q == NULL)
+ return 0;
+
+ cert = _hx509_get_cert(c);
+ spi = &cert->tbsCertificate.subjectPublicKeyInfo;
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ return 0;
+
+ ret = decode_RSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret) {
+ RSA_free(rsa);
+ return 0;
+ }
+ rsa->n = heim_int2BN(&pk.modulus);
+ rsa->e = heim_int2BN(&pk.publicExponent);
+
+ free_RSAPublicKey(&pk);
+
+ rsa->d = BN_dup(private_key->private_key.rsa->d);
+ rsa->p = BN_dup(private_key->private_key.rsa->p);
+ rsa->q = BN_dup(private_key->private_key.rsa->q);
+ rsa->dmp1 = BN_dup(private_key->private_key.rsa->dmp1);
+ rsa->dmq1 = BN_dup(private_key->private_key.rsa->dmq1);
+ rsa->iqmp = BN_dup(private_key->private_key.rsa->iqmp);
+
+ if (rsa->n == NULL || rsa->e == NULL ||
+ rsa->d == NULL || rsa->p == NULL|| rsa->q == NULL ||
+ rsa->dmp1 == NULL || rsa->dmq1 == NULL) {
+ RSA_free(rsa);
+ return 0;
+ }
+
+ ret = RSA_check_key(rsa);
+ RSA_free(rsa);
+
+ return ret == 1;
+}
+
+static int
+match_keys_ec(hx509_cert c, hx509_private_key private_key)
+{
+ return 1; /* XXX use EC_KEY_check_key */
+}
+
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_match_keys(hx509_cert c, hx509_private_key key)
+{
+ if (!key->ops)
+ return 0;
+ if (der_heim_oid_cmp(key->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0)
+ return match_keys_rsa(c, key);
+ if (der_heim_oid_cmp(key->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) == 0)
+ return match_keys_ec(c, key);
+ return 0;
+
+}
+
+
+static const heim_oid *
+find_keytype(const hx509_private_key key)
+{
+ const struct signature_alg *md;
+
+ if (key == NULL)
+ return NULL;
+
+ md = _hx509_find_sig_alg(key->signature_alg);
+ if (md == NULL)
+ return NULL;
+ return md->key_oid;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_select(const hx509_context context,
+ int type,
+ const hx509_private_key source,
+ hx509_peer_info peer,
+ AlgorithmIdentifier *selected)
+{
+ const AlgorithmIdentifier *def = NULL;
+ size_t i, j;
+ int ret, bits;
+
+ memset(selected, 0, sizeof(*selected));
+
+ if (type == HX509_SELECT_DIGEST) {
+ bits = SIG_DIGEST;
+ if (source)
+ def = alg_for_privatekey(source, type);
+ if (def == NULL)
+ def = _hx509_crypto_default_digest_alg;
+ } else if (type == HX509_SELECT_PUBLIC_SIG) {
+ bits = SIG_PUBLIC_SIG;
+ /* XXX depend on `source´ and `peer´ */
+ if (source)
+ def = alg_for_privatekey(source, type);
+ if (def == NULL)
+ def = _hx509_crypto_default_sig_alg;
+ } else if (type == HX509_SELECT_SECRET_ENC) {
+ bits = SIG_SECRET;
+ def = _hx509_crypto_default_secret_alg;
+ } else {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Unknown type %d of selection", type);
+ return EINVAL;
+ }
+
+ if (peer) {
+ const heim_oid *keytype = NULL;
+
+ keytype = find_keytype(source);
+
+ for (i = 0; i < peer->len; i++) {
+ for (j = 0; sig_algs[j]; j++) {
+ if ((sig_algs[j]->flags & bits) != bits)
+ continue;
+ if (der_heim_oid_cmp(sig_algs[j]->sig_oid,
+ &peer->val[i].algorithm) != 0)
+ continue;
+ if (keytype && sig_algs[j]->key_oid &&
+ der_heim_oid_cmp(keytype, sig_algs[j]->key_oid))
+ continue;
+
+ /* found one, use that */
+ ret = copy_AlgorithmIdentifier(&peer->val[i], selected);
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ if (bits & SIG_SECRET) {
+ const struct hx509cipher *cipher;
+
+ cipher = find_cipher_by_oid(&peer->val[i].algorithm);
+ if (cipher == NULL)
+ continue;
+ if (cipher->ai_func == NULL)
+ continue;
+ ret = copy_AlgorithmIdentifier(cipher->ai_func(), selected);
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ }
+ }
+
+ /* use default */
+ ret = copy_AlgorithmIdentifier(def, selected);
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crypto_available(hx509_context context,
+ int type,
+ hx509_cert source,
+ AlgorithmIdentifier **val,
+ unsigned int *plen)
+{
+ const heim_oid *keytype = NULL;
+ unsigned int len, i;
+ void *ptr;
+ int bits, ret;
+
+ *val = NULL;
+
+ if (type == HX509_SELECT_ALL) {
+ bits = SIG_DIGEST | SIG_PUBLIC_SIG | SIG_SECRET;
+ } else if (type == HX509_SELECT_DIGEST) {
+ bits = SIG_DIGEST;
+ } else if (type == HX509_SELECT_PUBLIC_SIG) {
+ bits = SIG_PUBLIC_SIG;
+ } else {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Unknown type %d of available", type);
+ return EINVAL;
+ }
+
+ if (source)
+ keytype = find_keytype(_hx509_cert_private_key(source));
+
+ len = 0;
+ for (i = 0; sig_algs[i]; i++) {
+ if ((sig_algs[i]->flags & bits) == 0)
+ continue;
+ if (sig_algs[i]->sig_alg == NULL)
+ continue;
+ if (keytype && sig_algs[i]->key_oid &&
+ der_heim_oid_cmp(sig_algs[i]->key_oid, keytype))
+ continue;
+
+ /* found one, add that to the list */
+ ptr = realloc(*val, sizeof(**val) * (len + 1));
+ if (ptr == NULL)
+ goto out;
+ *val = ptr;
+
+ ret = copy_AlgorithmIdentifier(sig_algs[i]->sig_alg, &(*val)[len]);
+ if (ret)
+ goto out;
+ len++;
+ }
+
+ /* Add AES */
+ if (bits & SIG_SECRET) {
+
+ for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) {
+
+ if (ciphers[i].flags & CIPHER_WEAK)
+ continue;
+ if (ciphers[i].ai_func == NULL)
+ continue;
+
+ ptr = realloc(*val, sizeof(**val) * (len + 1));
+ if (ptr == NULL)
+ goto out;
+ *val = ptr;
+
+ ret = copy_AlgorithmIdentifier((ciphers[i].ai_func)(), &(*val)[len]);
+ if (ret)
+ goto out;
+ len++;
+ }
+ }
+
+ *plen = len;
+ return 0;
+
+out:
+ for (i = 0; i < len; i++)
+ free_AlgorithmIdentifier(&(*val)[i]);
+ free(*val);
+ *val = NULL;
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_crypto_free_algs(AlgorithmIdentifier *val,
+ unsigned int len)
+{
+ unsigned int i;
+ for (i = 0; i < len; i++)
+ free_AlgorithmIdentifier(&val[i]);
+ free(val);
+}
diff --git a/third_party/heimdal/lib/hx509/data/PKITS.pdf b/third_party/heimdal/lib/hx509/data/PKITS.pdf
new file mode 100644
index 0000000..3a56862
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/PKITS.pdf
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/PKITS_data.zip b/third_party/heimdal/lib/hx509/data/PKITS_data.zip
new file mode 100644
index 0000000..50d6fbb
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/PKITS_data.zip
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/bleichenbacher-bad.pem b/third_party/heimdal/lib/hx509/data/bleichenbacher-bad.pem
new file mode 100644
index 0000000..2c71932
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/bleichenbacher-bad.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVoCAQYwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV
+BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD
+VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw0wNjA5MTEyMzU4NTVa
+Fw0wNjEwMTEyMzU4NTVaMGMxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpRdWVlbnNs
+YW5kMRowGAYDVQQKExFDcnlwdFNvZnQgUHR5IEx0ZDEjMCEGA1UEAxMaU2VydmVy
+IHRlc3QgY2VydCAoNTEyIGJpdCkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAn7PD
+hCeV/xIxUg8V70YRxK2A5jZbD92A12GN4PxyRQk0/lVmRUNMaJdq/qigpd9feP/u
+12S4PwTLb/8q/v657QIDAQABMA0GCSqGSIb3DQEBBQUAA0EAbynCRIlUQgaqyNgU
+DF6P14yRKUtX8akOP2TwStaSiVf/akYqfLFm3UGka5XbPj4rifrZ0/sOoZEEBvHQ
+e20sRA==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/bleichenbacher-good.pem b/third_party/heimdal/lib/hx509/data/bleichenbacher-good.pem
new file mode 100644
index 0000000..409147b
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/bleichenbacher-good.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVoCAQYwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV
+BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD
+VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw0wNjA5MTEyMzU5MDJa
+Fw0wNjEwMTEyMzU5MDJaMGMxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpRdWVlbnNs
+YW5kMRowGAYDVQQKExFDcnlwdFNvZnQgUHR5IEx0ZDEjMCEGA1UEAxMaU2VydmVy
+IHRlc3QgY2VydCAoNTEyIGJpdCkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAn7PD
+hCeV/xIxUg8V70YRxK2A5jZbD92A12GN4PxyRQk0/lVmRUNMaJdq/qigpd9feP/u
+12S4PwTLb/8q/v657QIDAQABMA0GCSqGSIb3DQEBBQUAA0EAc+fnj0rB2CYautG2
+4itiMOU4SN6JFTFDCTU/Gb5aR/Fiu7HJkuE5yGEnTdnwcId/T9sTW251yzCc1e2z
+rHX/kw==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/bleichenbacher-sf-pad-correct.pem b/third_party/heimdal/lib/hx509/data/bleichenbacher-sf-pad-correct.pem
new file mode 100644
index 0000000..3e73f5d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/bleichenbacher-sf-pad-correct.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICgzCCAWugAwIBAgIBFzANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYw
+ODE5MTY1MTMwWhcNMDYxMDE4MTY1MTMwWjARMQ8wDQYDVQQDEwZIYWNrZXIwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKSu6ChWttBsOpaBrYf4PzyCGNe6DuE7
+rmq4CMskdz8uiAJ3wVd8jGsjdeY4YzoXSVp+9mEF6XqNgyDf8Ub3kNgPYxvJ28lg
+QVpd5RdGWXHo14LWBTD1mtFkCiAhVlATsVNI/tjv2tv7Jp8EsylbDHe7hslA0rns
+Rr2cS9bvpM03AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEF
+BQADggEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADLL/Up63HkFWD15INcW
+Xd1nZGI+gO/whm58ICyJ1Js7ON6N4NyBTwe8513CvdOlOdG/Ctmy2gxEE47HhEed
+ST8AUooI0ey599t84P20gGRuOYIjr7c=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/ca.crt b/third_party/heimdal/lib/hx509/data/ca.crt
new file mode 100644
index 0000000..7aa8bcf
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ca.crt
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFczCCA1ugAwIBAgIJAI34CtjBcJHEMA0GCSqGSIb3DQEBCwUAMCoxGzAZBgNV
+BAMMEmh4NTA5IFRlc3QgUm9vdCBDQTELMAkGA1UEBhMCU0UwIBcNMTkwMzIyMjIy
+NTAxWhgPMjUxODExMjEyMjI1MDFaMCoxGzAZBgNVBAMMEmh4NTA5IFRlc3QgUm9v
+dCBDQTELMAkGA1UEBhMCU0UwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQDE4gbVQ/vlPFU2W62rukqiUkJ/EIDo1HE4r+xpxO12Ke45NtqSep0d2FfSvEu8
+dhot1jWIkijF7B/bFuB0LyFryCAV/zlU9rLoadCmur5ONIgXRm7eW19wxo5YRD4C
+A1IRwvT+Axz0TC3eyquUN1C0r7ZWbiOY8uQy3Sjfar16Z3TtqlKgo4R/yF7dIPJO
+OVHaznC+xsfLsYE2r9PqbTjBF3O1pIhwV9oA3tfs23EtvcZBP3y3LSsjnKaF0b/N
+XmjLNW9hbmAfN+16TEMOlVZvBjUPO3CC/GU0PJzm1/FqyzXWeRx5FZNi7fCPKg8J
+9QDAgK5mMn+ZPazuUt70uxUFrnRLCjCia/TgC+t2d+3AqsnRlYnLYDv/MeP/QwqH
+GK3+WuAS6uqXZMtilDhY+oiMTZ4vDHvwzJ5q3UhIpWXj5cSGWAxQurKgUsjT9sta
+gGmXlBauMYSzFM5T+TXica1qE7dNXjXr2sTy9BHIp+aWJuGkX9rSx8tHwbkIkVTp
+4UCZ+QoxBRaSmiuyFPM77yg6wZBuSuRRN/BNKvAhuJaE1MdA+vobbyyNbv56MU0+
+WI4ucD+b08JmJp3k+fgM0fKXBQlEL4mp7zeUoLmC1yCy48Zk1foPZTRH9pIGB/zU
+z4B8o20NmereB9bX0IjJ6eqMDqvAZWZ99Nf16Q3X88T88wIDAQABo4GZMIGWMB0G
+A1UdDgQWBBRTuMwJxp9C6tXkdCC0Ze1o+J21BTBaBgNVHSMEUzBRgBRTuMwJxp9C
+6tXkdCC0Ze1o+J21BaEupCwwKjEbMBkGA1UEAwwSaHg1MDkgVGVzdCBSb290IENB
+MQswCQYDVQQGEwJTRYIJAI34CtjBcJHEMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQD
+AgHmMA0GCSqGSIb3DQEBCwUAA4ICAQCdea8Tx/1vuQCqn2AOmIzEO9xgmaY3opl9
+0Px82DafNuFFJu9WlPrsKeADtSqpA/MwBjL8K+T3dhL4Bxhq8jed0gGsS3C3xFTl
+/RJbnFiLuveMErPTEtxaRYpa3oibQ15eJbDq533Is1x8oeK6NnHB6St3nboST0f3
+6SeAsoeHrI16eUEJQ3UKJYJlxATEqOpeaWwdlT6jF9u0WyENz0ijD0b9FPY/zq6D
+zx47Zd5F6aisrtKNXFjB9/oHV6jOh9OGxz6WfT1z2AZ+69jEm+xE8coq4nyWcJrS
+cf7ENBIw1Rknpxk3/H1p0q+Zze5/JBYlKOtEML1dwRIRquVgfhcI/tOq7m5jUxTl
+/6dW8FCnuEFBnUvUrZ0Hv3g2jvHElpPjYkLwZyKFYyvY/G+xCZAiqaYZ2kQqRmti
+KvSfh8fJlV2Jj2aDI1I4JjACG7LYBe7WXCs7TccRrhnx/RUY7cpJwEQrIOKBq8wx
+DD58oPgvkmNPP5lZcFARjnWcY8xS9KzT+KaZWM3ZPefg3Vk//3HfwZfDE3Y7IMgu
+quuLcpeGtMDyurnm6piUdPITt8yW+MMJR8V+PeF0zLdN4dA0nuJpZWZ3fvnevmAL
+jh/ia0LuzkhVSj1R1dXXopZevazh/tmAuKU9BbeYGvwI1RFXVvyzpGRfMbgaze3Q
+tIrreKeFxg==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/ca.key b/third_party/heimdal/lib/hx509/data/ca.key
new file mode 100644
index 0000000..83cff75
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ca.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDE4gbVQ/vlPFU2
+W62rukqiUkJ/EIDo1HE4r+xpxO12Ke45NtqSep0d2FfSvEu8dhot1jWIkijF7B/b
+FuB0LyFryCAV/zlU9rLoadCmur5ONIgXRm7eW19wxo5YRD4CA1IRwvT+Axz0TC3e
+yquUN1C0r7ZWbiOY8uQy3Sjfar16Z3TtqlKgo4R/yF7dIPJOOVHaznC+xsfLsYE2
+r9PqbTjBF3O1pIhwV9oA3tfs23EtvcZBP3y3LSsjnKaF0b/NXmjLNW9hbmAfN+16
+TEMOlVZvBjUPO3CC/GU0PJzm1/FqyzXWeRx5FZNi7fCPKg8J9QDAgK5mMn+ZPazu
+Ut70uxUFrnRLCjCia/TgC+t2d+3AqsnRlYnLYDv/MeP/QwqHGK3+WuAS6uqXZMti
+lDhY+oiMTZ4vDHvwzJ5q3UhIpWXj5cSGWAxQurKgUsjT9stagGmXlBauMYSzFM5T
++TXica1qE7dNXjXr2sTy9BHIp+aWJuGkX9rSx8tHwbkIkVTp4UCZ+QoxBRaSmiuy
+FPM77yg6wZBuSuRRN/BNKvAhuJaE1MdA+vobbyyNbv56MU0+WI4ucD+b08JmJp3k
++fgM0fKXBQlEL4mp7zeUoLmC1yCy48Zk1foPZTRH9pIGB/zUz4B8o20NmereB9bX
+0IjJ6eqMDqvAZWZ99Nf16Q3X88T88wIDAQABAoICAQCD8TXDFpxpM9WnaCkrPN1n
+itklbln1rulxo/Q7rc21ssQDc89m+uTwa1vvzmCzHDLPJQ8bR1gry+JNYTdqpWsw
+YB2goDo7xlh/iOpb0ipXHr1VW85RFcsQOQCMBq/HiZImdRDaahutXKAg/pGd8rQT
+Yu4/XfBdP+nObIhHsbDppwules+E+BCD0jRA3SOFaMSCbncAYxbiW0LM82iBYlD2
+llDlGi6Vm0pt6umpwiZHETcb4wAhghO2+fRfGgIAD5ULGfRaxy2DvmdX3mPSEiKq
+pO5KFvt/zMXGDBjaWz1e5HBgGyoJu3vagLsGNpl9gsPOPm6h7pW0jLCnxsHEINwk
+lGbhCR9ubaZMCNuwEppPNeusURG35XiSEHC4fBPhlG6pB737a6ih/w9dwOkLuijJ
+X3vOaVj2K5waExi72uij+GnZBylemOTAy9lE3xlUzhO74h9F8DuoJHMxXKU5a24L
+/hmnnIYHOJpHQIfcfkMIx9VuG/qsug+DdOxlgByT6hbkRbX2gGSP3iqy7XUnb2g0
+3QQdyQpz8wJ2x568EAAC0HKhQj2fcL1L5lpM5xpbg6s87o50reMhcAogb2mGjx3Q
+r8u9PJeYgJ5FOqu0zLbenWkb4OLtLz6kHhOdimkCrybL+bLQFdl9lNAOqgVuJxyT
+NaClP+v9lAACBkONihU8AQKCAQEA9hxExY0NXFDZHiLq4M4DG79BthLWLi4QFZI5
+2vpzrS5kT90rptAzBenTBWdUifAI8JPkq2R8VKB8j7F5YELQ9UTmoT+qMGEslQ1p
+RTE8fZln6UhLUIv5tTwhL6Afbs2Faz7Vd7rUq9eUkyxsjxtZe5cPH1dGplx0iPQI
+QQ1OasSWc8TmZWXRvcRWe5vWiJLFZKt6fJZWYyBBvu8L7PZ6QR5Oa2EO1UTPPX+W
+7+BwsoH9Bguv+hYliKEReN8SIfOF9E/OElrNooy7eANFTQ3pNEou68rqpzX4jgdW
+G4Nnsu9rkO8K+bb+/MLBvdxrEiKrNb+xwcVOFaJNqz6aNMC14QKCAQEAzMtbIpig
+dLUha3QXrXIsEjH/hlGx0c6Q9VH/toQbBNE4LOrS4QlurX3iRz79tNYLYdwLrZ2Q
++tK31/ilX/hGIiA1w6fHcrQokddMJhhGoR6nSUiybs+75Ac01xHDSYXCo/YOkr8m
+HOtRWi+0qJqzU4sticPwi2YStM6L2gNpEDU9FQTG/wgLLHPxDEjRpkhmgQbt8nEQ
+M+amXK1othrZVSTJl1hREF11DkzhkZYyGgY4ifAyAOPW7z5K0nS2drV8PExFSkr+
+2eriVvavu/40WbvadhTy1cyVL7N3svzY34TgwqsXX1Stz8dQBa+uImt9gxmZIK9I
+reONiErKBhClUwKCAQEAjY1IyM8OBjDCECFJMq+K/iSM6OoAomMAAUgvWpF+gvcR
+3xV4i+Nn1VjddFgwOX4Dxktp1GJhWFNOEV+kTgdgJBHTDJ+PhW/+smQaTh+5iQv4
+xiY8m0FHCERjWf8g1RwERuDG6qxcsdG2tMdyUQUL/JevrPkHu5ulszeYn8HFfoc/
+eaqgUWW0sw8AJuxFAhxYyEQQmSPm3/Cnn+fh1hMV0epadExIucVv5RFDgQh4CVPW
+cem6935RbDon0HuM9FYaj6BvCAOODpYfJTHMZDtCDD82qYv2VuIl6ZqynfSAalxm
+Y9/5UhM8qahiwo7KTo3+J1XwKWEQPkUxovLIwtqsQQKCAQEAgMgwWyUXYcy1Y1jx
+usRdKmP+h3zAEWuQhHQ4FZIlW3YlmTlhutmvm7HZpWvbJuii57r0LQ00qkXwDgPy
+GtOJZtRSeuL67QqVqIB3Bk2lvJQGJnNsoXpIcTCG7efhok5XA7wrleRWF0FzOv9c
+39nIgvS2gjeRAFgD02c/Uq1qWCLicmE6sg1g2WdfYZY5IBPPQbwVzauDwN9+JjF1
+824W1Q/5JQ8Iiv36Ki/2eRK2Ft9qlnNRPnYIJxJJAucaBrRBl7luqTVX5blq87zU
+7acBTJxw2Gh7/C5WclStJQUTbBunK0NjwzMAyfRQQgMjwclOeC6UuJUBYzgBPH+r
+Yvz8uQKCAQA8WchJ0UQmOP98voo0cnIX0lcZcddwzdsZm78p1PrXqmsrxnlmRILA
+wZ5okzIzEqu1Xltu5DS/CAAWdRkY/2LFGty1dW5UR47xsWE7fMf4dbPeOcBxgfh4
+sQgG7KcWY9mw3PZ4PmPP63nRC/1Ws/+dlvpNA77BjyHH7laTVZbUadS/0bCzhJG0
+RW27r5UcPV8IhKTNU8iOxvaN2U0N2RaaxZ8AaYj8UEeMlFp91DXYa5SCWY1yM0c7
+QYpO3EtSLj+ECk09lDzQBPUo4jzb5CoTFYDEdXr8Rt4I/r03fkOwHslWUzXVyqRe
+xC/DrYbFBHh4yQWuQPsmCbi6OkGKDvwA
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/crl1.crl b/third_party/heimdal/lib/hx509/data/crl1.crl
new file mode 100644
index 0000000..575f80e
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/crl1.crl
@@ -0,0 +1,16 @@
+-----BEGIN X509 CRL-----
+MIIChTBvMA0GCSqGSIb3DQEBBQUAMCoxGzAZBgNVBAMMEmh4NTA5IFRlc3QgUm9v
+dCBDQTELMAkGA1UEBhMCU0UXDTE5MDMyMjIyMjUxNloXDTI5MDEyODIyMjUxNlow
+FDASAgEDFw0xOTAzMjIyMjI1MTZaMA0GCSqGSIb3DQEBBQUAA4ICAQBCM4u2dByD
+hPsQUpPAPsZ/a7tPdvkgmcLtk2CFhtJqtDBT96SAr0tVpcpIbZoB0tH/MvJfhAaR
+AOLrgdmrwlKaIbq3uyEDIghRlRiG2WXX+gsP9yK1xS6AuQnXS8Pnyng0xo2V8fMy
+UCN+gKvO70O6dqcDApU1Tt5jxkPFACBYSSMsuunrjWGuttKGebJJGeBzU+PYt7bZ
+7CT2BgsLgl5J2DL6KO9tGjDmlGKKC2joF1PDkjzbIfs389eyOZqJu/3Q4EtBFzad
+Yz0DAEDzDrn7dj53NJp6dxbqOM86woak37dtDG5Mwu8KTQEGpfsqdofQf39nluJu
+70NHJrXP+9IQ7Tvb3bakZbyigw7J9PBXaHyImXN/gejYD/FQjghnmS2QU72JsSKT
+3nAN3I6MRAEIhoaxForCl3f+uHgtvQBBITSIUdnGaTZnI0mXrkHS9H4eWWJREZbc
+wBqKGZxbfy8ZbPaKv75Zzj0ZMns7vNybUqLEE/OcwrEjd/pCLwWZ6KzgtS1t22TU
+o3H26GNLzMQvg/1dVsRZWrkWxAjVNHtUIXXbmBOvSii3BX7jPIfH1bCZrfsl0xrS
+BsqhtIZj74hyTV1FX79CdFu0Ag/ugtzY4K8rdIu9kaPe2Ju6ulQBtpmCK++H7szP
+48fJOwV1aKzJGVCH61kSGc8ljyGjDEDn4Q==
+-----END X509 CRL-----
diff --git a/third_party/heimdal/lib/hx509/data/crl1.der b/third_party/heimdal/lib/hx509/data/crl1.der
new file mode 100644
index 0000000..a667423
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/crl1.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/eccurve.pem b/third_party/heimdal/lib/hx509/data/eccurve.pem
new file mode 100644
index 0000000..a76e47d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/eccurve.pem
@@ -0,0 +1,3 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
diff --git a/third_party/heimdal/lib/hx509/data/gen-req.sh b/third_party/heimdal/lib/hx509/data/gen-req.sh
new file mode 100755
index 0000000..09f0dfb
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/gen-req.sh
@@ -0,0 +1,360 @@
+#!/bin/sh
+# $Id$
+#
+# This script need openssl 0.9.8a or newer, so it can parse the
+# otherName section for pkinit certificates.
+#
+
+openssl=openssl
+
+# workaround until openssl -objects lands
+if ${openssl} version | grep '^OpenSSL 1\.[1-9]' >/dev/null ; then
+ config=openssl.1.1.cnf
+else
+ config=openssl.1.0.cnf
+fi
+
+gen_cert()
+{
+ keytype=${6:-rsa:4096}
+ ${openssl} req \
+ -new \
+ -subj "$1" \
+ -config ${config} \
+ -newkey $keytype \
+ -sha1 \
+ -nodes \
+ -keyout out.key \
+ -out cert.req > /dev/null 2>/dev/null
+
+ if [ "$3" = "ca" ] ; then
+ ${openssl} x509 \
+ -req \
+ -days 182500 \
+ -in cert.req \
+ -extfile ${config} \
+ -extensions $4 \
+ -signkey out.key \
+ -out cert.crt
+
+ ln -s ca.crt `${openssl} x509 -hash -noout -in cert.crt`.0
+
+ name=$3
+
+ elif [ "$3" = "proxy" ] ; then
+
+ ${openssl} x509 \
+ -req \
+ -in cert.req \
+ -days 182500 \
+ -out cert.crt \
+ -CA $2.crt \
+ -CAkey $2.key \
+ -CAcreateserial \
+ -extfile ${config} \
+ -extensions $4
+
+ name=$5
+ else
+
+ ${openssl} ca \
+ -name $4 \
+ -days 182500 \
+ -cert $2.crt \
+ -keyfile $2.key \
+ -in cert.req \
+ -out cert.crt \
+ -outdir . \
+ -batch \
+ -config ${config}
+
+ name=$3
+ fi
+
+ mv cert.crt $name.crt
+ mv out.key $name.key
+}
+
+echo "01" > serial
+> index.txt
+rm -f *.0
+
+gen_cert "/CN=hx509 Test Root CA/C=SE" "root" "ca" "v3_ca"
+gen_cert "/CN=OCSP responder/C=SE" "ca" "ocsp-responder" "ocsp"
+gen_cert "/CN=Test cert/C=SE" "ca" "test" "usr"
+gen_cert "/CN=Revoke cert/C=SE" "ca" "revoke" "usr"
+gen_cert "/CN=Test cert KeyEncipherment/C=SE" "ca" "test-ke-only" "usr_ke"
+gen_cert "/CN=Test cert DigitalSignature/C=SE" "ca" "test-ds-only" "usr_ds"
+gen_cert "/CN=pkinit/C=SE" "ca" "pkinit" "pkinit_client"
+$openssl ecparam -name secp256r1 -out eccurve.pem
+gen_cert "/CN=pkinit-ec/C=SE" "ca" "pkinit-ec" "pkinit_client" "XXX" ec:eccurve.pem
+gen_cert "/C=SE/CN=pkinit/CN=pkinit-proxy" "pkinit" "proxy" "proxy_cert" pkinit-proxy
+gen_cert "/CN=kdc/C=SE" "ca" "kdc" "pkinit_kdc"
+gen_cert "/CN=www.test.h5l.se/C=SE" "ca" "https" "https"
+gen_cert "/CN=Sub CA/C=SE" "ca" "sub-ca" "subca"
+gen_cert "/CN=Test sub cert/C=SE" "sub-ca" "sub-cert" "usr"
+gen_cert "/C=SE/CN=Test cert/CN=proxy" "test" "proxy" "proxy_cert" proxy-test
+gen_cert "/C=SE/CN=Test cert/CN=proxy/CN=child" "proxy-test" "proxy" "proxy_cert" proxy-level-test
+gen_cert "/C=SE/CN=Test cert/CN=no-proxy" "test" "proxy" "usr_cert" no-proxy-test
+gen_cert "/C=SE/CN=Test cert/CN=proxy10" "test" "proxy" "proxy10_cert" proxy10-test
+gen_cert "/C=SE/CN=Test cert/CN=proxy10/CN=child" "proxy10-test" "proxy" "proxy10_cert" proxy10-child-test
+gen_cert "/C=SE/CN=Test cert/CN=proxy10/CN=child/CN=child" "proxy10-child-test" "proxy" "proxy10_cert" proxy10-child-child-test
+
+
+# combine
+cat sub-ca.crt ca.crt > sub-ca-combined.crt
+cat test.crt test.key > test.combined.crt
+cat pkinit-proxy.crt pkinit.crt > pkinit-proxy-chain.crt
+
+# password protected key
+${openssl} rsa -in test.key -aes256 -passout pass:foobar -out test-pw.key
+${openssl} rsa -in pkinit.key -aes256 -passout pass:foo -out pkinit-pw.key
+
+
+${openssl} ca \
+ -name usr \
+ -cert ca.crt \
+ -keyfile ca.key \
+ -revoke revoke.crt \
+ -config ${config}
+
+${openssl} pkcs12 \
+ -export \
+ -in test.crt \
+ -inkey test.key \
+ -passout pass:foobar \
+ -out test.p12 \
+ -name "friendlyname-test" \
+ -certfile ca.crt \
+ -caname ca
+
+${openssl} pkcs12 \
+ -export \
+ -in sub-cert.crt \
+ -inkey sub-cert.key \
+ -passout pass:foobar \
+ -out sub-cert.p12 \
+ -name "friendlyname-sub-cert" \
+ -certfile sub-ca-combined.crt \
+ -caname sub-ca \
+ -caname ca
+
+${openssl} pkcs12 \
+ -keypbe NONE \
+ -certpbe NONE \
+ -export \
+ -in test.crt \
+ -inkey test.key \
+ -passout pass:foobar \
+ -out test-nopw.p12 \
+ -name "friendlyname-cert" \
+ -certfile ca.crt \
+ -caname ca
+
+${openssl} smime \
+ -sign \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -signer test.crt \
+ -inkey test.key \
+ -outform DER \
+ -out test-signed-data
+
+${openssl} smime \
+ -sign \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -signer test.crt \
+ -inkey test.key \
+ -noattr \
+ -outform DER \
+ -out test-signed-data-noattr
+
+${openssl} smime \
+ -sign \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -signer test.crt \
+ -inkey test.key \
+ -noattr \
+ -nocerts \
+ -outform DER \
+ -out test-signed-data-noattr-nocerts
+
+${openssl} smime \
+ -sign \
+ -md sha1 \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -signer test.crt \
+ -inkey test.key \
+ -outform DER \
+ -out test-signed-sha-1
+
+${openssl} smime \
+ -sign \
+ -md sha256 \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -signer test.crt \
+ -inkey test.key \
+ -outform DER \
+ -out test-signed-sha-256
+
+${openssl} smime \
+ -sign \
+ -md sha512 \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -signer test.crt \
+ -inkey test.key \
+ -outform DER \
+ -out test-signed-sha-512
+
+
+${openssl} smime \
+ -encrypt \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -outform DER \
+ -out test-enveloped-rc2-40 \
+ -rc2-40 \
+ test.crt
+
+${openssl} smime \
+ -encrypt \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -outform DER \
+ -out test-enveloped-rc2-64 \
+ -rc2-64 \
+ test.crt
+
+${openssl} smime \
+ -encrypt \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -outform DER \
+ -out test-enveloped-rc2-128 \
+ -rc2-128 \
+ test.crt
+
+${openssl} smime \
+ -encrypt \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -outform DER \
+ -out test-enveloped-des \
+ -des \
+ test.crt
+
+${openssl} smime \
+ -encrypt \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -outform DER \
+ -out test-enveloped-des-ede3 \
+ -des3 \
+ test.crt
+
+${openssl} smime \
+ -encrypt \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -outform DER \
+ -out test-enveloped-aes-128 \
+ -aes128 \
+ test.crt
+
+${openssl} smime \
+ -encrypt \
+ -nodetach \
+ -binary \
+ -in static-file \
+ -outform DER \
+ -out test-enveloped-aes-256 \
+ -aes256 \
+ test.crt
+
+echo ocsp requests
+
+${openssl} ocsp \
+ -issuer ca.crt \
+ -cert test.crt \
+ -reqout ocsp-req1.der
+
+${openssl} ocsp \
+ -index index.txt \
+ -rsigner ocsp-responder.crt \
+ -rkey ocsp-responder.key \
+ -CA ca.crt \
+ -reqin ocsp-req1.der \
+ -noverify \
+ -respout ocsp-resp1-ocsp.der
+
+${openssl} ocsp \
+ -index index.txt \
+ -rsigner ca.crt \
+ -rkey ca.key \
+ -CA ca.crt \
+ -reqin ocsp-req1.der \
+ -noverify \
+ -respout ocsp-resp1-ca.der
+
+${openssl} ocsp \
+ -index index.txt \
+ -rsigner ocsp-responder.crt \
+ -rkey ocsp-responder.key \
+ -CA ca.crt \
+ -resp_no_certs \
+ -reqin ocsp-req1.der \
+ -noverify \
+ -respout ocsp-resp1-ocsp-no-cert.der
+
+${openssl} ocsp \
+ -index index.txt \
+ -rsigner ocsp-responder.crt \
+ -rkey ocsp-responder.key \
+ -CA ca.crt \
+ -reqin ocsp-req1.der \
+ -resp_key_id \
+ -noverify \
+ -respout ocsp-resp1-keyhash.der
+
+${openssl} ocsp \
+ -issuer ca.crt \
+ -cert revoke.crt \
+ -reqout ocsp-req2.der
+
+${openssl} ocsp \
+ -index index.txt \
+ -rsigner ocsp-responder.crt \
+ -rkey ocsp-responder.key \
+ -CA ca.crt \
+ -reqin ocsp-req2.der \
+ -noverify \
+ -respout ocsp-resp2.der
+
+${openssl} ca \
+ -gencrl \
+ -name usr \
+ -crldays 3600 \
+ -keyfile ca.key \
+ -cert ca.crt \
+ -crl_reason superseded \
+ -out crl1.crl \
+ -config ${config}
+
+${openssl} crl -in crl1.crl -outform der -out crl1.der
diff --git a/third_party/heimdal/lib/hx509/data/https.crt b/third_party/heimdal/lib/hx509/data/https.crt
new file mode 100644
index 0000000..54d5df1
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/https.crt
@@ -0,0 +1,116 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 9 (0x9)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:10 2019 GMT
+ Not After : Nov 21 22:25:10 2518 GMT
+ Subject: C=SE, CN=www.test.h5l.se
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:bb:ca:85:9c:3d:6b:5a:21:1b:2c:84:35:48:37:
+ bc:13:62:93:ff:7b:be:49:40:e2:36:b5:7a:54:a4:
+ e3:0f:b1:87:29:de:6b:7d:86:ec:b6:25:c5:9c:dc:
+ 13:06:57:4c:80:1b:86:f0:ac:e6:64:8f:aa:63:cc:
+ 28:49:5c:84:09:b8:0f:31:99:dd:36:d2:42:b5:aa:
+ df:31:f6:27:ca:c2:4c:50:11:5b:01:94:17:da:2a:
+ 5c:21:e5:b5:81:23:69:3e:4f:1d:08:48:95:57:30:
+ 77:96:ae:9b:78:87:10:e4:6d:90:e8:78:ad:19:41:
+ 3d:b8:91:1c:b6:04:78:52:e5:e4:3f:28:df:01:13:
+ da:aa:cb:24:cf:f5:93:f9:02:b8:c5:dc:47:fb:79:
+ e5:de:9e:19:b3:28:ab:2d:bd:73:48:0f:71:0a:b6:
+ 81:5a:6d:02:6d:9c:c8:c3:14:d5:82:bf:19:b8:d0:
+ 6f:58:32:6c:76:91:f3:07:6b:25:4a:59:f4:2d:c9:
+ 8d:da:ee:cc:30:5b:5b:d8:f3:0d:63:28:8d:9c:df:
+ 21:b5:3a:41:e0:55:d0:5f:f1:32:45:0b:6b:40:b6:
+ d8:43:0c:7b:28:3d:2d:7c:40:19:a2:e0:d6:a2:0b:
+ 32:65:a3:81:e9:1c:e5:6a:f6:61:7c:66:fa:c6:10:
+ bf:5d:1d:d9:c1:1a:67:fb:a0:43:15:ff:f5:40:5a:
+ 0c:8a:4b:48:38:d5:c7:77:48:19:f7:21:de:73:17:
+ 97:cf:03:d7:c3:84:22:38:ae:f2:be:d2:61:af:37:
+ 38:31:41:01:97:58:93:ba:80:da:bb:00:33:a8:2b:
+ 98:34:80:8b:00:1e:83:02:c4:26:3f:5c:51:a9:29:
+ e3:ac:b1:36:31:57:87:43:94:57:3a:17:f4:6d:34:
+ bf:23:b6:a2:56:d2:b7:72:7e:35:34:d9:58:46:c1:
+ 64:2d:3f:e7:ff:e4:fd:42:11:d9:04:98:ba:9d:88:
+ ec:e7:ae:bb:11:42:fd:00:cb:24:17:27:94:2c:a0:
+ 34:df:18:8b:7a:bc:39:55:6c:02:3b:44:cf:a4:42:
+ f3:e3:81:5b:d6:90:8e:78:d7:3f:4c:ef:6c:de:4d:
+ 7e:41:ce:87:8f:c0:38:a4:57:05:63:32:85:c3:de:
+ 88:aa:8c:0b:04:df:c3:86:64:4c:19:91:e1:e4:b2:
+ f8:f6:f3:fe:93:c3:3e:c1:b1:74:b4:72:ff:88:94:
+ 8d:34:a3:b0:9d:55:aa:fe:bc:bc:41:55:49:8a:f1:
+ ee:dd:fa:0e:a1:fa:b9:71:a7:d5:fc:b7:fc:ab:c2:
+ af:8f:bd:6e:48:ec:54:f0:f8:a8:b4:d7:6c:11:0e:
+ f9:16:ab
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ AA:3C:0D:95:CD:14:0A:9C:A5:2D:09:6E:EE:5B:43:A9:AF:3F:6E:54
+ Signature Algorithm: sha1WithRSAEncryption
+ a3:ec:06:1b:66:b3:cb:a3:12:38:ef:30:dc:a6:a1:fc:d3:52:
+ d0:73:c8:a9:4d:0b:8e:02:2a:08:a6:4f:55:41:2f:46:2b:cf:
+ e9:04:07:9d:42:47:0d:88:64:1f:39:ae:d7:9b:30:43:47:f9:
+ ba:96:a8:2f:7a:6e:4b:22:9c:65:c7:9c:8c:c6:d2:f2:5f:a9:
+ fd:de:eb:9e:7a:13:b8:22:0c:59:15:90:ba:65:b7:08:3d:dd:
+ 2e:e2:09:be:47:53:25:0a:8c:d3:e0:78:e9:1a:15:8e:32:b2:
+ 5f:76:e1:68:3c:2f:33:3f:38:17:ff:3b:ad:43:b7:0e:87:08:
+ 97:6b:8d:a7:6c:3b:de:1a:18:3d:5b:74:0b:87:03:8a:49:b0:
+ 22:84:2a:72:f1:01:c3:b5:55:9e:4a:56:c1:96:6c:ba:9c:eb:
+ 58:ce:4e:53:fd:b8:99:02:c1:d5:62:ef:b5:44:73:1c:c6:4f:
+ 26:f9:8d:6b:e9:58:be:3c:4a:56:ef:65:6a:f5:71:1c:3b:8e:
+ f4:ae:43:44:ab:26:80:41:da:a9:6b:9b:63:49:bc:39:76:3b:
+ 1e:fe:a5:24:0e:4c:59:51:9d:47:c4:ce:2b:90:65:e8:f8:ae:
+ ab:aa:14:cc:d2:4a:cf:85:20:40:dd:80:49:ea:7c:98:04:ee:
+ 57:41:e6:bc:13:fc:28:5e:08:5c:ee:fa:1b:72:ea:80:e8:ba:
+ 7e:d6:34:eb:fc:88:f1:16:42:b2:bb:22:9c:e0:36:84:23:f5:
+ 20:86:dc:38:55:89:dc:0e:67:7c:c7:bb:2f:36:25:bc:ca:be:
+ 2b:1c:79:26:79:2b:49:17:3c:76:02:cf:f9:e3:8a:3f:15:69:
+ 2c:12:5c:99:93:85:11:c8:90:68:d6:f1:8d:87:30:bf:0d:ec:
+ 89:9a:f4:48:cc:26:95:c7:65:cd:30:cc:d0:93:c3:80:3f:ad:
+ a6:fa:7c:88:82:53:0e:9b:16:c3:dd:27:9a:d0:99:05:fb:2d:
+ d0:e6:fa:08:92:46:ee:dd:44:9d:56:b2:95:52:99:db:5a:20:
+ 16:c9:a7:a3:0b:a3:c5:d8:0a:b7:c2:cf:f7:95:a4:df:4c:f9:
+ 2f:69:a0:27:6e:0f:85:3e:76:b4:3d:6b:f7:4a:de:1a:de:a4:
+ d3:01:91:f1:44:59:44:2c:93:15:52:99:da:6e:93:b8:da:54:
+ b5:06:ff:82:9b:cf:57:0c:7d:06:6b:ff:ce:b9:c9:47:62:c9:
+ 15:f4:67:4e:57:12:74:d7:b5:31:53:cc:eb:d7:05:4d:34:58:
+ a9:5d:33:85:2d:72:6f:12:99:7e:60:63:27:05:74:8b:85:0c:
+ 0b:f9:b3:b4:e7:f6:4e:4b
+-----BEGIN CERTIFICATE-----
+MIIFBzCCAu+gAwIBAgIBCTANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUxMFoYDzI1
+MTgxMTIxMjIyNTEwWjAnMQswCQYDVQQGEwJTRTEYMBYGA1UEAwwPd3d3LnRlc3Qu
+aDVsLnNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu8qFnD1rWiEb
+LIQ1SDe8E2KT/3u+SUDiNrV6VKTjD7GHKd5rfYbstiXFnNwTBldMgBuG8KzmZI+q
+Y8woSVyECbgPMZndNtJCtarfMfYnysJMUBFbAZQX2ipcIeW1gSNpPk8dCEiVVzB3
+lq6beIcQ5G2Q6HitGUE9uJEctgR4UuXkPyjfARPaqsskz/WT+QK4xdxH+3nl3p4Z
+syirLb1zSA9xCraBWm0CbZzIwxTVgr8ZuNBvWDJsdpHzB2slSln0LcmN2u7MMFtb
+2PMNYyiNnN8htTpB4FXQX/EyRQtrQLbYQwx7KD0tfEAZouDWogsyZaOB6RzlavZh
+fGb6xhC/XR3ZwRpn+6BDFf/1QFoMiktIONXHd0gZ9yHecxeXzwPXw4QiOK7yvtJh
+rzc4MUEBl1iTuoDauwAzqCuYNICLAB6DAsQmP1xRqSnjrLE2MVeHQ5RXOhf0bTS/
+I7aiVtK3cn41NNlYRsFkLT/n/+T9QhHZBJi6nYjs5667EUL9AMskFyeULKA03xiL
+erw5VWwCO0TPpELz44Fb1pCOeNc/TO9s3k1+Qc6Hj8A4pFcFYzKFw96IqowLBN/D
+hmRMGZHh5LL49vP+k8M+wbF0tHL/iJSNNKOwnVWq/ry8QVVJivHu3foOofq5cafV
+/Lf8q8Kvj71uSOxU8PiotNdsEQ75FqsCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwHQYDVR0OBBYEFKo8DZXNFAqcpS0Jbu5bQ6mvP25UMA0GCSqGSIb3
+DQEBBQUAA4ICAQCj7AYbZrPLoxI47zDcpqH801LQc8ipTQuOAioIpk9VQS9GK8/p
+BAedQkcNiGQfOa7XmzBDR/m6lqgvem5LIpxlx5yMxtLyX6n93uueehO4IgxZFZC6
+ZbcIPd0u4gm+R1MlCozT4HjpGhWOMrJfduFoPC8zPzgX/zutQ7cOhwiXa42nbDve
+Ghg9W3QLhwOKSbAihCpy8QHDtVWeSlbBlmy6nOtYzk5T/biZAsHVYu+1RHMcxk8m
++Y1r6Vi+PEpW72Vq9XEcO470rkNEqyaAQdqpa5tjSbw5djse/qUkDkxZUZ1HxM4r
+kGXo+K6rqhTM0krPhSBA3YBJ6nyYBO5XQea8E/woXghc7vobcuqA6Lp+1jTr/Ijx
+FkKyuyKc4DaEI/Ughtw4VYncDmd8x7svNiW8yr4rHHkmeStJFzx2As/544o/FWks
+ElyZk4URyJBo1vGNhzC/DeyJmvRIzCaVx2XNMMzQk8OAP62m+nyIglMOmxbD3Sea
+0JkF+y3Q5voIkkbu3USdVrKVUpnbWiAWyaejC6PF2Aq3ws/3laTfTPkvaaAnbg+F
+Pna0PWv3St4a3qTTAZHxRFlELJMVUpnabpO42lS1Bv+Cm89XDH0Ga//OuclHYskV
+9GdOVxJ017UxU8zr1wVNNFipXTOFLXJvEpl+YGMnBXSLhQwL+bO05/ZOSw==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/https.key b/third_party/heimdal/lib/hx509/data/https.key
new file mode 100644
index 0000000..59d7bfd
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/https.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+PJKGZjLpgXkldTcRw00N0NRePp73WR86
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/j.pem b/third_party/heimdal/lib/hx509/data/j.pem
new file mode 100644
index 0000000..45ae8e8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/j.pem
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEajCCA1KgAwIBAgIBATANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJKUDEN
+MAsGA1UECgwESlBLSTEpMCcGA1UECwwgUHJlZmVjdHVyYWwgQXNzb2NpYXRpb24g
+Rm9yIEpQS0kxETAPBgNVBAsMCEJyaWRnZUNBMB4XDTAzMTIyNzA1MDgxNVoXDTEz
+MTIyNjE0NTk1OVowWjELMAkGA1UEBhMCSlAxDTALBgNVBAoMBEpQS0kxKTAnBgNV
+BAsMIFByZWZlY3R1cmFsIEFzc29jaWF0aW9uIEZvciBKUEtJMREwDwYDVQQLDAhC
+cmlkZ2VDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANTnUmg7K3m8
+52vd77kwkq156euwoWm5no8E8kmaTSc7x2RABPpqNTlMKdZ6ttsyYrqREeDkcvPL
+yF7yf/I8+innasNtsytcTAy8xY8Avsbd4JkCGW9dyPjk9pzzc3yLQ64Rx2fujRn2
+agcEVdPCr/XpJygX8FD5bbhkZ0CVoiASBmlHOcC3YpFlfbT1QcpOSOb7o+VdKVEi
+MMfbBuU2IlYIaSr/R1nO7RPNtkqkFWJ1/nKjKHyzZje7j70qSxb+BTGcNgTHa1YA
+UrogKB+UpBftmb4ds+XlkEJ1dvwokiSbCDaWFKD+YD4B2s0bvjCbw8xuZFYGhNyR
+/2D5XfN1s2MCAwEAAaOCATkwggE1MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MG0GA1UdHwRmMGQwYqBgoF6kXDBaMQswCQYDVQQGEwJKUDENMAsGA1UE
+CgwESlBLSTEpMCcGA1UECwwgUHJlZmVjdHVyYWwgQXNzb2NpYXRpb24gRm9yIEpQ
+S0kxETAPBgNVBAsMCEJyaWRnZUNBMIGDBgNVHREEfDB6pHgwdjELMAkGA1UEBhMC
+SlAxJzAlBgNVBAoMHuWFrOeahOWAi+S6uuiqjeiovOOCteODvOODk+OCuTEeMBwG
+A1UECwwV6YO96YGT5bqc55yM5Y2U6K2w5LyaMR4wHAYDVQQLDBXjg5bjg6rjg4Pj
+grjoqo3oqLzlsYAwHQYDVR0OBBYEFNQXMiCqQNkR2OaZmQgLtf8mR8p8MA0GCSqG
+SIb3DQEBBQUAA4IBAQATjJo4reTNPC5CsvAKu1RYT8PyXFVYHbKsEpGt4GR8pDCg
+HEGAiAhHSNrGh9CagZMXADvlG0gmMOnXowriQQixrtpkmx0TB8tNAlZptZWkZC+R
+8TnjOkHrk2nFAEC3ezbdK0R7MR4tJLDQCnhEWbg50rf0wZ/aF8uAaVeEtHXa6W0M
+Xq3dSe0XAcrLbX4zZHQTaWvdpLAIjl6DZ3SCieRMyoWUL+LXaLFdTP5WBCd+No58
+IounD9X4xxze2aeRVaiV/WnQ0OSPNS7n7YXy6xQdnaOU4KRW/Lne1EDf5IfWC/ih
+bVAmhZMbcrkWWcsR6aCPG+2mV3zTD6AUzuKPal8Y
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/kdc.crt b/third_party/heimdal/lib/hx509/data/kdc.crt
new file mode 100644
index 0000000..a92fcc0
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/kdc.crt
@@ -0,0 +1,122 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 8 (0x8)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:09 2019 GMT
+ Not After : Nov 21 22:25:09 2518 GMT
+ Subject: C=SE, CN=kdc
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:d1:73:ec:58:67:7a:65:30:ab:19:15:a1:bf:1e:
+ de:db:e5:4a:92:f0:99:8a:eb:02:6d:e4:31:1a:c7:
+ 4d:07:57:b1:82:9e:d2:d2:c7:f3:0b:b2:82:61:5c:
+ ba:38:c3:54:e9:e1:be:6b:5f:0d:22:62:2b:cb:d5:
+ 34:0e:63:0b:50:8a:8b:b3:be:6a:e1:85:dc:b1:28:
+ 13:ee:dd:6e:40:d5:48:1d:eb:aa:04:0b:e7:c8:1c:
+ 6d:60:54:b6:cc:be:52:5a:88:22:ce:07:2d:3f:cb:
+ fc:00:ab:8b:a5:e7:32:8e:b1:8b:03:d8:81:a2:69:
+ d4:9f:3a:ff:da:b5:e3:0d:e3:21:54:29:cb:61:ba:
+ 16:13:94:97:1b:72:24:6d:da:d7:d9:35:b1:57:f1:
+ 3b:9d:ee:90:76:4e:58:1f:4e:76:12:c6:89:2a:54:
+ bf:e8:53:5a:de:05:79:93:0b:41:2c:03:c5:30:58:
+ a8:e6:57:08:f9:47:7c:c0:3a:5c:eb:1b:33:68:52:
+ 02:19:08:e6:35:48:05:a7:51:22:89:1c:1e:c8:0b:
+ 55:73:b2:c9:75:f9:74:aa:de:5e:3a:54:f8:96:47:
+ cf:25:2d:75:e7:71:74:31:91:17:85:44:89:8a:16:
+ 88:ca:12:dd:0e:36:4d:e5:af:b3:db:d3:7c:53:8d:
+ 7a:08:69:92:72:81:c8:13:c7:71:96:8f:2d:54:98:
+ c9:63:10:26:be:59:8f:db:82:47:c1:29:c6:28:7f:
+ a0:16:bf:85:a2:eb:2f:2f:46:86:6b:77:1f:31:30:
+ d4:52:35:32:09:16:cd:48:ec:3c:4c:2c:03:e5:b9:
+ 90:e9:f7:b4:7d:97:91:31:27:4e:df:b6:bd:b6:ec:
+ ca:47:16:00:58:e9:87:4f:20:af:ef:4c:34:42:5b:
+ 3e:28:aa:cd:39:75:3b:6f:7c:b9:7b:50:76:67:25:
+ 31:46:f5:34:aa:c6:5a:22:77:b5:9d:6d:88:4d:f1:
+ e6:e7:ca:d2:d8:70:10:58:39:58:0f:ce:8d:b3:4d:
+ e4:f4:80:ca:31:75:3c:38:61:6c:d9:17:d2:aa:72:
+ f9:e0:ac:86:ab:33:16:84:e8:c8:de:58:9d:78:ac:
+ f1:2a:64:b8:e3:f2:cb:20:42:dd:f9:bd:2e:c2:84:
+ 6e:11:34:76:a5:c5:54:c5:51:9b:cb:85:d1:05:82:
+ 1c:33:d5:95:18:ad:4c:94:d2:7b:4f:72:23:ff:c1:
+ 4b:a2:ea:1a:3a:18:c2:f5:c8:08:76:00:12:25:e5:
+ ee:30:b9:8d:2f:0f:95:3d:70:ac:6a:eb:d8:c5:71:
+ 9a:cf:a9:a6:6a:ce:45:07:a4:41:de:85:fb:ad:e0:
+ 39:0b:6f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Extended Key Usage:
+ Signing KDC Response
+ X509v3 Subject Key Identifier:
+ 62:AF:D5:17:E4:9F:2A:8D:8A:CA:2B:05:E1:25:66:BB:61:03:77:EA
+ X509v3 Subject Alternative Name:
+ othername:<unsupported>
+ Signature Algorithm: sha1WithRSAEncryption
+ 41:29:9f:70:6b:36:28:cc:86:e1:4d:ae:25:34:b1:24:ab:f8:
+ 03:de:28:da:d1:13:8e:03:d3:5a:57:72:69:f9:04:1c:e0:1d:
+ 14:91:c7:a0:8b:ab:c7:61:6e:4e:86:2a:2a:40:22:10:10:58:
+ 0c:18:95:eb:d2:15:18:35:3c:fc:42:25:1a:dc:03:cb:ba:f3:
+ 81:80:d2:45:4e:c6:90:11:2f:e9:db:76:9a:e3:1d:0c:04:dc:
+ fb:d9:ec:bd:48:38:66:78:d6:52:c2:bc:ae:20:9b:1d:87:28:
+ 9f:38:fa:db:8f:17:1f:3e:29:85:17:a0:95:bd:72:88:0c:93:
+ 88:ba:8e:31:67:2b:03:b0:bf:3a:7e:e4:e2:82:f7:6c:36:1a:
+ d1:8e:7c:87:63:17:e4:68:7f:4b:e7:dc:40:b5:02:5a:62:be:
+ 54:ee:11:30:39:80:2a:c0:3e:8f:3b:67:cb:9d:9f:ee:c1:ea:
+ f1:4c:e8:55:24:6a:73:84:ef:82:ca:99:ec:84:05:5e:82:a1:
+ 52:40:5e:71:10:c9:c3:9b:18:ce:7f:50:db:8a:49:d4:b6:b9:
+ 5e:ef:13:4c:e8:be:76:2b:cc:f9:eb:9e:9b:4b:29:8e:ee:1c:
+ e5:bd:08:f0:50:63:e2:c3:94:20:2f:fe:cb:6a:ed:2b:2a:e2:
+ 51:44:3d:06:d1:b4:43:26:43:07:4d:c9:e1:4f:9d:3d:0f:a6:
+ 74:93:ff:51:74:c8:aa:2d:76:ab:93:6f:84:47:2d:70:37:d2:
+ 21:f0:cb:4d:a5:8b:df:91:4b:95:f0:ba:fe:d9:fc:f2:ed:b5:
+ e7:91:03:5a:ad:12:43:f3:ba:c8:a7:51:34:9b:40:bd:71:39:
+ af:b1:9f:e4:9f:3f:1b:27:a5:84:43:a2:c3:3f:52:63:a8:bf:
+ 8b:59:82:53:b5:26:64:16:73:90:f8:7b:7d:ce:f6:41:b6:8b:
+ 81:56:90:c2:ff:46:46:8f:63:3d:95:d9:f0:49:73:37:d9:14:
+ 2b:26:95:ac:19:29:1d:cb:c2:03:d7:36:4e:4a:39:3e:51:02:
+ de:aa:dc:6b:77:a8:57:ba:50:21:0e:8e:b7:48:bc:44:fa:45:
+ db:c9:bb:72:ea:e4:2a:7a:35:75:3c:68:29:5d:b9:57:0b:d3:
+ 2e:2c:4f:01:1b:f0:21:0c:fc:95:17:b7:40:be:aa:0c:f9:04:
+ 60:6a:d1:54:0d:b9:68:d7:e9:7a:f4:96:ad:f1:a0:15:15:c2:
+ 51:61:44:5f:0e:bb:98:d1:81:9f:c1:81:d6:e2:26:d5:11:56:
+ d2:cd:0f:9c:6b:69:f0:78:24:ff:bf:df:02:2b:0d:d1:83:5b:
+ 14:4d:c0:e2:80:47:65:2b
+-----BEGIN CERTIFICATE-----
+MIIFWzCCA0OgAwIBAgIBCDANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwOVoYDzI1
+MTgxMTIxMjIyNTA5WjAbMQswCQYDVQQGEwJTRTEMMAoGA1UEAwwDa2RjMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0XPsWGd6ZTCrGRWhvx7e2+VKkvCZ
+iusCbeQxGsdNB1exgp7S0sfzC7KCYVy6OMNU6eG+a18NImIry9U0DmMLUIqLs75q
+4YXcsSgT7t1uQNVIHeuqBAvnyBxtYFS2zL5SWogizgctP8v8AKuLpecyjrGLA9iB
+omnUnzr/2rXjDeMhVCnLYboWE5SXG3IkbdrX2TWxV/E7ne6Qdk5YH052EsaJKlS/
+6FNa3gV5kwtBLAPFMFio5lcI+Ud8wDpc6xszaFICGQjmNUgFp1EiiRweyAtVc7LJ
+dfl0qt5eOlT4lkfPJS1153F0MZEXhUSJihaIyhLdDjZN5a+z29N8U416CGmScoHI
+E8dxlo8tVJjJYxAmvlmP24JHwSnGKH+gFr+FousvL0aGa3cfMTDUUjUyCRbNSOw8
+TCwD5bmQ6fe0fZeRMSdO37a9tuzKRxYAWOmHTyCv70w0Qls+KKrNOXU7b3y5e1B2
+ZyUxRvU0qsZaIne1nW2ITfHm58rS2HAQWDlYD86Ns03k9IDKMXU8OGFs2RfSqnL5
+4KyGqzMWhOjI3lideKzxKmS44/LLIELd+b0uwoRuETR2pcVUxVGby4XRBYIcM9WV
+GK1MlNJ7T3Ij/8FLouoaOhjC9cgIdgASJeXuMLmNLw+VPXCsauvYxXGaz6mmas5F
+B6RB3oX7reA5C28CAwEAAaOBmDCBlTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAS
+BgNVHSUECzAJBgcrBgEFAgMFMB0GA1UdDgQWBBRir9UX5J8qjYrKKwXhJWa7YQN3
+6jBIBgNVHREEQTA/oD0GBisGAQUCAqAzMDGgDRsLVEVTVC5INUwuU0WhIDAeoAMC
+AQGhFzAVGwZrcmJ0Z3QbC1RFU1QuSDVMLlNFMA0GCSqGSIb3DQEBBQUAA4ICAQBB
+KZ9wazYozIbhTa4lNLEkq/gD3ija0ROOA9NaV3Jp+QQc4B0Ukcegi6vHYW5Ohioq
+QCIQEFgMGJXr0hUYNTz8QiUa3APLuvOBgNJFTsaQES/p23aa4x0MBNz72ey9SDhm
+eNZSwryuIJsdhyifOPrbjxcfPimFF6CVvXKIDJOIuo4xZysDsL86fuTigvdsNhrR
+jnyHYxfkaH9L59xAtQJaYr5U7hEwOYAqwD6PO2fLnZ/uwerxTOhVJGpzhO+Cypns
+hAVegqFSQF5xEMnDmxjOf1DbiknUtrle7xNM6L52K8z5656bSymO7hzlvQjwUGPi
+w5QgL/7Lau0rKuJRRD0G0bRDJkMHTcnhT509D6Z0k/9RdMiqLXark2+ERy1wN9Ih
+8MtNpYvfkUuV8Lr+2fzy7bXnkQNarRJD87rIp1E0m0C9cTmvsZ/knz8bJ6WEQ6LD
+P1JjqL+LWYJTtSZkFnOQ+Ht9zvZBtouBVpDC/0ZGj2M9ldnwSXM32RQrJpWsGSkd
+y8ID1zZOSjk+UQLeqtxrd6hXulAhDo63SLxE+kXbybty6uQqejV1PGgpXblXC9Mu
+LE8BG/AhDPyVF7dAvqoM+QRgatFUDblo1+l69Jat8aAVFcJRYURfDruY0YGfwYHW
+4ibVEVbSzQ+ca2nweCT/v98CKw3Rg1sUTcDigEdlKw==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/kdc.key b/third_party/heimdal/lib/hx509/data/kdc.key
new file mode 100644
index 0000000..1984f20
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/kdc.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+RCBsiAswkZ1vTsDII1BKYPknuvgH9g==
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/key.der b/third_party/heimdal/lib/hx509/data/key.der
new file mode 100644
index 0000000..e7c665e
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/key.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/key2.der b/third_party/heimdal/lib/hx509/data/key2.der
new file mode 100644
index 0000000..fe3f413
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/key2.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/mkcert.sh b/third_party/heimdal/lib/hx509/data/mkcert.sh
new file mode 100755
index 0000000..c06528d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/mkcert.sh
@@ -0,0 +1,83 @@
+#! /bin/bash
+
+set -e
+
+DAYS=182500
+
+key() {
+ local key=$1; shift
+
+ if [ ! -f "${key}.pem" ]; then
+ openssl genpkey \
+ -paramfile <(openssl ecparam -name prime256v1) \
+ -out "${key}.pem"
+ fi
+}
+
+req() {
+ local key=$1; shift
+ local dn=$1; shift
+
+ openssl req -new -sha256 -key "${key}.pem" \
+ -config <(printf "[req]\n%s\n%s\n[dn]\nCN_default=foo\n" \
+ "prompt = yes" "distinguished_name = dn") \
+ -subj "${dn}"
+}
+
+cert() {
+ local cert=$1; shift
+ local exts=$1; shift
+
+ openssl x509 -req -sha256 -out "${cert}.pem" \
+ -extfile <(printf "%s\n" "$exts") "$@"
+}
+
+genroot() {
+ local dn=$1; shift
+ local key=$1; shift
+ local cert=$1; shift
+
+ exts=$(printf "%s\n%s\n%s\n%s\n" \
+ "subjectKeyIdentifier = hash" \
+ "authorityKeyIdentifier = keyid" \
+ "basicConstraints = CA:true" \
+ "keyUsage = keyCertSign, cRLSign" )
+ key "$key"; req "$key" "$dn" |
+ cert "$cert" "$exts" -signkey "${key}.pem" \
+ -set_serial 1 -days "${DAYS}"
+}
+
+genee() {
+ local dn=$1; shift
+ local key=$1; shift
+ local cert=$1; shift
+ local cakey=$1; shift
+ local cacert=$1; shift
+
+ exts=$(printf "%s\n%s\n%s\n%s\n" \
+ "subjectKeyIdentifier = hash" \
+ "authorityKeyIdentifier = keyid, issuer" \
+ "basicConstraints = CA:false" \
+ "keyUsage = digitalSignature, keyEncipherment, dataEncipherment" \
+ )
+ key "$key"; req "$key" "$dn" |
+ cert "$cert" "$exts" -CA "${cacert}.pem" -CAkey "${cakey}.pem" \
+ -set_serial 2 -days "${DAYS}" "$@"
+}
+
+
+genroot "/C=SE/O=Heimdal/CN=CA secp256r1" \
+ secp256r1TestCA.key secp256r1TestCA.cert
+genee "/C=SE/O=Heimdal/CN=Server" \
+ secp256r2TestServer.key secp256r2TestServer.cert \
+ secp256r1TestCA.key secp256r1TestCA.cert
+genee "/C=SE/O=Heimdal/CN=Client" \
+ secp256r2TestClient.key secp256r2TestClient.cert \
+ secp256r1TestCA.key secp256r1TestCA.cert
+
+cat secp256r1TestCA.key.pem secp256r1TestCA.cert.pem > \
+ secp256r1TestCA.pem
+cat secp256r2TestClient.cert.pem secp256r2TestClient.key.pem > \
+ secp256r2TestClient.pem
+cat secp256r2TestServer.cert.pem secp256r2TestServer.key.pem > \
+ secp256r2TestServer.pem
diff --git a/third_party/heimdal/lib/hx509/data/n0ll.pem b/third_party/heimdal/lib/hx509/data/n0ll.pem
new file mode 100644
index 0000000..4b17377
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/n0ll.pem
@@ -0,0 +1,47 @@
+https://www.noisebridge.net/pipermail/noisebridge-discuss/2009-September/008400.html
+
+Jacob Appelbaum jacob at appelbaum.net
+Tue Sep 29 22:51:33 PDT 2009
+
+
+I hope this release will help with confirmation of the bug and with
+regression testing. Feel free to use this certificate for anything
+relating to free software too. Consider it released into the public
+domain of interesting integers.
+
+-----BEGIN CERTIFICATE-----
+MIIGTjCCBbegAwIBAgIDExefMA0GCSqGSIb3DQEBBQUAMIIBEjELMAkGA1UEBhMC
+RVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMSkwJwYD
+VQQKEyBJUFMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgcy5sLjEuMCwGA1UEChQl
+Z2VuZXJhbEBpcHNjYS5jb20gQy5JLkYuICBCLUI2MjIxMDY5NTEuMCwGA1UECxMl
+aXBzQ0EgQ0xBU0VBMSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMl
+aXBzQ0EgQ0xBU0VBMSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEgMB4GCSqGSIb3
+DQEJARYRZ2VuZXJhbEBpcHNjYS5jb20wHhcNMDkwNzMwMDcxNDQyWhcNMTEwNzMw
+MDcxNDQyWjCBnjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAU
+BgNVBAcTDVNhbiBGcmFuY2lzY28xFDASBgNVBAoTC05vaXNlYnJpZGdlMSMwIQYD
+VQQLExpNb3hpZSBNYXJsaW5zcGlrZSBGYW4gQ2x1YjEnMCUGA1UEAxQeKgB0aG91
+Z2h0Y3JpbWUubm9pc2VicmlkZ2UubmV0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQDPTRdCAI0MQZUxjEAwvF5CtigJdS8ZYdmrTezzRMQcAZVvJ+twB5hPHgXQ
+82xJRebeSHpZ8MKTajecAnJPvRQ2JqFwl9T+SyTozSkeYRqFsG+WBoMQE9aJn70H
+Z/FC3ptjZ4uW+QbvfJNLavk5MTJ/mFnvzpG+Bc7wgjPYdgZMnwIDAQABo4IDITCC
+Ax0wCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwCwYDVR0PBAQDAgP4MBMG
+A1UdJQQMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBStfpIwBXE+eXWUWtE3s5JqXon2
+TzAfBgNVHSMEGDAWgBQOB2DUOckbW12QeyPI0jSdSppGOTAJBgNVHREEAjAAMBwG
+A1UdEgQVMBOBEWdlbmVyYWxAaXBzY2EuY29tMHIGCWCGSAGG+EIBDQRlFmNPcmdh
+bml6YXRpb24gSW5mb3JtYXRpb24gTk9UIFZBTElEQVRFRC4gQ0xBU0VBMSBTZXJ2
+ZXIgQ2VydGlmaWNhdGUgaXNzdWVkIGJ5IGh0dHBzOi8vd3d3Lmlwc2NhLmNvbS8w
+LwYJYIZIAYb4QgECBCIWIGh0dHBzOi8vd3d3Lmlwc2NhLmNvbS9pcHNjYTIwMDIv
+MEMGCWCGSAGG+EIBBAQ2FjRodHRwczovL3d3dy5pcHNjYS5jb20vaXBzY2EyMDAy
+L2lwc2NhMjAwMkNMQVNFQTEuY3JsMEYGCWCGSAGG+EIBAwQ5FjdodHRwczovL3d3
+dy5pcHNjYS5jb20vaXBzY2EyMDAyL3Jldm9jYXRpb25DTEFTRUExLmh0bWw/MEMG
+CWCGSAGG+EIBBwQ2FjRodHRwczovL3d3dy5pcHNjYS5jb20vaXBzY2EyMDAyL3Jl
+bmV3YWxDTEFTRUExLmh0bWw/MEEGCWCGSAGG+EIBCAQ0FjJodHRwczovL3d3dy5p
+cHNjYS5jb20vaXBzY2EyMDAyL3BvbGljeUNMQVNFQTEuaHRtbDCBgwYDVR0fBHww
+ejA5oDegNYYzaHR0cDovL3d3dy5pcHNjYS5jb20vaXBzY2EyMDAyL2lwc2NhMjAw
+MkNMQVNFQTEuY3JsMD2gO6A5hjdodHRwOi8vd3d3YmFjay5pcHNjYS5jb20vaXBz
+Y2EyMDAyL2lwc2NhMjAwMkNMQVNFQTEuY3JsMDIGCCsGAQUFBwEBBCYwJDAiBggr
+BgEFBQcwAYYWaHR0cDovL29jc3AuaXBzY2EuY29tLzANBgkqhkiG9w0BAQUFAAOB
+gQAjzXaLBu+/+RP0vQ6WjW/Pxgm4WQYhecqZ2+7ZFbsUCMJPQ8XE2uv+rIteGnRF
+Zr3hYb+dVlfUnethjPhazZW+/hU4FePqmlbTtmMe+zMLThiScyC8y3EW4L4BZYcp
+p1drPlZIj2RmSgPQ99oToUk5O6t+LMg1N14ajr9TpM8yNQ==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/nist-data b/third_party/heimdal/lib/hx509/data/nist-data
new file mode 100644
index 0000000..7407b80
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/nist-data
@@ -0,0 +1,91 @@
+# $Id$
+# id verify cert hxtool-verify-arguments...
+# p(ass) f(ail)
+# Those id's that end with i are invariants of the orignal test
+#
+# 4.1 Signature Verification
+#
+4.1.1 p ValidCertificatePathTest1EE.crt GoodCACert.crt GoodCACRL.crl
+4.1.2 f InvalidCASignatureTest2EE.crt BadSignedCACert.crt BadSignedCACRL.crl
+4.1.3 f InvalidEESignatureTest3EE.crt GoodCACert.crt GoodCACRL.crl
+#4.1.4 p ValidDSASignaturesTest4EE.crt DSACACert.crt DSACACRL.crl
+#4.1.5 p ValidDSAParameterInheritanceTest5EE.crl DSAParametersInheritedCACert.crt DSAParametersInheritedCACRL.crl DSACACert.crt DSACACRL.crl
+#4.1.6 f InvalidDSASignaturesTest6EE.crt DSACACert.crt DSACACRL.crl
+#
+# 4.2 Validity Periods
+#
+4.2.1 f InvalidCAnotBeforeDateTest1EE.crt BadnotBeforeDateCACert.crt BadnotBeforeDateCACRL.crl
+4.2.2 f InvalidEEnotBeforeDateTest2EE.crt GoodCACert.crt GoodCACRL.crl
+4.2.3 p Validpre2000UTCnotBeforeDateTest3EE.crt GoodCACert.crt GoodCACRL.crl
+4.2.4 p ValidGeneralizedTimenotBeforeDateTest4EE.crt GoodCACert.crt GoodCACRL.crl
+4.2.5 f InvalidCAnotAfterDateTest5EE.crt BadnotAfterDateCACert.crt BadnotAfterDateCACRL.crl
+4.2.6 f InvalidEEnotAfterDateTest6EE.crt GoodCACert.crt GoodCACRL.crl
+4.2.7 f Invalidpre2000UTCEEnotAfterDateTest7EE.crt GoodCACert.crt GoodCACRL.crl
+#4.2.8 p ValidGeneralizedTimenotAfterDateTest8EE.crt GoodCACert.crt GoodCACRL.crl
+#
+# 4.4 CRtests
+#
+4.4.1 f InvalidMissingCRLTest1EE.crt NoCRLCACert.crt
+4.4.1i p InvalidMissingCRLTest1EE.crt --missing-revoke NoCRLCACert.crt
+4.4.2 f InvalidRevokedEETest3EE.crt GoodCACert.crt InvalidRevokedCATest2EE.crt GoodCACRL.crl RevokedsubCACRL.crl
+4.4.2i p InvalidRevokedEETest3EE.crt --missing-revoke GoodCACert.crt InvalidRevokedCATest2EE.crt
+4.4.3 f InvalidRevokedEETest3EE.crt GoodCACert.crt GoodCACRL.crl
+4.4.3i p InvalidRevokedEETest3EE.crt --missing-revoke GoodCACert.crt
+4.4.4 f InvalidBadCRLSignatureTest4EE.crt BadCRLSignatureCACert.crt BadCRLSignatureCACRL.crl
+4.4.4i p InvalidBadCRLSignatureTest4EE.crt --missing-revoke BadCRLSignatureCACert.crt
+4.4.5 f InvalidBadCRLIssuerNameTest5EE.crt BadCRLIssuerNameCACert.crt BadCRLIssuerNameCACRL.crl
+4.4.5i p InvalidBadCRLIssuerNameTest5EE.crt --missing-revoke BadCRLIssuerNameCACert.crt
+4.4.6 f InvalidWrongCRLTest6EE.crt WrongCRLCACert.crt WrongCRLCACRL.crl
+4.4.7 p ValidTwoCRLsTest7EE.crt TwoCRLsCACert.crt TwoCRLsCAGoodCRL.crl TwoCRLsCABadCRL.crl
+4.4.8 f InvalidUnknownCRLEntryExtensionTest8EE.crt UnknownCRLEntryExtensionCACert.crt UnknownCRLEntryExtensionCACRL.crl
+4.4.9 f InvalidUnknownCRLExtensionTest9EE.crt UnknownCRLExtensionCACert.crt UnknownCRLExtensionCACRL.crl
+4.4.10 f InvalidUnknownCRLExtensionTest10EE.crt UnknownCRLExtensionCACert.crt UnknownCRLExtensionCACRL.crl
+4.4.11 f InvalidOldCRLnextUpdateTest11EE.crt OldCRLnextUpdateCACert.crt OldCRLnextUpdateCACRL.crl
+4.4.12 f Invalidpre2000CRLnextUpdateTest12EE.crt pre2000CRLnextUpdateCACert.crt pre2000CRLnextUpdateCACRL.crl
+#4.4.13-xxx s ValidGeneralizedTimeCRLnextUpdateTest13EE.crt GeneralizedTimeCRLnextUpdateCACert.crt GeneralizedTimeCRLnextUpdateCACRL.crl
+4.4.14 p ValidNegativeSerialNumberTest14EE.crt NegativeSerialNumberCACert.crt NegativeSerialNumberCACRL.crl
+4.4.15 f InvalidNegativeSerialNumberTest15EE.crt NegativeSerialNumberCACert.crt NegativeSerialNumberCACRL.crl
+4.4.16 p ValidLongSerialNumberTest16EE.crt LongSerialNumberCACert.crt LongSerialNumberCACRL.crl
+4.4.17 p ValidLongSerialNumberTest17EE.crt LongSerialNumberCACert.crt LongSerialNumberCACRL.crl
+4.4.18 f InvalidLongSerialNumberTest18EE.crt LongSerialNumberCACert.crt LongSerialNumberCACRL.crl
+#
+#
+# 4.8 Ceificate Policies
+incomplete4.8.2 p AllCertificatesNoPoliciesTest2EE.crt NoPoliciesCACert.crt NoPoliciesCACRL.crl
+incomplete4.8.10 p AllCertificatesSamePoliciesTest10EE.crt PoliciesP12CACert.crt PoliciesP12CACRL.crl
+incomplete4.8.13 p AllCertificatesSamePoliciesTest13EE.crt PoliciesP123CACert.crt PoliciesP123CACRL.crl
+incomplete4.8.11 p AllCertificatesanyPolicyTest11EE.crt anyPolicyCACert.crt anyPolicyCACRL.crl
+unknown p AnyPolicyTest14EE.crt anyPolicyCACert.crt anyPolicyCACRL.crl
+unknown f BadSignedCACert.crt
+unknown f BadnotAfterDateCACert.crt
+unknown f BadnotBeforeDateCACert.crt
+#
+# 4.13 Name Constraints
+#
+4.13.1 p ValidDNnameConstraintsTest1EE.crt nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+4.13.2 f InvalidDNnameConstraintsTest2EE.crt nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+4.13.3 f InvalidDNnameConstraintsTest3EE.crt nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+4.13.4 p ValidDNnameConstraintsTest4EE.crt nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+4.13.5 p ValidDNnameConstraintsTest5EE.crt nameConstraintsDN2CACert.crt nameConstraintsDN2CACRL.crl
+4.13.6 p ValidDNnameConstraintsTest6EE.crt nameConstraintsDN3CACert.crt nameConstraintsDN3CACRL.crl
+4.13.7 f InvalidDNnameConstraintsTest7EE.crt nameConstraintsDN3CACert.crt nameConstraintsDN3CACRL.crl
+4.13.8 f InvalidDNnameConstraintsTest8EE.crt nameConstraintsDN4CACert.crt nameConstraintsDN4CACRL.crl
+4.13.9 f InvalidDNnameConstraintsTest9EE.crt nameConstraintsDN4CACert.crt nameConstraintsDN4CACRL.crl
+4.13.10 f InvalidDNnameConstraintsTest10EE.crt nameConstraintsDN5CACert.crt nameConstraintsDN5CACRL.crl
+4.13.11 p ValidDNnameConstraintsTest11EE.crt nameConstraintsDN5CACert.crt nameConstraintsDN5CACRL.crl
+4.13.12 f InvalidDNnameConstraintsTest12EE.crt nameConstraintsDN1subCA1Cert.crt nameConstraintsDN1subCA1CRL.crl nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+4.13.13 f InvalidDNnameConstraintsTest13EE.crt nameConstraintsDN1subCA1Cert.crt nameConstraintsDN1subCA1CRL.crl nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+4.13.14 p ValidDNnameConstraintsTest14EE.crt nameConstraintsDN1subCA2Cert.crt nameConstraintsDN1subCA2CRL.crl nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+4.13.15 f InvalidDNnameConstraintsTest15EE.crt nameConstraintsDN3subCA1Cert.crt nameConstraintsDN3subCA1CRL.crl nameConstraintsDN3CACert.crt nameConstraintsDN3CACRL.crl
+4.13.16 f InvalidDNnameConstraintsTest16EE.crt nameConstraintsDN3subCA1Cert.crt nameConstraintsDN3subCA1CRL.crl nameConstraintsDN3CACert.crt nameConstraintsDN3CACRL.crl
+4.13.17 f InvalidDNnameConstraintsTest17EE.crt nameConstraintsDN3subCA2Cert.crt nameConstraintsDN3subCA2CRL.crl nameConstraintsDN3CACert.crt nameConstraintsDN3CACRL.crl
+4.13.18 p ValidDNnameConstraintsTest18EE.crt nameConstraintsDN3subCA2Cert.crt nameConstraintsDN3subCA2CRL.crl nameConstraintsDN3CACert.crt nameConstraintsDN3CACRL.crl
+#
+# no crl for self issued cert
+#
+#4.13.19 p ValidDNnameConstraintsTest19EE.crt nameConstraintsDN1SelfIssuedCACert.crt nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+# ??
+4.13.20 f InvalidDNnameConstraintsTest20EE.crt nameConstraintsDN1CACert.crt nameConstraintsDN1CACRL.crl
+#4.13.21 p ValidRFC822nameConstraintsTest21EE.crt nameConstraintsRFC822CA1Cert.crt nameConstraintsRFC822CA1CRL.crl
+#page 74
+end
diff --git a/third_party/heimdal/lib/hx509/data/nist-data2 b/third_party/heimdal/lib/hx509/data/nist-data2
new file mode 100644
index 0000000..491beac
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/nist-data2
@@ -0,0 +1,291 @@
+# 4.1.1 Valid Signatures Test1 - Validate Successfully
+0 ValidCertificatePathTest1EE.crt
+# 4.1.2 Invalid CA Signature Test2 - Reject - Invalid signature on intermediate certificate
+1 InvalidCASignatureTest2EE.crt
+# 4.1.3 Invalid EE Signature Test3 - Reject - Invalid signature on end entity certificate
+1 InvalidEESignatureTest3EE.crt
+# 4.1.4 Valid DSA Signatures Test4 - Reject - Application can not process DSA signatures
+1 ValidDSASignaturesTest4EE.crt
+# 4.2.1 Invalid CA notBefore Date Test1 - Reject - notBefore date in intermediate certificate is after the current date
+1 InvalidCAnotBeforeDateTest1EE.crt
+# 4.2.2 Invalid EE notBefore Date Test2 - Reject - notBefore date in end entity certificate is after the current date
+1 InvalidEEnotBeforeDateTest2EE.crt
+# 4.2.3 Valid pre2000 UTC notBefore Date Test3 - Validate Successfully
+0 Validpre2000UTCnotBeforeDateTest3EE.crt
+# 4.2.4 Valid GeneralizedTime notBefore Date Test4 - Validate Successfully
+0 ValidGeneralizedTimenotBeforeDateTest4EE.crt
+# 4.2.5 Invalid CA notAfter Date Test5 - Reject - notAfter date in intermediate certificate is before the current date
+1 InvalidCAnotAfterDateTest5EE.crt
+# 4.2.6 Invalid EE notAfter Date Test6 - Reject - notAfter date in end entity certificate is before the current date
+1 InvalidEEnotAfterDateTest6EE.crt
+# 4.2.7 Invalid pre2000 UTC EE notAfter Date Test7 - Reject - notAfter date in end entity certificate is before the current date
+1 Invalidpre2000UTCEEnotAfterDateTest7EE.crt
+# 4.2.8 Valid GeneralizedTime notAfter Date Test8 - Validate Successfully
+0 ValidGeneralizedTimenotAfterDateTest8EE.crt
+# 4.3.1 Invalid Name Chaining EE Test1 - Reject - names do not chain
+1 InvalidNameChainingTest1EE.crt
+# 4.3.2 Invalid Name Chaining Order Test2 - Reject - names do not chain
+1 InvalidNameChainingOrderTest2EE.crt
+# 4.3.3 Valid Name Chaining Whitespace Test3 - Validate Successfully
+0 ValidNameChainingWhitespaceTest3EE.crt
+# 4.3.4 Valid Name Chaining Whitespace Test4 - Validate Successfully
+0 ValidNameChainingWhitespaceTest4EE.crt
+# 4.3.5 Valid Name Chaining Capitalization Test5 - Validate Successfully
+0 ValidNameChainingCapitalizationTest5EE.crt
+# 4.3.6 Valid Name Chaining UIDs Test6 - Validate Successfully
+0 ValidNameUIDsTest6EE.crt
+# 4.3.9 Valid UTF8String Encoded Names Test9 - Validate Successfully
+0 ValidUTF8StringEncodedNamesTest9EE.crt
+# 4.4.1 Missing CRL Test1 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidMissingCRLTest1EE.crt
+# 4.4.2 Invalid Revoked CA Test2 - Reject - an intermediate certificate has been revoked.
+2 InvalidRevokedCATest2EE.crt
+# 4.4.3 Invalid Revoked EE Test3 - Reject - the end entity certificate has been revoked
+2 InvalidRevokedEETest3EE.crt
+# 4.4.4. Invalid Bad CRL Signature Test4 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidBadCRLSignatureTest4EE.crt
+# 4.4.5 Invalid Bad CRL Issuer Name Test5 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidBadCRLIssuerNameTest5EE.crt
+# 4.4.6 Invalid Wrong CRL Test6 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidWrongCRLTest6EE.crt
+# 4.4.7 Valid Two CRLs Test7 - Validate Successfully
+0 ValidTwoCRLsTest7EE.crt
+# 4.4.8 Invalid Unknown CRL Entry Extension Test8 - Reject - the end entity certificate has been revoked
+2 InvalidUnknownCRLEntryExtensionTest8EE.crt
+# 4.4.9 Invalid Unknown CRL Extension Test9 - Reject - the end entity certificate has been revoked
+2 InvalidUnknownCRLExtensionTest9EE.crt
+# 4.4.10 Invalid Unknown CRL Extension Test10 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidUnknownCRLExtensionTest10EE.crt
+# 4.4.11 Invalid Old CRL nextUpdate Test11 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidOldCRLnextUpdateTest11EE.crt
+# 4.4.12 Invalid pre2000 CRL nextUpdate Tesst12 - Reject or Warn - status of end entity certificate can not be determined
+3 Invalidpre2000CRLnextUpdateTest12EE.crt
+# 4.4.13 Valid GeneralizedTime CRL nextUpdate Test13 - Validate Successfully
+0 ValidGeneralizedTimeCRLnextUpdateTest13EE.crt
+# 4.4.14 Valid Negative Serial Number Test14 - Validate Successfully
+0 ValidNegativeSerialNumberTest14EE.crt
+# 4.4.15 Invalid Negative Serial Number Test15 - Reject - the end entity certificate has been revoked
+2 InvalidNegativeSerialNumberTest15EE.crt
+# 4.4.16 Valid Long Serial Number Test16 - Validate Successfully
+0 ValidLongSerialNumberTest16EE.crt
+# 4.4.17 Valid Long Serial Number Test17 - Validate Successfully
+0 ValidLongSerialNumberTest17EE.crt
+# 4.4.18 Invalid Long Serial Number Test18 - Reject - the end entity certificate has been revoked
+2 InvalidLongSerialNumberTest18EE.crt
+# 4.4.19 Valid Separate Certificate and CRL Keys Test19 - Validate Successfully
+0 ValidSeparateCertificateandCRLKeysTest19EE.crt
+# 4.4.20 Invalid Separate Certificate and CRL Keys Test20 - Reject - the end entity certificate has been revoked
+2 InvalidSeparateCertificateandCRLKeysTest20EE.crt
+# 4.4.21 Invalid Separate Certificate and CRL Keys Test21 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidSeparateCertificateandCRLKeysTest21EE.crt
+# 4.5.1 Valid Basic Self-Issued Old With New Test1 - Validate Successfully
+0 ValidBasicSelfIssuedOldWithNewTest1EE.crt
+# 4.5.2 Invalid Basic Self-Issued Old With New Test2 - Reject - the end entity certificate has been revoked
+2 InvalidBasicSelfIssuedOldWithNewTest2EE.crt
+# 4.5.3 Valid Basic Self-Issued New With Old Test3 - Validate Successfully
+0 ValidBasicSelfIssuedNewWithOldTest3EE.crt
+# 4.5.4 Valid Basic Self-Issued New With Old Test4 - Validate Successfully
+0 ValidBasicSelfIssuedNewWithOldTest4EE.crt
+# 4.5.5 Invalid Basic Self-Issued New With Old Test5 - Reject - the end entity certificate has been revoked
+2 InvalidBasicSelfIssuedNewWithOldTest5EE.crt
+# 4.5.6 Valid Basic Self-Issued CRL Signing Key Test6 - Validate Successfully
+0 ValidBasicSelfIssuedCRLSigningKeyTest6EE.crt
+# 4.5.7 Invalid Basic Self-Issued CRL Signing Key Test7 - Reject - the end entity certificate has been revoked
+2 InvalidBasicSelfIssuedCRLSigningKeyTest7EE.crt
+# 4.5.8 Invalid Basic Self-Issued CRL Signing Key Test8 - Reject - invalid certification path
+1 InvalidBasicSelfIssuedCRLSigningKeyTest8EE.crt
+# 4.6.1 Invalid Missing basicConstraints Test1 - Reject - invalid certification path
+1 InvalidMissingbasicConstraintsTest1EE.crt
+# 4.6.2 Invalid cA False Test2 - Reject - invalid certification path
+1 InvalidcAFalseTest2EE.crt
+# 4.6.3 Invalid cA False Test3 - Reject - invalid certification path
+1 InvalidcAFalseTest3EE.crt
+# 4.6.4 Valid basicConstraints Not Critical Test4 - Validate Successfully
+0 ValidbasicConstraintsNotCriticalTest4EE.crt
+# 4.6.5 Invalid pathLenConstraint Test5 - Reject - invalid certification path
+1 InvalidpathLenConstraintTest5EE.crt
+# 4.6.6 Invalid pathLenConstraint Test6 - Reject - invalid certification path
+1 InvalidpathLenConstraintTest6EE.crt
+# 4.6.7 Valid pathLenConstraint Test7 - Validate Successfully
+0 ValidpathLenConstraintTest7EE.crt
+# 4.6.8 Valid pathLenConstraint Test8 - Validate Successfully
+0 ValidpathLenConstraintTest8EE.crt
+# 4.6.9 Invalid pathLenConstraint Test9 - Reject - invalid certification path
+1 InvalidpathLenConstraintTest9EE.crt
+# 4.6.10 Invalid pathLenConstraint Test10 - Reject - invalid certification path
+1 InvalidpathLenConstraintTest10EE.crt
+# 4.6.11 Invalid pathLenConstraint Test11 - Reject - invalid certification path
+1 InvalidpathLenConstraintTest11EE.crt
+# 4.6.12 Invalid pathLenConstraint Test12 - Reject - invalid certification path
+1 InvalidpathLenConstraintTest12EE.crt
+# 4.6.13 Valid pathLenConstraint Test13 - Validate Successfully
+0 ValidpathLenConstraintTest13EE.crt
+# 4.6.14 Valid pathLenConstraint Test14 - Validate Successfully
+0 ValidpathLenConstraintTest14EE.crt
+# 4.6.15 Valid Self-Issued pathLenConstraint Test15 - Validate Successfully
+0 ValidSelfIssuedpathLenConstraintTest15EE.crt
+# 4.6.16 Invalid Self-Issued pathLenConstraint Test16 - Reject - invalid certification path
+1 InvalidSelfIssuedpathLenConstraintTest16EE.crt
+# 4.6.17 Valid Self-Issued pathLenConstraint Test17 - Validate Successfully
+0 ValidSelfIssuedpathLenConstraintTest17EE.crt
+# 4.7.1 Invalid keyUsage Critical keyCertSign False Test1 - Reject - invalid certification path
+1 InvalidkeyUsageCriticalkeyCertSignFalseTest1EE.crt
+# 4.7.2 Invalid keyUsage Not Critical keyCertSign False Test2 - Reject - invalid certification path
+1 InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE.crt
+# 4.7.3 Valid keyUsage Not Critical Test3 - Validate Successfully
+0 ValidkeyUsageNotCriticalTest3EE.crt
+# 4.7.4 Invalid keyUsage Critical cRLSign False Test4 - Reject - invalid certification path
+1 InvalidkeyUsageCriticalcRLSignFalseTest4EE.crt
+# 4.7.5 Invalid keyUsage Not Critical cRLSign False Test5 - Reject - invalid certification path
+1 InvalidkeyUsageNotCriticalcRLSignFalseTest5EE.crt
+0 UserNoticeQualifierTest19EE.crt
+# 4.10.1 Valid Policy Mapping Test1, subtest 1 - Reject - unrecognized critical extension [Test using the default settings (i.e., <i>initial-policy-set</i> = <i>any-policy</i>)
+1 InvalidSelfIssuedrequireExplicitPolicyTest8EE.crt
+# 4.11.2 Valid inhibitPolicyMapping Test2 - Reject - unrecognized critical extension
+1 ValidinhibitPolicyMappingTest2EE.crt
+# 4.12.2 Valid inhibitAnyPolicy Test2 - Reject - unrecognized critical extension
+1 ValidinhibitAnyPolicyTest2EE.crt
+# 4.13.1 Valid DN nameConstraints Test1 - Validate Successfully
+0 ValidDNnameConstraintsTest1EE.crt
+# 4.13.2 Invalid DN nameConstraints Test2 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest2EE.crt
+# 4.13.3 Invalid DN nameConstraints Test3 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest3EE.crt
+# 4.13.4 Valid DN nameConstraints Test4 - Validate Successfully
+0 ValidDNnameConstraintsTest4EE.crt
+# 4.13.5 Valid DN nameConstraints Test5 - Validate Successfully
+0 ValidDNnameConstraintsTest5EE.crt
+# 4.13.6 Valid DN nameConstraints Test6 - Validate Successfully
+0 ValidDNnameConstraintsTest6EE.crt
+# 4.13.7 Invalid DN nameConstraints Test7 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest7EE.crt
+# 4.13.8 Invalid DN nameConstraints Test8 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest8EE.crt
+# 4.13.9 Invalid DN nameConstraints Test9 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest9EE.crt
+# 4.13.10 Invalid DN nameConstraints Test10 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest10EE.crt
+# 4.13.11 Valid DN nameConstraints Test11 - Validate Successfully
+0 ValidDNnameConstraintsTest11EE.crt
+# 4.13.12 Invalid DN nameConstraints Test12 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest12EE.crt
+# 4.13.13 Invalid DN nameConstraints Test13 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest13EE.crt
+# 4.13.14 Valid DN nameConstraints Test14 - Validate Successfully
+0 ValidDNnameConstraintsTest14EE.crt
+# 4.13.15 Invalid DN nameConstraints Test15 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest15EE.crt
+# 4.13.16 Invalid DN nameConstraints Test16 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest16EE.crt
+# 4.13.17 Invalid DN nameConstraints Test17 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest17EE.crt
+# 4.13.18 Valid DN nameConstraints Test18 - Validate Successfully
+0 ValidDNnameConstraintsTest18EE.crt
+# 4.13.19 Valid Self-Issued DN nameConstraints Test19 - Validate Successfully
+0 ValidDNnameConstraintsTest19EE.crt
+# 4.13.20 Invalid Self-Issued DN nameConstraints Test20 - Reject - name constraints violation
+1 InvalidDNnameConstraintsTest20EE.crt
+# 4.13.21 Valid RFC822 nameConstraints Test21 - Validate Successfully
+0 ValidRFC822nameConstraintsTest21EE.crt
+# 4.13.22 Invalid RFC822 nameConstraints Test22 - Reject - name constraints violation
+1 InvalidRFC822nameConstraintsTest22EE.crt
+# 4.13.23 Valid RFC822 nameConstraints Test23 - Validate Successfully
+0 ValidRFC822nameConstraintsTest23EE.crt
+# 4.13.24 Invalid RFC822 nameConstraints Test24 - Reject - name constraints violation
+1 InvalidRFC822nameConstraintsTest24EE.crt
+# 4.13.25 Valid RFC822 nameConstraints Test25 - Validate Successfully
+0 ValidRFC822nameConstraintsTest25EE.crt
+# 4.13.26 Invalid RFC822 nameConstraints Test26 - Reject - name constraints violation
+1 InvalidRFC822nameConstraintsTest26EE.crt
+# 4.13.27 Valid DN and RFC822 nameConstraints Test27 - Validate Successfully
+0 ValidDNandRFC822nameConstraintsTest27EE.crt
+# 4.13.28 Invalid DN and RFC822 nameConstraints Test28 - Reject - name constraints violation
+1 InvalidDNandRFC822nameConstraintsTest28EE.crt
+# 4.13.29 Invalid DN and RFC822 nameConstraints Test29 - Reject - name constraints violation
+1 InvalidDNandRFC822nameConstraintsTest29EE.crt
+# 4.13.30 Valid DNS nameConstraints Test30 - Validate Successfully
+0 ValidDNSnameConstraintsTest30EE.crt
+# 4.13.31 Invalid DNS nameConstraints Test31 - Reject - name constraints violation
+1 InvalidDNSnameConstraintsTest31EE.crt
+# 4.13.32 Valid DNS nameConstraints Test32 - Validate Successfully
+0 ValidDNSnameConstraintsTest32EE.crt
+# 4.13.33 Invalid DNS nameConstraints Test33 - Reject - name constraints violation
+1 InvalidDNSnameConstraintsTest33EE.crt
+# 4.13.34 Valid URI nameConstraints Test34 - Validate Successfully
+0 ValidURInameConstraintsTest34EE.crt
+# 4.13.35 Invalid URI nameConstraints Test35 - Reject - name constraints violation
+1 InvalidURInameConstraintsTest35EE.crt
+# 4.13.36 Valid URI nameConstraints Test36 - Validate Successfully
+0 ValidURInameConstraintsTest36EE.crt
+# 4.13.37 Invalid URI nameConstraints Test37 - Reject - name constraints violation
+1 InvalidURInameConstraintsTest37EE.crt
+# 4.13.38 Invalid DNS nameConstraints Test38 - Reject - name constraints violation
+1 InvalidDNSnameConstraintsTest38EE.crt
+# 4.14.1 Valid distributionPoint Test1 - Validate Successfully
+0 ValiddistributionPointTest1EE.crt
+# 4.14.2 Invalid distributionPoint Test2 - Reject - end entity certificate has been revoked
+2 InvaliddistributionPointTest2EE.crt
+# 4.14.3 Invalid distributionPoint Test3 - Reject or Warn - status of end entity certificate can not be determined
+3 InvaliddistributionPointTest3EE.crt
+# 4.14.4 Valid distributionPoint Test4 - Validate Successfully
+0 ValiddistributionPointTest4EE.crt
+# 4.14.5 Valid distributionPoint Test5 - Validate Successfully
+0 ValiddistributionPointTest5EE.crt
+# 4.14.6 Invalid distributionPoint Test6 - Reject - end entity certificate has been revoked
+2 InvaliddistributionPointTest6EE.crt
+# 4.14.7 Valid distributionPoint Test7 - Validate Successfully
+0 ValiddistributionPointTest7EE.crt
+# 4.14.8 Invalid distributionPoint Test8 - Reject or Warn - status of end entity certificate can not be determined
+3 InvaliddistributionPointTest8EE.crt
+# 4.14.9 Invalid distributionPoint Test9 - Reject or Warn - status of end entity certificate can not be determined
+3 InvaliddistributionPointTest9EE.crt
+# 4.14.10 Valid No issuingDistributionPoint Test10 - Validate Successfully
+0 ValidNoissuingDistributionPointTest10EE.crt
+# 4.14.11 Invalid onlyContainsUserCerts CRL Test11 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidonlyContainsUserCertsTest11EE.crt
+# 4.14.12 Invalid onlyContainsCACerts CRL Test12 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidonlyContainsCACertsTest12EE.crt
+# 4.14.13 Valid onlyContainsCACerts CRL Test13 - Validate Successfully
+0 ValidonlyContainsCACertsTest13EE.crt
+# 4.14.14 Invalid onlyContainsAttributeCerts Test14 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidonlyContainsAttributeCertsTest14EE.crt
+# 4.14.15 Invalid onlySomeReasons Test15 - Reject - end entity certificate has been revoked
+2 InvalidonlySomeReasonsTest15EE.crt
+# 4.14.16 Invalid onlySomeReasons Test16 - Reject - end entity certificate is on hold
+2 InvalidonlySomeReasonsTest16EE.crt
+# 4.14.17 Invalid onlySomeReasons Test17 - Reject or Warn - status of end entity certificate can not be determined
+3 InvalidonlySomeReasonsTest17EE.crt
+# 4.14.18 Valid onlySomeReasons Test18 - Validate Successfully
+0 ValidonlySomeReasonsTest18EE.crt
+# 4.14.19 Valid onlySomeReasons Test19 - Validate Successfully
+0 ValidonlySomeReasonsTest19EE.crt
+# 4.14.20 Invalid onlySomeReasons Test20 - Reject - end entity certificate has been revoked
+2 InvalidonlySomeReasonsTest20EE.crt
+# 4.14.21 Invalid onlySomeReasons Test21 - Reject - end entity certificate has been revoked
+2 InvalidonlySomeReasonsTest21EE.crt
+# 4.14.24 Valid IDP with indirectCRL Test24 - Reject or Warn - status of end entity certificate can not be determined
+3 ValidIDPwithindirectCRLTest24EE.crt
+# 4.15.1 Invalid deltaCRLIndicator No Base Test1 - Reject or Warn - status of end entity certificate can not be determined
+3 InvaliddeltaCRLIndicatorNoBaseTest1EE.crt
+# 4.15.2 Valid delta-CRL Test2 - Validate Successfully
+0 ValiddeltaCRLTest2EE.crt
+# 4.15.3 Invalid delta-CRL Test3 - Reject - end entity certificate has been revoked
+2 InvaliddeltaCRLTest3EE.crt
+# 4.15.4 Invalid delta-CRL Test4 - Reject - end entity certificate has been revoked
+2 InvaliddeltaCRLTest4EE.crt
+# 4.15.5 Valid delta-CRL Test5 - Validate Successfully
+0 ValiddeltaCRLTest5EE.crt
+# 4.15.6 Invalid delta-CRL Test6 - Reject - end entity certificate has been revoked
+2 InvaliddeltaCRLTest6EE.crt
+# 4.15.7 Valid delta-CRL Test7 - Validate Successfully
+0 ValiddeltaCRLTest7EE.crt
+# 4.15.8 Valid delta-CRL Test8 - Validate Successfully
+0 ValiddeltaCRLTest8EE.crt
+# 4.15.9 Invalid delta-CRL Test9 - Reject - end entity certificate has been revoked
+2 InvaliddeltaCRLTest9EE.crt
+# 4.15.10 Invalid delta-CRL Test10 - Reject or Warn - status of end entity certificate can not be determined
+3 InvaliddeltaCRLTest10EE.crt
+# 4.16.1 Valid Unknown Not Critical Certificate Extension Test1 - Validate Successfully
+0 ValidUnknownNotCriticalCertificateExtensionTest1EE.crt
+# 4.16.2 Invalid Unknown Critical Certificate Extension Test2 - Reject - unrecognized critical extension
+1 InvalidUnknownCriticalCertificateExtensionTest2EE.crt
diff --git a/third_party/heimdal/lib/hx509/data/nist-result2 b/third_party/heimdal/lib/hx509/data/nist-result2
new file mode 100644
index 0000000..93a22e7
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/nist-result2
@@ -0,0 +1,31 @@
+# $Id$
+# id FAIL
+4.2.8 EITHER depeneds on if time_t is 64 bit or not
+4.3.5 FAIL
+4.4.13 EITHER depeneds on if time_t is 64 bit or not
+4.5.1 FAIL
+4.5.4 FAIL
+4.5.6 FAIL
+4.6.15 FAIL
+4.6.17 FAIL
+4.11.2 FAIL
+4.12.2 FAIL
+4.13.19 FAIL
+4.13.21 FAIL
+4.13.23 FAIL
+4.13.26 FAIL
+4.13.27 FAIL
+4.13.30 FAIL
+4.13.33 FAIL
+4.13.34 FAIL
+4.13.37 FAIL
+4.14.1 FAIL
+4.14.4 FAIL
+4.14.5 FAIL
+4.14.7 FAIL
+4.14.13 FAIL
+4.14.18 FAIL
+4.14.19 FAIL
+4.15.4 FAIL
+4.15.5 FAIL
+4.16.2 FAIL
diff --git a/third_party/heimdal/lib/hx509/data/no-proxy-test.crt b/third_party/heimdal/lib/hx509/data/no-proxy-test.crt
new file mode 100644
index 0000000..5f27bcd
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/no-proxy-test.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFEzCCAvugAwIBAgIJAKQmPUkmhyKiMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNV
+BAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQwIBcNMTkwMzIyMjIyNTEzWhgPMjUx
+ODExMjEyMjI1MTNaMDQxCzAJBgNVBAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQx
+ETAPBgNVBAMMCG5vLXByb3h5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
+AgEA1wY5NrUAYbdIiJyOwEG5zo6892LuPxYrJ39qy0868pQMPuViT3IjP8fOockf
+IHFqM1M/rYEKBCaHm3W5d0+QET1T3pJq7eOTuG8Ep4BiMDm2mD0VeWOCeLJjfnuR
+8WPG3Fz//55NBCByMc4as5+Gw9b6z6Mh8pFMyz4zyMQI420Gss9hHoTiD9o4fhM4
+U5M+l+gvVYmvLhZ0Z8hIJAqnlRJoIKeEbcHS1qIqkN0vkdRZc5usJmxhpJi8SjTS
+pGiP53QDmGDJMHd5Fsfyv98n6T6fIkf+O2sAVnxdgOBIyYibdMH021/UzbXZHoDK
+Nx5HH9lr1R9vE5fy969yCQ10lgaNmlp68j8/5B8QPeRbe29DQ1rRBzheji1kkxbg
+8FU7GKu92GHrDhK5dasl7tH3qx4WKOAD4ENJI4vSDWo/IxkKYLLuNIMcVhmGORGl
+IvxaDVxr5wHdGgpBJwc2BxcHU6/8cuYGDewR2h/TWb6jTVmfq7lx+fefEkTDmOxI
+WbXwGtbZqqX5EzWp3VTBakONdRjJwxg3MShWJ1ZhYawzeTwZg3FOIn9W2tLkPNU7
+Ly/fZMBD6qJ5X/0gJGkx9QRlANMJnj0POaBvIyzkGz95QlyIoiAJyuzCKbX5WQdd
+jy1drnB2VdAjWyAjUP+9JsMJJKyLYOWxvemE1yAIIL9yjVMCAwEAAaM5MDcwCQYD
+VR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0OBBYEFN8mmgQlHmnWopRs35laOYZ9
+/02LMA0GCSqGSIb3DQEBCwUAA4ICAQB6dmCcY2XRzeZz2CQ2DdGJRyBDdHFf7W3O
+4Tk/w2ZQLFx6BwttOB94LcaVFRUHp9arkNDH6ne9ntQ1LZ3fPoRR4RXwQO8c+pIf
+ZDWPEr7Exv1F1zrjAEHx2UcP1pu4PU9PDqmGs2BarnHgKotfY8AXdJkl1g128LIQ
+WOmQFyI6Ny//4MT/5YB6OSr2zzrKz5FyGxKSG49xfPSSAf3mHAUxHzBJ1orpHIpo
+zQcrt6oRbi9G9cKVYTEVRVM2CgqMJwBUH7d9BRIab4hp7lqynFJKg3uOH90cmms1
+dY5NRmy+jmAqveEGvCw2+vmHtbj5NikwUBnRqZGW/XHLSj8niCtO2PT30xzpDiEa
+iYBGyuETV5vwFIbucdbenaBrrbvumr4lWqhjadQVwjhNcqdmhIxuGGXF/XGG5+do
+hFaYD5fguyfQDGaeFQIipPEyZcx0QcGA77g7eKYgPyBFZxGHS7P1x1GrchZIOH1q
+W59AuSwxKWGEAM2tlp2+Esp3Zj8UBy2nL9fXRyDEMerCuJUcbCLODGYDc0/s/7Cs
+G8ZNK+GXs68CgxJCbxY5uUcYQyVpRFi62jvghuPGQkxytQ/GWM+q94ncr8I2+lsO
+kTcdzYbAapst+XoPL9enQwAkw4yksJ8Rx2P2TDRAZl58+utRrdQyL0oD9cJy57LE
+cpMYhZWsyQ==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/no-proxy-test.key b/third_party/heimdal/lib/hx509/data/no-proxy-test.key
new file mode 100644
index 0000000..9f30400
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/no-proxy-test.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+vMvFV9XqcWhoGHKX/S/Hmjljr7ln1Dc=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-req1.der b/third_party/heimdal/lib/hx509/data/ocsp-req1.der
new file mode 100644
index 0000000..e536ebb
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-req1.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-req2.der b/third_party/heimdal/lib/hx509/data/ocsp-req2.der
new file mode 100644
index 0000000..e224fa6
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-req2.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-resp1-2.der b/third_party/heimdal/lib/hx509/data/ocsp-resp1-2.der
new file mode 100644
index 0000000..98d88e4
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-resp1-2.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-resp1-3.der b/third_party/heimdal/lib/hx509/data/ocsp-resp1-3.der
new file mode 100644
index 0000000..4c65016
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-resp1-3.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-resp1-ca.der b/third_party/heimdal/lib/hx509/data/ocsp-resp1-ca.der
new file mode 100644
index 0000000..228918c
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-resp1-ca.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-resp1-keyhash.der b/third_party/heimdal/lib/hx509/data/ocsp-resp1-keyhash.der
new file mode 100644
index 0000000..250a1f1
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-resp1-keyhash.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-resp1-ocsp-no-cert.der b/third_party/heimdal/lib/hx509/data/ocsp-resp1-ocsp-no-cert.der
new file mode 100644
index 0000000..6ebbd84
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-resp1-ocsp-no-cert.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-resp1-ocsp.der b/third_party/heimdal/lib/hx509/data/ocsp-resp1-ocsp.der
new file mode 100644
index 0000000..c97654a
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-resp1-ocsp.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-resp1.der b/third_party/heimdal/lib/hx509/data/ocsp-resp1.der
new file mode 100644
index 0000000..8546eba
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-resp1.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-resp2.der b/third_party/heimdal/lib/hx509/data/ocsp-resp2.der
new file mode 100644
index 0000000..d731f38
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-resp2.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-responder.crt b/third_party/heimdal/lib/hx509/data/ocsp-responder.crt
new file mode 100644
index 0000000..753ca56
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-responder.crt
@@ -0,0 +1,119 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:01 2019 GMT
+ Not After : Nov 21 22:25:01 2518 GMT
+ Subject: C=SE, CN=OCSP responder
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:b1:21:1d:c9:2b:44:9e:62:fe:13:94:ea:a1:e1:
+ cd:17:0e:bb:4d:1c:62:27:ee:d3:f7:61:c8:26:c1:
+ 0f:45:fc:10:d8:39:c3:da:86:a0:00:30:d7:ad:86:
+ ff:c6:36:6c:f5:e2:26:8c:f6:76:1b:d0:09:b6:a5:
+ f8:cb:d5:88:fc:ca:ca:28:49:ed:64:2b:f3:88:4e:
+ 8e:ec:7c:63:b8:75:6a:cc:73:b6:66:6c:c3:7c:e4:
+ d7:50:95:88:12:84:e7:5c:50:87:db:4c:bf:91:98:
+ b1:3a:44:57:0b:1a:7a:f1:93:e3:4c:69:8b:9f:d7:
+ b9:20:8d:0e:cb:ff:de:38:6f:6a:91:55:1a:6f:a6:
+ 82:1d:05:f6:fc:46:8c:83:8b:ab:6e:3f:6a:6f:c5:
+ 0c:cc:ff:3c:78:74:d4:f8:56:be:59:60:d5:3f:4d:
+ 3e:e4:e1:4b:2d:c5:2a:d1:6a:7a:21:b9:6e:61:10:
+ 03:79:88:5b:74:f4:29:0d:56:d3:6b:d5:7d:8c:59:
+ 5d:4e:89:0d:a3:a6:8b:43:28:e8:e2:f1:bb:d5:eb:
+ 65:9b:c2:d6:62:aa:df:66:d5:92:dd:84:6c:29:28:
+ 1a:e8:29:b3:09:d1:45:14:44:cb:30:03:73:3a:94:
+ a3:a3:24:89:15:fb:ca:e0:a6:62:35:48:f8:92:50:
+ 3a:ff:17:d8:4a:1e:a0:9c:d9:68:cc:21:e1:c9:36:
+ d1:47:bc:f1:56:3e:87:18:10:0d:f5:56:9a:c9:79:
+ 16:c0:08:a0:59:65:b2:00:dd:9a:e9:97:e7:8f:85:
+ ee:cd:0d:20:5e:2d:58:ff:8e:e3:ce:4f:36:65:c3:
+ f1:88:39:dd:34:29:db:8c:ed:6e:c8:7b:30:ad:49:
+ 58:e6:f9:5b:85:46:0a:04:0f:9e:ea:ca:a8:2a:35:
+ 0d:66:f3:48:b6:e3:c7:e0:e8:a3:ed:6c:f3:e4:cd:
+ 1d:45:f3:e2:2c:6c:5b:91:b8:26:dd:49:d4:78:d3:
+ 4e:57:3a:b5:af:cd:3a:05:d5:89:63:f5:bc:73:1f:
+ 26:cc:2c:4b:2d:81:b3:5d:49:28:04:46:f8:24:5a:
+ 68:1d:06:1b:2d:be:56:f9:b3:f4:d1:50:2f:95:9b:
+ 9f:45:c7:62:35:bc:46:a9:df:c6:45:21:e9:1c:7d:
+ a8:2e:b1:87:91:0b:7c:fb:97:52:31:f9:41:73:ba:
+ 83:22:4a:80:f9:ff:f1:95:74:79:f7:20:95:f0:17:
+ 20:7d:ac:55:e8:b0:c6:b2:a6:56:c6:c0:cf:3d:78:
+ d5:9e:37:41:b4:78:aa:30:f0:2d:59:7c:6a:c8:68:
+ cc:91:09:13:f8:9f:04:e3:a9:86:c2:74:ba:f6:32:
+ 44:0d:bd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Extended Key Usage:
+ OCSP No Check, OCSP Signing
+ X509v3 Subject Key Identifier:
+ D0:3C:E8:05:07:BB:9A:96:36:88:44:AA:9A:4F:62:9E:9F:33:5B:03
+ Signature Algorithm: sha1WithRSAEncryption
+ c0:72:d2:af:26:74:de:f8:7c:96:bf:ab:d2:ed:95:d9:bb:0b:
+ 07:31:8a:4b:21:f0:b5:7e:ab:b4:50:b0:af:bf:96:64:ce:38:
+ 99:3d:f3:26:02:4d:5a:da:71:ad:6d:a6:f7:fc:5e:46:16:3d:
+ 9e:cf:95:a3:5d:0c:4a:64:a1:84:88:b0:31:0e:eb:54:cb:99:
+ 42:45:09:92:ea:b7:74:f5:fb:ff:c6:91:31:27:bd:54:55:9f:
+ 6c:bb:e2:45:4a:33:ed:00:a5:4e:e2:7b:2c:98:f1:3b:bc:f2:
+ 87:33:e5:22:d8:fc:a8:4c:90:e2:df:ce:48:c8:3c:56:43:6c:
+ ac:f1:f6:e0:75:c2:a7:f9:33:87:4e:75:a6:22:17:78:32:88:
+ aa:f9:2a:40:4c:e0:25:6c:4c:0c:cb:6f:1a:7b:13:0d:35:a6:
+ 23:86:42:75:3c:c1:69:c1:c5:79:77:51:4b:19:14:e7:4b:f9:
+ df:0b:30:aa:c4:97:84:6e:57:7b:00:b3:a5:31:c6:9f:17:f1:
+ b0:4c:81:f7:e6:df:e8:c0:d2:91:03:c2:e3:dd:94:c4:f0:ee:
+ 1c:73:1c:33:ae:91:60:fe:cf:48:08:0a:95:c1:95:28:af:31:
+ 23:a6:2a:1c:d1:6c:7f:68:e8:a9:a4:27:8f:6f:29:33:a9:48:
+ 0c:03:8f:fa:b5:ef:2a:9a:ce:ed:ba:74:39:88:ef:3b:d9:93:
+ 77:34:30:d1:a3:5c:9d:f1:3c:30:19:c2:ca:2e:41:5b:23:bb:
+ 6a:67:35:e3:e2:c6:6e:a0:3e:76:50:db:6b:ee:02:98:81:bf:
+ 75:ac:3a:78:4f:f4:fb:d1:7a:1f:85:1a:24:cd:b8:06:7e:95:
+ 28:85:2a:c6:41:23:35:08:31:59:ce:ad:a3:23:1a:7a:11:26:
+ d9:45:57:bf:ea:e0:72:3a:f8:48:e0:c1:5c:b3:20:93:b5:1a:
+ 93:75:ef:f3:19:9d:ed:5d:9f:81:73:21:02:96:fa:ee:c9:4c:
+ c7:95:1b:aa:65:b9:69:15:3c:ef:b3:f6:e1:f5:89:78:05:50:
+ d3:54:c4:c9:40:e5:5f:3e:bd:36:d2:0e:27:99:5e:83:e5:4b:
+ bf:72:84:13:64:8d:d9:db:69:8b:04:37:e8:db:22:46:29:84:
+ 08:83:40:34:d8:e0:bf:cc:5c:7c:b2:bd:c5:38:7d:59:e6:9d:
+ 8a:78:87:08:13:6f:a5:7d:2f:88:80:ce:e5:86:38:6f:53:b8:
+ 99:ba:f5:21:9e:8f:5f:aa:3a:07:73:9b:02:f1:97:1f:8b:52:
+ 53:5e:24:af:d7:b9:a4:3f:4e:64:c8:62:26:b3:c0:44:dd:bb:
+ 29:8c:b5:66:05:5d:fd:f7
+-----BEGIN CERTIFICATE-----
+MIIFJjCCAw6gAwIBAgIBATANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwMVoYDzI1
+MTgxMTIxMjIyNTAxWjAmMQswCQYDVQQGEwJTRTEXMBUGA1UEAwwOT0NTUCByZXNw
+b25kZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCxIR3JK0SeYv4T
+lOqh4c0XDrtNHGIn7tP3YcgmwQ9F/BDYOcPahqAAMNethv/GNmz14iaM9nYb0Am2
+pfjL1Yj8ysooSe1kK/OITo7sfGO4dWrMc7ZmbMN85NdQlYgShOdcUIfbTL+RmLE6
+RFcLGnrxk+NMaYuf17kgjQ7L/944b2qRVRpvpoIdBfb8RoyDi6tuP2pvxQzM/zx4
+dNT4Vr5ZYNU/TT7k4UstxSrRanohuW5hEAN5iFt09CkNVtNr1X2MWV1OiQ2jpotD
+KOji8bvV62WbwtZiqt9m1ZLdhGwpKBroKbMJ0UUURMswA3M6lKOjJIkV+8rgpmI1
+SPiSUDr/F9hKHqCc2WjMIeHJNtFHvPFWPocYEA31VprJeRbACKBZZbIA3Zrpl+eP
+he7NDSBeLVj/juPOTzZlw/GIOd00KduM7W7IezCtSVjm+VuFRgoED57qyqgqNQ1m
+80i248fg6KPtbPPkzR1F8+IsbFuRuCbdSdR4005XOrWvzToF1Ylj9bxzHybMLEst
+gbNdSSgERvgkWmgdBhstvlb5s/TRUC+Vm59Fx2I1vEap38ZFIekcfagusYeRC3z7
+l1Ix+UFzuoMiSoD5//GVdHn3IJXwFyB9rFXosMayplbGwM89eNWeN0G0eKow8C1Z
+fGrIaMyRCRP4nwTjqYbCdLr2MkQNvQIDAQABo1kwVzAJBgNVHRMEAjAAMAsGA1Ud
+DwQEAwIF4DAeBgNVHSUEFzAVBgkrBgEFBQcwAQUGCCsGAQUFBwMJMB0GA1UdDgQW
+BBTQPOgFB7ualjaIRKqaT2KenzNbAzANBgkqhkiG9w0BAQUFAAOCAgEAwHLSryZ0
+3vh8lr+r0u2V2bsLBzGKSyHwtX6rtFCwr7+WZM44mT3zJgJNWtpxrW2m9/xeRhY9
+ns+Vo10MSmShhIiwMQ7rVMuZQkUJkuq3dPX7/8aRMSe9VFWfbLviRUoz7QClTuJ7
+LJjxO7zyhzPlItj8qEyQ4t/OSMg8VkNsrPH24HXCp/kzh051piIXeDKIqvkqQEzg
+JWxMDMtvGnsTDTWmI4ZCdTzBacHFeXdRSxkU50v53wswqsSXhG5XewCzpTHGnxfx
+sEyB9+bf6MDSkQPC492UxPDuHHMcM66RYP7PSAgKlcGVKK8xI6YqHNFsf2joqaQn
+j28pM6lIDAOP+rXvKprO7bp0OYjvO9mTdzQw0aNcnfE8MBnCyi5BWyO7amc14+LG
+bqA+dlDba+4CmIG/daw6eE/0+9F6H4UaJM24Bn6VKIUqxkEjNQgxWc6toyMaehEm
+2UVXv+rgcjr4SODBXLMgk7Uak3Xv8xmd7V2fgXMhApb67slMx5UbqmW5aRU877P2
+4fWJeAVQ01TEyUDlXz69NtIOJ5leg+VLv3KEE2SN2dtpiwQ36NsiRimECINANNjg
+v8xcfLK9xTh9WeadiniHCBNvpX0viIDO5YY4b1O4mbr1IZ6PX6o6B3ObAvGXH4tS
+U14kr9e5pD9OZMhiJrPARN27KYy1ZgVd/fc=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/ocsp-responder.key b/third_party/heimdal/lib/hx509/data/ocsp-responder.key
new file mode 100644
index 0000000..140aaf8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/ocsp-responder.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCxIR3JK0SeYv4T
+lOqh4c0XDrtNHGIn7tP3YcgmwQ9F/BDYOcPahqAAMNethv/GNmz14iaM9nYb0Am2
+pfjL1Yj8ysooSe1kK/OITo7sfGO4dWrMc7ZmbMN85NdQlYgShOdcUIfbTL+RmLE6
+RFcLGnrxk+NMaYuf17kgjQ7L/944b2qRVRpvpoIdBfb8RoyDi6tuP2pvxQzM/zx4
+dNT4Vr5ZYNU/TT7k4UstxSrRanohuW5hEAN5iFt09CkNVtNr1X2MWV1OiQ2jpotD
+KOji8bvV62WbwtZiqt9m1ZLdhGwpKBroKbMJ0UUURMswA3M6lKOjJIkV+8rgpmI1
+SPiSUDr/F9hKHqCc2WjMIeHJNtFHvPFWPocYEA31VprJeRbACKBZZbIA3Zrpl+eP
+he7NDSBeLVj/juPOTzZlw/GIOd00KduM7W7IezCtSVjm+VuFRgoED57qyqgqNQ1m
+80i248fg6KPtbPPkzR1F8+IsbFuRuCbdSdR4005XOrWvzToF1Ylj9bxzHybMLEst
+gbNdSSgERvgkWmgdBhstvlb5s/TRUC+Vm59Fx2I1vEap38ZFIekcfagusYeRC3z7
+l1Ix+UFzuoMiSoD5//GVdHn3IJXwFyB9rFXosMayplbGwM89eNWeN0G0eKow8C1Z
+fGrIaMyRCRP4nwTjqYbCdLr2MkQNvQIDAQABAoICAF7zLJ9Y5ViuIhrnGfubKjBx
+AjBsxaU4XkHfFcbIeOKAI5t1I6rxvbU8eXEYy+U56aDJEPdBasCv/XT+dWb22Y8B
+Oers7idjdBGeYvkcGOyZbJ2aba1sIkLB/TXCYoXi3o7a0GjbNFnc6ywb1Dksgbkc
+GJ0uet09a4yqcMMkLsA74Xl0kE5HiLn8J5DGVK5zYXsg3XJ6w8jkDUtY/Yz+Gt2Y
+jdd4Ff3lU0J+zdwiYsPPPf8j8WjlknkYJSV0ZLMKZ1mj0eO2jiwqq75doLF++bzL
+idU8VWXgyQ678BV01fIeAIZxS/s4Rfp+ghkD0HIXmbxralzxc6iHKF/99Nmhzm1o
+/1PxIqTkso1G1MvizXgr54Uxdv7N01nuL+nDp4q1kBieCqAfkDbbbk9NQb/BUntx
+ZZH2VJxFk8iit8O9BLPM5xiwsdx0QNeYEYQ+fXl75QCLN2zEMaPeOXpV6zWy2NR4
+P+eUEmOC+8Uj44/k21R/Zf46SAEB9YZPmAmq7rW9ACj17sj6e2F1hsVStMy3aG3h
+59GG+sxMSIHwMJ/xB9r+xfaoX4iUapogy1N8pKyYL2CT6c2VB+wBW0gJsuxEGIt3
+x5LugOkCiArTOxTO54tTNeO/5+G+PkYrZwQazCuj9PQPTymHd4ieh61t/8CpasxT
+faIJQt+GxtrBWI8Pc2h1AoIBAQDn22N6+XkouiQcg+sunLFwT5jxQsUZIOVAjx8r
+WFoCW6onzltldxrpP+dx0nOaeBbwHTCRDYrHJhA3bmpaXjBYI8MunfYzHTxmCS1p
+0/cGCMhcZc4dokOh60c1C9/UTG5hAEUAB9kP5C3H1OoL63OM24XQl7hwwX7WFOdL
+VzzADDssLlfhsWRuXa9H5uD2ziqmWE6EZE9Jx5UlolgGpAGcS4YoJi5PaMnYdIJF
+Cpf4gqhtuuUWD7brF9QCKF+e2lLqt4FHSrInqUIYD8HL4xToDPdKfxzUrI3S9dDh
+g7yWWIRKR1W2IFSxBCwedLHl7VGpmgJ+RVovsTbnU+DLplGnAoIBAQDDktp+6FJP
+WorkWUOUWLz4Ao3R3Cd/s7zZlhyNd2fjonH4uBry+X0Vunz2525YtOEVXcVM8Mqa
+KDRyluAwp9pHYc3mhqGafWjoyH1+aDbkALSRcrGw1MkodqgSqxSt1RjgjXwbJb9Y
+wUQSTxd7eJym2WtWAp0WQjH20ygMLu4Lt79HK6M+L0hi9lcxbNNxlQEC5FKgplLn
+urpACHtzrUiIBfpu0LJL0E3dUytQUSVXE02R8j8STRO4lu3n90lZktEl0XuHEHqr
+JXn+p1EhgxpMaYbKr+/v1dFc1RFnaDJO1b2IIaX4psKPPjF408EGqzwXeUen4RLJ
+oaaDSTftN+n7AoIBACBe8w6yUgYrpusMSAOkAOoLUvEsP1R40UkoMlPc7AQ0RBd2
+qjAKZwl10JyFo3pHlfxENwmpeFzBpbX3hoXDbMCBjbiueTc9t7cPRPXnkC+Zfk/Y
+LuTYSNUMgk6Xr9J2MVr9rKSKc/XSB8pEocC0SNe7tn0fEbM8cLb3CCvurB6sFn7e
+oYpzN/BoyBYj1/jdY/sBjUTStHc6lEpC1kNnFop5yOtGGWUg3j2IVr/I8NrTcyyO
+0Xk5DHLaStFaTa9iD/2RTU1k0mbTLNUrLgWHWN3lIYmXIbFXvh1cEKPLvsLG7QFp
+4D+jV++3A2nlJQlTDvm89OgoSqUp+t5lSZdlSzUCggEBAJ+BA+aA/7Bsfd6i1rUE
+coorOxMvZJ+ILbuf7AWMnxROhnl9Xa1QwS2ZfRW5xoteajyMz79imzqDE9NpLctA
++otBPzaGEwL2yTshWQhhYnMuCBaf3kAEK1NvAsDG+wSTScjKW6+gZ6Cxbx0nmFVB
+FzIVHK93Tjq7HhjaOk1FcSvpXn1jH641zem4U4Ch6wk1py9+m80eGXuZFRHoWRcM
++pzFk2wRlXizmO2rSSYmKDgOLDOdyMbaSf1ASyPm0NHXJfCcGw0a6ZDv7cE9ILQe
+QrKTVjW0rBGE5025EIqvtmgJdpyyJLTY/NDqvlp3CXSw7z+N0F0g+bustStZ6dz0
+v0UCggEAXsErHCyXn4LGb77CKdqLPC+VzXUtpY7ydBBOZ/4jemRdGn1YjgDPBrOl
+tESL90Ir1F36zAQOenEOn2VIfCX0DWFLKA5FE2dRM/7X79yTOFwZ2n9Ubz0ulNVH
+zHB2eSHsUhhVszRgqXFwWWwDnkqB7V8HkEXGodl77qf79KL6BQdBj9HbX/4Es1KS
+C4OedUoFIgkvDppiG3mS9JftlHpKM60ckDpkTREdbsRxy2UEjXSdaW/ycJne+xYs
+KKwq1IWDGJYoAfcH6f/9ipG/tC7e5ckUXIQO2bQdtVwqpL0b2VneiWdK4F8EmdiZ
+SSGDNVUC8knNaz9M5HYwvqUl1if8yg==
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/openssl.1.0.cnf b/third_party/heimdal/lib/hx509/data/openssl.1.0.cnf
new file mode 100644
index 0000000..b014656
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/openssl.1.0.cnf
@@ -0,0 +1,190 @@
+oid_section = new_oids
+
+[new_oids]
+pkkdcekuoid = 1.3.6.1.5.2.3.5
+
+[ca]
+
+default_ca = user
+
+[usr]
+database = index.txt
+serial = serial
+x509_extensions = usr_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[ocsp]
+database = index.txt
+serial = serial
+x509_extensions = ocsp_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[usr_ke]
+database = index.txt
+serial = serial
+x509_extensions = usr_cert_ke
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[usr_ds]
+database = index.txt
+serial = serial
+x509_extensions = usr_cert_ds
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[pkinit_client]
+database = index.txt
+serial = serial
+x509_extensions = pkinit_client_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[pkinit_kdc]
+database = index.txt
+serial = serial
+x509_extensions = pkinit_kdc_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[https]
+database = index.txt
+serial = serial
+x509_extensions = https_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[subca]
+database = index.txt
+serial = serial
+x509_extensions = v3_ca
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+
+[req]
+distinguished_name = req_distinguished_name
+x509_extensions = v3_ca # The extensions to add to the self signed cert
+
+string_mask = utf8only
+
+[v3_ca]
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+basicConstraints = CA:true
+keyUsage = cRLSign, keyCertSign, keyEncipherment, nonRepudiation, digitalSignature
+
+[usr_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier = hash
+
+[usr_cert_ke]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, keyEncipherment
+subjectKeyIdentifier = hash
+
+[proxy_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier = hash
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:0,policy:text:foo
+
+[pkinitc_principals]
+princ1 = GeneralString:bar
+
+[pkinitc_principal_seq]
+name_type = EXP:0,INTEGER:1
+name_string = EXP:1,SEQUENCE:pkinitc_principals
+
+[pkinitc_princ_name]
+realm = EXP:0,GeneralString:TEST.H5L.SE
+principal_name = EXP:1,SEQUENCE:pkinitc_principal_seq
+
+[pkinit_client_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier = hash
+subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:pkinitc_princ_name
+
+[https_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+#extendedKeyUsage = https-server XXX
+subjectKeyIdentifier = hash
+
+[pkinit_kdc_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = pkkdcekuoid
+subjectKeyIdentifier = hash
+subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:pkinitkdc_princ_name
+
+[pkinitkdc_princ_name]
+realm = EXP:0,GeneralString:TEST.H5L.SE
+principal_name = EXP:1,SEQUENCE:pkinitkdc_principal_seq
+
+[pkinitkdc_principal_seq]
+name_type = EXP:0,INTEGER:1
+name_string = EXP:1,SEQUENCE:pkinitkdc_principals
+
+[pkinitkdc_principals]
+princ1 = GeneralString:krbtgt
+princ2 = GeneralString:TEST.H5L.SE
+
+[proxy10_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier = hash
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:10,policy:text:foo
+
+[usr_cert_ds]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature
+subjectKeyIdentifier = hash
+
+[ocsp_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+# ocsp-nocheck and kp-OCSPSigning
+extendedKeyUsage = 1.3.6.1.5.5.7.48.1.5, 1.3.6.1.5.5.7.3.9
+subjectKeyIdentifier = hash
+
+[req_distinguished_name]
+countryName = Country Name (2 letter code)
+countryName_default = SE
+countryName_min = 2
+countryName_max = 2
+
+organizationalName = Organizational Unit Name (eg, section)
+
+commonName = Common Name (eg, YOUR name)
+commonName_max = 64
+
+#[req_attributes]
+#challengePassword = A challenge password
+#challengePassword_min = 4
+#challengePassword_max = 20
+
+[policy_match]
+countryName = match
+commonName = supplied
diff --git a/third_party/heimdal/lib/hx509/data/openssl.1.1.cnf b/third_party/heimdal/lib/hx509/data/openssl.1.1.cnf
new file mode 100644
index 0000000..110073f
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/openssl.1.1.cnf
@@ -0,0 +1,185 @@
+[ca]
+
+default_ca = user
+
+[usr]
+database = index.txt
+serial = serial
+x509_extensions = usr_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[ocsp]
+database = index.txt
+serial = serial
+x509_extensions = ocsp_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[usr_ke]
+database = index.txt
+serial = serial
+x509_extensions = usr_cert_ke
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[usr_ds]
+database = index.txt
+serial = serial
+x509_extensions = usr_cert_ds
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[pkinit_client]
+database = index.txt
+serial = serial
+x509_extensions = pkinit_client_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[pkinit_kdc]
+database = index.txt
+serial = serial
+x509_extensions = pkinit_kdc_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[https]
+database = index.txt
+serial = serial
+x509_extensions = https_cert
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+[subca]
+database = index.txt
+serial = serial
+x509_extensions = v3_ca
+default_md=sha1
+policy = policy_match
+email_in_dn = no
+certs = .
+
+
+[req]
+distinguished_name = req_distinguished_name
+x509_extensions = v3_ca # The extensions to add to the self signed cert
+
+string_mask = utf8only
+
+[v3_ca]
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+basicConstraints = CA:true
+keyUsage = cRLSign, keyCertSign, keyEncipherment, nonRepudiation, digitalSignature
+
+[usr_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier = hash
+
+[usr_cert_ke]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, keyEncipherment
+subjectKeyIdentifier = hash
+
+[proxy_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier = hash
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:0,policy:text:foo
+
+[pkinitc_principals]
+princ1 = GeneralString:bar
+
+[pkinitc_principal_seq]
+name_type = EXP:0,INTEGER:1
+name_string = EXP:1,SEQUENCE:pkinitc_principals
+
+[pkinitc_princ_name]
+realm = EXP:0,GeneralString:TEST.H5L.SE
+principal_name = EXP:1,SEQUENCE:pkinitc_principal_seq
+
+[pkinit_client_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier = hash
+subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:pkinitc_princ_name
+
+[https_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+#extendedKeyUsage = https-server XXX
+subjectKeyIdentifier = hash
+
+[pkinit_kdc_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = 1.3.6.1.5.2.3.5
+subjectKeyIdentifier = hash
+subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:pkinitkdc_princ_name
+
+[pkinitkdc_princ_name]
+realm = EXP:0,GeneralString:TEST.H5L.SE
+principal_name = EXP:1,SEQUENCE:pkinitkdc_principal_seq
+
+[pkinitkdc_principal_seq]
+name_type = EXP:0,INTEGER:1
+name_string = EXP:1,SEQUENCE:pkinitkdc_principals
+
+[pkinitkdc_principals]
+princ1 = GeneralString:krbtgt
+princ2 = GeneralString:TEST.H5L.SE
+
+[proxy10_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectKeyIdentifier = hash
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:10,policy:text:foo
+
+[usr_cert_ds]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature
+subjectKeyIdentifier = hash
+
+[ocsp_cert]
+basicConstraints=CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+# ocsp-nocheck and kp-OCSPSigning
+extendedKeyUsage = 1.3.6.1.5.5.7.48.1.5, 1.3.6.1.5.5.7.3.9
+subjectKeyIdentifier = hash
+
+[req_distinguished_name]
+countryName = Country Name (2 letter code)
+countryName_default = SE
+countryName_min = 2
+countryName_max = 2
+
+organizationalName = Organizational Unit Name (eg, section)
+
+commonName = Common Name (eg, YOUR name)
+commonName_max = 64
+
+#[req_attributes]
+#challengePassword = A challenge password
+#challengePassword_min = 4
+#challengePassword_max = 20
+
+[policy_match]
+countryName = match
+commonName = supplied
diff --git a/third_party/heimdal/lib/hx509/data/pkinit-ec.crt b/third_party/heimdal/lib/hx509/data/pkinit-ec.crt
new file mode 100644
index 0000000..54435d3
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/pkinit-ec.crt
@@ -0,0 +1,81 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 7 (0x7)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:06 2019 GMT
+ Not After : Nov 21 22:25:06 2518 GMT
+ Subject: C=SE, CN=pkinit-ec
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (256 bit)
+ pub:
+ 04:c0:2b:8e:f3:0c:c3:1b:88:94:eb:4e:6a:12:f2:
+ fb:63:99:77:a2:13:7a:16:ce:48:dc:48:9a:83:91:
+ 5e:a9:b8:ab:17:77:94:ae:55:09:8d:69:4a:a4:a8:
+ 6b:77:12:01:fb:3c:6f:cd:b1:e3:02:be:63:b1:43:
+ 8d:8f:df:8c:75
+ ASN1 OID: prime256v1
+ NIST CURVE: P-256
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ 77:9B:74:4B:75:90:50:CE:20:C3:00:9B:A5:23:F7:69:A8:C7:CC:34
+ X509v3 Subject Alternative Name:
+ othername:<unsupported>
+ Signature Algorithm: sha1WithRSAEncryption
+ 70:02:b8:13:0f:d9:2b:7a:e9:42:5c:82:6a:9d:ea:f8:51:dc:
+ a9:2e:67:ec:c3:cb:67:48:fe:6a:bd:58:86:67:c2:1f:d4:a0:
+ dc:7d:17:41:93:8d:e0:67:60:01:60:cc:34:1f:0e:b0:fc:9b:
+ 5f:f6:cf:91:2b:a3:ec:28:5b:80:ff:31:21:14:5b:3c:a2:5c:
+ 6b:3b:32:94:de:ab:03:d9:41:70:c1:4f:4e:49:4d:63:8f:9a:
+ 8b:be:14:87:b0:df:bc:64:83:e1:99:ce:e6:77:12:5a:43:e3:
+ 3b:d7:e9:10:5e:68:36:38:de:88:c2:78:af:97:a3:a2:4e:bf:
+ a9:2d:e1:98:f4:9a:35:ec:b4:2a:70:18:09:99:ff:80:fb:73:
+ 49:75:47:54:31:7a:e1:43:28:4b:53:71:81:92:4c:42:db:9b:
+ 52:38:ad:90:47:db:4e:da:75:6f:37:14:ce:56:6e:06:d0:40:
+ 8e:df:f1:71:23:98:ee:b4:43:b7:77:3a:1c:a5:a3:6f:3e:d3:
+ 5f:86:0b:6d:d4:b8:4a:2e:8a:e0:d7:d2:75:5f:ca:bc:9c:e2:
+ d8:b9:04:bf:ec:8a:1e:78:28:f5:13:73:9c:dd:2c:10:73:55:
+ cf:40:96:8d:8a:b4:1c:79:bd:aa:01:de:b2:de:c4:30:04:11:
+ af:d5:fb:cb:28:44:25:02:ab:b3:68:22:02:1b:99:b1:96:eb:
+ f7:f3:ad:6e:32:76:67:be:bb:78:bc:46:9a:1c:b3:8e:66:39:
+ eb:cb:d8:76:c8:06:e5:79:1e:f0:fa:54:3f:a1:ea:ff:60:e8:
+ fb:55:d9:1c:47:3a:e7:67:df:c8:69:1d:d1:9a:56:96:2b:01:
+ 79:ad:22:f2:7a:3b:e6:be:32:84:9a:e3:50:db:89:69:c1:3e:
+ 19:09:d5:b3:3c:2c:08:90:8b:93:aa:39:ae:48:90:ec:cf:79:
+ 3d:15:91:86:3e:38:0e:0a:99:b1:d9:78:14:59:17:44:c0:76:
+ 70:a0:7a:92:64:2a:60:04:aa:ce:6b:b1:d5:c1:3b:e8:1b:58:
+ 6f:7d:dd:dc:90:49:55:e1:37:5a:7b:75:89:da:08:c1:a5:33:
+ c9:f9:0d:4a:1d:08:e0:a8:be:3f:0e:a2:e0:10:71:92:50:f8:
+ 75:33:98:7c:be:c9:2f:c8:7c:b2:19:94:14:59:0b:1c:ca:bc:
+ 34:ff:03:a4:3c:f0:bd:ac:c8:f6:63:8f:59:d3:eb:65:e9:96:
+ 9b:21:a9:94:a7:7d:fe:dd:62:cd:77:62:6a:58:38:de:63:4c:
+ 0c:c3:ea:09:4f:6a:80:76:07:59:ba:15:d2:b4:c1:46:1e:11:
+ 50:5b:be:8d:8e:21:4e:78
+-----BEGIN CERTIFICATE-----
+MIIDcDCCAVigAwIBAgIBBzANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwNloYDzI1
+MTgxMTIxMjIyNTA2WjAhMQswCQYDVQQGEwJTRTESMBAGA1UEAwwJcGtpbml0LWVj
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwCuO8wzDG4iU605qEvL7Y5l3ohN6
+Fs5I3Eiag5FeqbirF3eUrlUJjWlKpKhrdxIB+zxvzbHjAr5jsUONj9+MdaNzMHEw
+CQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0OBBYEFHebdEt1kFDOIMMAm6Uj
+92mox8w0MDgGA1UdEQQxMC+gLQYGKwYBBQICoCMwIaANGwtURVNULkg1TC5TRaEQ
+MA6gAwIBAaEHMAUbA2JhcjANBgkqhkiG9w0BAQUFAAOCAgEAcAK4Ew/ZK3rpQlyC
+ap3q+FHcqS5n7MPLZ0j+ar1YhmfCH9Sg3H0XQZON4GdgAWDMNB8OsPybX/bPkSuj
+7ChbgP8xIRRbPKJcazsylN6rA9lBcMFPTklNY4+ai74Uh7DfvGSD4ZnO5ncSWkPj
+O9fpEF5oNjjeiMJ4r5ejok6/qS3hmPSaNey0KnAYCZn/gPtzSXVHVDF64UMoS1Nx
+gZJMQtubUjitkEfbTtp1bzcUzlZuBtBAjt/xcSOY7rRDt3c6HKWjbz7TX4YLbdS4
+Si6K4NfSdV/KvJzi2LkEv+yKHngo9RNznN0sEHNVz0CWjYq0HHm9qgHest7EMAQR
+r9X7yyhEJQKrs2giAhuZsZbr9/OtbjJ2Z767eLxGmhyzjmY568vYdsgG5Xke8PpU
+P6Hq/2Do+1XZHEc652ffyGkd0ZpWlisBea0i8no75r4yhJrjUNuJacE+GQnVszws
+CJCLk6o5rkiQ7M95PRWRhj44DgqZsdl4FFkXRMB2cKB6kmQqYASqzmux1cE76BtY
+b33d3JBJVeE3Wnt1idoIwaUzyfkNSh0I4Ki+Pw6i4BBxklD4dTOYfL7JL8h8shmU
+FFkLHMq8NP8DpDzwvazI9mOPWdPrZemWmyGplKd9/t1izXdialg43mNMDMPqCU9q
+gHYHWboV0rTBRh4RUFu+jY4hTng=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/pkinit-ec.key b/third_party/heimdal/lib/hx509/data/pkinit-ec.key
new file mode 100644
index 0000000..0ac3fe4
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/pkinit-ec.key
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg38AlgS7f0d6rvR6u
+mLJVGl/UF04RYiIeWsVJYUNS7RKhRANCAATAK47zDMMbiJTrTmoS8vtjmXeiE3oW
+zkjcSJqDkV6puKsXd5SuVQmNaUqkqGt3EgH7PG/NseMCvmOxQ42P34x1
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/pkinit-proxy-chain.crt b/third_party/heimdal/lib/hx509/data/pkinit-proxy-chain.crt
new file mode 100644
index 0000000..2b425bc
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/pkinit-proxy-chain.crt
@@ -0,0 +1,149 @@
+-----BEGIN CERTIFICATE-----
+MIIFODCCAyCgAwIBAgIJAJd7zCsMMPvAMA0GCSqGSIb3DQEBCwUAMB4xCzAJBgNV
+BAYTAlNFMQ8wDQYDVQQDDAZwa2luaXQwIBcNMTkwMzIyMjIyNTA3WhgPMjUxODEx
+MjEyMjI1MDdaMDUxCzAJBgNVBAYTAlNFMQ8wDQYDVQQDDAZwa2luaXQxFTATBgNV
+BAMMDHBraW5pdC1wcm94eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+AM1Z218qhKJXqYoQ/5DZgDYNtgJ9QLkPF0vsu6cScVhykomP1H2Qjj3+u6ZFB9oj
+YS/ynPgkCjwexA7nDGVxfrR0OksDNFGHwCu/O1wagzyeImgvYYzPlKyFqT+0IN0U
+TgEoMdtXUduxmJGr0wEr2aMeiETUGZmRc02icTPZIznhf9elXmAp7EQspjtN9P04
+OCkVygr/FKkeI7XsZR8Ekzqdaf400g1Cy+X2kMvnRs/Tyf3qp/xv1RG7cDz+vfiB
+8S/Vd2zSyBFboOZZi0xVo2vEs9nzBbmTQFAFINSEtIjJWb9Tupz4YwU/xK9bq2G7
+/eOrt4e9pXry1gUKNN/qZIEkbaebhWxW6twMVO/nyXlnklpF1PvPKHsWP2JqhJK7
+9SZcqZkeRit1wlwXsWhb+Zzl27K74P1WgNQF9lbPwhQ6bPmadirCs0kkMBDjmh4U
+SJe/5wIOLVPY0KrhQeC7Vn+hkCxqRTwZQuLKDWUKgd4jmvsjfLYAZVQfGUBX1x7D
+Wng44pW7ytOSbn/4pAu38FGGLbPcKNw+cRRtHtBpWiCthDIJ6gOmJo8JngaU2Iss
+ozmxfGZsdQscUEFVwL5X9YaBpWcGKKxZH7UXq3S3wOEm2yQ6/UrdJnuP+xIYFEiA
+c7tQIFBYoX11+/cZGyVZmn8ESM4ytw9QqmxxaDAEdaJjAgMBAAGjYDBeMAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdDgQWBBSU/W8lX964Gjrb5YqSo3zg3XLl
+vzAlBggrBgEFBQcBDgEB/wQWMBQCAQAwDwYIKwYBBQUHFQAEA2ZvbzANBgkqhkiG
+9w0BAQsFAAOCAgEA2bcpIO8UZ7eDFPtIZd6BWVrQZ5Dj+LZA0KV2v+FjoZQN0/w6
+gzloDy0x3yym6lOtCNPnjxcYP2yfGl5WZet1VXCR+KXXHGID2MkOXhXTCH7eSCGC
+GZpOsSEWs+CXyP/X5B85nkweK8MzLRdJc4Ilxf1UMMOdobgQnRq41NgWyQxA8xLR
+jEeGV+CD5dvteyqBeXvq4IBBUTqAiyoH57XXZRPwWH7z3h9f7B4GdoQuRBQK+idM
+hLgtQ9nkT+DDyoMC6iAtIVwntxsAIg5rUSkQ6RgS3ap6SC94+v3q5V2BJaJl/R+0
+X5ybK2hRKWsUAvU280UX/xT5EGeokHwegI+F7yQflTGI1p090uhBYZvWYj2G8wf5
+nlE7a2h8IG3SmS7qUlKe+RyPwHce+jgHU0aS3ROxreLGf/VX1k3yR2Vk0wpVWFis
+WeC7U5g+iqv/UomqxpFwP3iH8RJhL3eGYVmatHIXjm838wepofexqUL4UKoY/t+h
+FmMusCz2SFZFbgOHF1Vd9C3c/mr6J4HbYju1hO09iOFuQmoo4CjNjdKyLvP1HSap
+g7IPdLP0f++1gGcRq8AkFC9U/vZx5OPwjIku645WeOBoLhFcSoLxyhMeekSNXpwS
+WHArnvVC7cmuvuzmzf5+XqmBzSD0RWGK09drTZdryXuzv8djbeqldeRr1Zg=
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 6 (0x6)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:06 2019 GMT
+ Not After : Nov 21 22:25:06 2518 GMT
+ Subject: C=SE, CN=pkinit
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:e4:e6:1a:b1:de:91:30:34:8a:c7:f2:d9:0a:09:
+ 82:13:46:e9:db:c8:54:1e:0e:b0:b0:0a:e3:a3:b5:
+ 55:3c:6f:f8:45:8f:24:ed:56:c5:16:23:aa:ad:86:
+ 5a:5a:e0:8f:a2:f5:82:59:cc:70:b7:45:cc:1b:44:
+ a7:49:4b:ff:63:28:9d:01:22:79:ca:1a:6a:2b:75:
+ f8:40:c0:f0:93:b1:ab:85:cd:af:88:ac:30:f3:cb:
+ 42:87:fc:be:76:bb:fd:1c:a4:45:7a:66:37:47:ea:
+ aa:bf:c4:4b:47:fb:5b:ab:3f:c1:22:a9:06:f2:61:
+ 3d:5b:20:51:fc:ce:a7:82:74:6f:3d:ac:68:d6:78:
+ a2:77:83:26:af:23:63:20:3f:21:6e:29:1f:55:4c:
+ a6:d0:5a:51:e5:96:c1:cd:22:03:22:ee:de:42:3c:
+ 82:4d:29:20:c6:be:85:5b:04:3a:5f:8b:c7:e8:4e:
+ aa:3c:8e:dd:0d:d8:e5:d0:ff:0b:52:37:40:51:0d:
+ 33:f7:a8:05:07:76:dc:48:20:cd:52:38:a4:1f:44:
+ 11:cf:6d:58:a9:5a:9a:34:cb:93:07:30:e3:66:7b:
+ dc:d3:0b:6b:a2:1c:3f:19:ec:0b:0c:ea:29:6c:75:
+ 4d:7a:86:cf:35:87:9e:50:15:f3:34:73:0e:ac:4b:
+ a5:aa:1f:a2:f9:d5:8f:34:bd:5f:19:ae:22:8c:7f:
+ f7:ca:64:e6:ed:42:75:e5:92:9c:53:53:b7:66:68:
+ e5:07:eb:08:40:ec:bd:7c:ae:b0:c4:a5:4b:d7:4b:
+ 58:86:05:a8:91:db:ee:7a:3f:c4:fd:83:e5:7b:cb:
+ d0:8c:87:68:3b:83:67:e5:6a:5e:fa:28:b5:ee:07:
+ b1:0d:6a:93:1e:b0:c7:5c:57:fd:ce:e2:9c:0f:5e:
+ fe:41:cf:20:f2:1d:88:52:00:d4:83:fe:5b:d7:87:
+ 49:b0:78:2b:a7:60:c2:55:c6:c3:a2:6d:16:04:7f:
+ 8b:12:f7:65:c6:91:41:53:d8:ac:70:c0:3d:83:d8:
+ e0:6c:bb:3e:48:b8:c2:72:be:c0:35:61:40:ff:9f:
+ 97:18:9e:c7:39:0f:93:36:8f:0e:a6:3c:6d:5b:fd:
+ 89:6a:bb:ee:5e:43:f8:0d:29:7a:cf:23:bf:0b:c1:
+ 29:76:ae:a2:9a:73:b2:d0:b9:bd:48:51:25:8a:6b:
+ a9:c5:07:94:26:03:10:74:7b:fc:b7:5d:8f:2d:97:
+ 55:11:3e:7c:04:89:0e:b9:b9:73:2a:6c:5b:12:19:
+ 65:92:48:64:d5:4f:2c:79:3f:16:ad:65:97:21:db:
+ 3c:30:68:67:aa:42:14:86:59:57:b0:79:15:9e:a3:
+ 05:4f:33
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ 7A:C6:DB:B8:D2:75:D1:8D:BB:72:AE:B5:25:6E:6F:8C:AF:63:3A:4D
+ X509v3 Subject Alternative Name:
+ othername:<unsupported>
+ Signature Algorithm: sha1WithRSAEncryption
+ 7f:5c:76:fd:3d:ef:0c:7f:70:c7:09:d3:5c:c1:b6:40:25:47:
+ a3:6a:bf:4e:ad:d1:e6:cc:92:86:b6:6a:42:3d:4f:bc:f1:6f:
+ fd:7e:22:52:9c:dc:a6:0b:71:98:80:44:cf:f1:91:bb:50:c8:
+ 15:cd:8c:d8:9c:7d:8d:69:61:1b:4c:66:40:77:44:45:33:9c:
+ 9a:04:01:a1:4b:82:3a:d7:39:97:27:90:a6:71:9a:b1:9c:ce:
+ 60:01:8b:a5:6f:39:a3:e1:75:de:3c:5c:61:66:a5:50:db:0f:
+ 4a:03:32:8d:dd:e5:b6:ab:6a:b2:53:6a:4c:c9:99:74:f7:f5:
+ 1e:a5:06:1a:d3:64:26:c5:77:f4:a6:40:1a:c4:7e:22:05:a6:
+ a5:25:f7:5d:74:a5:c9:86:c0:3a:88:2e:6e:0e:58:4f:e5:6e:
+ e9:2a:34:2a:1d:1d:a4:e4:74:f3:a5:e5:56:5d:5f:02:c4:eb:
+ c7:12:f2:55:6a:f1:6c:ec:6e:b8:c1:2d:aa:4a:7d:ed:91:c8:
+ 78:1b:b7:b9:37:17:32:ee:1b:b5:d9:5c:98:d2:cf:d8:c6:90:
+ a5:c9:f1:eb:8d:2c:d4:90:b2:8c:e5:53:9a:66:20:92:8b:a2:
+ 0c:8b:76:9b:5f:5b:39:77:69:67:a7:8c:de:10:57:85:45:a4:
+ 8f:85:3a:59:5f:fc:0c:70:de:1c:67:33:5e:9b:a5:21:3d:bd:
+ 2e:de:3e:c2:0d:cf:8f:52:43:92:01:cc:47:da:af:47:85:69:
+ 94:d3:9f:c9:d5:5d:50:ca:27:a5:bb:c0:53:12:e0:e8:3c:ed:
+ 0d:bd:47:97:af:be:b8:f9:0c:10:2a:79:21:3c:15:ef:c0:a5:
+ eb:33:38:93:5b:a3:de:1a:97:eb:c3:db:04:1f:e8:f4:23:10:
+ ff:2d:1e:9b:4e:1f:8e:27:7d:71:34:e2:be:74:a2:62:69:9a:
+ 83:7b:6e:9e:e4:a2:7c:84:82:ff:83:b3:cd:d2:0f:74:05:72:
+ b8:b0:45:23:b6:cd:04:25:2d:58:7f:92:ce:68:f9:ba:d0:9e:
+ a8:e1:f8:c0:86:0e:aa:ee:f9:af:ff:5c:bf:46:76:08:b1:83:
+ e7:66:8b:ca:1b:8f:f4:9f:6a:ac:71:4e:3a:d1:77:fd:97:81:
+ ff:0e:d0:d1:4a:7e:6d:94:e6:8c:e1:28:92:b1:68:83:5a:62:
+ 48:0d:26:ee:28:60:57:ff:52:b8:1e:8c:03:d8:fb:c1:6e:4f:
+ fd:7a:46:0b:0f:c8:05:ad:3a:a4:68:be:fd:30:62:ce:f2:0a:
+ b1:34:2c:95:e7:e2:91:ec:a3:c6:4e:2d:a5:fe:09:45:84:38:
+ 9c:d7:f4:0b:18:22:9d:df
+-----BEGIN CERTIFICATE-----
+MIIFODCCAyCgAwIBAgIBBjANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwNloYDzI1
+MTgxMTIxMjIyNTA2WjAeMQswCQYDVQQGEwJTRTEPMA0GA1UEAwwGcGtpbml0MIIC
+IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OYasd6RMDSKx/LZCgmCE0bp
+28hUHg6wsArjo7VVPG/4RY8k7VbFFiOqrYZaWuCPovWCWcxwt0XMG0SnSUv/Yyid
+ASJ5yhpqK3X4QMDwk7Grhc2viKww88tCh/y+drv9HKRFemY3R+qqv8RLR/tbqz/B
+IqkG8mE9WyBR/M6ngnRvPaxo1niid4MmryNjID8hbikfVUym0FpR5ZbBzSIDIu7e
+QjyCTSkgxr6FWwQ6X4vH6E6qPI7dDdjl0P8LUjdAUQ0z96gFB3bcSCDNUjikH0QR
+z21YqVqaNMuTBzDjZnvc0wtrohw/GewLDOopbHVNeobPNYeeUBXzNHMOrEulqh+i
++dWPNL1fGa4ijH/3ymTm7UJ15ZKcU1O3ZmjlB+sIQOy9fK6wxKVL10tYhgWokdvu
+ej/E/YPle8vQjIdoO4Nn5Wpe+ii17gexDWqTHrDHXFf9zuKcD17+Qc8g8h2IUgDU
+g/5b14dJsHgrp2DCVcbDom0WBH+LEvdlxpFBU9iscMA9g9jgbLs+SLjCcr7ANWFA
+/5+XGJ7HOQ+TNo8OpjxtW/2JarvuXkP4DSl6zyO/C8Epdq6imnOy0Lm9SFElimup
+xQeUJgMQdHv8t12PLZdVET58BIkOublzKmxbEhllkkhk1U8seT8WrWWXIds8MGhn
+qkIUhllXsHkVnqMFTzMCAwEAAaNzMHEwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAw
+HQYDVR0OBBYEFHrG27jSddGNu3KutSVub4yvYzpNMDgGA1UdEQQxMC+gLQYGKwYB
+BQICoCMwIaANGwtURVNULkg1TC5TRaEQMA6gAwIBAaEHMAUbA2JhcjANBgkqhkiG
+9w0BAQUFAAOCAgEAf1x2/T3vDH9wxwnTXMG2QCVHo2q/Tq3R5syShrZqQj1PvPFv
+/X4iUpzcpgtxmIBEz/GRu1DIFc2M2Jx9jWlhG0xmQHdERTOcmgQBoUuCOtc5lyeQ
+pnGasZzOYAGLpW85o+F13jxcYWalUNsPSgMyjd3ltqtqslNqTMmZdPf1HqUGGtNk
+JsV39KZAGsR+IgWmpSX3XXSlyYbAOogubg5YT+Vu6So0Kh0dpOR086XlVl1fAsTr
+xxLyVWrxbOxuuMEtqkp97ZHIeBu3uTcXMu4btdlcmNLP2MaQpcnx640s1JCyjOVT
+mmYgkouiDIt2m19bOXdpZ6eM3hBXhUWkj4U6WV/8DHDeHGczXpulIT29Lt4+wg3P
+j1JDkgHMR9qvR4VplNOfydVdUMonpbvAUxLg6DztDb1Hl6++uPkMECp5ITwV78Cl
+6zM4k1uj3hqX68PbBB/o9CMQ/y0em04fjid9cTTivnSiYmmag3tunuSifISC/4Oz
+zdIPdAVyuLBFI7bNBCUtWH+Szmj5utCeqOH4wIYOqu75r/9cv0Z2CLGD52aLyhuP
+9J9qrHFOOtF3/ZeB/w7Q0Up+bZTmjOEokrFog1piSA0m7ihgV/9SuB6MA9j7wW5P
+/XpGCw/IBa06pGi+/TBizvIKsTQslefikeyjxk4tpf4JRYQ4nNf0Cxgind8=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/pkinit-proxy.crt b/third_party/heimdal/lib/hx509/data/pkinit-proxy.crt
new file mode 100644
index 0000000..d92acdf
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/pkinit-proxy.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFODCCAyCgAwIBAgIJAJd7zCsMMPvAMA0GCSqGSIb3DQEBCwUAMB4xCzAJBgNV
+BAYTAlNFMQ8wDQYDVQQDDAZwa2luaXQwIBcNMTkwMzIyMjIyNTA3WhgPMjUxODEx
+MjEyMjI1MDdaMDUxCzAJBgNVBAYTAlNFMQ8wDQYDVQQDDAZwa2luaXQxFTATBgNV
+BAMMDHBraW5pdC1wcm94eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+AM1Z218qhKJXqYoQ/5DZgDYNtgJ9QLkPF0vsu6cScVhykomP1H2Qjj3+u6ZFB9oj
+YS/ynPgkCjwexA7nDGVxfrR0OksDNFGHwCu/O1wagzyeImgvYYzPlKyFqT+0IN0U
+TgEoMdtXUduxmJGr0wEr2aMeiETUGZmRc02icTPZIznhf9elXmAp7EQspjtN9P04
+OCkVygr/FKkeI7XsZR8Ekzqdaf400g1Cy+X2kMvnRs/Tyf3qp/xv1RG7cDz+vfiB
+8S/Vd2zSyBFboOZZi0xVo2vEs9nzBbmTQFAFINSEtIjJWb9Tupz4YwU/xK9bq2G7
+/eOrt4e9pXry1gUKNN/qZIEkbaebhWxW6twMVO/nyXlnklpF1PvPKHsWP2JqhJK7
+9SZcqZkeRit1wlwXsWhb+Zzl27K74P1WgNQF9lbPwhQ6bPmadirCs0kkMBDjmh4U
+SJe/5wIOLVPY0KrhQeC7Vn+hkCxqRTwZQuLKDWUKgd4jmvsjfLYAZVQfGUBX1x7D
+Wng44pW7ytOSbn/4pAu38FGGLbPcKNw+cRRtHtBpWiCthDIJ6gOmJo8JngaU2Iss
+ozmxfGZsdQscUEFVwL5X9YaBpWcGKKxZH7UXq3S3wOEm2yQ6/UrdJnuP+xIYFEiA
+c7tQIFBYoX11+/cZGyVZmn8ESM4ytw9QqmxxaDAEdaJjAgMBAAGjYDBeMAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdDgQWBBSU/W8lX964Gjrb5YqSo3zg3XLl
+vzAlBggrBgEFBQcBDgEB/wQWMBQCAQAwDwYIKwYBBQUHFQAEA2ZvbzANBgkqhkiG
+9w0BAQsFAAOCAgEA2bcpIO8UZ7eDFPtIZd6BWVrQZ5Dj+LZA0KV2v+FjoZQN0/w6
+gzloDy0x3yym6lOtCNPnjxcYP2yfGl5WZet1VXCR+KXXHGID2MkOXhXTCH7eSCGC
+GZpOsSEWs+CXyP/X5B85nkweK8MzLRdJc4Ilxf1UMMOdobgQnRq41NgWyQxA8xLR
+jEeGV+CD5dvteyqBeXvq4IBBUTqAiyoH57XXZRPwWH7z3h9f7B4GdoQuRBQK+idM
+hLgtQ9nkT+DDyoMC6iAtIVwntxsAIg5rUSkQ6RgS3ap6SC94+v3q5V2BJaJl/R+0
+X5ybK2hRKWsUAvU280UX/xT5EGeokHwegI+F7yQflTGI1p090uhBYZvWYj2G8wf5
+nlE7a2h8IG3SmS7qUlKe+RyPwHce+jgHU0aS3ROxreLGf/VX1k3yR2Vk0wpVWFis
+WeC7U5g+iqv/UomqxpFwP3iH8RJhL3eGYVmatHIXjm838wepofexqUL4UKoY/t+h
+FmMusCz2SFZFbgOHF1Vd9C3c/mr6J4HbYju1hO09iOFuQmoo4CjNjdKyLvP1HSap
+g7IPdLP0f++1gGcRq8AkFC9U/vZx5OPwjIku645WeOBoLhFcSoLxyhMeekSNXpwS
+WHArnvVC7cmuvuzmzf5+XqmBzSD0RWGK09drTZdryXuzv8djbeqldeRr1Zg=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/pkinit-proxy.key b/third_party/heimdal/lib/hx509/data/pkinit-proxy.key
new file mode 100644
index 0000000..6ef1f81
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/pkinit-proxy.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+k2odOmvIh8CJ2ZSNtza8KPoCHxzIOQU=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/pkinit-pw.key b/third_party/heimdal/lib/hx509/data/pkinit-pw.key
new file mode 100644
index 0000000..3fef51f
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/pkinit-pw.key
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,0D7945ED368F10EDD0E5FB517DA6CEDD
+
+U4MlgA6Abbuv+8v7XUYDCxGw8Sd4xWCuxFWu3+twUWfpN3MG95RPuCf21m1rXb0q
+BmmyRECQg6Kk+wuFKVeKEVsBT5pJ/CmutPeE3Y1Oxkn0G09RygO3+mrZInnMdACM
+9TYYUA6AGp67hF4G5pJUk7T+Zdfu5+pxhzsRC24UussXFlUtnPCS1QlRWUraVpzs
+Ei7whYsfz3Fcw5ZEo5b9z+NHqU3OIMfLH2Q0dPJ7Y2gHtteaftmOcRALcz7l4Jug
++9YCAOChw110BII76z6fwPDKbJuMAlZuzhB4XFfYbwVqvMbddQslANb2PiaReIRk
+g52kJY/cMb/nSbGwf0ZG9rGhcuw6xdvMoTFPtqqy7NXEPxfwlxoAxsJVg3Rp/4d9
+M8h4mOyvDjuluIK1GpPfoHGCOYFyduaQQM+KZ00D08j/d8y8RkaD07eXOLQE3CHY
+qXO8fE91lpcnxVRVCv1s4v63/FgqYrrU7rqSgGaQwKrcZK4hZEbL8yMcJFe4KOoJ
+m2k7pqi6hL9JILz7G9+oMnrObOZ9aK+ZiXz09+CxWYhwCcQrFXaK9XKDUWLqoSZn
+ZBj1xYqwW1kvhybdUENHXUVh3G4lA2+sWbEKvSLwcAsiIVkYxuiWwb9Ix5MPCKDK
+ZuzIQaQ5Ji5PWPqCR4iYdt8BOq/r2iMEUwJ2KZtxRcPknbH94rt31dz0cXr/fFO7
+OPxGTuJILIvf+wRZ88x8hmYA6JjRvEHZDrlwmk+uoUBsOYEcVOg7hd/AQYd1BZWQ
+HvlELIHaGgLxmq/C1InRbU/mtnIElBHAI3NLKpSgGyFH4/Mis/M/VnHJaC3sWKFR
+bJ5SobVa8HYgRy6y3mA/DsssGVj/dz2riTzjCXpNmju83D8wGJ8iDjJP0183R6Zh
+Rb83W3EAjkHHLIkli40E7H6cq1RjXTrzUpvE21Yq5vbk6rBnLRO/lDX4lpFq0S09
+Rd8UQUhXqTxtOGXIFv/4sS2P4oIAx+mk4pf+iJMltjvCLCNkPQRL+NewVHMm4zhK
+25Yo9SNObY/zk3MBKH0FnI9xAmS7xUdzjRTQvP8yqBu0lQfJo2np3FsjtGyvjYWO
+b11L4mc+n19LCA2PpU4VaWxzPfZraBBHJy75Cqk0xXx/9obtrgZ89qXgv6H6PZod
+8W64PUK+895+07plpOAmrS0WZL0muh5m9qarabmY0dcoiG16Ln2iAEJTvepKn/7f
+H7rC8OoFr6AWYRx3hj0dLCSs6VPVFLAyQvEPIQlPFndT8rhuP9z8W1W6OkLpe8wE
+fVFL3jNMwRTYTB9aAwFFeyrf1CbaTs8iRmp0eZgTteNbhshx1UdAasj7sLLUhPFe
+cU9evtCKFqoRfSiuR0uzetWMJ7vODxGDzGkbDhIqVRpcUaBz6cQs4NlslT/ebXe9
+5xL6KWI3vUIJeWcTEJbl0sUTBbmRUxPB636XdQ4l3OIYI32NK29fX2+2pHXqaWql
+L8iVwsLJM422pxHdixGmO0eLEJ1Y62tvmTWQ3Nr+/ZJP8h9yk61gj2QTHOVAyz7o
+UxZie77035ni7vZG0AjJDz80hj9rnMpznTUZCiEzArJ65RTrUmiiedSyatHYEREK
+pC3+2aqvrOaGCikIfFTAEOKlE2ZjDVZ1jYCFCVFmJE6sqtpIigMeiChsFlxg4FCo
+zIvfFEY6QdnqHPvUMsXNr/p5xcVQjZhitk8Av/Lr1gSsHapiRthbty2Yry1r2QYb
+T79qdouzk0sGdIORM5SmEvPAp/459cy91nIEav4eC5JB/owPyPB5KNuz6wL1Guo1
+TUDoOyR/vkB4Ybm9A4se6ldYftK3nxiFCL96SgqFP5WC6JAwZROBKFn5HWLdVPOD
+/lXB3SxGe48PjcAX2ugo8KySpDC41uUIHOdxPeRBUPWB9bRpAmLfV7lsBC0X7fPn
+ghdnQav0TcQTOwChNaCLpUSzRzk71y7IR/4LASgm7WZUoV3YF8SarsA+wOvKCuKf
+GJJThmSRzhSU8xbyU8hdVAd0XT8d2tSNJLuJ8PT//dqbh4Ft6U+DjpNX2XUtAINe
+9L61Wpj5HduTqcXSTeHnhdDqjuAEGrFkR3P/SmA/r+uaaVvi/sbbh8cBqvjC3O40
+Z+e4JDd2XvjCgChdJIOUJfHfvLGjkaGJIecizT8YYGO0FyebnPPah/Jj1R4XPaWZ
++Wu2ro1/GKKnHCsIMbEz4BbGrapi63RWGRufNhVE518CX7vMsqgbMFgspfbUinrq
+N6+5MD/VLrMkbCrYxVFRP8Lcug4IJhRjV9olI9GEN7/ulMV8A4UQXh0K9zPox21H
+qPf9kgPpd8GNWXZ+/kQkuLL74BjWCiiUxqDLFY6xKN+5yPwe9l0TNymuSNAxJCMZ
+2Jyfhaq3R7AlqNisoNFO99g3HVXXowi1dK9hc2AqXdqyN5w6LrO8eAy3FQsBv8iD
+pFruJeMve+Fohzrnb8L9B+Wm5nNUH9Z1/jsR+/rnaoqOYl1qza88URqlcy8ovPXh
+1y3d0YGZi877nzLPTe0WPSy0ueJkolqx27hrwvLA6SLBFwzg174pS/vmH1cmEKep
+ZekUuAUxp3pJ70Dzu+lsSnye66mUxx56JHGcCD1uiUkB8qWXTGrnTGkxUJYfZoip
+0nX6YWChnuXkQ46B/tYIaP0+d94LkqjB2VzEDv71oXU4uTD2hqK2i2oTRlXQUBV6
+iNutj1sawU7WUXBRUf0ls8GRIHb6xvNVwnus0/P0VnUkTMCVE3ohQCw2SLkCiPtX
+o24G7A6jNCsM3XTfK68P0TqkMKg5wYJM7Pbt4FLBRxy0uzeR6GarMneLP173rH+Z
+LX5zVvEveL6yHnmKHZm9OoXj+NNn9HkUdCWqxmOvYNl8TTRh58ZCIUDJn4ZQcMkw
+jjOSPfaPWkNrLXm/PxW55JmLuewgHGWo/1gfJCrewrvkuXWJffL09An1/rJoqb0B
+TSFAKj6c/WhJvG7qFYLd7+tMfoet2exw34uP6tvi8Lq/u6cG63wXKbzhpXtvyqOs
+wOOVZDnTskF5syX20FojeKCLCUQWIMv/vYKlrCeA0zpxL4qQFxpHlHAI2cw8MtVa
+Cd8XFmQihZRm+7A8dDsL0AU5ai0kh7yIAhZZ/bR4sUscnbO2y/w+AItcBbfcCK8T
+-----END RSA PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/pkinit.crt b/third_party/heimdal/lib/hx509/data/pkinit.crt
new file mode 100644
index 0000000..3f20629
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/pkinit.crt
@@ -0,0 +1,119 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 6 (0x6)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:06 2019 GMT
+ Not After : Nov 21 22:25:06 2518 GMT
+ Subject: C=SE, CN=pkinit
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:e4:e6:1a:b1:de:91:30:34:8a:c7:f2:d9:0a:09:
+ 82:13:46:e9:db:c8:54:1e:0e:b0:b0:0a:e3:a3:b5:
+ 55:3c:6f:f8:45:8f:24:ed:56:c5:16:23:aa:ad:86:
+ 5a:5a:e0:8f:a2:f5:82:59:cc:70:b7:45:cc:1b:44:
+ a7:49:4b:ff:63:28:9d:01:22:79:ca:1a:6a:2b:75:
+ f8:40:c0:f0:93:b1:ab:85:cd:af:88:ac:30:f3:cb:
+ 42:87:fc:be:76:bb:fd:1c:a4:45:7a:66:37:47:ea:
+ aa:bf:c4:4b:47:fb:5b:ab:3f:c1:22:a9:06:f2:61:
+ 3d:5b:20:51:fc:ce:a7:82:74:6f:3d:ac:68:d6:78:
+ a2:77:83:26:af:23:63:20:3f:21:6e:29:1f:55:4c:
+ a6:d0:5a:51:e5:96:c1:cd:22:03:22:ee:de:42:3c:
+ 82:4d:29:20:c6:be:85:5b:04:3a:5f:8b:c7:e8:4e:
+ aa:3c:8e:dd:0d:d8:e5:d0:ff:0b:52:37:40:51:0d:
+ 33:f7:a8:05:07:76:dc:48:20:cd:52:38:a4:1f:44:
+ 11:cf:6d:58:a9:5a:9a:34:cb:93:07:30:e3:66:7b:
+ dc:d3:0b:6b:a2:1c:3f:19:ec:0b:0c:ea:29:6c:75:
+ 4d:7a:86:cf:35:87:9e:50:15:f3:34:73:0e:ac:4b:
+ a5:aa:1f:a2:f9:d5:8f:34:bd:5f:19:ae:22:8c:7f:
+ f7:ca:64:e6:ed:42:75:e5:92:9c:53:53:b7:66:68:
+ e5:07:eb:08:40:ec:bd:7c:ae:b0:c4:a5:4b:d7:4b:
+ 58:86:05:a8:91:db:ee:7a:3f:c4:fd:83:e5:7b:cb:
+ d0:8c:87:68:3b:83:67:e5:6a:5e:fa:28:b5:ee:07:
+ b1:0d:6a:93:1e:b0:c7:5c:57:fd:ce:e2:9c:0f:5e:
+ fe:41:cf:20:f2:1d:88:52:00:d4:83:fe:5b:d7:87:
+ 49:b0:78:2b:a7:60:c2:55:c6:c3:a2:6d:16:04:7f:
+ 8b:12:f7:65:c6:91:41:53:d8:ac:70:c0:3d:83:d8:
+ e0:6c:bb:3e:48:b8:c2:72:be:c0:35:61:40:ff:9f:
+ 97:18:9e:c7:39:0f:93:36:8f:0e:a6:3c:6d:5b:fd:
+ 89:6a:bb:ee:5e:43:f8:0d:29:7a:cf:23:bf:0b:c1:
+ 29:76:ae:a2:9a:73:b2:d0:b9:bd:48:51:25:8a:6b:
+ a9:c5:07:94:26:03:10:74:7b:fc:b7:5d:8f:2d:97:
+ 55:11:3e:7c:04:89:0e:b9:b9:73:2a:6c:5b:12:19:
+ 65:92:48:64:d5:4f:2c:79:3f:16:ad:65:97:21:db:
+ 3c:30:68:67:aa:42:14:86:59:57:b0:79:15:9e:a3:
+ 05:4f:33
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ 7A:C6:DB:B8:D2:75:D1:8D:BB:72:AE:B5:25:6E:6F:8C:AF:63:3A:4D
+ X509v3 Subject Alternative Name:
+ othername:<unsupported>
+ Signature Algorithm: sha1WithRSAEncryption
+ 7f:5c:76:fd:3d:ef:0c:7f:70:c7:09:d3:5c:c1:b6:40:25:47:
+ a3:6a:bf:4e:ad:d1:e6:cc:92:86:b6:6a:42:3d:4f:bc:f1:6f:
+ fd:7e:22:52:9c:dc:a6:0b:71:98:80:44:cf:f1:91:bb:50:c8:
+ 15:cd:8c:d8:9c:7d:8d:69:61:1b:4c:66:40:77:44:45:33:9c:
+ 9a:04:01:a1:4b:82:3a:d7:39:97:27:90:a6:71:9a:b1:9c:ce:
+ 60:01:8b:a5:6f:39:a3:e1:75:de:3c:5c:61:66:a5:50:db:0f:
+ 4a:03:32:8d:dd:e5:b6:ab:6a:b2:53:6a:4c:c9:99:74:f7:f5:
+ 1e:a5:06:1a:d3:64:26:c5:77:f4:a6:40:1a:c4:7e:22:05:a6:
+ a5:25:f7:5d:74:a5:c9:86:c0:3a:88:2e:6e:0e:58:4f:e5:6e:
+ e9:2a:34:2a:1d:1d:a4:e4:74:f3:a5:e5:56:5d:5f:02:c4:eb:
+ c7:12:f2:55:6a:f1:6c:ec:6e:b8:c1:2d:aa:4a:7d:ed:91:c8:
+ 78:1b:b7:b9:37:17:32:ee:1b:b5:d9:5c:98:d2:cf:d8:c6:90:
+ a5:c9:f1:eb:8d:2c:d4:90:b2:8c:e5:53:9a:66:20:92:8b:a2:
+ 0c:8b:76:9b:5f:5b:39:77:69:67:a7:8c:de:10:57:85:45:a4:
+ 8f:85:3a:59:5f:fc:0c:70:de:1c:67:33:5e:9b:a5:21:3d:bd:
+ 2e:de:3e:c2:0d:cf:8f:52:43:92:01:cc:47:da:af:47:85:69:
+ 94:d3:9f:c9:d5:5d:50:ca:27:a5:bb:c0:53:12:e0:e8:3c:ed:
+ 0d:bd:47:97:af:be:b8:f9:0c:10:2a:79:21:3c:15:ef:c0:a5:
+ eb:33:38:93:5b:a3:de:1a:97:eb:c3:db:04:1f:e8:f4:23:10:
+ ff:2d:1e:9b:4e:1f:8e:27:7d:71:34:e2:be:74:a2:62:69:9a:
+ 83:7b:6e:9e:e4:a2:7c:84:82:ff:83:b3:cd:d2:0f:74:05:72:
+ b8:b0:45:23:b6:cd:04:25:2d:58:7f:92:ce:68:f9:ba:d0:9e:
+ a8:e1:f8:c0:86:0e:aa:ee:f9:af:ff:5c:bf:46:76:08:b1:83:
+ e7:66:8b:ca:1b:8f:f4:9f:6a:ac:71:4e:3a:d1:77:fd:97:81:
+ ff:0e:d0:d1:4a:7e:6d:94:e6:8c:e1:28:92:b1:68:83:5a:62:
+ 48:0d:26:ee:28:60:57:ff:52:b8:1e:8c:03:d8:fb:c1:6e:4f:
+ fd:7a:46:0b:0f:c8:05:ad:3a:a4:68:be:fd:30:62:ce:f2:0a:
+ b1:34:2c:95:e7:e2:91:ec:a3:c6:4e:2d:a5:fe:09:45:84:38:
+ 9c:d7:f4:0b:18:22:9d:df
+-----BEGIN CERTIFICATE-----
+MIIFODCCAyCgAwIBAgIBBjANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwNloYDzI1
+MTgxMTIxMjIyNTA2WjAeMQswCQYDVQQGEwJTRTEPMA0GA1UEAwwGcGtpbml0MIIC
+IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OYasd6RMDSKx/LZCgmCE0bp
+28hUHg6wsArjo7VVPG/4RY8k7VbFFiOqrYZaWuCPovWCWcxwt0XMG0SnSUv/Yyid
+ASJ5yhpqK3X4QMDwk7Grhc2viKww88tCh/y+drv9HKRFemY3R+qqv8RLR/tbqz/B
+IqkG8mE9WyBR/M6ngnRvPaxo1niid4MmryNjID8hbikfVUym0FpR5ZbBzSIDIu7e
+QjyCTSkgxr6FWwQ6X4vH6E6qPI7dDdjl0P8LUjdAUQ0z96gFB3bcSCDNUjikH0QR
+z21YqVqaNMuTBzDjZnvc0wtrohw/GewLDOopbHVNeobPNYeeUBXzNHMOrEulqh+i
++dWPNL1fGa4ijH/3ymTm7UJ15ZKcU1O3ZmjlB+sIQOy9fK6wxKVL10tYhgWokdvu
+ej/E/YPle8vQjIdoO4Nn5Wpe+ii17gexDWqTHrDHXFf9zuKcD17+Qc8g8h2IUgDU
+g/5b14dJsHgrp2DCVcbDom0WBH+LEvdlxpFBU9iscMA9g9jgbLs+SLjCcr7ANWFA
+/5+XGJ7HOQ+TNo8OpjxtW/2JarvuXkP4DSl6zyO/C8Epdq6imnOy0Lm9SFElimup
+xQeUJgMQdHv8t12PLZdVET58BIkOublzKmxbEhllkkhk1U8seT8WrWWXIds8MGhn
+qkIUhllXsHkVnqMFTzMCAwEAAaNzMHEwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAw
+HQYDVR0OBBYEFHrG27jSddGNu3KutSVub4yvYzpNMDgGA1UdEQQxMC+gLQYGKwYB
+BQICoCMwIaANGwtURVNULkg1TC5TRaEQMA6gAwIBAaEHMAUbA2JhcjANBgkqhkiG
+9w0BAQUFAAOCAgEAf1x2/T3vDH9wxwnTXMG2QCVHo2q/Tq3R5syShrZqQj1PvPFv
+/X4iUpzcpgtxmIBEz/GRu1DIFc2M2Jx9jWlhG0xmQHdERTOcmgQBoUuCOtc5lyeQ
+pnGasZzOYAGLpW85o+F13jxcYWalUNsPSgMyjd3ltqtqslNqTMmZdPf1HqUGGtNk
+JsV39KZAGsR+IgWmpSX3XXSlyYbAOogubg5YT+Vu6So0Kh0dpOR086XlVl1fAsTr
+xxLyVWrxbOxuuMEtqkp97ZHIeBu3uTcXMu4btdlcmNLP2MaQpcnx640s1JCyjOVT
+mmYgkouiDIt2m19bOXdpZ6eM3hBXhUWkj4U6WV/8DHDeHGczXpulIT29Lt4+wg3P
+j1JDkgHMR9qvR4VplNOfydVdUMonpbvAUxLg6DztDb1Hl6++uPkMECp5ITwV78Cl
+6zM4k1uj3hqX68PbBB/o9CMQ/y0em04fjid9cTTivnSiYmmag3tunuSifISC/4Oz
+zdIPdAVyuLBFI7bNBCUtWH+Szmj5utCeqOH4wIYOqu75r/9cv0Z2CLGD52aLyhuP
+9J9qrHFOOtF3/ZeB/w7Q0Up+bZTmjOEokrFog1piSA0m7ihgV/9SuB6MA9j7wW5P
+/XpGCw/IBa06pGi+/TBizvIKsTQslefikeyjxk4tpf4JRYQ4nNf0Cxgind8=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/pkinit.key b/third_party/heimdal/lib/hx509/data/pkinit.key
new file mode 100644
index 0000000..ee1c842
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/pkinit.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+RGobyk3036+cucUBlLFJBboNBbUzUX7l
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy-level-test.crt b/third_party/heimdal/lib/hx509/data/proxy-level-test.crt
new file mode 100644
index 0000000..51422e9
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy-level-test.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFVzCCAz+gAwIBAgIJAOXO+qv/iXxCMA0GCSqGSIb3DQEBCwUAMDExCzAJBgNV
+BAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQxDjAMBgNVBAMMBXByb3h5MCAXDTE5
+MDMyMjIyMjUxM1oYDzI1MTgxMTIxMjIyNTEzWjBBMQswCQYDVQQGEwJTRTESMBAG
+A1UEAwwJVGVzdCBjZXJ0MQ4wDAYDVQQDDAVwcm94eTEOMAwGA1UEAwwFY2hpbGQw
+ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDPMfDYnc56LmPFqSovq3KU
+JcBypPhCtrOiJ3vwLIumg+NDlsWdhG0EP8xwGnbikR5RNAi/PtCV8cGsbQ3+Fio8
+AFVQs956itoNEvzoL+mbyNpyRVr1ph9qJ/lO5mzdNfCQb++b73bkygUaNKJPpdL5
+VNEwdG6bEEw59+WWqATO1geUv//AeNPqLrKFDMg5HQzCjGXhwE99J156AQ4fw6lB
+0Yubssk6AfHLKd+UKh8qRPdO1EYzgHRuSGW7olXUbiS3wmPe1/JYMEiILjGmvAaA
+Y55qsxFxZlTEehO99WF6rOrP1lC3ddcBd0fXBvJTiQeQsHT29tiVlPMFolmnQMbO
++bzLUPSx9ukW2deirFViXnC7pb5Cv8ZIJ2jsZEG3D4JM5WqZ1ycrKAq49r284LbX
+LZH0ZnKZyGJmOjsXqIwWJJthMSlb7l/TA4IdRePYlaYQNQqIYwsPv9o+wv3owUaV
+1PePDbn3PY/LENshooIV1yqhbA/Kr4ayiYuTFLtugPcWwi8sAXFNC3t6x4+xTK3n
+S0n30hvIWZGKes8Zz46E6ANAf+stiKWxhTROrfLNKvenKXc4iVETOzZkBhQPyhk5
+dM1aC9rylQnm30o8oVnNpn/z9rePdCDbBluHKcJE4xcX5QpU3qybO5W5A7dhxsjw
+0iVfuheSt5AkE4ixHAy/6wIDAQABo2AwXjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF
+4DAdBgNVHQ4EFgQUcMWBbIV1HXTcW9hfIFew2urXq5QwJQYIKwYBBQUHAQ4BAf8E
+FjAUAgEAMA8GCCsGAQUFBxUABANmb28wDQYJKoZIhvcNAQELBQADggIBAGygvoGy
+/aUbaxSh8v1ug6qHHDMuKAddD4qnTx/JKFyUbVDKOUsiu4kuwH5uoIMhhPd5XYBw
+2gNjptKIK/o5CMGXQpAFV9cfd7h57bhBTBU5xPvdsTsXSRObkmESE07XF81Vmqbz
+3+YvbhYy98mHszMnsBzG24pJYn+Xhv0KfUINV+BzZZPZmwTkZki8gnAYpSfA85d2
+o1SO2iP0kZPUOoTRDrvN6v7EHFxar8Prka2jpHBPRXUMANbGl/Iu8SvzukVDc6g2
+pEBa9vDPl2Tf44kct+hB7Q735xSfHF5PJaUt/kzVclFNNWyNRr99S1mM5IEnAzF3
+PpkWHOke/L1dvWJT7wBSPfwMtyQz7gIGdA6Oq5CkFxE259bb7XJxRwfQV12pB4p5
+ZYUDTpd1P3D+VHQzmGi+EcgKEgoydVp4ikQj7aDUQWzZ49st+xLf0suE6wmNNSd+
+4dSuAeHbV0rglwlo/LZ3UPCd8nzhGTC5t6L8vFcDkrC7RyaBR8X2OeTU8FLP02me
+q1eMF8k89L6ar1etLfC2+PKRGhcohJjm1Yz7sTSIeT6SnSxZeoSGAkFFwy70in0m
+9mJNpNGfBCr2DS+XncpvVcIIYjHmrKWYfbnxYcouh/PJqpKKp4JXceV0u1e2DlvQ
+Cew9o2dZuv5HXMmm4xpp8gyrzCzfZuExPIZd
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy-level-test.key b/third_party/heimdal/lib/hx509/data/proxy-level-test.key
new file mode 100644
index 0000000..352bfa8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy-level-test.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+JKt/SLgrlhrJ0BbcNKAnbzOeBZGPpEJk
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy-test.crt b/third_party/heimdal/lib/hx509/data/proxy-test.crt
new file mode 100644
index 0000000..9f9cd57
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy-test.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFNzCCAx+gAwIBAgIJAKQmPUkmhyKhMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNV
+BAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQwIBcNMTkwMzIyMjIyNTEyWhgPMjUx
+ODExMjEyMjI1MTJaMDExCzAJBgNVBAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQx
+DjAMBgNVBAMMBXByb3h5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
+mPssWfri3hoR0eHC6FCqD3arIBwD+XeYJXS2y1zA9UgijF3rLDtyubOpp/5Or6Q9
+cO7m4S+4xPquasRUS8A9rJZbxi3rvptVFD2DBmpEUNbIjlW2TdymVpAtD4uHKpOA
+xeEtn6eXlhDiiJcrrqLds9cLGRG0XaPSeTwzOWqhtyEeMp3rvVQ/NeMfYsMF87m/
+txopxdJnpEoArfxxudLSQ0HrILLVV/VbQZEcoJXEMjhZg0Qnw5rHRV5dacLT1gmp
+w2yRQID0rbcKk6b3ukRHXD9OWNt074afzQJmMx1MDlHv8IeFtksxEhhA9i612LTi
+KaU5Ac11ZJ+ew2YnV3HU3roH6BHeGeWHDhxqfpV7DPXZsUVo18kgrY+w1E+lcntT
+FfWzWXF6p7gIPQKBy/IG3FsdP9ugx4y54Jl2HsrBEOjA+x45TL80T0zA0FXuu6fo
+oljNGUrQfPQiWJCnQmWkjIhbVo+aovSlBRnOSsAxr7H8Ry4jhF2eQG8TIDgPRGs3
+DSwIUJyQwoRvRpo01ZJ1akfUkJFzv519EuN/zfNVTO2KBGVXprkONCZVN6eVpDVb
+rClN5iUqCimn4xhv2T7VhDKO6mjOMMR38kdLvsrtAWBpob7hDHr0zAXCwSwheVVM
+fMkYHZEyVfsxS7/ooA+oLwbHewhV3zJQDK6zAgyi+jUCAwEAAaNgMF4wCQYDVR0T
+BAIwADALBgNVHQ8EBAMCBeAwHQYDVR0OBBYEFFyuJ+5Hf8HzZNaew6ZLRnQubKRB
+MCUGCCsGAQUFBwEOAQH/BBYwFAIBADAPBggrBgEFBQcVAAQDZm9vMA0GCSqGSIb3
+DQEBCwUAA4ICAQAqn4xPkjg5lR+wOQwxKyxfzjS2ycEW+8WLp+l5p7qHmv4JOSwO
+/XFf+6sRSGiBwHAoOq3yJlU0NzEq+uLjEg20/MXQ1+R4N2AsWeD4wUxRkjoukmc+
+at4wza4SoJLzfv8rYtZRx7quXDq+tFgAHxZv/AB3tghCyjS9JiaAc2aA015XmAec
+qZcLjWoDmIH4mgT+LuenPbS9Dus8mGbOiTsns+iVCMZKJOBU1KF1UUy+f+J3SGqX
+nsHzfMiFqU8qA6sQ3mZy4yqPG0Yu7r6YfpV2HQPCLXy1VZ3BINf/9YINaUCe/NpQ
+Md1Pd6Q3U8+QObyAxXVfmTRFGCDu+S0NlpEfZzPnRYxr0ZfwC3SKWMwVHugv9v3k
+qkZAgB4T9u0TqBjuB9mWoyzYEqwRgFg0AjrtgWXPJ8MSnth3eSrJjcXhEqgq1NvJ
+SZVPzYW9RKB7lAM/4cDmrGXB1Lq2g4b5R8H1wzBtjL+CGjCuly9uR5HvxCOPLzPm
+btZTRQtdA96L490wcv4D3JHN9ro0cq4QB2m6XKr2wDDh3CEgQQmaTKufWR8zAL8R
+5HYtKxt1dcz6w4FiLgq9g+ADZMwJpErmGgldX/NKMz4Rfy0qMCprIn6XgPWWlSC4
+BT/0EyLjDJhwap661H5sMkchCx4uywG6EvQBRf4bxpWQgxReSO1znefULQ==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy-test.key b/third_party/heimdal/lib/hx509/data/proxy-test.key
new file mode 100644
index 0000000..fc303ea
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy-test.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+2f09X6R0XLrRpaAyQD+H/2DVhYM4CV0=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy10-child-child-test.crt b/third_party/heimdal/lib/hx509/data/proxy10-child-child-test.crt
new file mode 100644
index 0000000..a606da6
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy10-child-child-test.crt
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFezCCA2OgAwIBAgIJAIZ6hp81I2P5MA0GCSqGSIb3DQEBCwUAMEMxCzAJBgNV
+BAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQxEDAOBgNVBAMMB3Byb3h5MTAxDjAM
+BgNVBAMMBWNoaWxkMCAXDTE5MDMyMjIyMjUxNloYDzI1MTgxMTIxMjIyNTE2WjBT
+MQswCQYDVQQGEwJTRTESMBAGA1UEAwwJVGVzdCBjZXJ0MRAwDgYDVQQDDAdwcm94
+eTEwMQ4wDAYDVQQDDAVjaGlsZDEOMAwGA1UEAwwFY2hpbGQwggIiMA0GCSqGSIb3
+DQEBAQUAA4ICDwAwggIKAoICAQDOIxKHoKGi1/5V21RKfDqag54mjcz/ye0NvVHq
+QKXJ4I8EVZyP7fwtl4ElcZ0GyHhqetXsulqgzuoGns5eCAq9mMkX4+3/EXvy0lyz
+rVa+K5ysq6rsUMg7LPpiWA6RM3YYahNedzk3gsRghJ8q0vbvpTzNZQ1A+IOY1kdX
+AeqyBqUT6kLycPYzU/eL2WzVe6pTRt1p2LKckDjxCKJI1ocYhWrdFhbB9YduvEVp
+IRTINGXGvTpk8ZwzvgUQk1BmeGc1qqnmY+/wEEfpu1OZD2+5rJWQ7pSyB1jnMBxq
+mTc5jkrMkzJX9F5JleVY9+bKZcBGu34mmAa4vXfwQOnM2HXAvjw3DJGlZCuNKExs
+Ji9RyZcbe1NZqlBkp9l79cnlqURV6HftFFTyBNloEaNdzi81rYiMlxEoHEHqjLvo
+9HCNV90WDDHPxDG+iOKyY6OAZ/QtjGEAjizp1NYHYkvTG3PzVvqQCsNF4iWzksQY
+3M0OgyDybskOxvUN0NzDrF7Zw0+SqBSnYGWokVoghzQHMQHEOv/gYvrdw4kGs5Db
+RXPfiYKJSPlsFgi0zXpZgm8Br2GxEW6ZfADaK/eONC6FW2W4aL9oqC8XyV2kYi/v
+69G+UeULhVELL4bsUf0moPELFpuwyShHqfQ4l5Us5m66zxc/I0ekz2N66mWv/WQ9
+LNJBBwIDAQABo2AwXjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHQ4EFgQU
+W8uH0Ungtk49Eykz3IE+8z536hswJQYIKwYBBQUHAQ4BAf8EFjAUAgEKMA8GCCsG
+AQUFBxUABANmb28wDQYJKoZIhvcNAQELBQADggIBAAJxESHv3qYUiqzpmWI13Bbe
+v9UqS/Le+WmWosv7JfbBV/aL9T2FF0uw/sMojKxxs88wfipYAaf7Or92JBlaSyt0
+YMhmhW7+miLEoWqeKkRfBx0q5IHvtmQMpNjDxA9uTXJW0U6FIyhVxXRte/3x4owk
+KUfq5P43ErPMEVipaM0ns2y4+d9WimFtUY/52l/NqH84pwgP/2JuNYtRaOZ5pjyO
+//zSUpiDbyE1OCeBG2b+YqKwDnCdxdqj0pZps/1fLieBr89GbS4SEMlqRgqN6LxO
+XHkfS3frkD87l32zTuQnhD8vxKU01Kr85t6CPL+FIUhjUCxG3Tll8Z+coxgZp8IX
+bjpyJfEx9834UqA3EDKpcuh3vndvov0nXe5XnxpmYevuCpd5fIjnbAdimFMshni7
+WhW+9HzKGTAKqaGXqRyEsPybm6Psw6F60p5Kbr9X8/+WM8j3mReQI4n1yKfW25kR
+HlqLPmwrJUOGDsf2NV0kYg/8Zd+D5uT02LUKQPh5gd/9X/vm/YNJfmLvkK9V0yI9
+5U6nxRe+kQDreWSpP0mS2Bl3o/mOKDwinn4zZLU3IStrvhoVEo9LeIuehsul8zpk
+57x1zHsKwviywBdAeJOXglQRhGhy76+jcN6Ii5rx6Na7uSlTSQqyz23bXfK8BcJr
+TpIzMZLfa2s8faTjnjAD
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy10-child-child-test.key b/third_party/heimdal/lib/hx509/data/proxy10-child-child-test.key
new file mode 100644
index 0000000..7a55601
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy10-child-child-test.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+9MZTi7RGridSLpg1QpqCUOcbhOQ1MKQ=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy10-child-test.crt b/third_party/heimdal/lib/hx509/data/proxy10-child-test.crt
new file mode 100644
index 0000000..41cb814
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy10-child-test.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFWzCCA0OgAwIBAgIJAM764JrT/2XxMA0GCSqGSIb3DQEBCwUAMDMxCzAJBgNV
+BAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQxEDAOBgNVBAMMB3Byb3h5MTAwIBcN
+MTkwMzIyMjIyNTE1WhgPMjUxODExMjEyMjI1MTVaMEMxCzAJBgNVBAYTAlNFMRIw
+EAYDVQQDDAlUZXN0IGNlcnQxEDAOBgNVBAMMB3Byb3h5MTAxDjAMBgNVBAMMBWNo
+aWxkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAunD1wwgtFwDOISki
+iVAOjL6Q5UHHTFfGMkeXoa6/awWWjRBZT3IdmQlxxXlIEafS9vBMHW4xvAVf2qmg
+Y6IZUy4EGqdrzzzmf64E/QcRW6vGbqRQY+HBqYDum+se4s/XfCo7sSpTLbZexhoM
+Uz22k3reKzT3xurXtbxF+GQcpb2HnESnTL2omtU2PgEZ8BwhjisEB3rHTtQFqBdF
+1uKSvRDJtDlRSUbqgY+cYCFUMdIEtn4djesLdH7yt34i5kukN6RajXJjQTSQFGce
+egHaMOcQ7phIv7j305RFJ6waCXBwTCsBoIPtE2wt9pfgFu8zbcWenXpaxR+en/n/
+2gU7LnK3Odiyh3d0/8a+79gWxXWbmXYV2oYVl9ZxSTqa+wvzYQNaw1BPyhGxd95S
+ACJOw8XuZ16ZEqQArEH59A36NKdca3OYjAf82fOEfRdIHm/CD9SEv9NRQpgzfzBF
+cmiaIdBJg+QJJ0eNr4wZq8sVp1BKlnDosEq0nJ2ywRB3SeTbUrz3VxR3pzjtNYO1
+fIXUnBsoymZtUYqKiNLVP96RyWplxF204zEMP3zx0vIx6pKMmsWivEURxNdo/Hih
+eiDaHB93aw+5+2pgKQOfIg0jj4ChE1JN2FOKBDAFq5+9rJIDuhS0Nk4PtYy/7n4R
+iIDKiqWDNc2syEZYlEO+1QQuuRkCAwEAAaNgMF4wCQYDVR0TBAIwADALBgNVHQ8E
+BAMCBeAwHQYDVR0OBBYEFD4E+/XklnqAWUXZrJa01AuHb6IbMCUGCCsGAQUFBwEO
+AQH/BBYwFAIBCjAPBggrBgEFBQcVAAQDZm9vMA0GCSqGSIb3DQEBCwUAA4ICAQCW
+nwXJetxx3Y82aBcyG9sQ5CTObsP1TyHRLVOKeA5jTwVxzSjQOChuUijLFbJ3QYds
+vP8h56rjn/hLSi7kdyh1s1+mDBRFWAdJCfRfQhQWvgK7eaphln7qUXUXa3JR66Um
+R56cXa6A4KG/Qk2vKw+NLjwA01vF+YzynSijwlaRptYLeOZMUMRzD8IwgqkdOAHr
+6+RgiRrRIfdZHYmMX3/7sTvzub/d4QfANjKYPVkciW3E71yeAOLOfjjK68u4OMhn
+F3I1vHdhCwxh87gZdiutmUjd+xTHhyIVObpSYIfSDCWqjp70s6sTJBdTCDnzIzcp
+ta/iI1EkyOH0aOSzz4bclcWAA2R95JeQDP8PLUA8gddvkbW4f9RJbWMjAk3r6WMM
+aSlx7djzbKdXIPd7ecFRWPf5uianNHlTHH2pZkHQoENCfuNTs688OzjBEDzUIvOZ
+S3WbVlqSzdf8Xp8xDp01QbC/bwFJwOeZjdj4IVyF2vXB2AaE5Xy/umQz1NVnIqK1
+CUyv/EP4HMbWIW5hpaMZJ4VyhrkoX4sLDj25UN1VAzD9lTyAGpMv5KpWd98MhYIX
+7KHP69Xo4CHf1RibdRVX6jW+GvzRZGHPb8/V8qgDwOM7UPjsPuwq5Cacatu0L7tQ
+hqtYGxcBDAIRIrmm5sZCW4F3l4efUtzozWZS0vzTyQ==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy10-child-test.key b/third_party/heimdal/lib/hx509/data/proxy10-child-test.key
new file mode 100644
index 0000000..7bc4a02
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy10-child-test.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+Qshwvh4gXup0rcomkC+d8vVJ2p35
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy10-test.crt b/third_party/heimdal/lib/hx509/data/proxy10-test.crt
new file mode 100644
index 0000000..9c89f71
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy10-test.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFOTCCAyGgAwIBAgIJAKQmPUkmhyKjMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNV
+BAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQwIBcNMTkwMzIyMjIyNTE0WhgPMjUx
+ODExMjEyMjI1MTRaMDMxCzAJBgNVBAYTAlNFMRIwEAYDVQQDDAlUZXN0IGNlcnQx
+EDAOBgNVBAMMB3Byb3h5MTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQDNg69jn3h/uqasiOdUD/MC0vJoty8ncj9NNGfU7YHSLpRnuoyPL8YCRRbxreUP
+lIQ5oAP4ERJSrrRoGWuwiboLthVj6OgM1IrU3h9M2sM7fGdLuQ1QCDP0Bsu23MPJ
+KWMCUpd3p7jXrPaf1WzUtaIql0pjFFlqZRf1RLKvH4CfDgrx/k3z49RsiZtgxlw2
+VxpGL3cPUO1MfXEq4roMWdT0sGTdJ56l+PkF3Ud2DxhNytmDv1ld2gZhC5H+k5ZI
+i3Xr8zvV2dCbSlnnXk8qTG+aWwYGpHFQPwP7UxQDUAk1XAX5/EBXwCTtTDNIhsNI
+qB6k9MF3/mreD5frofvx6xEW8CShdAf2vUXC4OB8hkO/NfdaoQj99PL7d8LiICoQ
+u4fOZ20g6zpkS+jir5p3KcVbotN0cMkx+k2YwyK3PoLV74RTsigOjp828eoT24Wz
+3/ztjn4C0uMHcJz4yHmbrn7xOf7VcaNvfm9qz9H/MpA2Eg0Nj4nBOnTf25WLvgAB
+KN0Ctb6rNbF3UfVR9h+QK4kQqhlwjMfPqeT2AL0fOFptotcdPj86z2ux1cvC/w/x
+nZrgUdcj2GLLwvCxsmVx5RWOWU7wFoo3WrWpyx228CvaPTQXN5LKBIgpf1wmk4b1
+wR3afu0I5RQp8kSD/2kQBW24D4+AAH1jFbP9IfxxSXCazwIDAQABo2AwXjAJBgNV
+HRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHQ4EFgQUiFCNi8YSDjclAz4L6ZPKi9Ea
+afQwJQYIKwYBBQUHAQ4BAf8EFjAUAgEKMA8GCCsGAQUFBxUABANmb28wDQYJKoZI
+hvcNAQELBQADggIBAKEy0OrFQkrc3ngQXWBhOEoLElCYIpM5sVDn3aV8i8UAQ9KB
+uLfxmcJm5CPZcIAFive2TWVFm1eq6Fr1WEwZ2QtAOXwoQwCzKISsdFRHEV2SNtpg
+uUaKlhGUwiYWt9aPkmJD4f/VZB7LeKE4CWij88ey+76Lujkr+ILZnrXnc2Mwf3J/
+x+/RfINh4oM+7rHccAJtznwZ2TCBvF3p5cLlLH413IGnZjO/myz/EMQ8MJoFNc1G
+dWXOZiGnbq2+W9rHKOV9rYhIZ5YLLiK6L7vKsLph83KTzWCZW7tZDAmwtFFXw406
+bdWL2+gWBC9SxN3RI2jB9VQtaLspOW+v8qH7cpDaO0xTXufsVjiGbh1hPdAMonQd
+k0rrrkLY6ADguTYkHs1E3Eebki6LvyyXBEKJYTtEHfJnU2T1YWwFpqvvFOghFljH
+QyZC29QR0rLB2B6uepQXl6d4fFYfYkhjx7SANDx4QA8QqrLgeGjufj9H3yk9e/XX
+lto6sjNsOOncP9svCxJgQkp4w7tQxqZ/51RBQAJkUDkwPImstxTIFRdqeXHxLynX
+3zTv6rt/767S7SlDfLZs8OrGKPeILGDBF/Q0UQrnrk9Oa+yP2QV3Msg79odKqBzK
+MfcQ4DFIfnN9ZRq7d0ffaZ6AqJVbwYM/LlGFAC57z2xsuEAR+Ea0/bjzbaib
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/proxy10-test.key b/third_party/heimdal/lib/hx509/data/proxy10-test.key
new file mode 100644
index 0000000..733c2ff
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/proxy10-test.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+M4C7UTY7JIl5vsDk5aoxq4Rt6pBkQQ==
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/revoke.crt b/third_party/heimdal/lib/hx509/data/revoke.crt
new file mode 100644
index 0000000..ded2325
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/revoke.crt
@@ -0,0 +1,116 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 3 (0x3)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:03 2019 GMT
+ Not After : Nov 21 22:25:03 2518 GMT
+ Subject: C=SE, CN=Revoke cert
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:ce:ac:a3:c6:69:47:c4:dd:f4:d9:0e:ac:42:90:
+ ae:57:f2:68:c4:77:89:9a:65:cd:8f:97:fc:68:6b:
+ 6b:65:0f:52:2d:d1:db:83:2c:1e:39:35:dd:fb:f6:
+ e8:c1:40:e9:ab:a6:48:23:e9:f0:e1:8f:72:27:6c:
+ e2:8d:04:e9:ca:e3:fe:ac:d9:28:16:be:db:19:fc:
+ 9a:20:d6:93:1f:15:b8:b6:97:cf:07:5a:da:ab:aa:
+ 97:c0:e9:39:7d:f9:df:96:c9:99:8f:6f:51:3f:64:
+ 13:0e:ad:0e:4e:2e:66:6f:72:6f:63:a6:a5:fd:85:
+ 0f:ac:ea:03:4d:81:14:bc:f3:5b:e5:fc:f6:6a:f7:
+ 57:b3:c3:b0:ed:4b:43:b1:cf:e2:1f:f6:44:07:83:
+ 27:b8:ef:19:9f:35:2b:95:59:b9:e1:69:c5:19:07:
+ 06:d7:17:da:35:4b:ba:74:68:c3:d3:28:ab:1e:b4:
+ 8a:ba:2b:f3:5e:06:75:0c:c8:a2:a9:ea:ec:29:1a:
+ 98:fb:b6:00:e0:98:78:cf:ea:36:2c:e1:51:8e:15:
+ 74:ba:4e:2d:8c:df:9b:72:72:52:b7:c7:82:45:35:
+ ba:c3:62:bf:29:d0:c0:17:6b:be:3b:e4:87:6a:26:
+ 34:4f:84:b5:ad:34:72:5f:4c:96:d8:d4:cd:5d:6f:
+ a3:ac:b1:55:a8:c8:c6:5d:99:0b:f0:bd:5e:f2:85:
+ 3e:74:05:d7:0f:9f:95:5a:14:1f:19:31:af:55:75:
+ 2a:80:22:7b:f7:ff:89:4b:70:5a:74:52:77:7a:ac:
+ 6b:86:2d:cc:5e:ca:57:3d:a1:20:d0:95:80:0b:48:
+ 26:52:69:9d:19:7f:0e:a9:63:97:70:b6:25:64:79:
+ ae:19:45:f8:7f:fd:23:75:9b:0f:d5:57:ae:56:50:
+ 9a:0c:fd:eb:f2:1b:a9:0a:3d:a2:1d:f3:07:cd:b9:
+ 63:5b:3d:95:21:9a:f6:27:2e:46:6a:3f:8f:48:b9:
+ e5:d7:ef:27:08:fc:45:37:70:23:88:a2:89:50:7e:
+ a3:ba:06:b3:b9:50:60:7d:aa:d6:eb:1c:b9:79:1c:
+ 16:06:d2:07:d3:c6:09:73:2a:8a:92:10:93:cc:52:
+ b4:bf:4b:09:d6:71:c1:60:57:3e:2f:12:13:90:18:
+ 06:44:cf:79:6f:50:78:11:8c:e9:ab:2b:97:19:5f:
+ b2:67:a9:fa:9b:b0:99:44:35:0e:00:18:6f:9a:00:
+ 39:e2:ac:e2:79:25:e1:46:d2:18:e4:80:d5:ca:ed:
+ 15:dc:7f:a7:90:7f:26:71:26:38:6b:ef:be:92:0c:
+ 07:64:24:64:a7:85:9d:2b:d9:14:bc:64:40:46:eb:
+ 78:b9:dd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ 3B:AC:F2:D9:72:19:FF:77:61:0C:6B:2C:C0:69:D0:28:46:8A:C1:D7
+ Signature Algorithm: sha1WithRSAEncryption
+ 23:5d:75:da:82:54:6a:eb:29:cf:e0:55:da:4e:69:c3:d1:7b:
+ 27:20:37:ca:3e:ac:ba:55:30:0d:a6:57:44:de:1b:71:aa:57:
+ 80:8d:55:e1:48:fb:43:dc:23:d3:fd:85:ab:36:35:11:1d:41:
+ 30:59:ff:e4:61:e1:4d:14:8b:64:9e:cc:a0:71:19:a3:a9:10:
+ 84:47:72:dd:2b:56:5e:78:a9:ed:f1:32:8b:b4:5b:87:aa:bd:
+ 74:4f:ee:50:ba:36:d5:70:56:40:7d:64:d6:04:42:ae:50:2b:
+ 95:48:f5:74:8b:a6:b5:5c:49:9d:9c:f1:0c:0f:0a:f1:53:43:
+ ec:1f:59:6f:1e:54:ca:9d:b2:39:73:58:28:b7:0b:74:e3:ed:
+ d4:36:ef:7d:1d:c6:1f:2c:ff:a7:df:a2:a7:9e:94:b9:3f:3d:
+ 18:fa:07:d6:e9:03:f6:3a:d1:79:55:df:af:12:13:ef:45:af:
+ 63:57:fc:ef:db:5c:bd:e7:93:b5:81:35:e9:a9:e4:39:99:b9:
+ 32:7b:6f:1a:14:41:3a:fa:68:3c:0a:ae:9e:95:51:72:32:dc:
+ d6:e9:98:7d:65:db:ce:57:1f:1a:e5:2a:5a:c0:07:26:64:f0:
+ 49:ff:af:97:74:fe:98:20:94:7f:f7:3c:a7:46:ed:ad:e5:1b:
+ 7a:08:c4:d4:ce:3f:8a:ef:07:79:ec:d5:f1:1b:2b:f6:e0:95:
+ 31:ef:8e:bd:b8:ec:a7:84:f8:ff:c6:39:7a:15:8d:4b:4e:05:
+ c8:e6:2e:bb:bb:74:5a:51:92:f7:b1:04:55:2b:dc:42:18:d5:
+ 83:95:c4:d0:73:10:62:d5:55:8d:ea:a0:fd:ff:ef:10:9b:8f:
+ b3:ba:8a:91:75:5e:b9:9d:36:7d:53:5d:8d:1b:0d:c5:bb:1c:
+ 23:fc:08:5b:1f:3a:d5:1c:35:61:48:58:8e:c0:42:7c:3c:c8:
+ a0:17:8a:04:13:a6:03:49:cf:86:18:39:32:e4:fe:32:38:bd:
+ 53:bd:49:fa:65:63:3d:41:6a:c7:65:f5:df:7d:7b:8d:d0:74:
+ b2:c3:8b:bd:1e:4f:96:15:a0:7b:23:fe:81:e0:de:7f:06:b3:
+ f8:a2:52:cf:43:91:49:6f:ae:d8:6f:4f:51:85:7b:c2:f7:f8:
+ c8:4d:e0:a8:48:9a:5b:05:e2:60:fd:b7:bb:b7:7a:2b:35:e6:
+ 15:f3:e8:5f:b6:cb:d5:b0:7b:45:70:db:fe:82:97:c5:6b:be:
+ a9:60:21:87:19:b6:91:32:2f:01:b3:04:84:a3:1d:8b:06:00:
+ 3e:37:f4:c3:ff:b4:55:cb:cc:d1:d1:96:9b:d8:1a:0b:9f:47:
+ 66:b7:90:9c:d1:09:c2:aa
+-----BEGIN CERTIFICATE-----
+MIIFAzCCAuugAwIBAgIBAzANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwM1oYDzI1
+MTgxMTIxMjIyNTAzWjAjMQswCQYDVQQGEwJTRTEUMBIGA1UEAwwLUmV2b2tlIGNl
+cnQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDOrKPGaUfE3fTZDqxC
+kK5X8mjEd4maZc2Pl/xoa2tlD1It0duDLB45Nd379ujBQOmrpkgj6fDhj3InbOKN
+BOnK4/6s2SgWvtsZ/Jog1pMfFbi2l88HWtqrqpfA6Tl9+d+WyZmPb1E/ZBMOrQ5O
+LmZvcm9jpqX9hQ+s6gNNgRS881vl/PZq91ezw7DtS0Oxz+If9kQHgye47xmfNSuV
+WbnhacUZBwbXF9o1S7p0aMPTKKsetIq6K/NeBnUMyKKp6uwpGpj7tgDgmHjP6jYs
+4VGOFXS6Ti2M35tyclK3x4JFNbrDYr8p0MAXa7475IdqJjRPhLWtNHJfTJbY1M1d
+b6OssVWoyMZdmQvwvV7yhT50BdcPn5VaFB8ZMa9VdSqAInv3/4lLcFp0Und6rGuG
+Lcxeylc9oSDQlYALSCZSaZ0Zfw6pY5dwtiVkea4ZRfh//SN1mw/VV65WUJoM/evy
+G6kKPaId8wfNuWNbPZUhmvYnLkZqP49IueXX7ycI/EU3cCOIoolQfqO6BrO5UGB9
+qtbrHLl5HBYG0gfTxglzKoqSEJPMUrS/SwnWccFgVz4vEhOQGAZEz3lvUHgRjOmr
+K5cZX7JnqfqbsJlENQ4AGG+aADnirOJ5JeFG0hjkgNXK7RXcf6eQfyZxJjhr776S
+DAdkJGSnhZ0r2RS8ZEBG63i53QIDAQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQE
+AwIF4DAdBgNVHQ4EFgQUO6zy2XIZ/3dhDGsswGnQKEaKwdcwDQYJKoZIhvcNAQEF
+BQADggIBACNdddqCVGrrKc/gVdpOacPReycgN8o+rLpVMA2mV0TeG3GqV4CNVeFI
++0PcI9P9has2NREdQTBZ/+Rh4U0Ui2SezKBxGaOpEIRHct0rVl54qe3xMou0W4eq
+vXRP7lC6NtVwVkB9ZNYEQq5QK5VI9XSLprVcSZ2c8QwPCvFTQ+wfWW8eVMqdsjlz
+WCi3C3Tj7dQ2730dxh8s/6ffoqeelLk/PRj6B9bpA/Y60XlV368SE+9Fr2NX/O/b
+XL3nk7WBNemp5DmZuTJ7bxoUQTr6aDwKrp6VUXIy3NbpmH1l285XHxrlKlrAByZk
+8En/r5d0/pgglH/3PKdG7a3lG3oIxNTOP4rvB3ns1fEbK/bglTHvjr247KeE+P/G
+OXoVjUtOBcjmLru7dFpRkvexBFUr3EIY1YOVxNBzEGLVVY3qoP3/7xCbj7O6ipF1
+XrmdNn1TXY0bDcW7HCP8CFsfOtUcNWFIWI7AQnw8yKAXigQTpgNJz4YYOTLk/jI4
+vVO9SfplYz1Basdl9d99e43QdLLDi70eT5YVoHsj/oHg3n8Gs/iiUs9DkUlvrthv
+T1GFe8L3+MhN4KhImlsF4mD9t7u3eis15hXz6F+2y9Wwe0Vw2/6Cl8VrvqlgIYcZ
+tpEyLwGzBISjHYsGAD439MP/tFXLzNHRlpvYGgufR2a3kJzRCcKq
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/revoke.key b/third_party/heimdal/lib/hx509/data/revoke.key
new file mode 100644
index 0000000..d70b74f
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/revoke.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+oSgi/Llzrh+HYp+WubHP32IPhXGX+w0=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r1TestCA.cert.pem b/third_party/heimdal/lib/hx509/data/secp256r1TestCA.cert.pem
new file mode 100644
index 0000000..3522b09
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r1TestCA.cert.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBuTCCAWCgAwIBAgIBATAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UECgwHSGVpbWRhbDEVMBMGA1UEAwwMQ0Egc2VjcDI1NnIxMCAXDTE5MDMyMjIy
+MjUyNVoYDzI1MTgxMTIxMjIyNTI1WjA2MQswCQYDVQQGEwJTRTEQMA4GA1UECgwH
+SGVpbWRhbDEVMBMGA1UEAwwMQ0Egc2VjcDI1NnIxMFkwEwYHKoZIzj0CAQYIKoZI
+zj0DAQcDQgAE5SuFK+KhglopQr1aMjl4ZEBaw4HYM2yVORyBOQWx3e8Pj90bFocE
+4gyS4P2V0YraxACsQgMp+s4e8/6gXPeMtqNdMFswHQYDVR0OBBYEFOtR3wCoaF9m
+8dWylzOdd5vfbwmDMB8GA1UdIwQYMBaAFOtR3wCoaF9m8dWylzOdd5vfbwmDMAwG
+A1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoGCCqGSM49BAMCA0cAMEQCIF/JTbEv
+iuYcuREFzWgZ/AgfLe2sRwEgSy6UcAWOYllkAiApMzA3xKjaX1/hhkDGKZnHfcTM
+tRuM0FuTdO+e15ku8w==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r1TestCA.key.pem b/third_party/heimdal/lib/hx509/data/secp256r1TestCA.key.pem
new file mode 100644
index 0000000..3888266
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r1TestCA.key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgL2N0gdHhAjBGcJ40
+gHePPMwGKygIVDXTfjysn9zPiSOhRANCAATlK4Ur4qGCWilCvVoyOXhkQFrDgdgz
+bJU5HIE5BbHd7w+P3RsWhwTiDJLg/ZXRitrEAKxCAyn6zh7z/qBc94y2
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r1TestCA.pem b/third_party/heimdal/lib/hx509/data/secp256r1TestCA.pem
new file mode 100644
index 0000000..9b1df2c
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r1TestCA.pem
@@ -0,0 +1,17 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgL2N0gdHhAjBGcJ40
+gHePPMwGKygIVDXTfjysn9zPiSOhRANCAATlK4Ur4qGCWilCvVoyOXhkQFrDgdgz
+bJU5HIE5BbHd7w+P3RsWhwTiDJLg/ZXRitrEAKxCAyn6zh7z/qBc94y2
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIBuTCCAWCgAwIBAgIBATAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UECgwHSGVpbWRhbDEVMBMGA1UEAwwMQ0Egc2VjcDI1NnIxMCAXDTE5MDMyMjIy
+MjUyNVoYDzI1MTgxMTIxMjIyNTI1WjA2MQswCQYDVQQGEwJTRTEQMA4GA1UECgwH
+SGVpbWRhbDEVMBMGA1UEAwwMQ0Egc2VjcDI1NnIxMFkwEwYHKoZIzj0CAQYIKoZI
+zj0DAQcDQgAE5SuFK+KhglopQr1aMjl4ZEBaw4HYM2yVORyBOQWx3e8Pj90bFocE
+4gyS4P2V0YraxACsQgMp+s4e8/6gXPeMtqNdMFswHQYDVR0OBBYEFOtR3wCoaF9m
+8dWylzOdd5vfbwmDMB8GA1UdIwQYMBaAFOtR3wCoaF9m8dWylzOdd5vfbwmDMAwG
+A1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoGCCqGSM49BAMCA0cAMEQCIF/JTbEv
+iuYcuREFzWgZ/AgfLe2sRwEgSy6UcAWOYllkAiApMzA3xKjaX1/hhkDGKZnHfcTM
+tRuM0FuTdO+e15ku8w==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r2TestClient.cert.pem b/third_party/heimdal/lib/hx509/data/secp256r2TestClient.cert.pem
new file mode 100644
index 0000000..5763c5a
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r2TestClient.cert.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVegAwIBAgIBAjAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UECgwHSGVpbWRhbDEVMBMGA1UEAwwMQ0Egc2VjcDI1NnIxMCAXDTE5MDMyMjIy
+MjUyNVoYDzI1MTgxMTIxMjIyNTI1WjAwMQswCQYDVQQGEwJTRTEQMA4GA1UECgwH
+SGVpbWRhbDEPMA0GA1UEAwwGQ2xpZW50MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
+QgAE7v8wIgEd/yBAtDmW2nW7II4IDDy7KVlnv6/+sHegoe4AjIvqr9CNldkfcVHl
+VwjMA1q83lIB/H0IUmk+T6lQhKNaMFgwHQYDVR0OBBYEFKNeDhfd+znE5CBO6aNl
+DAUjXdF6MB8GA1UdIwQYMBaAFOtR3wCoaF9m8dWylzOdd5vfbwmDMAkGA1UdEwQC
+MAAwCwYDVR0PBAQDAgSwMAoGCCqGSM49BAMCA0cAMEQCIBVFIAeFxXeB4LURDxv/
+YnGzJJK0b+pjK5hVEPYww8dzAiAmHrccM21Ga7S+/yFWIvMe3BKtAl0O62TTI2Fg
+CHiIWQ==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r2TestClient.key.pem b/third_party/heimdal/lib/hx509/data/secp256r2TestClient.key.pem
new file mode 100644
index 0000000..36c67f9
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r2TestClient.key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg6oD5CbNzN7oAWqcq
+dKJKw2WU5EwnUV05+7S9gXgeW/qhRANCAATu/zAiAR3/IEC0OZbadbsgjggMPLsp
+WWe/r/6wd6Ch7gCMi+qv0I2V2R9xUeVXCMwDWrzeUgH8fQhSaT5PqVCE
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r2TestClient.pem b/third_party/heimdal/lib/hx509/data/secp256r2TestClient.pem
new file mode 100644
index 0000000..9f49adc
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r2TestClient.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVegAwIBAgIBAjAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UECgwHSGVpbWRhbDEVMBMGA1UEAwwMQ0Egc2VjcDI1NnIxMCAXDTE5MDMyMjIy
+MjUyNVoYDzI1MTgxMTIxMjIyNTI1WjAwMQswCQYDVQQGEwJTRTEQMA4GA1UECgwH
+SGVpbWRhbDEPMA0GA1UEAwwGQ2xpZW50MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
+QgAE7v8wIgEd/yBAtDmW2nW7II4IDDy7KVlnv6/+sHegoe4AjIvqr9CNldkfcVHl
+VwjMA1q83lIB/H0IUmk+T6lQhKNaMFgwHQYDVR0OBBYEFKNeDhfd+znE5CBO6aNl
+DAUjXdF6MB8GA1UdIwQYMBaAFOtR3wCoaF9m8dWylzOdd5vfbwmDMAkGA1UdEwQC
+MAAwCwYDVR0PBAQDAgSwMAoGCCqGSM49BAMCA0cAMEQCIBVFIAeFxXeB4LURDxv/
+YnGzJJK0b+pjK5hVEPYww8dzAiAmHrccM21Ga7S+/yFWIvMe3BKtAl0O62TTI2Fg
+CHiIWQ==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg6oD5CbNzN7oAWqcq
+dKJKw2WU5EwnUV05+7S9gXgeW/qhRANCAATu/zAiAR3/IEC0OZbadbsgjggMPLsp
+WWe/r/6wd6Ch7gCMi+qv0I2V2R9xUeVXCMwDWrzeUgH8fQhSaT5PqVCE
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r2TestServer.cert.pem b/third_party/heimdal/lib/hx509/data/secp256r2TestServer.cert.pem
new file mode 100644
index 0000000..71935b8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r2TestServer.cert.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVegAwIBAgIBAjAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UECgwHSGVpbWRhbDEVMBMGA1UEAwwMQ0Egc2VjcDI1NnIxMCAXDTE5MDMyMjIy
+MjUyNVoYDzI1MTgxMTIxMjIyNTI1WjAwMQswCQYDVQQGEwJTRTEQMA4GA1UECgwH
+SGVpbWRhbDEPMA0GA1UEAwwGU2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
+QgAEi1mCfULxkG8qSQ2gSh9YvQksJNodPaUmPt32NG1IqJHY+U613keG14Oo/vwr
+h6CVEyTmeqyYSihqV9s2wUHLxKNaMFgwHQYDVR0OBBYEFOowNM57NxGxrHqV/oT9
+eT5DM+iuMB8GA1UdIwQYMBaAFOtR3wCoaF9m8dWylzOdd5vfbwmDMAkGA1UdEwQC
+MAAwCwYDVR0PBAQDAgSwMAoGCCqGSM49BAMCA0cAMEQCIB2nDbiSg6jTeoSWfCvG
+23Pn4xxbes8Nb+/8+1lDjLWPAiA+KB8wJhUA4hO/Axfu85wKjddpbGtJR0JlxtEe
+whF52Q==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r2TestServer.key.pem b/third_party/heimdal/lib/hx509/data/secp256r2TestServer.key.pem
new file mode 100644
index 0000000..fb57e79
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r2TestServer.key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKo/47DaveCl90GxH
+LCE7IGBua2XsE+jI4RUWZrqjhBGhRANCAASLWYJ9QvGQbypJDaBKH1i9CSwk2h09
+pSY+3fY0bUiokdj5TrXeR4bXg6j+/CuHoJUTJOZ6rJhKKGpX2zbBQcvE
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/secp256r2TestServer.pem b/third_party/heimdal/lib/hx509/data/secp256r2TestServer.pem
new file mode 100644
index 0000000..31a354d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/secp256r2TestServer.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVegAwIBAgIBAjAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UECgwHSGVpbWRhbDEVMBMGA1UEAwwMQ0Egc2VjcDI1NnIxMCAXDTE5MDMyMjIy
+MjUyNVoYDzI1MTgxMTIxMjIyNTI1WjAwMQswCQYDVQQGEwJTRTEQMA4GA1UECgwH
+SGVpbWRhbDEPMA0GA1UEAwwGU2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
+QgAEi1mCfULxkG8qSQ2gSh9YvQksJNodPaUmPt32NG1IqJHY+U613keG14Oo/vwr
+h6CVEyTmeqyYSihqV9s2wUHLxKNaMFgwHQYDVR0OBBYEFOowNM57NxGxrHqV/oT9
+eT5DM+iuMB8GA1UdIwQYMBaAFOtR3wCoaF9m8dWylzOdd5vfbwmDMAkGA1UdEwQC
+MAAwCwYDVR0PBAQDAgSwMAoGCCqGSM49BAMCA0cAMEQCIB2nDbiSg6jTeoSWfCvG
+23Pn4xxbes8Nb+/8+1lDjLWPAiA+KB8wJhUA4hO/Axfu85wKjddpbGtJR0JlxtEe
+whF52Q==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKo/47DaveCl90GxH
+LCE7IGBua2XsE+jI4RUWZrqjhBGhRANCAASLWYJ9QvGQbypJDaBKH1i9CSwk2h09
+pSY+3fY0bUiokdj5TrXeR4bXg6j+/CuHoJUTJOZ6rJhKKGpX2zbBQcvE
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/sf-class2-root.pem b/third_party/heimdal/lib/hx509/data/sf-class2-root.pem
new file mode 100644
index 0000000..d552e65
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/sf-class2-root.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/static-file b/third_party/heimdal/lib/hx509/data/static-file
new file mode 100644
index 0000000..2216857
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/static-file
@@ -0,0 +1,84 @@
+This is a static file don't change the content, it is used in the test
+
+#!/bin/sh
+#
+# Copyright (c) 2005 Kungliga Tekniska Hgskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+srcdir="@srcdir@"
+
+echo "try printing"
+./hxtool print \
+ --pass=PASS:foobar \
+ PKCS12:$srcdir/data/test.p12 || exit 1
+
+echo "make sure entry is found (friendlyname)"
+./hxtool query \
+ --pass=PASS:foobar \
+ --friendlyname=friendlyname-test \
+ PKCS12:$srcdir/data/test.p12 || exit 1
+
+echo "make sure entry is not found (friendlyname)"
+./hxtool query \
+ --pass=PASS:foobar \
+ --friendlyname=friendlyname-test-not \
+ PKCS12:$srcdir/data/test.p12 && exit 1
+
+echo "check for ca cert (friendlyname)"
+./hxtool query \
+ --pass=PASS:foobar \
+ --friendlyname=ca \
+ PKCS12:$srcdir/data/test.p12 || exit 1
+
+echo "make sure entry is not found (friendlyname)"
+./hxtool query \
+ --pass=PASS:foobar \
+ --friendlyname=friendlyname-test \
+ PKCS12:$srcdir/data/sub-cert.p12 && exit 1
+
+echo "make sure entry is found (friendlyname|private key)"
+./hxtool query \
+ --pass=PASS:foobar \
+ --friendlyname=friendlyname-test \
+ --private-key \
+ PKCS12:$srcdir/data/test.p12 || exit 1
+
+echo "make sure entry is not found (friendlyname|private key)"
+./hxtool query \
+ --pass=PASS:foobar \
+ --friendlyname=ca \
+ --private-key \
+ PKCS12:$srcdir/data/test.p12 && exit 1
+
+exit 0
+
diff --git a/third_party/heimdal/lib/hx509/data/sub-ca.crt b/third_party/heimdal/lib/hx509/data/sub-ca.crt
new file mode 100644
index 0000000..25f3ae8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/sub-ca.crt
@@ -0,0 +1,123 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 10 (0xa)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:10 2019 GMT
+ Not After : Nov 21 22:25:10 2518 GMT
+ Subject: C=SE, CN=Sub CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:c7:18:39:67:2a:c4:6b:c6:1a:64:23:bb:ba:4c:
+ 47:22:35:91:b7:c9:eb:57:b9:8b:8f:83:62:be:0a:
+ 56:49:cc:ed:de:7e:f9:44:db:8f:f9:f9:ec:db:a2:
+ 4a:d3:fa:b1:36:c0:93:e9:2b:d0:9a:64:65:43:52:
+ 64:0e:af:3c:0a:23:57:d9:66:44:0c:ef:a6:73:7e:
+ 4d:71:94:76:5d:d2:2e:9c:02:1e:44:4b:67:0d:61:
+ 05:ff:f1:cc:29:94:93:ab:f7:b6:d7:33:d0:9e:b4:
+ 02:1a:7b:03:bb:9c:52:00:21:43:97:ff:59:f3:b1:
+ eb:16:67:b1:5a:66:26:99:04:12:28:bb:68:97:38:
+ 66:cf:d3:cc:da:41:d8:4f:e2:f9:59:48:da:ca:55:
+ b9:2a:63:43:6b:0d:c5:58:75:8e:6e:55:d2:77:cd:
+ df:8a:14:82:a2:72:f3:e8:93:a1:e4:72:f3:c0:93:
+ b3:0b:72:98:ad:53:93:53:86:fc:b0:3b:77:1c:aa:
+ f5:64:77:ce:92:0c:07:82:60:39:e9:d6:bc:df:dc:
+ ad:f9:4f:42:d2:db:42:76:6e:0b:f5:fa:58:05:7f:
+ 3c:d9:cf:eb:d2:c0:9a:26:2c:e8:90:73:0a:3c:42:
+ e5:f9:0b:cd:53:2d:16:14:75:f8:47:2e:04:1a:47:
+ d8:a6:20:0f:ec:96:fe:14:30:87:30:84:04:74:42:
+ 45:b3:3b:c1:48:84:54:4e:69:9b:f5:cb:7a:da:75:
+ 1e:26:93:87:5e:a2:c6:8f:fd:0f:96:84:76:2d:18:
+ 86:f7:87:1e:95:47:10:45:b5:45:ea:38:b7:e0:22:
+ 28:c6:98:42:5f:ed:69:d6:73:a3:d4:72:de:74:f7:
+ 2a:d2:90:5d:66:86:a1:b5:a4:fb:c7:37:94:65:82:
+ 80:d7:88:84:be:d6:5f:fd:25:88:0b:ee:6b:bb:4b:
+ 94:c6:e1:39:95:74:93:44:44:8e:3f:7e:13:33:49:
+ 8e:e3:f4:a0:43:e7:2d:15:f7:02:e9:bf:a8:94:65:
+ 71:df:45:35:f7:cc:03:b6:e4:d6:32:d2:98:66:ba:
+ d6:da:76:35:e0:81:76:25:0a:94:3f:6c:a6:53:49:
+ 52:c5:38:44:4d:ea:b4:fd:50:ee:63:e1:1b:51:ef:
+ 62:64:0e:39:cb:10:73:9d:fd:b0:2e:15:5a:cb:90:
+ 1c:9f:e9:88:37:14:92:32:7b:7a:00:fd:35:b4:d3:
+ 8c:99:90:74:95:7d:bf:25:41:04:68:56:38:3e:f1:
+ f5:97:b5:f3:cc:b8:16:99:40:1f:9d:eb:51:88:46:
+ 2a:62:b9:a5:bd:ad:97:db:58:5a:d4:6c:ed:32:db:
+ b4:5a:f5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 63:34:08:C8:42:04:47:74:99:65:DD:4F:EA:C5:0F:05:D9:F8:CE:47
+ X509v3 Authority Key Identifier:
+ keyid:53:B8:CC:09:C6:9F:42:EA:D5:E4:74:20:B4:65:ED:68:F8:9D:B5:05
+ DirName:/CN=hx509 Test Root CA/C=SE
+ serial:8D:F8:0A:D8:C1:70:91:C4
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment, Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 77:0b:fc:11:37:04:49:92:2b:97:e1:ee:b6:94:33:11:be:bb:
+ db:8b:6e:ce:42:11:39:b2:be:61:03:a2:ef:d4:06:1f:63:d2:
+ af:1f:c5:43:80:67:1d:10:a0:3d:93:d1:7f:bd:be:9e:21:48:
+ d0:a8:ea:8c:32:0a:f7:eb:b0:c7:0f:ac:a7:8b:c6:1a:18:10:
+ 51:88:fd:1a:53:4b:1b:7b:94:5e:59:02:92:72:6c:df:32:3a:
+ 9c:f5:87:c9:fd:a2:f8:d3:df:34:be:75:7e:51:15:eb:b0:df:
+ 87:1b:15:df:fc:97:1e:06:f9:6e:8b:79:45:3d:c4:76:d2:1d:
+ 8e:04:8f:72:d6:b0:7c:09:79:23:47:7a:9a:41:76:7e:c3:3d:
+ 2d:46:26:db:72:64:a8:1d:ca:94:fe:d8:69:e7:24:1f:dc:c8:
+ 7b:4f:2f:89:7b:a3:8c:33:7f:0f:54:16:f4:45:60:e1:df:68:
+ f5:5b:3a:ce:1c:63:e6:81:ca:a6:aa:e4:a2:c1:07:e3:ec:ef:
+ ef:ad:cc:ac:5a:e1:57:40:15:09:b3:0f:f1:58:b2:2a:45:eb:
+ 5e:16:03:9c:2c:c1:ce:22:48:67:06:5e:0a:fd:fd:d5:76:8e:
+ a8:db:2c:38:15:b4:c1:e4:0f:12:98:0a:43:19:e6:74:b9:8b:
+ e3:7a:92:2e:2a:30:1d:b7:85:39:d5:29:2f:54:16:7d:b0:f6:
+ f9:17:e2:95:07:ff:0f:e6:16:55:6d:97:c8:41:c6:5f:8f:a9:
+ 3c:3a:19:8d:66:29:13:f3:00:6d:31:f3:f1:14:a5:e8:c7:2c:
+ c0:18:4b:5e:15:88:eb:59:44:97:91:1c:78:d7:a0:4d:a1:bf:
+ bf:b0:67:4f:68:df:d3:d0:c4:6e:b8:1d:36:bd:a8:c8:b4:67:
+ 34:c0:b2:28:8a:e9:1a:30:14:b3:be:d5:a3:a0:57:4f:b7:ff:
+ a0:9e:c0:28:58:90:43:57:e7:7c:d0:81:90:41:54:85:56:4b:
+ cd:f4:a3:63:3b:1a:8f:82:0d:2c:9d:79:58:40:f4:f6:37:a0:
+ fc:77:db:82:ab:de:fa:0c:7f:c2:ce:35:80:4e:f7:d8:0d:8b:
+ cd:5b:8c:a9:82:ec:a3:a1:ca:b8:4e:29:fd:35:79:dc:4d:f3:
+ bf:ee:41:a0:88:63:b9:65:22:bb:0d:27:e8:91:d4:20:51:06:
+ f9:e7:9a:e9:7c:4c:4a:64:b5:4f:22:79:36:ad:79:e8:b8:6a:
+ 6f:f8:e8:39:48:7b:3f:87:14:9a:22:ec:7d:33:94:35:42:29:
+ 56:11:de:15:bd:4c:c2:5d:ff:9f:82:72:a2:00:b3:e9:68:38:
+ 5b:ab:dd:0d:90:73:cd:80
+-----BEGIN CERTIFICATE-----
+MIIFXzCCA0egAwIBAgIBCjANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUxMFoYDzI1
+MTgxMTIxMjIyNTEwWjAeMQswCQYDVQQGEwJTRTEPMA0GA1UEAwwGU3ViIENBMIIC
+IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxxg5ZyrEa8YaZCO7ukxHIjWR
+t8nrV7mLj4NivgpWSczt3n75RNuP+fns26JK0/qxNsCT6SvQmmRlQ1JkDq88CiNX
+2WZEDO+mc35NcZR2XdIunAIeREtnDWEF//HMKZSTq/e21zPQnrQCGnsDu5xSACFD
+l/9Z87HrFmexWmYmmQQSKLtolzhmz9PM2kHYT+L5WUjaylW5KmNDaw3FWHWOblXS
+d83fihSConLz6JOh5HLzwJOzC3KYrVOTU4b8sDt3HKr1ZHfOkgwHgmA56da839yt
++U9C0ttCdm4L9fpYBX882c/r0sCaJizokHMKPELl+QvNUy0WFHX4Ry4EGkfYpiAP
+7Jb+FDCHMIQEdEJFszvBSIRUTmmb9ct62nUeJpOHXqLGj/0PloR2LRiG94celUcQ
+RbVF6ji34CIoxphCX+1p1nOj1HLedPcq0pBdZoahtaT7xzeUZYKA14iEvtZf/SWI
+C+5ru0uUxuE5lXSTRESOP34TM0mO4/SgQ+ctFfcC6b+olGVx30U198wDtuTWMtKY
+ZrrW2nY14IF2JQqUP2ymU0lSxThETeq0/VDuY+EbUe9iZA45yxBznf2wLhVay5Ac
+n+mINxSSMnt6AP01tNOMmZB0lX2/JUEEaFY4PvH1l7XzzLgWmUAfnetRiEYqYrml
+va2X21ha1GztMtu0WvUCAwEAAaOBmTCBljAdBgNVHQ4EFgQUYzQIyEIER3SZZd1P
+6sUPBdn4zkcwWgYDVR0jBFMwUYAUU7jMCcafQurV5HQgtGXtaPidtQWhLqQsMCox
+GzAZBgNVBAMMEmh4NTA5IFRlc3QgUm9vdCBDQTELMAkGA1UEBhMCU0WCCQCN+ArY
+wXCRxDAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB5jANBgkqhkiG9w0BAQUFAAOC
+AgEAdwv8ETcESZIrl+HutpQzEb6724tuzkIRObK+YQOi79QGH2PSrx/FQ4BnHRCg
+PZPRf72+niFI0KjqjDIK9+uwxw+sp4vGGhgQUYj9GlNLG3uUXlkCknJs3zI6nPWH
+yf2i+NPfNL51flEV67DfhxsV3/yXHgb5bot5RT3EdtIdjgSPctawfAl5I0d6mkF2
+fsM9LUYm23JkqB3KlP7YaeckH9zIe08viXujjDN/D1QW9EVg4d9o9Vs6zhxj5oHK
+pqrkosEH4+zv763MrFrhV0AVCbMP8ViyKkXrXhYDnCzBziJIZwZeCv391XaOqNss
+OBW0weQPEpgKQxnmdLmL43qSLiowHbeFOdUpL1QWfbD2+RfilQf/D+YWVW2XyEHG
+X4+pPDoZjWYpE/MAbTHz8RSl6McswBhLXhWI61lEl5EceNegTaG/v7BnT2jf09DE
+brgdNr2oyLRnNMCyKIrpGjAUs77Vo6BXT7f/oJ7AKFiQQ1fnfNCBkEFUhVZLzfSj
+Yzsaj4INLJ15WED09jeg/Hfbgqve+gx/ws41gE732A2LzVuMqYLso6HKuE4p/TV5
+3E3zv+5BoIhjuWUiuw0n6JHUIFEG+eea6XxMSmS1TyJ5Nq156Lhqb/joOUh7P4cU
+miLsfTOUNUIpVhHeFb1Mwl3/n4JyogCz6Wg4W6vdDZBzzYA=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/sub-ca.key b/third_party/heimdal/lib/hx509/data/sub-ca.key
new file mode 100644
index 0000000..1475e42
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/sub-ca.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+Ca7fU8eAAjotw7SSdeefJ+CduL/cNQE=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/sub-cert.crt b/third_party/heimdal/lib/hx509/data/sub-cert.crt
new file mode 100644
index 0000000..b98c463
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/sub-cert.crt
@@ -0,0 +1,116 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 11 (0xb)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, CN=Sub CA
+ Validity
+ Not Before: Mar 22 22:25:12 2019 GMT
+ Not After : Nov 21 22:25:12 2518 GMT
+ Subject: C=SE, CN=Test sub cert
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:ef:45:00:67:2b:7e:d2:ea:7d:80:b1:ae:81:5e:
+ fb:dd:82:ca:de:db:98:37:70:e8:3c:a2:01:87:8b:
+ 88:2e:40:30:22:d4:65:1d:7e:cb:cb:d5:40:e0:51:
+ 06:f0:f3:d9:00:db:5d:6a:0f:d3:11:bc:a1:3c:69:
+ 25:65:a9:87:b5:8a:3e:6c:79:2a:e8:5b:1a:9e:b4:
+ a4:81:5b:c3:83:f6:fd:9a:a8:48:6a:c4:ce:7f:81:
+ 26:83:c9:e5:b5:c9:a2:18:ed:0c:ea:1a:26:59:49:
+ df:56:ea:c2:33:2f:65:c2:14:30:5d:78:4e:91:09:
+ 6d:f5:77:ee:e8:0e:fe:ca:14:92:af:73:c4:8e:91:
+ b1:62:1a:c1:46:3e:36:d2:33:6a:7f:05:4e:d5:7b:
+ fe:69:4f:6c:b1:be:89:e6:7e:8d:5b:de:10:6c:a6:
+ bc:4a:05:66:17:19:71:e3:2c:62:bf:8b:4b:3c:6d:
+ fb:2a:7b:95:d5:d4:02:f0:43:e0:ce:cc:7a:30:fb:
+ a9:93:d2:50:a0:17:67:c6:08:8d:3c:9c:83:69:1f:
+ b7:ab:cf:d0:77:b6:8e:cc:89:0d:82:cd:e1:fb:53:
+ 2c:1d:f6:6b:81:0d:8f:da:dc:6a:34:93:06:23:32:
+ fb:83:90:40:8a:7f:ad:cf:2c:81:6a:10:cb:59:29:
+ d4:f2:af:b2:ee:f0:7b:b2:d5:0f:9d:5c:e6:d3:eb:
+ 18:9b:89:01:11:5f:e7:f4:50:34:e6:2c:31:b1:f3:
+ 60:af:03:a5:40:00:47:88:76:cd:52:da:1b:11:03:
+ 57:f5:3d:a1:01:f6:2f:9e:f5:01:37:22:a0:7d:5f:
+ 40:87:2d:69:72:70:80:05:16:24:2d:a6:b1:5e:ca:
+ 40:ad:f2:da:7f:c9:8f:7a:32:b2:8c:be:9b:de:66:
+ 17:92:81:83:8d:1a:f5:c9:8b:9a:3b:4a:84:b2:24:
+ 63:97:60:f6:3a:c0:84:88:2a:dd:6b:f8:e7:44:29:
+ 79:cf:98:d9:ab:36:93:10:a8:7a:7b:90:bc:bb:e0:
+ 43:c1:93:13:80:9d:cb:a6:68:67:94:67:6b:3a:58:
+ bd:02:39:20:88:e1:64:8e:a1:7a:6b:99:3b:9b:00:
+ 65:11:b5:fd:b7:18:55:fe:67:f4:94:ab:c2:08:a7:
+ 3a:d8:a7:b4:6e:d9:e9:89:1e:b0:81:1e:23:31:a9:
+ 17:b7:c7:f9:df:5b:90:2c:46:96:c5:d5:a6:cc:8b:
+ e4:db:fd:4b:47:8d:8f:bb:e4:41:d0:99:fe:81:83:
+ 88:a7:f0:a5:81:ae:c9:62:f6:4f:d8:12:60:33:20:
+ 6f:d1:39:37:f5:1f:05:40:62:43:9b:97:a5:7b:16:
+ cc:93:e5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ 48:26:75:6B:4D:E0:98:93:39:02:40:D3:F1:1C:6D:D0:D8:45:A6:04
+ Signature Algorithm: sha1WithRSAEncryption
+ aa:a0:db:44:96:6c:b5:c7:96:93:a3:11:e5:dc:1f:69:08:87:
+ f5:5f:50:25:99:03:6c:d2:89:55:4c:04:d4:8a:49:73:e8:e1:
+ 82:4f:f6:45:24:1c:ef:46:09:b2:19:09:16:5f:11:05:13:e7:
+ 3f:ca:5b:af:4f:6f:39:df:a8:71:1c:cb:62:2b:8d:42:b9:a7:
+ 58:76:72:db:88:8d:3a:e0:33:5c:ef:41:c7:30:d6:d0:9a:9c:
+ 70:f1:72:74:e6:0d:6c:1c:11:ff:f3:4a:ee:3d:d2:f7:3a:56:
+ 9f:41:63:83:60:4c:6b:63:d5:9a:a1:c8:22:b2:a5:8c:03:99:
+ 2f:04:65:a8:52:1b:1c:cb:4b:e4:b1:a0:86:7c:d7:85:e9:9a:
+ 8b:8f:f1:2d:e9:45:d0:f4:ee:51:cf:13:da:ff:ea:e8:cc:30:
+ cc:ed:f3:7e:f9:4d:59:a3:d2:ca:f2:4f:5b:73:65:63:de:39:
+ 0e:87:e1:16:30:65:d0:fa:da:0d:57:df:82:de:09:2c:24:7a:
+ ef:9c:d8:fa:7c:5a:25:f1:1e:e3:e1:56:c5:79:c3:13:37:38:
+ 03:dd:b4:6f:c0:61:b7:cb:41:bb:77:0c:c3:4f:14:e0:8c:e9:
+ 89:4b:55:6b:dc:ce:11:9b:f0:68:32:e2:64:c8:75:6a:80:26:
+ 88:fc:c1:ad:56:07:57:07:2d:fc:10:c8:42:94:f6:f4:7a:e2:
+ 94:ee:05:aa:28:7a:f3:d6:62:4a:fb:99:c0:df:dd:ca:77:14:
+ 70:6e:63:d1:68:25:6b:de:51:8b:8c:0c:5e:68:79:25:a5:68:
+ 74:c1:43:23:75:4f:eb:30:c6:84:79:a9:df:25:a6:66:56:cd:
+ 9c:95:40:b0:12:c0:60:9d:b3:99:02:4d:d1:de:25:2d:00:49:
+ e4:8f:81:8f:14:5d:3e:1c:c4:ac:11:ac:ef:0d:a7:ca:0c:01:
+ 88:54:26:bb:38:c7:24:b8:4b:45:97:40:9b:21:ea:7b:e0:5b:
+ 5f:d4:3d:dc:01:0a:8e:3d:db:31:8b:e8:23:8b:5c:48:34:95:
+ de:71:cc:61:43:aa:59:0e:be:0a:7f:75:8d:fb:b9:f0:fd:28:
+ e9:76:8d:5f:ea:9c:59:07:28:a5:b4:df:8f:0b:3c:c7:ad:00:
+ fe:9e:28:86:cd:52:fe:e3:78:81:ed:5e:73:40:1c:06:02:a8:
+ b1:84:b3:ec:56:ce:a3:70:22:ce:ab:0f:4b:8d:36:09:2d:6d:
+ 5e:93:2d:c4:20:c4:bd:8e:78:68:0a:84:81:b9:85:b7:cb:03:
+ c0:26:b9:c3:d8:e7:ab:c6:a6:7c:55:a4:e6:96:b3:65:84:5b:
+ 7e:bd:1e:c9:94:f6:25:c7
+-----BEGIN CERTIFICATE-----
+MIIE+TCCAuGgAwIBAgIBCzANBgkqhkiG9w0BAQUFADAeMQswCQYDVQQGEwJTRTEP
+MA0GA1UEAwwGU3ViIENBMCAXDTE5MDMyMjIyMjUxMloYDzI1MTgxMTIxMjIyNTEy
+WjAlMQswCQYDVQQGEwJTRTEWMBQGA1UEAwwNVGVzdCBzdWIgY2VydDCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO9FAGcrftLqfYCxroFe+92Cyt7bmDdw
+6DyiAYeLiC5AMCLUZR1+y8vVQOBRBvDz2QDbXWoP0xG8oTxpJWWph7WKPmx5Kuhb
+Gp60pIFbw4P2/ZqoSGrEzn+BJoPJ5bXJohjtDOoaJllJ31bqwjMvZcIUMF14TpEJ
+bfV37ugO/soUkq9zxI6RsWIawUY+NtIzan8FTtV7/mlPbLG+ieZ+jVveEGymvEoF
+ZhcZceMsYr+LSzxt+yp7ldXUAvBD4M7MejD7qZPSUKAXZ8YIjTycg2kft6vP0He2
+jsyJDYLN4ftTLB32a4ENj9rcajSTBiMy+4OQQIp/rc8sgWoQy1kp1PKvsu7we7LV
+D51c5tPrGJuJARFf5/RQNOYsMbHzYK8DpUAAR4h2zVLaGxEDV/U9oQH2L571ATci
+oH1fQIctaXJwgAUWJC2msV7KQK3y2n/Jj3oysoy+m95mF5KBg40a9cmLmjtKhLIk
+Y5dg9jrAhIgq3Wv450Qpec+Y2as2kxCoenuQvLvgQ8GTE4Cdy6ZoZ5RnazpYvQI5
+IIjhZI6hemuZO5sAZRG1/bcYVf5n9JSrwginOtintG7Z6YkesIEeIzGpF7fH+d9b
+kCxGlsXVpsyL5Nv9S0eNj7vkQdCZ/oGDiKfwpYGuyWL2T9gSYDMgb9E5N/UfBUBi
+Q5uXpXsWzJPlAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1Ud
+DgQWBBRIJnVrTeCYkzkCQNPxHG3Q2EWmBDANBgkqhkiG9w0BAQUFAAOCAgEAqqDb
+RJZstceWk6MR5dwfaQiH9V9QJZkDbNKJVUwE1IpJc+jhgk/2RSQc70YJshkJFl8R
+BRPnP8pbr09vOd+ocRzLYiuNQrmnWHZy24iNOuAzXO9BxzDW0JqccPFydOYNbBwR
+//NK7j3S9zpWn0Fjg2BMa2PVmqHIIrKljAOZLwRlqFIbHMtL5LGghnzXhemai4/x
+LelF0PTuUc8T2v/q6MwwzO3zfvlNWaPSyvJPW3NlY945DofhFjBl0PraDVffgt4J
+LCR675zY+nxaJfEe4+FWxXnDEzc4A920b8Bht8tBu3cMw08U4IzpiUtVa9zOEZvw
+aDLiZMh1aoAmiPzBrVYHVwct/BDIQpT29HrilO4Fqih689ZiSvuZwN/dyncUcG5j
+0Wgla95Ri4wMXmh5JaVodMFDI3VP6zDGhHmp3yWmZlbNnJVAsBLAYJ2zmQJN0d4l
+LQBJ5I+BjxRdPhzErBGs7w2nygwBiFQmuzjHJLhLRZdAmyHqe+BbX9Q93AEKjj3b
+MYvoI4tcSDSV3nHMYUOqWQ6+Cn91jfu58P0o6XaNX+qcWQcopbTfjws8x60A/p4o
+hs1S/uN4ge1ec0AcBgKosYSz7FbOo3AizqsPS402CS1tXpMtxCDEvY54aAqEgbmF
+t8sDwCa5w9jnq8amfFWk5pazZYRbfr0eyZT2Jcc=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/sub-cert.key b/third_party/heimdal/lib/hx509/data/sub-cert.key
new file mode 100644
index 0000000..481dabb
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/sub-cert.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+G2q8/Es7kVEQkUPyRNnHTWdnKW+vIQ0=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/sub-cert.p12 b/third_party/heimdal/lib/hx509/data/sub-cert.p12
new file mode 100644
index 0000000..18898c8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/sub-cert.p12
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/tcg-devid.pem b/third_party/heimdal/lib/hx509/data/tcg-devid.pem
new file mode 100644
index 0000000..66b769c
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/tcg-devid.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlFeGFt
+cGxlQ0EwHhcNMTQwMTE1MTU0MDUwWhcNMTUwMTE1MTU0MDUwWjAAMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAncvm0aOBK05rdNInYXzJGV5SFteVUFpt
+XFxg4evROvlulB3BzUmFGQYFDcItVnJX2fAvf0UJLtLBVBQggb5ylL6bRpj72cS3
+oyNbs0CGmix9Z1QDjkZZFvIsD1GcKO0tvsCvsEItH8Cm0fq8WcGFijWLdRD5eulP
+55pq1bAHAvIo4+VLMJVBG71xrKGZeHPjKoq6seYjh7AGy+hk2vmFzpZ8Ghdgqv+K
+02IZ7FEdzuylHW8U3qsxBHysMut4inj6AiVf467OOs5meHiifIK9MGkovMrfY9iX
+uUVUs/KXpE1sgeoX9BLvx1BPcODosr5K+z5i71OtIXy4CXrPvcGzRwIDAQABo4IB
+hzCCAYMwQAYIKwYBBQUHAQEENDAyMDAGCCsGAQUFBzAChiRodHRwOi8vd3d3LmV4
+YW1wbGUuY29tL0V4YW1wbGVDQS5jcnQwDgYDVR0PAQH/BAQDAgAgMIGBBgNVHREB
+Af8EdzB1pEswSTEWMBQGBWeBBQIBDAtpZDo1NDQzNDcwMDEXMBUGBWeBBQICDAxB
+QkNERUYxMjM0NTYxFjAUBgVngQUCAwwLaWQ6MDAwMTAwMjOgJgYIKwYBBQUHCASg
+GjAYBgVngQUBAgQPdHBtc2VyaWFsbnVtYmVyMAwGA1UdEwEB/wQCMAAwNQYDVR0f
+BC4wLDAqoCigJoYkaHR0cDovL3d3dy5leGFtcGxlLmNvbS9FeGFtcGxlQ0EuY3Js
+MBAGA1UdIAQJMAcwBQYDKgMEMB8GA1UdIwQYMBaAFDR3ZyRMRK/nnirgskxpV5Uk
+sz3aMBAGA1UdJQQJMAcGBWeBBQgBMCEGA1UdCQQaMBgwFgYFZ4EFAhAxDTALDAMy
+LjACAQACAWMwDQYJKoZIhvcNAQELBQADggEBABtrZu0n/7jPTYxak2n30AUakS7f
+Ihomojo14e8Lp/HF7/2VaUcohJH4KekCHTf8wpPxM/b9xRKLSOORA2Ey255Q2h8T
+v19he0dcdTvDPNQVY3AKaFO4cNiXeOYPR8n3IDYK5QdPqrdRX4/Bc34QcTWFDALx
+C00L/kDvBjV7l0Et2DBJIiBNziVKxs1xn136buZYRam6ZJhTRzNMMQ0eZ279Um4M
+39EI4DIFv6FzX0sC5waacVg6HFYd933NtdkDWV0VTGuk+5V8rH4Sjx+sywHahkoz
+BJhQBai2qiWEt7bB0ExGN2ZXPjiQiG4UHvLgGlCOUHX7EDNf0dvfUIZ6hLY=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/tcg-ek-cp.pem b/third_party/heimdal/lib/hx509/data/tcg-ek-cp.pem
new file mode 100644
index 0000000..f6631b2
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/tcg-ek-cp.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID7zCCAtegAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlFeGFt
+cGxlQ0EwHhcNMTQwMTE1MTU0MDUwWhcNMTUwMTE1MTU0MDUwWjAAMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAncvm0aOBK05rdNInYXzJGV5SFteVUFpt
+XFxg4evROvlulB3BzUmFGQYFDcItVnJX2fAvf0UJLtLBVBQggb5ylL6bRpj72cS3
+oyNbs0CGmix9Z1QDjkZZFvIsD1GcKO0tvsCvsEItH8Cm0fq8WcGFijWLdRD5eulP
+55pq1bAHAvIo4+VLMJVBG71xrKGZeHPjKoq6seYjh7AGy+hk2vmFzpZ8Ghdgqv+K
+02IZ7FEdzuylHW8U3qsxBHysMut4inj6AiVf467OOs5meHiifIK9MGkovMrfY9iX
+uUVUs/KXpE1sgeoX9BLvx1BPcODosr5K+z5i71OtIXy4CXrPvcGzRwIDAQABo4IB
+XjCCAVowQAYIKwYBBQUHAQEENDAyMDAGCCsGAQUFBzAChiRodHRwOi8vd3d3LmV4
+YW1wbGUuY29tL0V4YW1wbGVDQS5jcnQwDgYDVR0PAQH/BAQDAgAgMFkGA1UdEQEB
+/wRPME2kSzBJMRYwFAYFZ4EFAgEMC2lkOjU0NDM0NzAwMRcwFQYFZ4EFAgIMDEFC
+Q0RFRjEyMzQ1NjEWMBQGBWeBBQIDDAtpZDowMDAxMDAyMzAMBgNVHRMBAf8EAjAA
+MDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly93d3cuZXhhbXBsZS5jb20vRXhhbXBs
+ZUNBLmNybDAQBgNVHSAECTAHMAUGAyoDBDAfBgNVHSMEGDAWgBQ0d2ckTESv554q
+4LJMaVeVJLM92jAQBgNVHSUECTAHBgVngQUIATAhBgNVHQkEGjAYMBYGBWeBBQIQ
+MQ0wCwwDMi4wAgEAAgFjMA0GCSqGSIb3DQEBCwUAA4IBAQAba2btJ/+4z02MWpNp
+99AFGpEu3yIaJqI6NeHvC6fxxe/9lWlHKISR+CnpAh03/MKT8TP2/cUSi0jjkQNh
+MtueUNofE79fYXtHXHU7wzzUFWNwCmhTuHDYl3jmD0fJ9yA2CuUHT6q3UV+PwXN+
+EHE1hQwC8QtNC/5A7wY1e5dBLdgwSSIgTc4lSsbNcZ9d+m7mWEWpumSYU0czTDEN
+Hmdu/VJuDN/RCOAyBb+hc19LAucGmnFYOhxWHfd9zbXZA1ldFUxrpPuVfKx+Eo8f
+rMsB2oZKMwSYUAWotqolhLe2wdBMRjdmVz44kIhuFB7y4BpQjlB1+xAzX9Hb31CG
+eoS2
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/test-ds-only.crt b/third_party/heimdal/lib/hx509/data/test-ds-only.crt
new file mode 100644
index 0000000..ce0de74
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-ds-only.crt
@@ -0,0 +1,117 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 5 (0x5)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:05 2019 GMT
+ Not After : Nov 21 22:25:05 2518 GMT
+ Subject: C=SE, CN=Test cert DigitalSignature
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:d2:e5:b6:27:f7:6c:c1:d0:ba:8a:4a:6a:4e:b5:
+ a6:92:2e:5b:98:d7:0c:6a:7e:f4:bf:19:30:2d:ee:
+ 1c:5a:ee:28:f6:5c:a8:12:03:20:c7:e8:2b:b1:44:
+ 9f:b7:54:27:6e:17:fc:c0:f6:f7:ea:38:d2:c8:77:
+ ab:6a:ae:d1:ab:9f:1e:79:df:8a:51:55:aa:6c:6a:
+ 13:74:74:2f:c0:20:57:ef:f3:e1:71:da:b0:ec:62:
+ e9:8a:01:da:f6:e6:c6:5a:fe:11:61:58:5c:a0:01:
+ ec:0e:af:70:0d:72:94:a1:d4:1c:76:53:ae:39:a0:
+ cf:70:d8:d9:7c:95:18:2b:5f:36:00:2f:5c:be:a2:
+ d5:8e:0e:e3:aa:76:0c:1f:86:b3:69:fe:e4:29:0a:
+ 30:b1:ca:83:1a:f2:88:fc:91:2f:58:be:a4:a0:25:
+ 82:bf:16:b3:ca:70:09:7e:cf:29:f9:2e:58:0b:4a:
+ 3a:3c:6d:e7:05:63:d5:53:90:ed:ee:96:9e:8e:d7:
+ a8:ef:50:8b:37:bd:dc:88:f5:12:bc:04:4e:e4:f3:
+ ec:5d:9d:e6:46:14:e1:e1:6b:15:ab:f4:52:f6:12:
+ 76:ae:2d:a7:65:ec:8f:bd:90:51:52:4d:e7:cf:ba:
+ 23:01:7a:85:8b:22:41:a6:98:08:e4:33:00:c1:e2:
+ 82:b0:b2:c6:f4:6a:34:c6:a9:d7:b1:cc:c6:1a:0b:
+ ad:69:1f:89:af:e0:63:cd:51:c9:36:7f:08:f0:31:
+ 97:ea:78:bb:ae:21:4c:aa:2d:32:de:36:03:cf:64:
+ f8:8a:c0:c5:b3:c4:f9:79:74:7a:8b:d5:ec:bf:19:
+ 87:c9:25:0c:99:7d:56:a3:93:68:97:c3:cc:08:fb:
+ 37:c0:2c:cb:87:f2:b4:4e:fe:ce:86:69:2b:8e:c3:
+ 9e:40:a9:b6:43:6e:d6:b6:3d:08:43:24:09:58:8d:
+ af:d2:5d:1c:0e:cd:bc:e3:0b:b3:4b:a5:69:a8:3c:
+ d7:07:d0:7f:d7:78:c7:5c:a4:9f:e1:a2:bc:76:77:
+ 80:25:0e:82:2b:43:1e:e4:67:49:47:d9:65:45:57:
+ ed:59:d7:6e:a1:8d:76:a0:c2:65:52:c8:c8:57:5d:
+ dd:b4:d2:4f:27:a5:08:f1:88:7e:d2:3e:5d:60:c6:
+ 67:fb:c9:19:e7:78:cc:41:6d:24:11:cd:a4:e6:cf:
+ 56:8c:41:4d:af:d6:e2:22:c0:a3:64:2c:4b:27:f6:
+ b3:87:9d:08:e6:2a:2f:db:c8:50:57:95:a3:cf:67:
+ 77:f8:80:15:f3:45:00:47:f8:80:6e:21:b5:80:f1:
+ 81:29:45:3f:a9:8a:e2:12:12:4d:c4:90:e3:da:ab:
+ 08:80:bd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation
+ X509v3 Subject Key Identifier:
+ C4:44:DE:34:6C:55:F0:21:00:F4:CF:F0:55:67:92:FB:8F:B3:40:46
+ Signature Algorithm: sha1WithRSAEncryption
+ a3:9c:c7:b7:3d:fc:8e:3d:5b:58:98:b0:05:63:fd:a7:50:c2:
+ d4:e8:c2:48:b8:b0:a8:e3:f9:c2:8b:11:47:a1:11:5e:e8:4d:
+ 75:c5:b8:d9:ec:af:81:95:1e:ec:d8:f6:8a:b6:17:12:ab:d4:
+ 30:84:cb:35:6a:c8:50:5e:1c:55:26:77:ee:84:f0:80:92:95:
+ c3:37:50:b3:23:21:7a:3b:63:5a:18:e4:48:fc:de:9b:26:50:
+ 38:9e:2f:a3:ad:03:5f:0c:b0:a1:0e:41:0b:01:71:b9:a2:df:
+ 84:f6:c4:d6:9d:8b:f7:a8:ed:cc:7e:b6:8c:5c:bc:26:0c:97:
+ 77:15:dc:fb:66:4b:0d:01:d9:8e:58:8e:1c:bf:35:47:b8:10:
+ d4:12:e5:80:09:b3:d8:4a:f4:0a:3f:6a:2f:9f:47:16:80:a7:
+ 92:6a:d4:3b:79:7b:25:b9:3e:14:a9:90:4e:92:6e:92:7b:6f:
+ 04:3a:0d:c6:63:77:82:e2:2d:e9:24:63:ce:a0:b1:8c:23:1d:
+ db:79:b8:4f:77:b8:7f:d2:49:5d:b4:60:a0:78:bb:d6:d7:56:
+ ff:23:c1:fa:46:cd:9a:2b:0d:87:df:b5:98:eb:7e:fd:af:6e:
+ 9d:03:de:d3:97:e7:19:09:20:13:ce:2e:b5:89:f0:47:ad:b2:
+ 3d:f1:5e:77:8b:dd:d3:6e:e2:a8:3c:cd:6a:22:a1:63:92:8c:
+ 2e:ca:0a:0d:aa:2c:15:98:de:27:08:e5:ee:a5:e0:e5:54:30:
+ 26:2f:32:ab:c3:de:e0:82:32:2a:dd:39:cb:3c:75:95:8f:9e:
+ 37:34:34:80:14:27:aa:c6:89:d3:8f:7a:35:19:3b:8b:c1:56:
+ 06:76:b3:0c:12:05:10:f4:5a:62:ff:d5:ef:e0:f8:da:aa:dc:
+ 2b:14:73:ad:31:c8:da:19:fe:54:51:32:0f:3b:7f:13:21:0d:
+ 5c:4f:33:e7:07:92:36:fd:01:04:d4:e6:4c:ba:dc:b4:75:c0:
+ f6:1f:3c:5a:4a:34:40:87:3b:8c:44:60:de:11:8d:18:41:0a:
+ e4:e9:d6:19:f5:7b:8f:53:3c:d8:3d:7c:4f:f4:b0:86:93:69:
+ c1:f1:e0:cd:8f:df:cd:ef:33:31:a8:e1:93:cf:bd:13:13:66:
+ 55:ef:44:63:06:0a:11:7a:78:e7:5c:6f:d0:f9:9d:bf:90:e5:
+ f4:d1:54:31:b8:0d:ed:ed:c0:e2:63:5c:13:01:ff:a8:11:c5:
+ 7d:42:e1:94:63:6a:63:99:0f:82:ef:49:f7:93:92:e6:72:d7:
+ ed:88:d6:ab:b2:25:8c:37:8d:08:22:a0:80:9b:14:fb:a4:a2:
+ 4f:43:be:ff:d4:e9:7e:79
+-----BEGIN CERTIFICATE-----
+MIIFEjCCAvqgAwIBAgIBBTANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwNVoYDzI1
+MTgxMTIxMjIyNTA1WjAyMQswCQYDVQQGEwJTRTEjMCEGA1UEAwwaVGVzdCBjZXJ0
+IERpZ2l0YWxTaWduYXR1cmUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQDS5bYn92zB0LqKSmpOtaaSLluY1wxqfvS/GTAt7hxa7ij2XKgSAyDH6CuxRJ+3
+VCduF/zA9vfqONLId6tqrtGrnx5534pRVapsahN0dC/AIFfv8+Fx2rDsYumKAdr2
+5sZa/hFhWFygAewOr3ANcpSh1Bx2U645oM9w2Nl8lRgrXzYAL1y+otWODuOqdgwf
+hrNp/uQpCjCxyoMa8oj8kS9YvqSgJYK/FrPKcAl+zyn5LlgLSjo8becFY9VTkO3u
+lp6O16jvUIs3vdyI9RK8BE7k8+xdneZGFOHhaxWr9FL2EnauLadl7I+9kFFSTefP
+uiMBeoWLIkGmmAjkMwDB4oKwssb0ajTGqdexzMYaC61pH4mv4GPNUck2fwjwMZfq
+eLuuIUyqLTLeNgPPZPiKwMWzxPl5dHqL1ey/GYfJJQyZfVajk2iXw8wI+zfALMuH
+8rRO/s6GaSuOw55AqbZDbta2PQhDJAlYja/SXRwOzbzjC7NLpWmoPNcH0H/XeMdc
+pJ/horx2d4AlDoIrQx7kZ0lH2WVFV+1Z126hjXagwmVSyMhXXd200k8npQjxiH7S
+Pl1gxmf7yRnneMxBbSQRzaTmz1aMQU2v1uIiwKNkLEsn9rOHnQjmKi/byFBXlaPP
+Z3f4gBXzRQBH+IBuIbWA8YEpRT+piuISEk3EkOPaqwiAvQIDAQABozkwNzAJBgNV
+HRMEAjAAMAsGA1UdDwQEAwIGwDAdBgNVHQ4EFgQUxETeNGxV8CEA9M/wVWeS+4+z
+QEYwDQYJKoZIhvcNAQEFBQADggIBAKOcx7c9/I49W1iYsAVj/adQwtTowki4sKjj
++cKLEUehEV7oTXXFuNnsr4GVHuzY9oq2FxKr1DCEyzVqyFBeHFUmd+6E8ICSlcM3
+ULMjIXo7Y1oY5Ej83psmUDieL6OtA18MsKEOQQsBcbmi34T2xNadi/eo7cx+toxc
+vCYMl3cV3PtmSw0B2Y5Yjhy/NUe4ENQS5YAJs9hK9Ao/ai+fRxaAp5Jq1Dt5eyW5
+PhSpkE6SbpJ7bwQ6DcZjd4LiLekkY86gsYwjHdt5uE93uH/SSV20YKB4u9bXVv8j
+wfpGzZorDYfftZjrfv2vbp0D3tOX5xkJIBPOLrWJ8Eetsj3xXneL3dNu4qg8zWoi
+oWOSjC7KCg2qLBWY3icI5e6l4OVUMCYvMqvD3uCCMirdOcs8dZWPnjc0NIAUJ6rG
+idOPejUZO4vBVgZ2swwSBRD0WmL/1e/g+Nqq3CsUc60xyNoZ/lRRMg87fxMhDVxP
+M+cHkjb9AQTU5ky63LR1wPYfPFpKNECHO4xEYN4RjRhBCuTp1hn1e49TPNg9fE/0
+sIaTacHx4M2P383vMzGo4ZPPvRMTZlXvRGMGChF6eOdcb9D5nb+Q5fTRVDG4De3t
+wOJjXBMB/6gRxX1C4ZRjamOZD4LvSfeTkuZy1+2I1quyJYw3jQgioICbFPukok9D
+vv/U6X55
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/test-ds-only.key b/third_party/heimdal/lib/hx509/data/test-ds-only.key
new file mode 100644
index 0000000..9129038
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-ds-only.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+dDGZmYTPMav2jSUmg7BMOO1iyjFI0hg=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/test-enveloped-aes-128 b/third_party/heimdal/lib/hx509/data/test-enveloped-aes-128
new file mode 100644
index 0000000..a75409b
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-enveloped-aes-128
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-enveloped-aes-256 b/third_party/heimdal/lib/hx509/data/test-enveloped-aes-256
new file mode 100644
index 0000000..4fda391
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-enveloped-aes-256
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-enveloped-des b/third_party/heimdal/lib/hx509/data/test-enveloped-des
new file mode 100644
index 0000000..944da00
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-enveloped-des
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-enveloped-des-ede3 b/third_party/heimdal/lib/hx509/data/test-enveloped-des-ede3
new file mode 100644
index 0000000..c27dfbc
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-enveloped-des-ede3
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-128 b/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-128
new file mode 100644
index 0000000..72f8158
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-128
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-40 b/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-40
new file mode 100644
index 0000000..0e5eb02
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-40
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-64 b/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-64
new file mode 100644
index 0000000..9ce6694
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-enveloped-rc2-64
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-ke-only.crt b/third_party/heimdal/lib/hx509/data/test-ke-only.crt
new file mode 100644
index 0000000..a6cc06a
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-ke-only.crt
@@ -0,0 +1,117 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 4 (0x4)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:04 2019 GMT
+ Not After : Nov 21 22:25:04 2518 GMT
+ Subject: C=SE, CN=Test cert KeyEncipherment
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:bf:5f:55:ca:c5:c6:c5:00:a6:40:17:fc:1f:a2:
+ c7:e7:41:1b:29:37:6e:ba:7c:01:19:f3:4a:d7:c4:
+ 9a:83:17:4d:40:cd:30:d1:9f:fd:94:49:41:5c:7a:
+ 2d:32:83:81:29:15:e3:b2:1f:06:1b:f5:f3:7f:91:
+ cf:dc:82:b1:4e:d5:a9:48:da:63:49:b8:b8:41:0d:
+ cf:eb:76:df:1a:33:5a:7b:2f:ed:13:5d:ce:77:85:
+ bc:1f:52:b4:ff:96:20:48:09:19:d7:0d:55:ed:a8:
+ 9f:de:bd:26:2a:cf:2c:f4:48:d3:eb:94:f1:b4:ca:
+ 5b:6d:1b:21:82:46:98:23:84:d7:be:08:90:54:f4:
+ 46:ef:59:6e:8b:8c:7f:65:90:5a:c3:fb:c4:1d:97:
+ 9e:1a:be:82:96:d7:86:5b:d7:1a:0e:04:1f:30:71:
+ 99:70:40:28:6c:b2:16:3c:19:f1:f3:9f:54:22:9c:
+ e0:e5:2b:c9:30:a1:01:cf:7e:1f:a2:40:d7:d3:ad:
+ 23:6d:fe:55:dc:ad:87:88:ee:e8:9b:81:e8:72:8d:
+ 2a:25:58:ff:81:18:f0:24:9a:13:31:f9:30:7c:ed:
+ f1:d5:4b:13:ce:bf:83:48:47:9c:44:99:0d:52:e7:
+ 52:4f:02:91:10:fe:77:39:f3:fc:ce:04:bf:57:4e:
+ 3b:17:a3:c2:94:85:10:d6:76:a2:c0:04:45:d1:ff:
+ 96:a7:c0:a8:39:bb:7a:4c:f4:96:4c:5f:2d:63:85:
+ 52:6e:74:5d:70:7a:de:35:7c:92:9f:ed:e6:85:c8:
+ f0:1d:b7:be:29:54:78:5e:7c:4a:a2:b8:85:ee:b7:
+ 20:2d:0c:78:a6:32:be:c0:a2:89:4f:f4:c8:e0:3c:
+ 3a:4c:b3:68:a1:a7:eb:b5:c7:21:74:b9:3d:0e:07:
+ 3f:ce:35:29:b5:33:1f:ac:d8:36:dd:d1:54:3d:47:
+ c9:29:c6:26:23:e8:51:8d:25:9a:8c:96:84:74:e9:
+ f0:10:d8:96:f5:ad:22:31:8f:e9:6a:a5:9b:3b:00:
+ 93:5e:80:22:f1:3a:e5:2d:10:7b:c6:a8:b9:6b:8f:
+ ab:33:64:99:fe:aa:77:7a:0f:96:f9:3f:fe:15:6d:
+ 8e:4a:95:a7:35:9b:f4:20:cb:a2:a1:d9:f6:62:6b:
+ a7:4e:b4:22:3d:22:73:f4:7e:0d:af:62:41:7a:d2:
+ 15:ab:b9:a2:25:a8:87:e0:b5:1b:be:c0:16:d1:e4:
+ 40:5b:56:a7:ab:39:d1:85:02:f5:4f:95:3f:37:dc:
+ 97:e4:89:c8:20:ab:11:9f:d8:f1:77:d6:b0:60:4f:
+ ab:f9:88:37:ef:9f:bc:2a:f3:22:3d:2e:21:82:63:
+ c6:21:73
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ FF:78:58:BD:A5:C2:4D:D1:07:04:05:DD:15:6A:A2:2C:92:EB:54:04
+ Signature Algorithm: sha1WithRSAEncryption
+ 0d:b6:af:48:3b:0f:01:49:0b:12:d7:bc:9f:35:09:2a:42:e4:
+ d2:86:d2:c5:53:65:1b:a4:d5:52:87:28:dc:01:70:97:f3:0b:
+ 87:35:67:bb:b7:dd:f9:80:09:d3:84:33:11:2a:fe:0b:85:75:
+ 4b:d1:84:0c:46:35:d3:69:b8:fe:fc:a3:5a:c7:10:8c:2b:36:
+ c8:f0:ab:e7:f8:98:6c:b5:ec:1e:26:69:31:9b:07:29:03:ee:
+ 21:34:5c:52:1a:58:4a:c5:10:43:6b:8e:fc:9d:94:12:67:d0:
+ 12:40:55:14:f0:8f:d5:a7:a9:c7:d4:65:99:53:0d:3f:9a:23:
+ ab:13:ed:25:eb:33:56:b8:b3:ed:f5:6d:6b:a4:26:6c:80:6d:
+ 4c:27:8e:e5:5f:4d:e8:83:0b:c8:ca:17:6c:de:b9:af:ff:2f:
+ cb:9c:25:24:5f:09:e4:d9:62:a8:6e:de:da:c9:9e:1f:be:bf:
+ 19:1a:df:01:e2:dc:8c:ef:64:40:8e:b3:2a:0d:29:a9:7f:e7:
+ fa:bb:4b:76:41:c4:82:e7:07:d0:21:d5:1a:88:64:27:58:1a:
+ 8f:9e:48:e8:cb:40:d2:f0:ff:68:06:10:1b:5a:c3:1b:9f:48:
+ 52:b6:a0:8a:4c:0e:be:f3:e4:ed:a1:7a:9c:52:91:38:15:fc:
+ 92:ff:82:55:10:bd:d7:a2:1c:bb:e4:8c:56:d5:f6:c7:77:12:
+ 2f:cb:61:c6:75:a2:71:9c:4e:96:b3:0f:b6:d7:85:cb:52:0f:
+ 96:87:4a:05:15:ba:f7:31:b0:76:54:07:b8:59:38:5e:7a:03:
+ a4:87:60:e9:12:4d:aa:3a:98:d6:b9:46:a1:73:40:87:27:cf:
+ aa:87:66:e8:32:37:74:0c:93:ff:a9:ef:52:3b:a2:36:1e:16:
+ 1c:07:45:e9:65:9f:9e:de:ff:7b:b1:c4:a8:7e:59:25:79:1f:
+ da:7f:35:85:36:ea:cf:79:ff:71:96:77:28:3a:e6:af:68:f5:
+ 4c:c3:1a:20:7b:09:8d:66:15:b0:92:0a:4b:39:e4:f1:06:9e:
+ 9e:4e:f1:ca:bf:81:77:e7:00:82:79:26:0f:d1:f9:a2:4d:9a:
+ c8:7a:da:f6:d0:1e:65:04:02:2b:14:0b:84:45:eb:5d:6c:68:
+ 04:d7:a6:98:85:8c:fb:7e:de:42:63:68:5d:cd:a1:3d:4b:85:
+ 5e:e5:c3:38:a6:79:f4:02:5c:d0:ea:53:c6:91:84:08:b2:eb:
+ 2f:02:bb:5d:3b:bc:f2:e7:8d:67:44:70:0f:96:63:25:25:1a:
+ 38:1a:cc:a6:72:2d:41:23:8c:cc:95:12:4b:4f:64:91:21:79:
+ 96:46:70:8d:68:dc:dc:d5
+-----BEGIN CERTIFICATE-----
+MIIFETCCAvmgAwIBAgIBBDANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwNFoYDzI1
+MTgxMTIxMjIyNTA0WjAxMQswCQYDVQQGEwJTRTEiMCAGA1UEAwwZVGVzdCBjZXJ0
+IEtleUVuY2lwaGVybWVudDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+AL9fVcrFxsUApkAX/B+ix+dBGyk3brp8ARnzStfEmoMXTUDNMNGf/ZRJQVx6LTKD
+gSkV47IfBhv183+Rz9yCsU7VqUjaY0m4uEENz+t23xozWnsv7RNdzneFvB9StP+W
+IEgJGdcNVe2on969JirPLPRI0+uU8bTKW20bIYJGmCOE174IkFT0Ru9ZbouMf2WQ
+WsP7xB2Xnhq+gpbXhlvXGg4EHzBxmXBAKGyyFjwZ8fOfVCKc4OUryTChAc9+H6JA
+19OtI23+Vdyth4ju6JuB6HKNKiVY/4EY8CSaEzH5MHzt8dVLE86/g0hHnESZDVLn
+Uk8CkRD+dznz/M4Ev1dOOxejwpSFENZ2osAERdH/lqfAqDm7ekz0lkxfLWOFUm50
+XXB63jV8kp/t5oXI8B23vilUeF58SqK4he63IC0MeKYyvsCiiU/0yOA8OkyzaKGn
+67XHIXS5PQ4HP841KbUzH6zYNt3RVD1HySnGJiPoUY0lmoyWhHTp8BDYlvWtIjGP
+6WqlmzsAk16AIvE65S0Qe8aouWuPqzNkmf6qd3oPlvk//hVtjkqVpzWb9CDLoqHZ
+9mJrp060Ij0ic/R+Da9iQXrSFau5oiWoh+C1G77AFtHkQFtWp6s50YUC9U+VPzfc
+l+SJyCCrEZ/Y8XfWsGBPq/mIN++fvCrzIj0uIYJjxiFzAgMBAAGjOTA3MAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgVgMB0GA1UdDgQWBBT/eFi9pcJN0QcEBd0VaqIskutU
+BDANBgkqhkiG9w0BAQUFAAOCAgEADbavSDsPAUkLEte8nzUJKkLk0obSxVNlG6TV
+Uoco3AFwl/MLhzVnu7fd+YAJ04QzESr+C4V1S9GEDEY102m4/vyjWscQjCs2yPCr
+5/iYbLXsHiZpMZsHKQPuITRcUhpYSsUQQ2uO/J2UEmfQEkBVFPCP1aepx9RlmVMN
+P5ojqxPtJeszVriz7fVta6QmbIBtTCeO5V9N6IMLyMoXbN65r/8vy5wlJF8J5Nli
+qG7e2smeH76/GRrfAeLcjO9kQI6zKg0pqX/n+rtLdkHEgucH0CHVGohkJ1gaj55I
+6MtA0vD/aAYQG1rDG59IUragikwOvvPk7aF6nFKROBX8kv+CVRC916Icu+SMVtX2
+x3cSL8thxnWicZxOlrMPtteFy1IPlodKBRW69zGwdlQHuFk4XnoDpIdg6RJNqjqY
+1rlGoXNAhyfPqodm6DI3dAyT/6nvUjuiNh4WHAdF6WWfnt7/e7HEqH5ZJXkf2n81
+hTbqz3n/cZZ3KDrmr2j1TMMaIHsJjWYVsJIKSznk8Qaenk7xyr+Bd+cAgnkmD9H5
+ok2ayHra9tAeZQQCKxQLhEXrXWxoBNemmIWM+37eQmNoXc2hPUuFXuXDOKZ59AJc
+0OpTxpGECLLrLwK7XTu88ueNZ0RwD5ZjJSUaOBrMpnItQSOMzJUSS09kkSF5lkZw
+jWjc3NU=
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/test-ke-only.key b/third_party/heimdal/lib/hx509/data/test-ke-only.key
new file mode 100644
index 0000000..1b463b9
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-ke-only.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+hFXFVI1+i1ntO6Nf9NvTPVfjJNWzW7Y=
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/test-nopw.p12 b/third_party/heimdal/lib/hx509/data/test-nopw.p12
new file mode 100644
index 0000000..e94aa9a
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-nopw.p12
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-pw.key b/third_party/heimdal/lib/hx509/data/test-pw.key
new file mode 100644
index 0000000..495eef6
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-pw.key
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,AE05E89E216F377B4B073A88DA8A284B
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/test-signed-data b/third_party/heimdal/lib/hx509/data/test-signed-data
new file mode 100644
index 0000000..1228c85
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-signed-data
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-signed-data-noattr b/third_party/heimdal/lib/hx509/data/test-signed-data-noattr
new file mode 100644
index 0000000..f230779
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-signed-data-noattr
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-signed-data-noattr-nocerts b/third_party/heimdal/lib/hx509/data/test-signed-data-noattr-nocerts
new file mode 100644
index 0000000..49fba9b
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-signed-data-noattr-nocerts
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-signed-sha-1 b/third_party/heimdal/lib/hx509/data/test-signed-sha-1
new file mode 100644
index 0000000..8ad1121
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-signed-sha-1
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-signed-sha-256 b/third_party/heimdal/lib/hx509/data/test-signed-sha-256
new file mode 100644
index 0000000..1228c85
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-signed-sha-256
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test-signed-sha-512 b/third_party/heimdal/lib/hx509/data/test-signed-sha-512
new file mode 100644
index 0000000..1e40abe
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test-signed-sha-512
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/test.combined.crt b/third_party/heimdal/lib/hx509/data/test.combined.crt
new file mode 100644
index 0000000..a07dbf1
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test.combined.crt
@@ -0,0 +1,168 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:02 2019 GMT
+ Not After : Nov 21 22:25:02 2518 GMT
+ Subject: C=SE, CN=Test cert
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:da:1d:4d:ca:51:9d:f1:9f:d7:a4:7a:45:f8:75:
+ 98:66:b2:c5:7d:53:de:42:35:74:81:cd:1e:9f:f3:
+ 43:d7:a7:83:7f:fb:a2:ce:3c:44:37:80:4f:21:36:
+ a6:f6:c9:51:74:9e:e2:9b:bf:ad:e4:eb:72:11:64:
+ 36:88:b3:a9:91:63:c7:ee:38:c4:f5:8c:06:71:e5:
+ 09:b7:eb:57:5d:bf:db:5b:72:07:c5:29:e8:6f:33:
+ b3:a2:27:ef:1f:50:f0:55:33:63:41:23:e0:b2:f7:
+ 21:77:4b:ab:9d:73:2a:bb:b6:4e:88:7f:7c:e5:c6:
+ 37:3e:b6:20:c1:57:3e:6d:57:78:ef:0d:47:e9:41:
+ e7:fa:b6:2d:32:3f:42:05:8d:56:af:f5:c4:b8:6e:
+ 99:1a:e7:07:d5:a1:3f:29:7d:ce:b2:39:a6:ab:06:
+ 7a:e2:26:39:d8:96:9e:3b:c8:af:79:3e:9a:24:4e:
+ 4b:b2:af:e4:07:0e:71:dc:2f:70:27:97:3c:a2:fa:
+ 69:9b:57:4b:c5:53:5e:28:0c:b0:c7:57:1f:a2:b2:
+ 26:0f:5f:bf:d3:45:78:90:5a:2c:fc:6a:67:33:b6:
+ c1:7e:cd:17:c0:58:9e:ba:85:c5:15:5a:5a:67:db:
+ bf:2f:05:cd:38:d9:94:c9:95:7f:9b:68:b0:62:ff:
+ 37:92:cf:d8:77:be:cb:72:3d:0f:b9:80:44:57:c0:
+ c9:10:01:fd:07:25:30:eb:d8:48:05:af:98:fa:c4:
+ 64:6d:59:a6:6a:8d:1b:d4:4b:f3:07:98:68:e3:bb:
+ 59:c9:21:f8:11:b4:a2:82:1b:0d:e8:8c:e0:a5:e1:
+ 1c:71:ca:c3:2d:90:43:c3:ee:99:2c:7d:41:48:39:
+ c8:00:72:0d:80:39:23:a1:3a:27:ed:07:ca:32:8f:
+ 34:ca:bb:9d:67:13:7d:31:ed:4a:db:35:7a:ce:b3:
+ 89:e3:64:9d:3e:47:4e:d3:b7:bd:ab:12:16:10:bb:
+ 66:e8:1a:77:4c:2a:e0:b9:16:69:66:14:83:4e:4a:
+ f3:6f:ab:85:6a:70:c6:9b:ce:93:ab:75:36:a3:a5:
+ aa:9f:45:d6:a2:7f:17:c7:6f:f9:f5:e7:35:51:a5:
+ 75:c5:07:be:26:ce:7b:3f:29:3a:74:6b:17:79:4e:
+ cf:4c:0a:69:75:58:db:eb:a8:dd:f1:e6:cc:a3:18:
+ 53:a5:c5:a5:5a:a1:cf:37:6a:b1:9f:d3:d4:eb:0f:
+ 02:40:d2:ae:68:ce:bc:c5:46:e3:ee:f8:97:88:ee:
+ c8:a7:01:7a:a1:23:af:f3:31:2c:2a:6f:12:77:dc:
+ 3c:51:9d:40:f4:9a:2a:7b:85:29:1f:3e:c3:d5:37:
+ 8e:6e:09
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ 1B:F4:EC:34:42:BA:8B:67:AC:55:F2:37:5D:B4:68:A9:D8:5E:58:7B
+ Signature Algorithm: sha1WithRSAEncryption
+ 95:f7:1c:99:72:42:4f:d3:bd:ba:3f:7a:75:bb:01:3a:ad:ce:
+ 6b:7b:b7:3d:5d:3b:46:51:ea:9a:36:94:70:36:1c:3b:fc:ba:
+ 9d:8b:0d:44:36:08:ad:a6:73:82:bc:23:ed:f9:5a:09:8f:9d:
+ 62:11:c1:94:7c:61:66:1f:8b:b9:0a:dc:3a:b5:eb:22:54:de:
+ a3:e5:8a:94:10:1f:84:52:6d:fe:27:c8:e5:cb:a5:8e:a9:83:
+ 16:95:0d:6c:3e:57:85:e1:ec:82:05:47:6d:28:ad:0d:84:fa:
+ 40:a0:96:f4:84:aa:d1:e1:0b:b7:91:e2:47:4f:05:97:f8:10:
+ a0:e8:57:bd:ed:48:65:55:75:da:e5:34:e8:f1:20:95:d6:40:
+ 8c:42:bf:b4:d9:55:c8:30:e8:d5:ce:d8:1d:30:65:90:39:eb:
+ e2:83:ed:11:03:cd:07:c0:e1:c4:91:84:a0:97:8e:6d:22:e6:
+ 75:77:21:7c:32:8b:48:ed:d6:b2:19:2e:af:26:ad:7d:6c:ce:
+ 09:e1:78:b6:72:61:60:22:92:b8:df:42:6b:34:6b:5f:35:ef:
+ f1:d3:c6:7f:92:05:3c:d0:08:77:01:66:f7:57:b8:65:de:d3:
+ d2:b1:bf:93:b1:8c:a3:27:e6:d4:e2:2b:9b:cd:9d:be:31:82:
+ 5b:53:dd:5a:bd:39:05:5f:8c:56:f2:7f:9b:b7:ef:e6:07:96:
+ bf:8a:d9:8d:bb:62:98:86:de:aa:91:c3:fe:e7:bb:a7:1f:f0:
+ fd:1f:6c:a6:04:04:f0:c2:51:a1:91:8c:9a:ee:f9:87:42:37:
+ 7e:9c:27:72:59:dc:60:a8:8e:d1:81:97:f1:15:c3:d8:a9:4e:
+ 9a:09:e9:81:76:39:36:b3:08:a1:e5:5e:97:37:ba:43:8f:06:
+ 1a:70:69:3b:fe:79:a6:5e:2d:26:04:e9:bc:5f:57:c9:d0:80:
+ c2:0d:4b:c7:0e:dd:04:e5:15:49:9d:d7:ff:ee:a3:1c:04:56:
+ 7d:e2:a0:d3:39:1a:59:bd:85:b0:eb:54:ea:81:8b:e1:17:94:
+ a5:fe:e3:0c:d0:74:42:ee:4a:f4:66:90:49:4b:64:bc:47:35:
+ f5:b2:60:8e:74:05:d0:a6:d2:94:b4:e0:0f:4b:3f:35:ea:2a:
+ e0:24:58:c1:6e:d0:65:6e:58:f7:e1:90:02:ae:40:23:25:e9:
+ 80:9a:d2:a7:ea:5d:fc:6d:f8:45:0f:db:53:91:55:32:46:e3:
+ 6a:c0:54:0a:5a:4c:e8:1a:1e:a6:33:3e:fe:ed:b6:ad:cf:6a:
+ 3c:2f:b2:6c:47:75:f1:29:43:31:69:c3:0c:42:56:5b:d9:b8:
+ 99:7b:ff:2b:50:87:34:2e
+-----BEGIN CERTIFICATE-----
+MIIFATCCAumgAwIBAgIBAjANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwMloYDzI1
+MTgxMTIxMjIyNTAyWjAhMQswCQYDVQQGEwJTRTESMBAGA1UEAwwJVGVzdCBjZXJ0
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2h1NylGd8Z/XpHpF+HWY
+ZrLFfVPeQjV0gc0en/ND16eDf/uizjxEN4BPITam9slRdJ7im7+t5OtyEWQ2iLOp
+kWPH7jjE9YwGceUJt+tXXb/bW3IHxSnobzOzoifvH1DwVTNjQSPgsvchd0urnXMq
+u7ZOiH985cY3PrYgwVc+bVd47w1H6UHn+rYtMj9CBY1Wr/XEuG6ZGucH1aE/KX3O
+sjmmqwZ64iY52JaeO8iveT6aJE5Lsq/kBw5x3C9wJ5c8ovppm1dLxVNeKAywx1cf
+orImD1+/00V4kFos/GpnM7bBfs0XwFieuoXFFVpaZ9u/LwXNONmUyZV/m2iwYv83
+ks/Yd77Lcj0PuYBEV8DJEAH9ByUw69hIBa+Y+sRkbVmmao0b1EvzB5ho47tZySH4
+EbSighsN6IzgpeEcccrDLZBDw+6ZLH1BSDnIAHINgDkjoTon7QfKMo80yrudZxN9
+Me1K2zV6zrOJ42SdPkdO07e9qxIWELtm6Bp3TCrguRZpZhSDTkrzb6uFanDGm86T
+q3U2o6Wqn0XWon8Xx2/59ec1UaV1xQe+Js57Pyk6dGsXeU7PTAppdVjb66jd8ebM
+oxhTpcWlWqHPN2qxn9PU6w8CQNKuaM68xUbj7viXiO7IpwF6oSOv8zEsKm8Sd9w8
+UZ1A9Joqe4UpHz7D1TeObgkCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMC
+BeAwHQYDVR0OBBYEFBv07DRCuotnrFXyN120aKnYXlh7MA0GCSqGSIb3DQEBBQUA
+A4ICAQCV9xyZckJP0726P3p1uwE6rc5re7c9XTtGUeqaNpRwNhw7/Lqdiw1ENgit
+pnOCvCPt+VoJj51iEcGUfGFmH4u5Ctw6tesiVN6j5YqUEB+EUm3+J8jly6WOqYMW
+lQ1sPleF4eyCBUdtKK0NhPpAoJb0hKrR4Qu3keJHTwWX+BCg6Fe97UhlVXXa5TTo
+8SCV1kCMQr+02VXIMOjVztgdMGWQOevig+0RA80HwOHEkYSgl45tIuZ1dyF8MotI
+7dayGS6vJq19bM4J4Xi2cmFgIpK430JrNGtfNe/x08Z/kgU80Ah3AWb3V7hl3tPS
+sb+TsYyjJ+bU4iubzZ2+MYJbU91avTkFX4xW8n+bt+/mB5a/itmNu2KYht6qkcP+
+57unH/D9H2ymBATwwlGhkYya7vmHQjd+nCdyWdxgqI7RgZfxFcPYqU6aCemBdjk2
+swih5V6XN7pDjwYacGk7/nmmXi0mBOm8X1fJ0IDCDUvHDt0E5RVJndf/7qMcBFZ9
+4qDTORpZvYWw61TqgYvhF5Sl/uMM0HRC7kr0ZpBJS2S8RzX1smCOdAXQptKUtOAP
+Sz816irgJFjBbtBlblj34ZACrkAjJemAmtKn6l38bfhFD9tTkVUyRuNqwFQKWkzo
+Gh6mMz7+7batz2o8L7JsR3XxKUMxacMMQlZb2biZe/8rUIc0Lg==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+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
+UK2bY3PmI10F5vBYLQpLlCYUyBDf
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/test.crt b/third_party/heimdal/lib/hx509/data/test.crt
new file mode 100644
index 0000000..40663c4
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test.crt
@@ -0,0 +1,116 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Mar 22 22:25:02 2019 GMT
+ Not After : Nov 21 22:25:02 2518 GMT
+ Subject: C=SE, CN=Test cert
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:da:1d:4d:ca:51:9d:f1:9f:d7:a4:7a:45:f8:75:
+ 98:66:b2:c5:7d:53:de:42:35:74:81:cd:1e:9f:f3:
+ 43:d7:a7:83:7f:fb:a2:ce:3c:44:37:80:4f:21:36:
+ a6:f6:c9:51:74:9e:e2:9b:bf:ad:e4:eb:72:11:64:
+ 36:88:b3:a9:91:63:c7:ee:38:c4:f5:8c:06:71:e5:
+ 09:b7:eb:57:5d:bf:db:5b:72:07:c5:29:e8:6f:33:
+ b3:a2:27:ef:1f:50:f0:55:33:63:41:23:e0:b2:f7:
+ 21:77:4b:ab:9d:73:2a:bb:b6:4e:88:7f:7c:e5:c6:
+ 37:3e:b6:20:c1:57:3e:6d:57:78:ef:0d:47:e9:41:
+ e7:fa:b6:2d:32:3f:42:05:8d:56:af:f5:c4:b8:6e:
+ 99:1a:e7:07:d5:a1:3f:29:7d:ce:b2:39:a6:ab:06:
+ 7a:e2:26:39:d8:96:9e:3b:c8:af:79:3e:9a:24:4e:
+ 4b:b2:af:e4:07:0e:71:dc:2f:70:27:97:3c:a2:fa:
+ 69:9b:57:4b:c5:53:5e:28:0c:b0:c7:57:1f:a2:b2:
+ 26:0f:5f:bf:d3:45:78:90:5a:2c:fc:6a:67:33:b6:
+ c1:7e:cd:17:c0:58:9e:ba:85:c5:15:5a:5a:67:db:
+ bf:2f:05:cd:38:d9:94:c9:95:7f:9b:68:b0:62:ff:
+ 37:92:cf:d8:77:be:cb:72:3d:0f:b9:80:44:57:c0:
+ c9:10:01:fd:07:25:30:eb:d8:48:05:af:98:fa:c4:
+ 64:6d:59:a6:6a:8d:1b:d4:4b:f3:07:98:68:e3:bb:
+ 59:c9:21:f8:11:b4:a2:82:1b:0d:e8:8c:e0:a5:e1:
+ 1c:71:ca:c3:2d:90:43:c3:ee:99:2c:7d:41:48:39:
+ c8:00:72:0d:80:39:23:a1:3a:27:ed:07:ca:32:8f:
+ 34:ca:bb:9d:67:13:7d:31:ed:4a:db:35:7a:ce:b3:
+ 89:e3:64:9d:3e:47:4e:d3:b7:bd:ab:12:16:10:bb:
+ 66:e8:1a:77:4c:2a:e0:b9:16:69:66:14:83:4e:4a:
+ f3:6f:ab:85:6a:70:c6:9b:ce:93:ab:75:36:a3:a5:
+ aa:9f:45:d6:a2:7f:17:c7:6f:f9:f5:e7:35:51:a5:
+ 75:c5:07:be:26:ce:7b:3f:29:3a:74:6b:17:79:4e:
+ cf:4c:0a:69:75:58:db:eb:a8:dd:f1:e6:cc:a3:18:
+ 53:a5:c5:a5:5a:a1:cf:37:6a:b1:9f:d3:d4:eb:0f:
+ 02:40:d2:ae:68:ce:bc:c5:46:e3:ee:f8:97:88:ee:
+ c8:a7:01:7a:a1:23:af:f3:31:2c:2a:6f:12:77:dc:
+ 3c:51:9d:40:f4:9a:2a:7b:85:29:1f:3e:c3:d5:37:
+ 8e:6e:09
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ 1B:F4:EC:34:42:BA:8B:67:AC:55:F2:37:5D:B4:68:A9:D8:5E:58:7B
+ Signature Algorithm: sha1WithRSAEncryption
+ 95:f7:1c:99:72:42:4f:d3:bd:ba:3f:7a:75:bb:01:3a:ad:ce:
+ 6b:7b:b7:3d:5d:3b:46:51:ea:9a:36:94:70:36:1c:3b:fc:ba:
+ 9d:8b:0d:44:36:08:ad:a6:73:82:bc:23:ed:f9:5a:09:8f:9d:
+ 62:11:c1:94:7c:61:66:1f:8b:b9:0a:dc:3a:b5:eb:22:54:de:
+ a3:e5:8a:94:10:1f:84:52:6d:fe:27:c8:e5:cb:a5:8e:a9:83:
+ 16:95:0d:6c:3e:57:85:e1:ec:82:05:47:6d:28:ad:0d:84:fa:
+ 40:a0:96:f4:84:aa:d1:e1:0b:b7:91:e2:47:4f:05:97:f8:10:
+ a0:e8:57:bd:ed:48:65:55:75:da:e5:34:e8:f1:20:95:d6:40:
+ 8c:42:bf:b4:d9:55:c8:30:e8:d5:ce:d8:1d:30:65:90:39:eb:
+ e2:83:ed:11:03:cd:07:c0:e1:c4:91:84:a0:97:8e:6d:22:e6:
+ 75:77:21:7c:32:8b:48:ed:d6:b2:19:2e:af:26:ad:7d:6c:ce:
+ 09:e1:78:b6:72:61:60:22:92:b8:df:42:6b:34:6b:5f:35:ef:
+ f1:d3:c6:7f:92:05:3c:d0:08:77:01:66:f7:57:b8:65:de:d3:
+ d2:b1:bf:93:b1:8c:a3:27:e6:d4:e2:2b:9b:cd:9d:be:31:82:
+ 5b:53:dd:5a:bd:39:05:5f:8c:56:f2:7f:9b:b7:ef:e6:07:96:
+ bf:8a:d9:8d:bb:62:98:86:de:aa:91:c3:fe:e7:bb:a7:1f:f0:
+ fd:1f:6c:a6:04:04:f0:c2:51:a1:91:8c:9a:ee:f9:87:42:37:
+ 7e:9c:27:72:59:dc:60:a8:8e:d1:81:97:f1:15:c3:d8:a9:4e:
+ 9a:09:e9:81:76:39:36:b3:08:a1:e5:5e:97:37:ba:43:8f:06:
+ 1a:70:69:3b:fe:79:a6:5e:2d:26:04:e9:bc:5f:57:c9:d0:80:
+ c2:0d:4b:c7:0e:dd:04:e5:15:49:9d:d7:ff:ee:a3:1c:04:56:
+ 7d:e2:a0:d3:39:1a:59:bd:85:b0:eb:54:ea:81:8b:e1:17:94:
+ a5:fe:e3:0c:d0:74:42:ee:4a:f4:66:90:49:4b:64:bc:47:35:
+ f5:b2:60:8e:74:05:d0:a6:d2:94:b4:e0:0f:4b:3f:35:ea:2a:
+ e0:24:58:c1:6e:d0:65:6e:58:f7:e1:90:02:ae:40:23:25:e9:
+ 80:9a:d2:a7:ea:5d:fc:6d:f8:45:0f:db:53:91:55:32:46:e3:
+ 6a:c0:54:0a:5a:4c:e8:1a:1e:a6:33:3e:fe:ed:b6:ad:cf:6a:
+ 3c:2f:b2:6c:47:75:f1:29:43:31:69:c3:0c:42:56:5b:d9:b8:
+ 99:7b:ff:2b:50:87:34:2e
+-----BEGIN CERTIFICATE-----
+MIIFATCCAumgAwIBAgIBAjANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMCAXDTE5MDMyMjIyMjUwMloYDzI1
+MTgxMTIxMjIyNTAyWjAhMQswCQYDVQQGEwJTRTESMBAGA1UEAwwJVGVzdCBjZXJ0
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2h1NylGd8Z/XpHpF+HWY
+ZrLFfVPeQjV0gc0en/ND16eDf/uizjxEN4BPITam9slRdJ7im7+t5OtyEWQ2iLOp
+kWPH7jjE9YwGceUJt+tXXb/bW3IHxSnobzOzoifvH1DwVTNjQSPgsvchd0urnXMq
+u7ZOiH985cY3PrYgwVc+bVd47w1H6UHn+rYtMj9CBY1Wr/XEuG6ZGucH1aE/KX3O
+sjmmqwZ64iY52JaeO8iveT6aJE5Lsq/kBw5x3C9wJ5c8ovppm1dLxVNeKAywx1cf
+orImD1+/00V4kFos/GpnM7bBfs0XwFieuoXFFVpaZ9u/LwXNONmUyZV/m2iwYv83
+ks/Yd77Lcj0PuYBEV8DJEAH9ByUw69hIBa+Y+sRkbVmmao0b1EvzB5ho47tZySH4
+EbSighsN6IzgpeEcccrDLZBDw+6ZLH1BSDnIAHINgDkjoTon7QfKMo80yrudZxN9
+Me1K2zV6zrOJ42SdPkdO07e9qxIWELtm6Bp3TCrguRZpZhSDTkrzb6uFanDGm86T
+q3U2o6Wqn0XWon8Xx2/59ec1UaV1xQe+Js57Pyk6dGsXeU7PTAppdVjb66jd8ebM
+oxhTpcWlWqHPN2qxn9PU6w8CQNKuaM68xUbj7viXiO7IpwF6oSOv8zEsKm8Sd9w8
+UZ1A9Joqe4UpHz7D1TeObgkCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMC
+BeAwHQYDVR0OBBYEFBv07DRCuotnrFXyN120aKnYXlh7MA0GCSqGSIb3DQEBBQUA
+A4ICAQCV9xyZckJP0726P3p1uwE6rc5re7c9XTtGUeqaNpRwNhw7/Lqdiw1ENgit
+pnOCvCPt+VoJj51iEcGUfGFmH4u5Ctw6tesiVN6j5YqUEB+EUm3+J8jly6WOqYMW
+lQ1sPleF4eyCBUdtKK0NhPpAoJb0hKrR4Qu3keJHTwWX+BCg6Fe97UhlVXXa5TTo
+8SCV1kCMQr+02VXIMOjVztgdMGWQOevig+0RA80HwOHEkYSgl45tIuZ1dyF8MotI
+7dayGS6vJq19bM4J4Xi2cmFgIpK430JrNGtfNe/x08Z/kgU80Ah3AWb3V7hl3tPS
+sb+TsYyjJ+bU4iubzZ2+MYJbU91avTkFX4xW8n+bt+/mB5a/itmNu2KYht6qkcP+
+57unH/D9H2ymBATwwlGhkYya7vmHQjd+nCdyWdxgqI7RgZfxFcPYqU6aCemBdjk2
+swih5V6XN7pDjwYacGk7/nmmXi0mBOm8X1fJ0IDCDUvHDt0E5RVJndf/7qMcBFZ9
+4qDTORpZvYWw61TqgYvhF5Sl/uMM0HRC7kr0ZpBJS2S8RzX1smCOdAXQptKUtOAP
+Sz816irgJFjBbtBlblj34ZACrkAjJemAmtKn6l38bfhFD9tTkVUyRuNqwFQKWkzo
+Gh6mMz7+7batz2o8L7JsR3XxKUMxacMMQlZb2biZe/8rUIc0Lg==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/test.key b/third_party/heimdal/lib/hx509/data/test.key
new file mode 100644
index 0000000..03de157
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+UK2bY3PmI10F5vBYLQpLlCYUyBDf
+-----END PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/data/test.p12 b/third_party/heimdal/lib/hx509/data/test.p12
new file mode 100644
index 0000000..32d9c81
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/test.p12
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/win-u16-in-printablestring.der b/third_party/heimdal/lib/hx509/data/win-u16-in-printablestring.der
new file mode 100644
index 0000000..5f6178f
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/win-u16-in-printablestring.der
Binary files differ
diff --git a/third_party/heimdal/lib/hx509/data/yutaka-pad-broken-ca.pem b/third_party/heimdal/lib/hx509/data/yutaka-pad-broken-ca.pem
new file mode 100644
index 0000000..32685d1
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/yutaka-pad-broken-ca.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICijCCAfOgAwIBAgIJAOSnzE4Qx2H+MA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNV
+BAYTAkpQMRQwEgYDVQQKEwtDQSBURVNUIDEtNDEUMBIGA1UEAxMLQ0EgVEVTVCAx
+LTQwHhcNMDYwOTA3MTYzMzE4WhcNMDYxMDA3MTYzMzE4WjA5MQswCQYDVQQGEwJK
+UDEUMBIGA1UEChMLQ0EgVEVTVCAxLTQxFDASBgNVBAMTC0NBIFRFU1QgMS00MIGd
+MA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDZfFjkPDZeorxWqk7/DKM2d/9Nao28
+dM6T5sb5L41hD5C1kXV6MJev5ALASSxtI6OVOmZO4gfubnsvcj0NTZO4SeF1yL1r
+VDPdx7juQI1cbDiG/EwIMW29UIdj9h052JTmEbpT0RuP/4JWmAWrdO5UE40xua7S
+z2/6+DB2ZklFoQIBA6OBmzCBmDAdBgNVHQ4EFgQU340JbeYcg6V9zi8aozy48aIh
+tfgwaQYDVR0jBGIwYIAU340JbeYcg6V9zi8aozy48aIhtfihPaQ7MDkxCzAJBgNV
+BAYTAkpQMRQwEgYDVQQKEwtDQSBURVNUIDEtNDEUMBIGA1UEAxMLQ0EgVEVTVCAx
+LTSCCQDkp8xOEMdh/jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBABsH
+aJ/c/3cGHssi8IvVRci/aavqj607y7l22nKDtG1p4KAjnfNhBMOhRhFv00nJnokK
+y0uc4DIegAW1bxQjqcMNNEmGbzAeixH/cRCot8C1LobEQmxNWCY2DJLWoI3wwqr8
+uUSnI1CDZ5402etkCiNXsDy/eYDrF+2KonkIWRrr
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/yutaka-pad-broken-cert.pem b/third_party/heimdal/lib/hx509/data/yutaka-pad-broken-cert.pem
new file mode 100644
index 0000000..b0726ea
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/yutaka-pad-broken-cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzTCCAjagAwIBAgIJAOSnzE4Qx2H/MA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNV
+BAYTAkpQMRQwEgYDVQQKEwtDQSBURVNUIDEtNDEUMBIGA1UEAxMLQ0EgVEVTVCAx
+LTQwHhcNMDYwOTA3MTY0MDM3WhcNMDcwOTA3MTY0MDM3WjBPMQswCQYDVQQGEwJK
+UDEOMAwGA1UECBMFVG9reW8xFjAUBgNVBAoTDVRFU1QgMiBDTElFTlQxGDAWBgNV
+BAMTD3d3dzIuZXhhbXBsZS5qcDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+vSpZ6ig9DpeKB60h7ii1RitNuvkn4INOfEXjCjPSFwmIbGJqnyWvKTiMKzguEYkG
+6CZAbsx44t3kvsVDeUd5WZBRgMoeQd1tNJBU4BXxOA8bVzdwstzaPeeufQtZDvKf
+M4ej+fo/j9lYH9udCug1huaNybcCtijzGonkddX4JEUCAwEAAaOBxjCBwzAJBgNV
+HRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZp
+Y2F0ZTAdBgNVHQ4EFgQUK0DZtd8K1P2ij9gVKUNcHlx7uCIwaQYDVR0jBGIwYIAU
+340JbeYcg6V9zi8aozy48aIhtfihPaQ7MDkxCzAJBgNVBAYTAkpQMRQwEgYDVQQK
+EwtDQSBURVNUIDEtNDEUMBIGA1UEAxMLQ0EgVEVTVCAxLTSCCQDkp8xOEMdh/jAN
+BgkqhkiG9w0BAQUFAAOBgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAUKJ+eFJYSvXwGF2wxzDXj+x5YCItrHFmrEy4AXXAW+H0NgJVNvqRY/O
+Kw==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/yutaka-pad-ok-ca.pem b/third_party/heimdal/lib/hx509/data/yutaka-pad-ok-ca.pem
new file mode 100644
index 0000000..32685d1
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/yutaka-pad-ok-ca.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICijCCAfOgAwIBAgIJAOSnzE4Qx2H+MA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNV
+BAYTAkpQMRQwEgYDVQQKEwtDQSBURVNUIDEtNDEUMBIGA1UEAxMLQ0EgVEVTVCAx
+LTQwHhcNMDYwOTA3MTYzMzE4WhcNMDYxMDA3MTYzMzE4WjA5MQswCQYDVQQGEwJK
+UDEUMBIGA1UEChMLQ0EgVEVTVCAxLTQxFDASBgNVBAMTC0NBIFRFU1QgMS00MIGd
+MA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDZfFjkPDZeorxWqk7/DKM2d/9Nao28
+dM6T5sb5L41hD5C1kXV6MJev5ALASSxtI6OVOmZO4gfubnsvcj0NTZO4SeF1yL1r
+VDPdx7juQI1cbDiG/EwIMW29UIdj9h052JTmEbpT0RuP/4JWmAWrdO5UE40xua7S
+z2/6+DB2ZklFoQIBA6OBmzCBmDAdBgNVHQ4EFgQU340JbeYcg6V9zi8aozy48aIh
+tfgwaQYDVR0jBGIwYIAU340JbeYcg6V9zi8aozy48aIhtfihPaQ7MDkxCzAJBgNV
+BAYTAkpQMRQwEgYDVQQKEwtDQSBURVNUIDEtNDEUMBIGA1UEAxMLQ0EgVEVTVCAx
+LTSCCQDkp8xOEMdh/jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBABsH
+aJ/c/3cGHssi8IvVRci/aavqj607y7l22nKDtG1p4KAjnfNhBMOhRhFv00nJnokK
+y0uc4DIegAW1bxQjqcMNNEmGbzAeixH/cRCot8C1LobEQmxNWCY2DJLWoI3wwqr8
+uUSnI1CDZ5402etkCiNXsDy/eYDrF+2KonkIWRrr
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/yutaka-pad-ok-cert.pem b/third_party/heimdal/lib/hx509/data/yutaka-pad-ok-cert.pem
new file mode 100644
index 0000000..9a89e59
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/yutaka-pad-ok-cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzTCCAjagAwIBAgIJAOSnzE4Qx2H/MA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNV
+BAYTAkpQMRQwEgYDVQQKEwtDQSBURVNUIDEtNDEUMBIGA1UEAxMLQ0EgVEVTVCAx
+LTQwHhcNMDYwOTA3MTY0MDM3WhcNMDcwOTA3MTY0MDM3WjBPMQswCQYDVQQGEwJK
+UDEOMAwGA1UECBMFVG9reW8xFjAUBgNVBAoTDVRFU1QgMiBDTElFTlQxGDAWBgNV
+BAMTD3d3dzIuZXhhbXBsZS5qcDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+vSpZ6ig9DpeKB60h7ii1RitNuvkn4INOfEXjCjPSFwmIbGJqnyWvKTiMKzguEYkG
+6CZAbsx44t3kvsVDeUd5WZBRgMoeQd1tNJBU4BXxOA8bVzdwstzaPeeufQtZDvKf
+M4ej+fo/j9lYH9udCug1huaNybcCtijzGonkddX4JEUCAwEAAaOBxjCBwzAJBgNV
+HRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZp
+Y2F0ZTAdBgNVHQ4EFgQUK0DZtd8K1P2ij9gVKUNcHlx7uCIwaQYDVR0jBGIwYIAU
+340JbeYcg6V9zi8aozy48aIhtfihPaQ7MDkxCzAJBgNVBAYTAkpQMRQwEgYDVQQK
+EwtDQSBURVNUIDEtNDEUMBIGA1UEAxMLQ0EgVEVTVCAxLTSCCQDkp8xOEMdh/jAN
+BgkqhkiG9w0BAQUFAAOBgQCkGhwCDLRwWbDnDFReXkIZ1/9OhfiR8yL1idP9iYVU
+cSoWxSHPBWkv6LORFS03APcXCSzDPJ9pxTjFjGGFSI91fNrzkKdHU/+0WCF2uTh7
+Dz2blqtcmnJqMSn1xHxxfM/9e6M3XwFUMf7SGiKRAbDfsauPafEPTn83vSeKj1lg
+Dw==
+-----END CERTIFICATE-----
diff --git a/third_party/heimdal/lib/hx509/data/yutaka-pad.key b/third_party/heimdal/lib/hx509/data/yutaka-pad.key
new file mode 100644
index 0000000..1763623
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/data/yutaka-pad.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC9KlnqKD0Ol4oHrSHuKLVGK026+Sfgg058ReMKM9IXCYhsYmqf
+Ja8pOIwrOC4RiQboJkBuzHji3eS+xUN5R3lZkFGAyh5B3W00kFTgFfE4DxtXN3Cy
+3No95659C1kO8p8zh6P5+j+P2Vgf250K6DWG5o3JtwK2KPMaieR11fgkRQIDAQAB
+AoGBAJCYvwJun713uNsFTNpv46EvmMtDiWfk9ymnglVaJ03Uy6ON11Kvy6UGxJ6E
+4zIkPFNYaghH5GAGncP1pg4exHKRGJTNcQbMf9iOsCTOuvKSWbBZpnJcFllKyESK
+PTt72D6x/cuzDXVTeWvQMoOILa09szW7aqFNIdxae4Vq7a4BAkEA6MoehuRtZ4N9
+Jtc9cIpSKOOatZ1UajWEFV2yVHaDED2kkWxKjppPzRn06LzX8LWm1RT0qe3Zyasi
+iXCXlno/+QJBANAGvY+k/+OvzWnv1yTKO8OmrMqkSzh3KAhFbiVWdQaqMSCWtKYk
+GoOKnq0PB73ExhdbTFmxC4KBPHTC2guOca0CQCD78pNebnoKUYNdYCFAGCAfD97H
+6hwadRqp6gi5uhxk/5pzY6UNDF2dXexURayfsIHktD4Xq5I9o2kiAPibXdECQQDC
+KihwlL9K02JVSMl0y1XxDfclxSd4cq9o2PUv4HymVeA43LGMiRI+SPpF6Ut+ctW6
+IzsmVDu7+chl6yD9vFyZAkA3Auv9UxKL3kPtvu5G/lrCVmwzVfAzuwtnmSfp1+M5
+yTYBz+VFSsYrdlDZ3jdLnFzVOMiIm9pZca/L93QjmXJ+
+-----END RSA PRIVATE KEY-----
diff --git a/third_party/heimdal/lib/hx509/doxygen.c b/third_party/heimdal/lib/hx509/doxygen.c
new file mode 100644
index 0000000..a6d3d9c
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/doxygen.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/** @mainpage Heimdal PKIX/X.509 library
+ *
+ * @section intro Introduction
+ *
+ * Heimdal libhx509 library is a implementation of the PKIX/X.509 and
+ * related protocols.
+ *
+ * PKIX/X.509 is ...
+ *
+ *
+ * Sections in this manual are:
+ * - @ref page_name
+ * - @ref page_cert
+ * - @ref page_keyset
+ * - @ref page_error
+ * - @ref page_lock
+ * - @ref page_cms
+ * - @ref page_ca
+ * - @ref page_revoke
+ * - @ref page_print
+ * - @ref page_env
+ *
+ * The project web page:
+ * http://www.h5l.org/
+ *
+ */
+
+/** @defgroup hx509 hx509 library */
+
+/** @defgroup hx509_error hx509 error functions
+ * See the @ref page_error for description and examples. */
+/** @defgroup hx509_cert hx509 certificate functions
+ * See the @ref page_cert for description and examples. */
+/** @defgroup hx509_keyset hx509 certificate store functions
+ * See the @ref page_keyset for description and examples. */
+/** @defgroup hx509_cms hx509 CMS/pkcs7 functions
+ * See the @ref page_cms for description and examples. */
+/** @defgroup hx509_crypto hx509 crypto functions */
+/** @defgroup hx509_misc hx509 misc functions */
+/** @defgroup hx509_name hx509 name functions
+ * See the @ref page_name for description and examples. */
+/** @defgroup hx509_revoke hx509 revokation checking functions
+ * See the @ref page_revoke for description and examples. */
+/** @defgroup hx509_verify hx509 verification functions */
+/** @defgroup hx509_lock hx509 lock functions
+ * See the @ref page_lock for description and examples. */
+/** @defgroup hx509_query hx509 query functions */
+/** @defgroup hx509_ca hx509 CA functions
+ * See the @ref page_ca for description and examples. */
+/** @defgroup hx509_peer hx509 certificate selecting functions */
+/** @defgroup hx509_print hx509 printing functions */
+/** @defgroup hx509_env hx509 environment functions */
diff --git a/third_party/heimdal/lib/hx509/env.c b/third_party/heimdal/lib/hx509/env.c
new file mode 100644
index 0000000..7970438
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/env.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2007 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/**
+ * @page page_env Hx509 environment functions
+ *
+ * See the library functions here: @ref hx509_env
+ */
+
+/**
+ * Add a new key/value pair to the hx509_env.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to add
+ * @param value value to add
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_env
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_env_add(hx509_context context, hx509_env *env,
+ const char *key, const char *value)
+{
+ hx509_env n;
+
+ n = malloc(sizeof(*n));
+ if (n == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ n->type = env_string;
+ n->next = NULL;
+ n->name = strdup(key);
+ if (n->name == NULL) {
+ free(n);
+ return ENOMEM;
+ }
+ n->u.string = strdup(value);
+ if (n->u.string == NULL) {
+ free(n->name);
+ free(n);
+ return ENOMEM;
+ }
+
+ /* add to tail */
+ if (*env) {
+ hx509_env e = *env;
+ while (e->next)
+ e = e->next;
+ e->next = n;
+ } else
+ *env = n;
+
+ return 0;
+}
+
+/**
+ * Add a new key/binding pair to the hx509_env.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to add
+ * @param list binding list to add
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_env
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_env_add_binding(hx509_context context, hx509_env *env,
+ const char *key, hx509_env list)
+{
+ hx509_env n;
+
+ n = malloc(sizeof(*n));
+ if (n == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ n->type = env_list;
+ n->next = NULL;
+ n->name = strdup(key);
+ if (n->name == NULL) {
+ free(n);
+ return ENOMEM;
+ }
+ n->u.list = list;
+
+ /* add to tail */
+ if (*env) {
+ hx509_env e = *env;
+ while (e->next)
+ e = e->next;
+ e->next = n;
+ } else
+ *env = n;
+
+ return 0;
+}
+
+
+/**
+ * Search the hx509_env for a length based key.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to search for.
+ * @param len length of key.
+ *
+ * @return the value if the key is found, NULL otherwise.
+ *
+ * @ingroup hx509_env
+ */
+
+HX509_LIB_FUNCTION const char * HX509_LIB_CALL
+hx509_env_lfind(hx509_context context, hx509_env env,
+ const char *key, size_t len)
+{
+ while(env) {
+ if (strncmp(key, env->name ,len) == 0
+ && env->name[len] == '\0' && env->type == env_string)
+ return env->u.string;
+ env = env->next;
+ }
+ return NULL;
+}
+
+/**
+ * Search the hx509_env for a key.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to search for.
+ *
+ * @return the value if the key is found, NULL otherwise.
+ *
+ * @ingroup hx509_env
+ */
+
+HX509_LIB_FUNCTION const char * HX509_LIB_CALL
+hx509_env_find(hx509_context context, hx509_env env, const char *key)
+{
+ while(env) {
+ if (strcmp(key, env->name) == 0 && env->type == env_string)
+ return env->u.string;
+ env = env->next;
+ }
+ return NULL;
+}
+
+/**
+ * Search the hx509_env for a binding.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to search for.
+ *
+ * @return the binding if the key is found, NULL if not found.
+ *
+ * @ingroup hx509_env
+ */
+
+hx509_env
+hx509_env_find_binding(hx509_context context,
+ hx509_env env,
+ const char *key)
+{
+ while(env) {
+ if (strcmp(key, env->name) == 0 && env->type == env_list)
+ return env->u.list;
+ env = env->next;
+ }
+ return NULL;
+}
+
+static void
+env_free(hx509_env b)
+{
+ while(b) {
+ hx509_env next = b->next;
+
+ if (b->type == env_string)
+ free(b->u.string);
+ else if (b->type == env_list)
+ env_free(b->u.list);
+
+ free(b->name);
+ free(b);
+ b = next;
+ }
+}
+
+/**
+ * Free an hx509_env environment context.
+ *
+ * @param env the environment to free.
+ *
+ * @ingroup hx509_env
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_env_free(hx509_env *env)
+{
+ if (*env)
+ env_free(*env);
+ *env = NULL;
+}
diff --git a/third_party/heimdal/lib/hx509/error.c b/third_party/heimdal/lib/hx509/error.c
new file mode 100644
index 0000000..aee4f79
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/error.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/**
+ * @page page_error Hx509 error reporting functions
+ *
+ * See the library functions here: @ref hx509_error
+ */
+
+struct hx509_error_data {
+ hx509_error next;
+ int code;
+ char *msg;
+};
+
+/**
+ * Resets the error strings the hx509 context.
+ *
+ * @param context A hx509 context.
+ *
+ * @ingroup hx509_error
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_clear_error_string(hx509_context context)
+{
+ if (context) {
+ heim_release(context->error);
+ context->error = NULL;
+ }
+}
+
+/**
+ * Add an error message to the hx509 context.
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * - HX509_ERROR_APPEND appends the error string to the old messages
+ (code is updated).
+ * @param code error code related to error message
+ * @param fmt error message format
+ * @param ap arguments to error message format
+ *
+ * @ingroup hx509_error
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_set_error_stringv(hx509_context context, int flags, int code,
+ const char *fmt, va_list ap)
+{
+ heim_error_t msg;
+
+ if (context == NULL)
+ return;
+
+ msg = heim_error_createv(code, fmt, ap);
+ if (msg) {
+ if (flags & HX509_ERROR_APPEND)
+ heim_error_append(msg, context->error);
+ heim_release(context->error);
+ }
+ context->error = msg;
+}
+
+/**
+ * See hx509_set_error_stringv().
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * - HX509_ERROR_APPEND appends the error string to the old messages
+ (code is updated).
+ * @param code error code related to error message
+ * @param fmt error message format
+ * @param ... arguments to error message format
+ *
+ * @ingroup hx509_error
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_set_error_string(hx509_context context, int flags, int code,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ hx509_set_error_stringv(context, flags, code, fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * Sets ENOMEM as the error on a hx509 context.
+ *
+ * @param context A hx509 context.
+ *
+ * @ingroup hx509_error
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_enomem(hx509_context context)
+{
+ return heim_enomem(context->hcontext);
+}
+
+/**
+ * Get an error string from context associated with error_code.
+ *
+ * @param context A hx509 context.
+ * @param error_code Get error message for this error code.
+ *
+ * @return error string, free with hx509_free_error_string().
+ *
+ * @ingroup hx509_error
+ */
+
+HX509_LIB_FUNCTION char * HX509_LIB_CALL
+hx509_get_error_string(hx509_context context, int error_code)
+{
+ heim_string_t s = NULL;
+ const char *cstr = NULL;
+ char *str;
+
+ if (context) {
+ if (context->error &&
+ heim_error_get_code(context->error) == error_code &&
+ (s = heim_error_copy_string(context->error)))
+ cstr = heim_string_get_utf8(s);
+
+ if (cstr == NULL)
+ cstr = com_right(context->et_list, error_code);
+
+ if (cstr == NULL && error_code > -1)
+ cstr = strerror(error_code);
+ } /* else this could be an error in hx509_context_init() */
+
+ if (cstr == NULL)
+ cstr = error_message(error_code); /* never returns NULL */
+
+ str = strdup(cstr);
+ heim_release(s);
+ return str;
+}
+
+/**
+ * Free error string returned by hx509_get_error_string().
+ *
+ * @param str error string to free.
+ *
+ * @ingroup hx509_error
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_free_error_string(char *str)
+{
+ free(str);
+}
+
+/**
+ * Print error message and fatally exit from error code
+ *
+ * @param context A hx509 context.
+ * @param exit_code exit() code from process.
+ * @param error_code Error code for the reason to exit.
+ * @param fmt format string with the exit message.
+ * @param ... argument to format string.
+ *
+ * @ingroup hx509_error
+ */
+
+HX509_LIB_NORETURN_FUNCTION
+ __attribute__ ((__noreturn__, __format__ (__printf__, 4, 5)))
+void HX509_LIB_CALL
+hx509_err(hx509_context context, int exit_code,
+ int error_code, const char *fmt, ...)
+{
+ va_list ap;
+ const char *msg;
+ char *str;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ msg = hx509_get_error_string(context, error_code);
+ if (msg == NULL)
+ msg = "no error";
+
+ errx(exit_code, "%s: %s", ret != -1 ? str : "ENOMEM", msg);
+}
diff --git a/third_party/heimdal/lib/hx509/file.c b/third_party/heimdal/lib/hx509/file.c
new file mode 100644
index 0000000..a22f625
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/file.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_map_file_os(const char *fn, heim_octet_string *os)
+{
+ size_t length;
+ void *data;
+ int ret;
+
+ ret = rk_undumpdata(fn, &data, &length);
+
+ os->data = data;
+ os->length = length;
+
+ return ret;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_unmap_file_os(heim_octet_string *os)
+{
+ rk_xfree(os->data);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_write_file(const char *fn, const void *data, size_t length)
+{
+ rk_dumpdata(fn, data, length);
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+print_pem_stamp(FILE *f, const char *type, const char *str)
+{
+ fprintf(f, "-----%s %s-----\n", type, str);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_pem_write(hx509_context context, const char *type,
+ hx509_pem_header *headers, FILE *f,
+ const void *data, size_t size)
+{
+ const char *p = data;
+ size_t length;
+ char *line;
+
+#define ENCODE_LINE_LENGTH 54
+
+ print_pem_stamp(f, "BEGIN", type);
+
+ while (headers) {
+ fprintf(f, "%s: %s\n%s",
+ headers->header, headers->value,
+ headers->next ? "" : "\n");
+ headers = headers->next;
+ }
+
+ while (size > 0) {
+ ssize_t l;
+
+ length = size;
+ if (length > ENCODE_LINE_LENGTH)
+ length = ENCODE_LINE_LENGTH;
+
+ l = rk_base64_encode(p, length, &line);
+ if (l < 0) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "malloc - out of memory");
+ return ENOMEM;
+ }
+ size -= length;
+ fprintf(f, "%s\n", line);
+ p += length;
+ free(line);
+ }
+
+ print_pem_stamp(f, "END", type);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_pem_add_header(hx509_pem_header **headers,
+ const char *header, const char *value)
+{
+ hx509_pem_header *h;
+
+ h = calloc(1, sizeof(*h));
+ if (h == NULL)
+ return ENOMEM;
+ h->header = strdup(header);
+ if (h->header == NULL) {
+ free(h);
+ return ENOMEM;
+ }
+ h->value = strdup(value);
+ if (h->value == NULL) {
+ free(h->header);
+ free(h);
+ return ENOMEM;
+ }
+
+ h->next = *headers;
+ *headers = h;
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_pem_free_header(hx509_pem_header *headers)
+{
+ hx509_pem_header *h;
+ while (headers) {
+ h = headers;
+ headers = headers->next;
+ free(h->header);
+ free(h->value);
+ free(h);
+ }
+}
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION const char * HX509_LIB_CALL
+hx509_pem_find_header(const hx509_pem_header *h, const char *header)
+{
+ while(h) {
+ if (strcmp(header, h->header) == 0)
+ return h->value;
+ h = h->next;
+ }
+ return NULL;
+}
+
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_pem_read(hx509_context context,
+ FILE *f,
+ hx509_pem_read_func func,
+ void *ctx)
+{
+ hx509_pem_header *headers = NULL;
+ char *type = NULL;
+ void *data = NULL;
+ size_t len = 0;
+ char buf[1024];
+ int ret = HX509_PARSING_KEY_FAILED;
+
+ enum { BEFORE, SEARCHHEADER, INHEADER, INDATA, DONE } where;
+
+ where = BEFORE;
+
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+ int i;
+
+ i = strcspn(buf, "\n");
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ if (i > 0)
+ i--;
+ }
+ if (buf[i] == '\r') {
+ buf[i] = '\0';
+ if (i > 0)
+ i--;
+ }
+
+ switch (where) {
+ case BEFORE:
+ if (strncmp("-----BEGIN ", buf, 11) == 0) {
+ type = strdup(buf + 11);
+ if (type == NULL)
+ break;
+ p = strchr(type, '-');
+ if (p)
+ *p = '\0';
+ where = SEARCHHEADER;
+ }
+ break;
+ case SEARCHHEADER:
+ p = strchr(buf, ':');
+ if (p == NULL) {
+ where = INDATA;
+ goto indata;
+ }
+ fallthrough;
+ case INHEADER:
+ if (buf[0] == '\0') {
+ where = INDATA;
+ break;
+ }
+ p = strchr(buf, ':');
+ if (p) {
+ *p++ = '\0';
+ while (isspace((int)*p))
+ p++;
+ ret = hx509_pem_add_header(&headers, buf, p);
+ if (ret)
+ abort();
+ }
+ break;
+ case INDATA:
+ indata:
+
+ if (strncmp("-----END ", buf, 9) == 0) {
+ where = DONE;
+ break;
+ }
+
+ p = emalloc(i);
+ i = rk_base64_decode(buf, p);
+ if (i < 0) {
+ free(p);
+ goto out;
+ }
+
+ data = erealloc(data, len + i);
+ memcpy(((char *)data) + len, p, i);
+ free(p);
+ len += i;
+ break;
+ case DONE:
+ abort();
+ }
+
+ if (where == DONE) {
+ ret = (*func)(context, type, headers, data, len, ctx);
+ out:
+ free(data);
+ data = NULL;
+ len = 0;
+ free(type);
+ type = NULL;
+ where = BEFORE;
+ hx509_pem_free_header(headers);
+ headers = NULL;
+ if (ret)
+ break;
+ }
+ }
+
+ if (where != BEFORE) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "File ends before end of PEM end tag");
+ ret = HX509_PARSING_KEY_FAILED;
+ }
+ if (data)
+ free(data);
+ if (type)
+ free(type);
+ if (headers)
+ hx509_pem_free_header(headers);
+
+ return ret;
+}
+
+/*
+ * On modern systems there's no such thing as scrubbing a file. Not this way
+ * anyways. However, for now we'll cargo-cult this along just as in lib/krb5.
+ */
+static int
+scrub_file(int fd, ssize_t sz)
+{
+ char buf[128];
+
+ memset(buf, 0, sizeof(buf));
+ while (sz > 0) {
+ ssize_t tmp;
+ size_t wr = sizeof(buf) > sz ? (size_t)sz : sizeof(buf);
+
+ tmp = write(fd, buf, wr);
+ if (tmp == -1)
+ return errno;
+ sz -= tmp;
+ }
+#ifdef _MSC_VER
+ return _commit(fd);
+#else
+ return fsync(fd);
+#endif
+}
+
+int
+_hx509_erase_file(hx509_context context, const char *fn)
+{
+ struct stat sb1, sb2;
+ int ret;
+ int fd;
+
+ if (fn == NULL)
+ return 0;
+
+ /* This is based on _krb5_erase_file(), minus file locking */
+ ret = lstat(fn, &sb1);
+ if (ret == -1 && errno == ENOENT)
+ return 0;
+ if (ret == -1) {
+ hx509_set_error_string(context, 0, errno, "hx509_certs_destroy: "
+ "stat of \"%s\": %s", fn, strerror(errno));
+ return errno;
+ }
+
+ fd = open(fn, O_RDWR | O_BINARY | O_CLOEXEC | O_NOFOLLOW);
+ if (fd < 0)
+ return errno == ENOENT ? 0 : errno;
+ rk_cloexec(fd);
+
+ if (unlink(fn) < 0) {
+ ret = errno;
+ (void) close(fd);
+ hx509_set_error_string(context, 0, ret, "hx509_certs_destroy: "
+ "unlinking \"%s\": %s", fn, strerror(ret));
+ return ret;
+ }
+
+ /* check TOCTOU, symlinks */
+ ret = fstat(fd, &sb2);
+ if (ret < 0) {
+ ret = errno;
+ hx509_set_error_string(context, 0, ret, "hx509_certs_destroy: "
+ "fstat of %d, \"%s\": %s", fd, fn,
+ strerror(ret));
+ (void) close(fd);
+ return ret;
+ }
+ if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
+ (void) close(fd);
+ return EPERM;
+ }
+
+ /* there are still hard links to this file */
+ if (sb2.st_nlink != 0) {
+ close(fd);
+ return 0;
+ }
+
+ ret = scrub_file(fd, sb2.st_size);
+ (void) close(fd);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/hx509/hx509.h b/third_party/heimdal/lib/hx509/hx509.h
new file mode 100644
index 0000000..75d6473
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/hx509.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_HX509_H
+#define HEIMDAL_HX509_H 1
+
+#include <rfc2459_asn1.h>
+#include <rfc4108_asn1.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <heimbase.h>
+
+typedef struct hx509_cert_attribute_data *hx509_cert_attribute;
+typedef struct hx509_cert_data *hx509_cert;
+typedef struct hx509_certs_data *hx509_certs;
+typedef struct hx509_context_data *hx509_context;
+typedef struct hx509_crypto_data *hx509_crypto;
+typedef struct hx509_lock_data *hx509_lock;
+typedef struct hx509_name_data *hx509_name;
+typedef struct hx509_private_key *hx509_private_key;
+typedef struct hx509_private_key_ops hx509_private_key_ops;
+typedef struct hx509_validate_ctx_data *hx509_validate_ctx;
+typedef struct hx509_verify_ctx_data *hx509_verify_ctx;
+typedef struct hx509_revoke_ctx_data *hx509_revoke_ctx;
+typedef struct hx509_query_data hx509_query;
+typedef void * hx509_cursor;
+typedef struct hx509_request_data *hx509_request;
+typedef struct hx509_error_data *hx509_error;
+typedef struct hx509_peer_info *hx509_peer_info;
+typedef struct hx509_ca_tbs *hx509_ca_tbs;
+typedef struct hx509_env_data *hx509_env;
+typedef struct hx509_crl *hx509_crl;
+
+typedef void (*hx509_vprint_func)(void *, const char *, va_list);
+
+typedef enum {
+ HX509_SAN_TYPE_UNSUPPORTED = 0,
+ /* The following correspond to the enum GeneralName_enum values: */
+ HX509_SAN_TYPE_EMAIL = 2,
+ HX509_SAN_TYPE_DNSNAME = 3,
+ HX509_SAN_TYPE_DN = 4,
+ HX509_SAN_TYPE_REGISTERED_ID = 7,
+ /*
+ * Missing support for:
+ * - URI SANs
+ * - IP address SANs
+ * - various otherName SANs we know about (e.g., DNSSRV)
+ *
+ * The following are otherName SAN types, and assigned manually here:
+ */
+ HX509_SAN_TYPE_XMPP = 32,
+ HX509_SAN_TYPE_PKINIT = 33,
+ HX509_SAN_TYPE_MS_UPN = 34,
+ HX509_SAN_TYPE_DNSSRV = 35, /* SRVName [RFC4985] */
+ HX509_SAN_TYPE_PERMANENT_ID = 36, /* PermanentIdentifier [RFC4043] */
+ HX509_SAN_TYPE_HW_MODULE = 37, /* HardwareModuleName [RFC4108] */
+} hx509_san_type;
+
+enum {
+ HX509_VHN_F_ALLOW_NO_MATCH = 1
+};
+
+enum {
+ HX509_VALIDATE_F_VALIDATE = 1,
+ HX509_VALIDATE_F_VERBOSE = 2
+};
+
+enum {
+ HX509_CRYPTO_PADDING_PKCS7 = 0,
+ HX509_CRYPTO_PADDING_NONE = 1
+};
+
+enum {
+ HX509_KEY_FORMAT_GUESS = 0,
+ HX509_KEY_FORMAT_DER = 1,
+ HX509_KEY_FORMAT_WIN_BACKUPKEY = 2,
+ HX509_KEY_FORMAT_PKCS8 = 3,
+};
+typedef uint32_t hx509_key_format_t;
+
+struct hx509_cert_attribute_data {
+ heim_oid oid;
+ heim_octet_string data;
+};
+
+typedef enum {
+ HX509_PROMPT_TYPE_PASSWORD = 0x1, /* password, hidden */
+ HX509_PROMPT_TYPE_QUESTION = 0x2, /* question, not hidden */
+ HX509_PROMPT_TYPE_INFO = 0x4 /* infomation, reply doesn't matter */
+} hx509_prompt_type;
+
+typedef struct hx509_prompt {
+ const char *prompt;
+ hx509_prompt_type type;
+ heim_octet_string reply;
+} hx509_prompt;
+
+typedef int (*hx509_prompter_fct)(void *, const hx509_prompt *);
+
+typedef struct hx509_octet_string_list {
+ size_t len;
+ heim_octet_string *val;
+} hx509_octet_string_list;
+
+typedef struct hx509_pem_header {
+ struct hx509_pem_header *next;
+ char *header;
+ char *value;
+} hx509_pem_header;
+
+typedef int
+(*hx509_pem_read_func)(hx509_context, const char *, const hx509_pem_header *,
+ const void *, size_t, void *ctx);
+
+/*
+ * Options passed to hx509_query_match_option.
+ */
+typedef enum {
+ HX509_QUERY_OPTION_PRIVATE_KEY = 1,
+ HX509_QUERY_OPTION_KU_ENCIPHERMENT = 2,
+ HX509_QUERY_OPTION_KU_DIGITALSIGNATURE = 3,
+ HX509_QUERY_OPTION_KU_KEYCERTSIGN = 4,
+ HX509_QUERY_OPTION_END = 0xffff
+} hx509_query_option;
+
+/* flags to hx509_certs_init */
+#define HX509_CERTS_CREATE 0x01
+#define HX509_CERTS_UNPROTECT_ALL 0x02
+#define HX509_CERTS_NO_PRIVATE_KEYS 0x04
+
+/* flags to hx509_certs_store */
+#define HX509_CERTS_STORE_NO_PRIVATE_KEYS 0x04
+
+
+/* flags to hx509_set_error_string */
+#define HX509_ERROR_APPEND 0x01
+
+/* flags to hx509_cms_unenvelope */
+#define HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT 0x01
+#define HX509_CMS_UE_ALLOW_WEAK 0x02
+
+/* flags to hx509_cms_envelope_1 */
+#define HX509_CMS_EV_NO_KU_CHECK 0x01
+#define HX509_CMS_EV_ALLOW_WEAK 0x02
+#define HX509_CMS_EV_ID_NAME 0x04
+
+/* flags to hx509_cms_verify_signed */
+#define HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH 0x01
+#define HX509_CMS_VS_NO_KU_CHECK 0x02
+#define HX509_CMS_VS_ALLOW_ZERO_SIGNER 0x04
+#define HX509_CMS_VS_NO_VALIDATE 0x08
+
+/* flags from hx509_cms_verify_signed_ext (out verify_flags) */
+#define HX509_CMS_VSE_VALIDATED 0x01
+
+/* selectors passed to hx509_crypto_select and hx509_crypto_available */
+#define HX509_SELECT_ALL 0
+#define HX509_SELECT_DIGEST 1
+#define HX509_SELECT_PUBLIC_SIG 2
+#define HX509_SELECT_PUBLIC_ENC 3
+#define HX509_SELECT_SECRET_ENC 4
+
+/* flags to hx509_ca_tbs_set_template */
+#define HX509_CA_TEMPLATE_SUBJECT 1
+#define HX509_CA_TEMPLATE_SERIAL 2
+#define HX509_CA_TEMPLATE_NOTBEFORE 4
+#define HX509_CA_TEMPLATE_NOTAFTER 8
+#define HX509_CA_TEMPLATE_SPKI 16
+#define HX509_CA_TEMPLATE_KU 32
+#define HX509_CA_TEMPLATE_EKU 64
+#define HX509_CA_TEMPLATE_PKINIT_MAX_LIFE 128
+
+/* flags hx509_cms_create_signed* */
+#define HX509_CMS_SIGNATURE_DETACHED 0x01
+#define HX509_CMS_SIGNATURE_ID_NAME 0x02
+#define HX509_CMS_SIGNATURE_NO_SIGNER 0x04
+#define HX509_CMS_SIGNATURE_LEAF_ONLY 0x08
+#define HX509_CMS_SIGNATURE_NO_CERTS 0x10
+
+/* hx509_verify_hostname nametype */
+typedef enum {
+ HX509_HN_HOSTNAME = 0,
+ HX509_HN_DNSSRV
+} hx509_hostname_type;
+
+#include <hx509-protos.h>
+#include <hx509_err.h>
+
+#endif /* HEIMDAL_HX509_H */
diff --git a/third_party/heimdal/lib/hx509/hx509_err.et b/third_party/heimdal/lib/hx509/hx509_err.et
new file mode 100644
index 0000000..db81f5d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/hx509_err.et
@@ -0,0 +1,110 @@
+#
+# Error messages for the hx509 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table hx
+prefix HX509
+
+# path validation and construction related errors
+error_code BAD_TIMEFORMAT, "ASN.1 failed call to system time library"
+error_code EXTENSION_NOT_FOUND, "Extension not found"
+error_code NO_PATH, "Certification path not found"
+error_code PARENT_NOT_CA, "Parent certificate is not a CA"
+error_code CA_PATH_TOO_DEEP, "CA path too deep"
+error_code SIG_ALG_NO_SUPPORTED, "Signature algorithm not supported"
+error_code SIG_ALG_DONT_MATCH_KEY_ALG, "Signature algorithm doesn't match certificate key"
+error_code CERT_USED_BEFORE_TIME, "Certificate used before it became valid"
+error_code CERT_USED_AFTER_TIME, "Certificate used after it became invalid"
+error_code PRIVATE_KEY_MISSING, "Private key required for the operation is missing"
+error_code ALG_NOT_SUPP, "Algorithm not supported"
+error_code ISSUER_NOT_FOUND, "Issuer couldn't be found"
+error_code VERIFY_CONSTRAINTS, "Error verifying constraints"
+error_code RANGE, "Number too large"
+error_code NAME_CONSTRAINT_ERROR, "Error while verifying name constraints"
+error_code PATH_TOO_LONG, "Path is too long, failed to find valid anchor"
+error_code KU_CERT_MISSING, "Required keyusage for this certificate is missing"
+error_code CERT_NOT_FOUND, "Certificate not found"
+error_code UNKNOWN_LOCK_COMMAND, "Unknown lock command"
+error_code PARENT_IS_CA, "Parent certificate is a CA"
+error_code EXTRA_DATA_AFTER_STRUCTURE, "Extra data was found after the structure"
+error_code PROXY_CERT_INVALID, "Proxy certificate is invalid"
+error_code PROXY_CERT_NAME_WRONG, "Proxy certificate name is wrong"
+error_code NAME_MALFORMED, "Name is malformed"
+error_code CERTIFICATE_MALFORMED, "Certificate is malformed"
+error_code CERTIFICATE_MISSING_EKU, "Certificate is missing a required EKU"
+error_code PROXY_CERTIFICATE_NOT_CANONICALIZED, "Proxy certificate not canonicalized"
+error_code NO_ITEM, "No such item / iteration end"
+
+# cms related errors
+index 32
+prefix HX509_CMS
+error_code FAILED_CREATE_SIGATURE, "Failed to create signature"
+error_code MISSING_SIGNER_DATA, "Missing signer data"
+error_code SIGNER_NOT_FOUND, "Couldn't find signers certificate"
+error_code NO_DATA_AVAILABLE, "No data to perform the operation on"
+error_code INVALID_DATA, "Data in the message is invalid"
+error_code PADDING_ERROR, "Padding in the message invalid"
+error_code NO_RECIPIENT_CERTIFICATE, "Couldn't find recipient certificate"
+error_code DATA_OID_MISMATCH, "Mismatch bewteen signed type and unsigned type"
+
+# crypto related errors
+index 64
+prefix HX509_CRYPTO
+error_code INTERNAL_ERROR, "Internal error in the crypto engine"
+error_code EXTERNAL_ERROR, "External error in the crypto engine"
+error_code SIGNATURE_MISSING, "Signature missing for data"
+error_code BAD_SIGNATURE, "Signature is not valid"
+error_code SIG_NO_CONF, "Sigature doesn't provide confidentiality"
+error_code SIG_INVALID_FORMAT, "Invalid format on signature"
+error_code OID_MISMATCH, "Mismatch between oids"
+error_code NO_PROMPTER, "No prompter function defined"
+error_code SIGNATURE_WITHOUT_SIGNER, "Signature requires signer, but none available"
+error_code RSA_PUBLIC_ENCRYPT, "RSA public encyption failed"
+error_code RSA_PRIVATE_ENCRYPT, "RSA private encyption failed"
+error_code RSA_PUBLIC_DECRYPT, "RSA public decryption failed"
+error_code RSA_PRIVATE_DECRYPT, "RSA private decryption failed"
+error_code ALGORITHM_BEST_BEFORE, "Algorithm has passed its best before date"
+error_code KEY_FORMAT_UNSUPPORTED, "Key format is unsupported"
+
+# revoke related errors
+index 96
+prefix HX509
+error_code CRL_USED_BEFORE_TIME, "CRL used before it became valid"
+error_code CRL_USED_AFTER_TIME, "CRL used after it became invalid"
+error_code CRL_INVALID_FORMAT, "CRL have invalid format"
+error_code CERT_REVOKED, "Certificate is revoked"
+error_code REVOKE_STATUS_MISSING, "No revoke status found for certificates"
+error_code CRL_UNKNOWN_EXTENSION, "Unknown extension"
+error_code REVOKE_WRONG_DATA, "Got wrong CRL/OCSP data from server"
+error_code REVOKE_NOT_SAME_PARENT, "Doesn't have same parent as other certificates"
+error_code CERT_NOT_IN_OCSP, "Certificates not in OCSP reply"
+
+# misc error
+index 108
+error_code LOCAL_ATTRIBUTE_MISSING, "No local key attribute"
+error_code PARSING_KEY_FAILED, "Failed to parse key"
+error_code UNSUPPORTED_OPERATION, "Unsupported operation"
+error_code UNIMPLEMENTED_OPERATION, "Unimplemented operation"
+error_code PARSING_NAME_FAILED, "Failed to parse name"
+
+# keystore related error
+index 128
+prefix HX509_PKCS11
+error_code NO_SLOT, "No smartcard reader/device found"
+error_code NO_TOKEN, "No smartcard in reader"
+error_code NO_MECH, "No supported mech(s)"
+error_code TOKEN_CONFUSED, "Token or slot failed in inconsistent way"
+error_code OPEN_SESSION, "Failed to open session to slot"
+error_code LOGIN, "Failed to login to slot"
+error_code LOAD, "Failed to load PKCS module"
+
+# pkinit related errors
+error_code PIN_INCORRECT, "Incorrect User PIN"
+error_code PIN_LOCKED, "User PIN locked"
+error_code PIN_NOT_INITIALIZED, "User PIN not initialized"
+error_code PIN_EXPIRED, "User PIN expired"
+
+end
diff --git a/third_party/heimdal/lib/hx509/hx_locl.h b/third_party/heimdal/lib/hx509/hx_locl.h
new file mode 100644
index 0000000..d653f7d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/hx_locl.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2004 - 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <assert.h>
+#include <stdarg.h>
+#include <err.h>
+#include <limits.h>
+
+#include <roken.h>
+
+#include <getarg.h>
+#include <base64.h>
+#include <hex.h>
+#include <com_err.h>
+#include <parse_units.h>
+#include <parse_bytes.h>
+
+#include <krb5-types.h>
+
+#include <rfc2459_asn1.h>
+#include <rfc4108_asn1.h>
+#include <cms_asn1.h>
+#include <pkcs8_asn1.h>
+#include <pkcs9_asn1.h>
+#include <pkcs12_asn1.h>
+#include <ocsp_asn1.h>
+#include <pkcs10_asn1.h>
+#include <asn1_err.h>
+#include <pkinit_asn1.h>
+
+#include <der.h>
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/*
+ * We use OpenSSL for EC, but to do this we need to disable cross-references
+ * between OpenSSL and hcrypto bn.h and such. Source files that use OpenSSL EC
+ * must define HEIM_NO_CRYPTO_HDRS before including this file.
+ */
+
+#define HC_DEPRECATED_CRYPTO
+#ifndef HEIM_NO_CRYPTO_HDRS
+#include "crypto-headers.h"
+#endif
+
+struct hx509_keyset_ops;
+struct hx509_collector;
+struct hx509_generate_private_context;
+typedef struct hx509_path hx509_path;
+
+#include <heimbase.h>
+
+#include <hx509.h>
+
+typedef void (*_hx509_cert_release_func)(struct hx509_cert_data *, void *);
+
+
+#include "sel.h"
+
+#include <hx509-private.h>
+#include <hx509_err.h>
+
+struct hx509_peer_info {
+ hx509_cert cert;
+ AlgorithmIdentifier *val;
+ size_t len;
+};
+
+#define HX509_CERTS_FIND_SERIALNUMBER 1
+#define HX509_CERTS_FIND_ISSUER 2
+#define HX509_CERTS_FIND_SUBJECT 4
+#define HX509_CERTS_FIND_ISSUER_KEY_ID 8
+#define HX509_CERTS_FIND_SUBJECT_KEY_ID 16
+
+struct hx509_name_data {
+ Name der_name;
+};
+
+struct hx509_path {
+ size_t len;
+ hx509_cert *val;
+};
+
+struct hx509_query_data {
+ int match;
+#define HX509_QUERY_FIND_ISSUER_CERT 0x000001
+#define HX509_QUERY_MATCH_SERIALNUMBER 0x000002
+#define HX509_QUERY_MATCH_ISSUER_NAME 0x000004
+#define HX509_QUERY_MATCH_SUBJECT_NAME 0x000008
+#define HX509_QUERY_MATCH_SUBJECT_KEY_ID 0x000010
+#define HX509_QUERY_MATCH_ISSUER_ID 0x000020
+#define HX509_QUERY_PRIVATE_KEY 0x000040
+#define HX509_QUERY_KU_ENCIPHERMENT 0x000080
+#define HX509_QUERY_KU_DIGITALSIGNATURE 0x000100
+#define HX509_QUERY_KU_KEYCERTSIGN 0x000200
+#define HX509_QUERY_KU_CRLSIGN 0x000400
+#define HX509_QUERY_KU_NONREPUDIATION 0x000800
+#define HX509_QUERY_KU_KEYAGREEMENT 0x001000
+#define HX509_QUERY_KU_DATAENCIPHERMENT 0x002000
+#define HX509_QUERY_ANCHOR 0x004000
+#define HX509_QUERY_MATCH_CERTIFICATE 0x008000
+#define HX509_QUERY_MATCH_LOCAL_KEY_ID 0x010000
+#define HX509_QUERY_NO_MATCH_PATH 0x020000
+#define HX509_QUERY_MATCH_FRIENDLY_NAME 0x040000
+#define HX509_QUERY_MATCH_FUNCTION 0x080000
+#define HX509_QUERY_MATCH_KEY_HASH_SHA1 0x100000
+#define HX509_QUERY_MATCH_TIME 0x200000
+#define HX509_QUERY_MATCH_EKU 0x400000
+#define HX509_QUERY_MATCH_EXPR 0x800000
+#define HX509_QUERY_MASK 0xffffff
+ Certificate *subject;
+ Certificate *certificate;
+ heim_integer *serial;
+ heim_octet_string *subject_id;
+ heim_octet_string *local_key_id;
+ Name *issuer_name;
+ Name *subject_name;
+ hx509_path *path;
+ char *friendlyname;
+ int (*cmp_func)(hx509_context, hx509_cert, void *);
+ void *cmp_func_ctx;
+ heim_octet_string *keyhash_sha1;
+ time_t timenow;
+ heim_oid *eku;
+ struct hx_expr *expr;
+};
+
+struct hx509_keyset_ops {
+ const char *name;
+ int flags;
+ int (*init)(hx509_context, hx509_certs, void **,
+ int, const char *, hx509_lock);
+ int (*store)(hx509_context, hx509_certs, void *, int, hx509_lock);
+ int (*free)(hx509_certs, void *);
+ int (*add)(hx509_context, hx509_certs, void *, hx509_cert);
+ int (*query)(hx509_context, hx509_certs, void *,
+ const hx509_query *, hx509_cert *);
+ int (*iter_start)(hx509_context, hx509_certs, void *, void **);
+ int (*iter)(hx509_context, hx509_certs, void *, void *, hx509_cert *);
+ int (*iter_end)(hx509_context, hx509_certs, void *, void *);
+ int (*printinfo)(hx509_context, hx509_certs,
+ void *, int (*)(void *, const char *), void *);
+ int (*getkeys)(hx509_context, hx509_certs, void *, hx509_private_key **);
+ int (*addkey)(hx509_context, hx509_certs, void *, hx509_private_key);
+ int (*destroy)(hx509_context, hx509_certs, void *);
+};
+
+struct _hx509_password {
+ size_t len;
+ char **val;
+};
+
+extern hx509_lock _hx509_empty_lock;
+
+struct hx509_context_data {
+ struct hx509_keyset_ops **ks_ops;
+ int ks_num_ops;
+ int flags;
+#define HX509_CTX_VERIFY_MISSING_OK 1
+ int ocsp_time_diff;
+#define HX509_DEFAULT_OCSP_TIME_DIFF (5*60)
+ heim_error_t error;
+ struct et_list *et_list;
+ char *querystat;
+ hx509_certs default_trust_anchors;
+ heim_context hcontext;
+ heim_config_section *cf;
+};
+
+/* _hx509_calculate_path flag field */
+#define HX509_CALCULATE_PATH_NO_ANCHOR 1
+
+/* environment */
+struct hx509_env_data {
+ enum { env_string, env_list } type;
+ char *name;
+ struct hx509_env_data *next;
+ union {
+ char *string;
+ struct hx509_env_data *list;
+ } u;
+};
+
+
+extern const AlgorithmIdentifier * _hx509_crypto_default_sig_alg;
+extern const AlgorithmIdentifier * _hx509_crypto_default_digest_alg;
+extern const AlgorithmIdentifier * _hx509_crypto_default_secret_alg;
+
+/*
+ * Private bits from crypto.c, so crypto-ec.c can also see them.
+ *
+ * This is part of the use-OpenSSL-for-EC hack.
+ */
+
+struct hx509_crypto;
+
+struct signature_alg;
+
+struct hx509_generate_private_context {
+ const heim_oid *key_oid;
+ int isCA;
+ unsigned long num_bits;
+};
+
+struct hx509_private_key_ops {
+ const char *pemtype;
+ const heim_oid *key_oid;
+ int (*available)(const hx509_private_key,
+ const AlgorithmIdentifier *);
+ int (*get_spki)(hx509_context,
+ const hx509_private_key,
+ SubjectPublicKeyInfo *);
+ int (*export)(hx509_context context,
+ const hx509_private_key,
+ hx509_key_format_t,
+ heim_octet_string *);
+ int (*import)(hx509_context, const AlgorithmIdentifier *,
+ const void *, size_t, hx509_key_format_t,
+ hx509_private_key);
+ int (*generate_private_key)(hx509_context,
+ struct hx509_generate_private_context *,
+ hx509_private_key);
+ BIGNUM *(*get_internal)(hx509_context, hx509_private_key, const char *);
+};
+
+struct hx509_private_key {
+ unsigned int ref;
+ const struct signature_alg *md;
+ const heim_oid *signature_alg;
+ union {
+ RSA *rsa;
+ void *keydata;
+ void *ecdsa; /* EC_KEY */
+ } private_key;
+ hx509_private_key_ops *ops;
+};
+
+/*
+ *
+ */
+
+struct signature_alg {
+ const char *name;
+ const heim_oid *sig_oid;
+ const AlgorithmIdentifier *sig_alg;
+ const heim_oid *key_oid;
+ const AlgorithmIdentifier *digest_alg;
+ int flags;
+#define PROVIDE_CONF 0x1
+#define REQUIRE_SIGNER 0x2
+#define SELF_SIGNED_OK 0x4
+#define WEAK_SIG_ALG 0x8
+
+#define SIG_DIGEST 0x100
+#define SIG_PUBLIC_SIG 0x200
+#define SIG_SECRET 0x400
+
+#define RA_RSA_USES_DIGEST_INFO 0x1000000
+
+ time_t best_before; /* refuse signature made after best before date */
+ const EVP_MD *(*evp_md)(void);
+ int (*verify_signature)(hx509_context context,
+ const struct signature_alg *,
+ const Certificate *,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ const heim_octet_string *);
+ int (*create_signature)(hx509_context,
+ const struct signature_alg *,
+ const hx509_private_key,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ AlgorithmIdentifier *,
+ heim_octet_string *);
+ int digest_size;
+};
+
+/*
+ * Configurable options
+ */
+
+#ifdef __APPLE__
+#define HX509_DEFAULT_ANCHORS "KEYCHAIN:system-anchors"
+#endif
diff --git a/third_party/heimdal/lib/hx509/hxtool-commands.in b/third_party/heimdal/lib/hx509/hxtool-commands.in
new file mode 100644
index 0000000..279095d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/hxtool-commands.in
@@ -0,0 +1,1057 @@
+/*
+ * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* $Id$ */
+
+command = {
+ name = "list-oids"
+ help = "List known OIDs"
+ function = "hxtool_list_oids"
+ min_args="0"
+ max_args="0"
+}
+command = {
+ name = "cms-create-sd"
+ name = "cms-sign"
+ option = {
+ long = "certificate"
+ short = "c"
+ type = "strings"
+ argument = "certificate-store"
+ help = "certificate stores to pull certificates from"
+ }
+ option = {
+ long = "signer"
+ short = "s"
+ type = "string"
+ argument = "signer-friendly-name"
+ help = "certificate to sign with"
+ }
+ option = {
+ long = "anchors"
+ type = "strings"
+ argument = "certificate-store"
+ help = "trust anchors"
+ }
+ option = {
+ long = "pool"
+ type = "strings"
+ argument = "certificate-pool"
+ help = "certificate store to pull certificates from"
+ }
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "peer-alg"
+ type = "strings"
+ argument = "oid"
+ help = "oid that the peer support"
+ }
+ option = {
+ long = "content-type"
+ type = "string"
+ argument = "oid"
+ help = "content type oid"
+ }
+ option = {
+ long = "content-info"
+ type = "flag"
+ help = "wrapped out-data in a ContentInfo"
+ }
+ option = {
+ long = "pem"
+ type = "flag"
+ help = "wrap out-data in PEM armor"
+ }
+ option = {
+ long = "detached-signature"
+ type = "flag"
+ help = "create a detached signature"
+ }
+ option = {
+ long = "signer"
+ type = "-flag"
+ help = "do not sign"
+ }
+ option = {
+ long = "id-by-name"
+ type = "flag"
+ help = "use subject name for CMS Identifier"
+ }
+ option = {
+ long = "embedded-certs"
+ type = "-flag"
+ help = "don't embed certificates"
+ }
+ option = {
+ long = "embed-leaf-only"
+ type = "flag"
+ help = "only embed leaf certificate"
+ }
+ min_args="1"
+ max_args="2"
+ argument="in-file out-file"
+ help = "Wrap a file within a SignedData object"
+}
+command = {
+ name = "cms-verify-sd"
+ option = {
+ long = "anchors"
+ short = "D"
+ type = "strings"
+ argument = "certificate-store"
+ help = "trust anchors"
+ }
+ option = {
+ long = "certificate"
+ short = "c"
+ type = "strings"
+ argument = "certificate-store"
+ help = "certificate store to pull certificates from"
+ }
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "missing-revoke"
+ type = "flag"
+ help = "missing CRL/OCSP is ok"
+ }
+ option = {
+ long = "content-info"
+ type = "flag"
+ help = "unwrap in-data that's in a ContentInfo"
+ }
+ option = {
+ long = "pem"
+ type = "flag"
+ help = "unwrap in-data from PEM armor"
+ }
+ option = {
+ long = "signer-allowed"
+ type = "-flag"
+ help = "allow no signer"
+ }
+ option = {
+ long = "allow-wrong-oid"
+ type = "flag"
+ help = "allow wrong oid flag"
+ }
+ option = {
+ long = "signed-content"
+ type = "string"
+ help = "file containing content"
+ }
+ option = {
+ long = "oid-sym"
+ type = "flag"
+ help = "show symbolic name for OID"
+ }
+ min_args="1"
+ max_args="2"
+ argument="in-file [out-file]"
+ help = "Verify a file within a SignedData object"
+}
+command = {
+ name = "cms-unenvelope"
+ option = {
+ long = "certificate"
+ short = "c"
+ type = "strings"
+ argument = "certificate-store"
+ help = "certificate used to decrypt the data"
+ }
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "content-info"
+ type = "flag"
+ help = "wrapped out-data in a ContentInfo"
+ }
+ option = {
+ long = "allow-weak-crypto"
+ type = "flag"
+ help = "allow weak crypto"
+ }
+ min_args="2"
+ argument="in-file out-file"
+ help = "Unenvelope a file containing a EnvelopedData object"
+}
+command = {
+ name = "cms-envelope"
+ function = "cms_create_enveloped"
+ option = {
+ long = "certificate"
+ short = "c"
+ type = "strings"
+ argument = "certificate-store"
+ help = "certificates used to receive the data"
+ }
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "encryption-type"
+ type = "string"
+ argument = "enctype"
+ help = "enctype"
+ }
+ option = {
+ long = "content-type"
+ type = "string"
+ argument = "oid"
+ help = "content type oid"
+ }
+ option = {
+ long = "content-info"
+ type = "flag"
+ help = "wrapped out-data in a ContentInfo"
+ }
+ option = {
+ long = "allow-weak-crypto"
+ type = "flag"
+ help = "allow weak crypto"
+ }
+ min_args="2"
+ argument="in-file out-file"
+ help = "Envelope a file containing a EnvelopedData object"
+}
+command = {
+ name = "verify"
+ function = "pcert_verify"
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "allow-proxy-certificate"
+ type = "flag"
+ help = "allow proxy certificates"
+ }
+ option = {
+ long = "missing-revoke"
+ type = "flag"
+ help = "missing CRL/OCSP is ok"
+ }
+ option = {
+ long = "time"
+ type = "string"
+ help = "time when to validate the chain"
+ }
+ option = {
+ long = "verbose"
+ short = "v"
+ type = "flag"
+ help = "verbose logging"
+ }
+ option = {
+ long = "max-depth"
+ type = "integer"
+ help = "maximum search length of certificate trust anchor"
+ }
+ option = {
+ long = "hostname"
+ type = "string"
+ help = "match hostname to certificate"
+ }
+ argument = "cert:foo chain:cert1 chain:cert2 anchor:anchor1 anchor:anchor2"
+ help = "Verify certificate chain"
+}
+command = {
+ name = "print"
+ function = "pcert_print"
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "content"
+ type = "flag"
+ help = "print the content of the certificates"
+ }
+ option = {
+ long = "raw-json"
+ type = "flag"
+ help = "print the DER content of the certificates as JSON"
+ }
+ option = {
+ long = "never-fail"
+ type = "flag"
+ help = "never fail with an error code"
+ }
+ option = {
+ long = "info"
+ type = "flag"
+ help = "print the information about the certificate store"
+ }
+ min_args="1"
+ argument="certificate ..."
+ help = "Print certificates"
+}
+command = {
+ name = "validate"
+ function = "pcert_validate"
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ min_args="1"
+ argument="certificate ..."
+ help = "Validate content of certificates"
+}
+command = {
+ name = "certificate-copy"
+ name = "cc"
+ option = {
+ long = "in-pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "out-pass"
+ type = "string"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ min_args="2"
+ argument="in-certificates-1 ... out-certificate"
+ help = "Copy in certificates stores into out certificate store"
+}
+command = {
+ name = "ocsp-fetch"
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "sign"
+ type = "string"
+ argument = "certificate"
+ help = "certificate use to sign the request"
+ }
+ option = {
+ long = "url-path"
+ type = "string"
+ argument = "url"
+ help = "part after host in url to put in the request"
+ }
+ option = {
+ long = "nonce"
+ type = "-flag"
+ default = "1"
+ help = "don't include nonce in request"
+ }
+ option = {
+ long = "pool"
+ type = "strings"
+ argument = "certificate-store"
+ help = "pool to find parent certificate in"
+ }
+ min_args="2"
+ argument="outfile certs ..."
+ help = "Fetch OCSP responses for the following certs"
+}
+command = {
+ option = {
+ long = "ocsp-file"
+ type = "string"
+ help = "OCSP file"
+ }
+ name = "ocsp-verify"
+ min_args="1"
+ argument="certificates ..."
+ help = "Check that certificates are in OCSP file and valid"
+}
+command = {
+ name = "ocsp-print"
+ option = {
+ long = "verbose"
+ type = "flag"
+ help = "verbose"
+ }
+ min_args="1"
+ argument="ocsp-response-file ..."
+ help = "Print the OCSP responses"
+}
+command = {
+ name = "revoke-print"
+ option = {
+ long = "verbose"
+ type = "flag"
+ help = "verbose"
+ }
+ min_args="1"
+ argument="ocsp/crl files"
+ help = "Print the OCSP/CRL files"
+}
+command = {
+ name = "generate-key"
+ option = {
+ long = "type"
+ type = "string"
+ help = "keytype"
+ }
+ option = {
+ long = "key-bits"
+ type = "integer"
+ help = "number of bits in the generated key";
+ }
+ option = {
+ long = "verbose"
+ type = "flag"
+ help = "verbose status"
+ }
+ min_args="1"
+ max_args="1"
+ argument="output-file"
+ help = "Generate a private key"
+}
+command = {
+ name = "request-create"
+ option = {
+ long = "subject"
+ type = "string"
+ help = "Subject DN"
+ }
+ option = {
+ long = "eku"
+ type = "strings"
+ argument = "oid-string"
+ help = "Add Extended Key Usage OID"
+ }
+ option = {
+ long = "email"
+ type = "strings"
+ help = "Email address in SubjectAltName"
+ }
+ option = {
+ long = "jid"
+ type = "strings"
+ help = "XMPP (Jabber) address in SubjectAltName"
+ }
+ option = {
+ long = "dnsname"
+ type = "strings"
+ help = "Hostname or domainname in SubjectAltName"
+ }
+ option = {
+ long = "kerberos"
+ type = "strings"
+ help = "Kerberos principal name as SubjectAltName"
+ }
+ option = {
+ long = "ms-kerberos"
+ type = "strings"
+ help = "Kerberos principal name as SubjectAltName (Microsoft variant)"
+ }
+ option = {
+ long = "registered"
+ type = "strings"
+ help = "Registered object ID as SubjectAltName"
+ }
+ option = {
+ long = "dn"
+ type = "strings"
+ help = "Directory name as SubjectAltName"
+ }
+ option = {
+ long = "type"
+ type = "string"
+ help = "Type of request CRMF or PKCS10, defaults to PKCS10"
+ }
+ option = {
+ long = "key"
+ type = "string"
+ help = "Key-pair"
+ }
+ option = {
+ long = "generate-key"
+ type = "string"
+ help = "keytype"
+ }
+ option = {
+ long = "key-bits"
+ type = "integer"
+ help = "number of bits in the generated key";
+ }
+ option = {
+ long = "verbose"
+ type = "flag"
+ help = "verbose status"
+ }
+ min_args="1"
+ max_args="1"
+ argument="output-file"
+ help = "Create a CRMF or PKCS10 request"
+}
+command = {
+ name = "request-print"
+ option = {
+ long = "verbose"
+ type = "flag"
+ help = "verbose printing"
+ }
+ min_args="1"
+ argument="requests ..."
+ help = "Print requests"
+}
+command = {
+ name = "query"
+ option = {
+ long = "exact"
+ type = "flag"
+ help = "exact match"
+ }
+ option = {
+ long = "private-key"
+ type = "flag"
+ help = "search for private key"
+ }
+ option = {
+ long = "friendlyname"
+ type = "string"
+ argument = "name"
+ help = "match on friendly name"
+ }
+ option = {
+ long = "eku"
+ type = "string"
+ argument = "oid-string"
+ help = "match on EKU"
+ }
+ option = {
+ long = "expr"
+ type = "string"
+ argument = "expression"
+ help = "match on expression"
+ }
+ option = {
+ long = "keyEncipherment"
+ type = "flag"
+ help = "match keyEncipherment certificates"
+ }
+ option = {
+ long = "digitalSignature"
+ type = "flag"
+ help = "match digitalSignature certificates"
+ }
+ option = {
+ long = "print"
+ type = "flag"
+ help = "print matches"
+ }
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ min_args="1"
+ argument="certificates ..."
+ help = "Query the certificates for a match"
+}
+command = {
+ name = "info"
+}
+command = {
+ name = "random-data"
+ min_args="1"
+ argument="bytes"
+ help = "Generates random bytes and prints them to standard output"
+}
+command = {
+ option = {
+ long = "type"
+ type = "string"
+ help = "type of CMS algorithm"
+ }
+ option = {
+ long = "oid-syms"
+ type = "flag"
+ help = "show symbolic names for OIDs"
+ }
+ name = "crypto-available"
+ min_args="0"
+ help = "Print available CMS crypto types"
+}
+command = {
+ option = {
+ long = "type"
+ type = "string"
+ help = "type of CMS algorithm"
+ }
+ option = {
+ long = "certificate"
+ type = "string"
+ help = "source certificate limiting the choices"
+ }
+ option = {
+ long = "peer-cmstype"
+ type = "strings"
+ help = "peer limiting cmstypes"
+ }
+ option = {
+ long = "oid-sym"
+ type = "flag"
+ help = "show symbolic name for OID"
+ }
+ name = "crypto-select"
+ min_args="0"
+ help = "Print selected CMS type"
+}
+command = {
+ option = {
+ long = "decode"
+ short = "d"
+ type = "flag"
+ help = "decode instead of encode"
+ }
+ name = "hex"
+ function = "hxtool_hex"
+ min_args="0"
+ help = "Encode input to hex"
+}
+command = {
+ option = {
+ long = "issue-ca"
+ type = "flag"
+ help = "Issue a CA certificate"
+ }
+ option = {
+ long = "issue-proxy"
+ type = "flag"
+ help = "Issue a proxy certificate"
+ }
+ option = {
+ long = "domain-controller"
+ type = "flag"
+ help = "Issue a MS domaincontroller certificate"
+ }
+ option = {
+ long = "subject"
+ type = "string"
+ help = "Subject of issued certificate"
+ }
+ option = {
+ long = "ca-certificate"
+ type = "string"
+ help = "Issuing CA certificate"
+ }
+ option = {
+ long = "self-signed"
+ type = "flag"
+ help = "Issuing a self-signed certificate"
+ }
+ option = {
+ long = "ca-private-key"
+ type = "string"
+ help = "Private key for self-signed certificate"
+ }
+ option = {
+ long = "certificate"
+ type = "string"
+ help = "Issued certificate"
+ }
+ option = {
+ long = "type"
+ type = "strings"
+ help = "Types of certificate to issue (can be used more then once)"
+ }
+ option = {
+ long = "lifetime"
+ type = "string"
+ help = "Lifetime of certificate"
+ }
+ option = {
+ long = "signature-algorithm"
+ type = "string"
+ help = "Signature algorithm to use"
+ }
+ option = {
+ long = "serial-number"
+ type = "string"
+ help = "serial-number of certificate"
+ }
+ option = {
+ long = "path-length"
+ default = "-1"
+ type = "integer"
+ help = "Maximum path length (CA and proxy certificates), -1 no limit"
+ }
+ option = {
+ long = "eku"
+ type = "strings"
+ argument = "oid-string"
+ help = "Add Extended Key Usage OID"
+ }
+ option = {
+ long = "ku"
+ type = "strings"
+ help = "Key Usage (digitalSignature, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly)"
+ }
+ option = {
+ long = "hostname"
+ type = "strings"
+ help = "DNS names this certificate is allowed to serve"
+ }
+ option = {
+ long = "dnssrv"
+ type = "strings"
+ help = "DNS SRV names this certificate is allowed to serve"
+ }
+ option = {
+ long = "email"
+ type = "strings"
+ help = "email addresses assigned to this certificate"
+ }
+ option = {
+ long = "pk-init-principal"
+ type = "strings"
+ help = "PK-INIT principal (for SAN)"
+ }
+ option = {
+ long = "ms-upn"
+ type = "string"
+ help = "Microsoft UPN (for SAN)"
+ }
+ option = {
+ long = "jid"
+ type = "string"
+ help = "XMPP jabber id (for SAN)"
+ }
+ option = {
+ long = "permanent-id"
+ type = "string"
+ help = "PermanentIdentifier ([oid]:[serial])"
+ }
+ option = {
+ long = "hardware-module-name"
+ type = "string"
+ help = "HardwareModuleName (oid:serial)"
+ }
+ option = {
+ long = "policy"
+ type = "strings"
+ help = "Certificate Policy OID and optional URI and/or notice (OID:URI<space>notice_text)"
+ }
+ option = {
+ long = "policy-mapping"
+ type = "strings"
+ help = "Certificate Policy mapping (OID:OID)"
+ }
+ option = {
+ long = "pkinit-max-life"
+ type = "string"
+ help = "maximum Kerberos ticket lifetime extension for PKINIT"
+ }
+ option = {
+ long = "req"
+ type = "string"
+ help = "certificate request"
+ }
+ option = {
+ long = "certificate-private-key"
+ type = "string"
+ help = "private-key"
+ }
+ option = {
+ long = "generate-key"
+ type = "string"
+ help = "keytype"
+ }
+ option = {
+ long = "key-bits"
+ type = "integer"
+ help = "number of bits in the generated key"
+ }
+ option = {
+ long = "crl-uri"
+ type = "string"
+ help = "URI to CRL"
+ }
+ option = {
+ long = "template-certificate"
+ type = "string"
+ help = "certificate"
+ }
+ option = {
+ long = "template-fields"
+ type = "string"
+ help = "flag"
+ }
+ name = "certificate-sign"
+ name = "cert-sign"
+ name = "issue-certificate"
+ name = "ca"
+ function = "hxtool_ca"
+ min_args="0"
+ help = "Issue a certificate"
+}
+command = {
+ name = "test-crypto"
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "verbose"
+ type = "flag"
+ help = "verbose printing"
+ }
+ min_args="1"
+ argument="certificates..."
+ help = "Test crypto system related to the certificates"
+}
+command = {
+ option = {
+ long = "type"
+ type = "integer"
+ help = "type of statistics"
+ }
+ name = "statistic-print"
+ min_args="0"
+ help = "Print statistics"
+}
+command = {
+ option = {
+ long = "signer"
+ type = "string"
+ help = "signer certificate"
+ }
+ option = {
+ long = "pass"
+ type = "strings"
+ argument = "password"
+ help = "password, prompter, or environment"
+ }
+ option = {
+ long = "crl-file"
+ type = "string"
+ help = "CRL output file"
+ }
+ option = {
+ long = "lifetime"
+ type = "string"
+ help = "time the crl will be valid"
+ }
+ name = "crl-sign"
+ min_args="0"
+ argument="certificates..."
+ help = "Create a CRL"
+}
+command = {
+ option = {
+ long = "verbose"
+ short = "v"
+ type = "flag"
+ help = "verbose"
+ }
+ option = {
+ long = "end-entity"
+ type = "flag"
+ help = "check the first EE certificate in the store"
+ }
+ option = {
+ long = "ca"
+ type = "flag"
+ help = "check the first CA certificate in the store"
+ }
+ option = {
+ long = "cert-num"
+ type = "integer"
+ default = "-1"
+ help = "check the nth certificate in the store"
+ }
+ option = {
+ long = "expr"
+ type = "string"
+ argument = "expression"
+ help = "test the first certificate matching expression"
+ }
+ option = {
+ long = "has-email-san"
+ short = "M"
+ type = "strings"
+ argument = "email-address"
+ help = "check that cert has email SAN"
+ }
+ option = {
+ long = "has-xmpp-san"
+ type = "strings"
+ short = "X"
+ argument = "jabber address"
+ help = "check that cert has XMPP SAN"
+ }
+ option = {
+ long = "has-ms-upn-san"
+ short = "U"
+ type = "strings"
+ argument = "UPN"
+ help = "check that cert has UPN SAN"
+ }
+ option = {
+ long = "has-dnsname-san"
+ short = "D"
+ type = "strings"
+ argument = "domainname"
+ help = "check that cert has domainname SAN"
+ }
+ option = {
+ long = "has-pkinit-san"
+ short = "P"
+ type = "strings"
+ argument = "Kerberos principal name"
+ help = "check that cert has PKINIT SAN"
+ }
+ option = {
+ long = "has-registeredID-san"
+ short = "R"
+ type = "strings"
+ argument = "OID"
+ help = "check that cert has registeredID SAN"
+ }
+ option = {
+ long = "has-eku"
+ short = "E"
+ type = "strings"
+ argument = "OID"
+ help = "check that cert has EKU"
+ }
+ option = {
+ long = "has-ku"
+ short = "K"
+ type = "strings"
+ argument = "key usage element"
+ help = "check that cert has key usage"
+ }
+ option = {
+ long = "exact"
+ type = "flag"
+ help = "check that cert has only given SANs/EKUs/KUs"
+ }
+ option = {
+ long = "valid-now"
+ short = "n"
+ type = "flag"
+ help = "check that current time is in certicate's validity period"
+ }
+ option = {
+ long = "valid-at"
+ type = "string"
+ argument = "datetime"
+ help = "check that the certificate is valid at given time"
+ }
+ option = {
+ long = "not-after-eq"
+ type = "string"
+ argument = "datetime"
+ help = "check that the certificate's notAfter is as given"
+ }
+ option = {
+ long = "not-after-lt"
+ type = "string"
+ argument = "datetime"
+ help = "check that the certificate's notAfter is before the given time"
+ }
+ option = {
+ long = "not-after-gt"
+ type = "string"
+ argument = "datetime"
+ help = "check that the certificate's notAfter is after the given time"
+ }
+ option = {
+ long = "not-before-eq"
+ type = "string"
+ argument = "datetime"
+ help = "check that the certificate's notBefore is as given"
+ }
+ option = {
+ long = "not-before-lt"
+ type = "string"
+ argument = "datetime"
+ help = "check that the certificate's notBefore is before the given time"
+ }
+ option = {
+ long = "not-before-gt"
+ type = "string"
+ argument = "datetime"
+ help = "check that the certificate's notBefore is after the given time"
+ }
+ option = {
+ long = "has-private-key"
+ type = "flag"
+ help = "check that the certificate has a private key"
+ }
+ option = {
+ long = "lacks-private-key"
+ type = "flag"
+ help = "check that the certificate does not have a private key"
+ }
+ name = "acert"
+ min_args = "1"
+ max_args = "1"
+ argument = "certificate-store"
+ help = "Assert certificate content"
+}
+command = {
+ name = "help"
+ name = "?"
+ argument = "[command]"
+ min_args = "0"
+ max_args = "1"
+ help = "Help! I need somebody"
+}
diff --git a/third_party/heimdal/lib/hx509/hxtool-version.rc b/third_party/heimdal/lib/hx509/hxtool-version.rc
new file mode 100644
index 0000000..7e5197c
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/hxtool-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "Heimdal X.509 Certificate Tool"
+#define RC_FILE_ORIG_0409 "hxtool.exe"
+
+#include "../../windows/version.rc"
diff --git a/third_party/heimdal/lib/hx509/hxtool.c b/third_party/heimdal/lib/hx509/hxtool.c
new file mode 100644
index 0000000..1bcfdfa
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/hxtool.c
@@ -0,0 +1,3162 @@
+/*
+ * Copyright (c) 2004 - 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+#include <hxtool-commands.h>
+#include <sl.h>
+#include <rtbl.h>
+#include <parse_time.h>
+
+static hx509_context context;
+
+static char *stat_file_string;
+static int version_flag;
+static int help_flag;
+
+struct getargs args[] = {
+ { "statistic-file", 0, arg_string, &stat_file_string, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int code)
+{
+ arg_printusage(args, num_args, NULL, "command");
+ printf("Use \"%s help\" to get more help\n", getprogname());
+ exit(code);
+}
+
+/*
+ *
+ */
+
+static void
+lock_strings(hx509_lock lock, getarg_strings *pass)
+{
+ int i;
+ for (i = 0; i < pass->num_strings; i++) {
+ int ret = hx509_lock_command_string(lock, pass->strings[i]);
+ if (ret)
+ errx(1, "hx509_lock_command_string: %s: %d",
+ pass->strings[i], ret);
+ }
+}
+
+static char *
+fix_store_name(hx509_context contextp, const char *sn, const char *def_type)
+{
+ const char *residue = strchr(sn, ':');
+ char *s = NULL;
+
+ if (residue) {
+ s = estrdup(sn);
+ s[residue - sn] = '\0';
+ if (_hx509_ks_type(contextp, s)) {
+ free(s);
+ return estrdup(sn);
+ }
+ free(s);
+ s = NULL;
+ }
+ if (asprintf(&s, "%s:%s", def_type, sn) == -1 || s == NULL)
+ err(1, "Out of memory");
+ return s;
+}
+
+static char *
+fix_csr_name(const char *cn, const char *def_type)
+{
+ char *s = NULL;
+
+ if (strncmp(cn, "PKCS10:", sizeof("PKCS10:") - 1) == 0 || strchr(cn, ':'))
+ return estrdup(cn);
+ if (asprintf(&s, "%s:%s", def_type, cn) == -1 || s == NULL)
+ err(1, "Out of memory");
+ return s;
+}
+
+/*
+ *
+ */
+
+static void
+certs_strings(hx509_context contextp, const char *type, hx509_certs certs,
+ hx509_lock lock, const getarg_strings *s)
+{
+ int i, ret;
+
+ for (i = 0; i < s->num_strings; i++) {
+ char *sn = fix_store_name(contextp, s->strings[i], "FILE");
+
+ ret = hx509_certs_append(contextp, certs, lock, sn);
+ if (ret)
+ hx509_err(contextp, 1, ret,
+ "hx509_certs_append: %s %s", type, sn);
+ free(sn);
+ }
+}
+
+/*
+ *
+ */
+
+static void
+parse_oid(const char *str, const heim_oid *def, heim_oid *oid)
+{
+ int ret;
+
+ if (str) {
+ const heim_oid *found = NULL;
+
+ ret = der_find_heim_oid_by_name(str, &found);
+ if (ret == 0)
+ ret = der_copy_oid(found, oid);
+ else
+ ret = der_parse_heim_oid(str, " .", oid);
+ } else {
+ ret = der_copy_oid(def, oid);
+ }
+ if (ret)
+ errx(1, "parse_oid failed for: %s", str ? str : "default oid");
+}
+
+/*
+ *
+ */
+
+static void
+peer_strings(hx509_context contextp,
+ hx509_peer_info *peer,
+ const getarg_strings *s)
+{
+ AlgorithmIdentifier *val;
+ int ret, i;
+
+ ret = hx509_peer_info_alloc(contextp, peer);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_peer_info_alloc");
+
+ val = calloc(s->num_strings, sizeof(*val));
+ if (val == NULL)
+ err(1, "malloc");
+
+ for (i = 0; i < s->num_strings; i++)
+ parse_oid(s->strings[i], NULL, &val[i].algorithm);
+
+ ret = hx509_peer_info_set_cms_algs(contextp, *peer, val, s->num_strings);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_peer_info_set_cms_algs");
+
+ for (i = 0; i < s->num_strings; i++)
+ free_AlgorithmIdentifier(&val[i]);
+ free(val);
+}
+
+/*
+ *
+ */
+
+struct pem_data {
+ heim_octet_string *os;
+ int detached_data;
+};
+
+static int
+pem_reader(hx509_context contextp, const char *type,
+ const hx509_pem_header *headers,
+ const void *data , size_t length, void *ctx)
+{
+ struct pem_data *p = (struct pem_data *)ctx;
+ const char *h;
+
+ p->os->data = malloc(length);
+ if (p->os->data == NULL)
+ return ENOMEM;
+ memcpy(p->os->data, data, length);
+ p->os->length = length;
+
+ h = hx509_pem_find_header(headers, "Content-disposition");
+ if (h && strcasecmp(h, "detached") == 0)
+ p->detached_data = 1;
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+int
+cms_verify_sd(struct cms_verify_sd_options *opt, int argc, char **argv)
+{
+ hx509_verify_ctx ctx = NULL;
+ heim_oid type;
+ heim_octet_string c, co, signeddata, *sd = NULL;
+ hx509_certs store = NULL;
+ hx509_certs signers = NULL;
+ hx509_certs anchors = NULL;
+ hx509_lock lock;
+ int ret, flags = 0;
+
+ size_t sz;
+ void *p = NULL;
+
+ if (opt->missing_revoke_flag)
+ hx509_context_set_missing_revoke(context, 1);
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ ret = hx509_verify_init_ctx(context, &ctx);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_verify_init_ctx");
+
+ ret = hx509_certs_init(context, "MEMORY:cms-anchors", 0, NULL, &anchors);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+ ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &store);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ certs_strings(context, "anchors", anchors, lock, &opt->anchors_strings);
+ certs_strings(context, "store", store, lock, &opt->certificate_strings);
+
+ if (opt->pem_flag) {
+ struct pem_data pd;
+ FILE *f;
+
+ pd.os = &co;
+ pd.detached_data = 0;
+
+ f = fopen(argv[0], "r");
+ if (f == NULL)
+ err(1, "Failed to open file %s", argv[0]);
+
+ ret = hx509_pem_read(context, f, pem_reader, &pd);
+ fclose(f);
+ if (ret)
+ errx(1, "PEM reader failed: %d", ret);
+
+ if (pd.detached_data && opt->signed_content_string == NULL) {
+ char *r = strrchr(argv[0], '.');
+ if (r && strcasecmp(r, ".pem") == 0) {
+ char *s = strdup(argv[0]);
+ if (s == NULL)
+ errx(1, "malloc: out of memory");
+ s[r - argv[0]] = '\0';
+ ret = _hx509_map_file_os(s, &signeddata);
+ if (ret)
+ errx(1, "map_file: %s: %d", s, ret);
+ free(s);
+ sd = &signeddata;
+ }
+ }
+
+ } else {
+ ret = rk_undumpdata(argv[0], &p, &sz);
+ if (ret)
+ err(1, "map_file: %s: %d", argv[0], ret);
+
+ co.data = p;
+ co.length = sz;
+ }
+
+ if (opt->signed_content_string) {
+ ret = _hx509_map_file_os(opt->signed_content_string, &signeddata);
+ if (ret)
+ errx(1, "map_file: %s: %d", opt->signed_content_string, ret);
+ sd = &signeddata;
+ }
+
+ if (opt->content_info_flag) {
+ heim_octet_string uwco;
+ heim_oid oid;
+
+ ret = hx509_cms_unwrap_ContentInfo(&co, &oid, &uwco, NULL);
+ if (ret)
+ errx(1, "hx509_cms_unwrap_ContentInfo: %d", ret);
+
+ if (der_heim_oid_cmp(&oid, &asn1_oid_id_pkcs7_signedData) != 0)
+ errx(1, "Content is not SignedData");
+ der_free_oid(&oid);
+
+ if (p == NULL)
+ der_free_octet_string(&co);
+ else {
+ rk_xfree(p);
+ p = NULL;
+ }
+ co = uwco;
+ }
+
+ hx509_verify_attach_anchors(ctx, anchors);
+
+ if (!opt->signer_allowed_flag)
+ flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
+ if (opt->allow_wrong_oid_flag)
+ flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
+
+ ret = hx509_cms_verify_signed(context, ctx, flags, co.data, co.length, sd,
+ store, &type, &c, &signers);
+ if (p != co.data)
+ der_free_octet_string(&co);
+ else
+ rk_xfree(p);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_cms_verify_signed");
+
+ {
+ char *str;
+ if (opt->oid_sym_flag)
+ der_print_heim_oid_sym(&type, '.', &str);
+ else
+ der_print_heim_oid(&type, '.', &str);
+ printf("type: %s\n", str);
+ free(str);
+ der_free_oid(&type);
+ }
+ if (signers == NULL) {
+ printf("unsigned\n");
+ } else {
+ printf("signers:\n");
+ hx509_certs_iter_f(context, signers, hx509_ci_print_names, stdout);
+ }
+
+ hx509_verify_destroy_ctx(ctx);
+
+ hx509_certs_free(&store);
+ hx509_certs_free(&signers);
+ hx509_certs_free(&anchors);
+
+ hx509_lock_free(lock);
+
+ if (argc > 1) {
+ ret = _hx509_write_file(argv[1], c.data, c.length);
+ if (ret)
+ errx(1, "hx509_write_file: %d", ret);
+ }
+
+ der_free_octet_string(&c);
+
+ if (sd)
+ _hx509_unmap_file_os(sd);
+
+ return 0;
+}
+
+static int HX509_LIB_CALL
+print_signer(hx509_context contextp, void *ctx, hx509_cert cert)
+{
+ hx509_pem_header **header = ctx;
+ char *signer_name = NULL;
+ hx509_name name;
+ int ret;
+
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret)
+ errx(1, "hx509_cert_get_subject");
+
+ ret = hx509_name_to_string(name, &signer_name);
+ hx509_name_free(&name);
+ if (ret)
+ errx(1, "hx509_name_to_string");
+
+ hx509_pem_add_header(header, "Signer", signer_name);
+
+ free(signer_name);
+ return 0;
+}
+
+int
+cms_create_sd(struct cms_create_sd_options *opt, int argc, char **argv)
+{
+ heim_oid contentType;
+ hx509_peer_info peer = NULL;
+ heim_octet_string o;
+ hx509_query *q;
+ hx509_lock lock;
+ hx509_certs store, pool, anchors, signer = NULL;
+ size_t sz;
+ void *p;
+ int ret, flags = 0;
+ const char *outfile = NULL;
+ char *infile, *freeme = NULL;
+
+ memset(&contentType, 0, sizeof(contentType));
+
+ infile = argv[0];
+
+ if (argc < 2) {
+ ret = asprintf(&freeme, "%s.%s", infile,
+ opt->pem_flag ? "pem" : "cms-signeddata");
+ if (ret == -1 || freeme == NULL)
+ errx(1, "out of memory");
+ outfile = freeme;
+ } else
+ outfile = argv[1];
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &store);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+ ret = hx509_certs_init(context, "MEMORY:cert-pool", 0, NULL, &pool);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ certs_strings(context, "store", store, lock, &opt->certificate_strings);
+ certs_strings(context, "pool", pool, lock, &opt->pool_strings);
+
+ if (opt->anchors_strings.num_strings) {
+ ret = hx509_certs_init(context, "MEMORY:cert-anchors",
+ 0, NULL, &anchors);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+ certs_strings(context, "anchors", anchors, lock, &opt->anchors_strings);
+ } else
+ anchors = NULL;
+
+ if (opt->detached_signature_flag)
+ flags |= HX509_CMS_SIGNATURE_DETACHED;
+ if (opt->id_by_name_flag)
+ flags |= HX509_CMS_SIGNATURE_ID_NAME;
+ if (!opt->signer_flag) {
+ flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
+
+ }
+
+ if (opt->signer_flag) {
+ ret = hx509_query_alloc(context, &q);
+ if (ret)
+ errx(1, "hx509_query_alloc: %d", ret);
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ if (opt->signer_string)
+ hx509_query_match_friendly_name(q, opt->signer_string);
+
+ ret = hx509_certs_filter(context, store, q, &signer);
+ hx509_query_free(context, q);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_find");
+ }
+ if (!opt->embedded_certs_flag)
+ flags |= HX509_CMS_SIGNATURE_NO_CERTS;
+ if (opt->embed_leaf_only_flag)
+ flags |= HX509_CMS_SIGNATURE_LEAF_ONLY;
+
+ ret = rk_undumpdata(infile, &p, &sz);
+ if (ret)
+ err(1, "map_file: %s: %d", infile, ret);
+
+ if (opt->peer_alg_strings.num_strings)
+ peer_strings(context, &peer, &opt->peer_alg_strings);
+
+ parse_oid(opt->content_type_string, &asn1_oid_id_pkcs7_data, &contentType);
+
+ ret = hx509_cms_create_signed(context,
+ flags,
+ &contentType,
+ p,
+ sz,
+ NULL,
+ signer,
+ peer,
+ anchors,
+ pool,
+ &o);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_cms_create_signed: %d", ret);
+
+ hx509_certs_free(&anchors);
+ hx509_certs_free(&pool);
+ hx509_certs_free(&store);
+ rk_xfree(p);
+ hx509_lock_free(lock);
+ hx509_peer_info_free(peer);
+ der_free_oid(&contentType);
+
+ if (opt->content_info_flag) {
+ heim_octet_string wo;
+
+ ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &o, &wo);
+ if (ret)
+ errx(1, "hx509_cms_wrap_ContentInfo: %d", ret);
+
+ der_free_octet_string(&o);
+ o = wo;
+ }
+
+ if (opt->pem_flag) {
+ hx509_pem_header *header = NULL;
+ FILE *f;
+
+ hx509_pem_add_header(&header, "Content-disposition",
+ opt->detached_signature_flag ?
+ "detached" : "inline");
+ if (signer) {
+ ret = hx509_certs_iter_f(context, signer, print_signer, header);
+ if (ret)
+ hx509_err(context, 1, ret, "print signer");
+ }
+
+ f = fopen(outfile, "w");
+ if (f == NULL)
+ err(1, "open %s", outfile);
+
+ ret = hx509_pem_write(context, "CMS SIGNEDDATA", header, f,
+ o.data, o.length);
+ fclose(f);
+ hx509_pem_free_header(header);
+ if (ret)
+ errx(1, "hx509_pem_write: %d", ret);
+
+ } else {
+ ret = _hx509_write_file(outfile, o.data, o.length);
+ if (ret)
+ errx(1, "hx509_write_file: %d", ret);
+ }
+
+ hx509_certs_free(&signer);
+ free(o.data);
+ free(freeme);
+
+ return 0;
+}
+
+int
+cms_unenvelope(struct cms_unenvelope_options *opt, int argc, char **argv)
+{
+ heim_oid contentType = { 0, NULL };
+ heim_octet_string o, co;
+ hx509_certs certs;
+ size_t sz;
+ void *p;
+ int ret;
+ hx509_lock lock;
+ int flags = 0;
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ ret = rk_undumpdata(argv[0], &p, &sz);
+ if (ret)
+ err(1, "map_file: %s: %d", argv[0], ret);
+
+ co.data = p;
+ co.length = sz;
+
+ if (opt->content_info_flag) {
+ heim_octet_string uwco;
+ heim_oid oid;
+
+ ret = hx509_cms_unwrap_ContentInfo(&co, &oid, &uwco, NULL);
+ if (ret)
+ errx(1, "hx509_cms_unwrap_ContentInfo: %d", ret);
+
+ if (der_heim_oid_cmp(&oid, &asn1_oid_id_pkcs7_envelopedData) != 0)
+ errx(1, "Content is not SignedData");
+ der_free_oid(&oid);
+
+ co = uwco;
+ }
+
+ ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &certs);
+ if (ret)
+ errx(1, "hx509_certs_init: MEMORY: %d", ret);
+
+ certs_strings(context, "store", certs, lock, &opt->certificate_strings);
+
+ if (opt->allow_weak_crypto_flag)
+ flags |= HX509_CMS_UE_ALLOW_WEAK;
+
+ ret = hx509_cms_unenvelope(context, certs, flags, co.data, co.length,
+ NULL, 0, &contentType, &o);
+ if (co.data != p)
+ der_free_octet_string(&co);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_cms_unenvelope");
+
+ rk_xfree(p);
+ hx509_lock_free(lock);
+ hx509_certs_free(&certs);
+ der_free_oid(&contentType);
+
+ ret = _hx509_write_file(argv[1], o.data, o.length);
+ if (ret)
+ errx(1, "hx509_write_file: %d", ret);
+
+ der_free_octet_string(&o);
+
+ return 0;
+}
+
+int
+cms_create_enveloped(struct cms_envelope_options *opt, int argc, char **argv)
+{
+ heim_oid contentType;
+ heim_octet_string o;
+ const heim_oid *enctype = NULL;
+ hx509_query *q;
+ hx509_certs certs;
+ hx509_cert cert;
+ int ret;
+ size_t sz;
+ void *p;
+ hx509_lock lock;
+ int flags = 0;
+
+ memset(&contentType, 0, sizeof(contentType));
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ ret = rk_undumpdata(argv[0], &p, &sz);
+ if (ret)
+ err(1, "map_file: %s: %d", argv[0], ret);
+
+ ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &certs);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ certs_strings(context, "store", certs, lock, &opt->certificate_strings);
+
+ if (opt->allow_weak_crypto_flag)
+ flags |= HX509_CMS_EV_ALLOW_WEAK;
+
+ if (opt->encryption_type_string) {
+ enctype = hx509_crypto_enctype_by_name(opt->encryption_type_string);
+ if (enctype == NULL)
+ errx(1, "encryption type: %s no found",
+ opt->encryption_type_string);
+ }
+
+ ret = hx509_query_alloc(context, &q);
+ if (ret)
+ errx(1, "hx509_query_alloc: %d", ret);
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_ENCIPHERMENT);
+
+ ret = hx509_certs_find(context, certs, q, &cert);
+ hx509_query_free(context, q);
+ if (ret)
+ errx(1, "hx509_certs_find: %d", ret);
+
+ parse_oid(opt->content_type_string, &asn1_oid_id_pkcs7_data, &contentType);
+
+ ret = hx509_cms_envelope_1(context, flags, cert, p, sz, enctype,
+ &contentType, &o);
+ if (ret)
+ errx(1, "hx509_cms_envelope_1: %d", ret);
+
+ hx509_cert_free(cert);
+ hx509_certs_free(&certs);
+ rk_xfree(p);
+ der_free_oid(&contentType);
+
+ if (opt->content_info_flag) {
+ heim_octet_string wo;
+
+ ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_envelopedData, &o, &wo);
+ if (ret)
+ errx(1, "hx509_cms_wrap_ContentInfo: %d", ret);
+
+ der_free_octet_string(&o);
+ o = wo;
+ }
+
+ hx509_lock_free(lock);
+
+ ret = _hx509_write_file(argv[1], o.data, o.length);
+ if (ret)
+ errx(1, "hx509_write_file: %d", ret);
+
+ der_free_octet_string(&o);
+
+ return 0;
+}
+
+static void
+print_certificate(hx509_context hxcontext, hx509_cert cert, int verbose)
+{
+ const char *fn;
+ int ret;
+
+ fn = hx509_cert_get_friendly_name(cert);
+ if (fn)
+ printf(" friendly name: %s\n", fn);
+ printf(" private key: %s\n",
+ _hx509_cert_private_key(cert) ? "yes" : "no");
+
+ ret = hx509_print_cert(hxcontext, cert, stdout);
+ if (ret)
+ errx(1, "failed to print cert");
+
+ if (verbose) {
+ hx509_validate_ctx vctx;
+
+ hx509_validate_ctx_init(hxcontext, &vctx);
+ hx509_validate_ctx_set_print(vctx, hx509_print_stdout, stdout);
+ hx509_validate_ctx_add_flags(vctx, HX509_VALIDATE_F_VALIDATE);
+ hx509_validate_ctx_add_flags(vctx, HX509_VALIDATE_F_VERBOSE);
+
+ hx509_validate_cert(hxcontext, vctx, cert);
+
+ hx509_validate_ctx_free(vctx);
+ }
+}
+
+
+struct print_s {
+ int counter;
+ int verbose;
+};
+
+static int HX509_LIB_CALL
+print_f(hx509_context hxcontext, void *ctx, hx509_cert cert)
+{
+ struct print_s *s = ctx;
+
+ printf("cert: %d\n", s->counter++);
+ print_certificate(context, cert, s->verbose);
+
+ return 0;
+}
+
+static int HX509_LIB_CALL
+print_fjson(hx509_context hxcontext, void *ctx, hx509_cert cert)
+{
+ const Certificate *c = NULL;
+ char *json = NULL;
+
+ c = _hx509_get_cert(cert);
+ if (c)
+ json = print_Certificate(c, ASN1_PRINT_INDENT);
+ if (json)
+ printf("%s\n", json);
+ else
+ hx509_err(context, 1, errno, "Could not format certificate as JSON");
+ free(json);
+ return 0;
+}
+
+
+int
+pcert_print(struct print_options *opt, int argc, char **argv)
+{
+ hx509_certs certs;
+ hx509_lock lock;
+ struct print_s s;
+
+ s.counter = 0;
+ s.verbose = opt->content_flag;
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ while(argc--) {
+ char *sn = fix_store_name(context, argv[0], "FILE");
+ int ret;
+
+ ret = hx509_certs_init(context, sn, 0, lock, &certs);
+ free(sn);
+ if (ret) {
+ if (opt->never_fail_flag) {
+ printf("ignoreing failure: %d\n", ret);
+ continue;
+ }
+ hx509_err(context, 1, ret, "hx509_certs_init");
+ }
+ if (opt->raw_json_flag) {
+ hx509_certs_iter_f(context, certs, print_fjson, &s);
+ } else {
+ if (opt->info_flag)
+ hx509_certs_info(context, certs, NULL, NULL);
+ hx509_certs_iter_f(context, certs, print_f, &s);
+ }
+ hx509_certs_free(&certs);
+ argv++;
+ }
+
+ hx509_lock_free(lock);
+
+ return 0;
+}
+
+
+static int HX509_LIB_CALL
+validate_f(hx509_context hxcontext, void *ctx, hx509_cert c)
+{
+ hx509_validate_cert(hxcontext, ctx, c);
+ return 0;
+}
+
+int
+pcert_validate(struct validate_options *opt, int argc, char **argv)
+{
+ hx509_validate_ctx ctx;
+ hx509_certs certs;
+ hx509_lock lock;
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ hx509_validate_ctx_init(context, &ctx);
+ hx509_validate_ctx_set_print(ctx, hx509_print_stdout, stdout);
+ hx509_validate_ctx_add_flags(ctx, HX509_VALIDATE_F_VALIDATE);
+
+ while(argc--) {
+ char *sn = fix_store_name(context, argv[0], "FILE");
+ int ret;
+
+ ret = hx509_certs_init(context, sn, 0, lock, &certs);
+ if (ret)
+ errx(1, "hx509_certs_init: %d", ret);
+ hx509_certs_iter_f(context, certs, validate_f, ctx);
+ hx509_certs_free(&certs);
+ argv++;
+ free(sn);
+ }
+ hx509_validate_ctx_free(ctx);
+
+ hx509_lock_free(lock);
+
+ return 0;
+}
+
+int
+certificate_copy(struct certificate_copy_options *opt, int argc, char **argv)
+{
+ hx509_certs certs;
+ hx509_lock inlock, outlock = NULL;
+ char *sn;
+ int ret;
+
+ hx509_lock_init(context, &inlock);
+ lock_strings(inlock, &opt->in_pass_strings);
+
+ if (opt->out_pass_string) {
+ hx509_lock_init(context, &outlock);
+ ret = hx509_lock_command_string(outlock, opt->out_pass_string);
+ if (ret)
+ errx(1, "hx509_lock_command_string: %s: %d",
+ opt->out_pass_string, ret);
+ }
+
+ sn = fix_store_name(context, argv[argc - 1], "FILE");
+ ret = hx509_certs_init(context, sn,
+ HX509_CERTS_CREATE, inlock, &certs);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init %s", sn);
+ free(sn);
+
+ while(argc-- > 1) {
+ int retx;
+
+ sn = fix_store_name(context, argv[0], "FILE");
+ retx = hx509_certs_append(context, certs, inlock, sn);
+ if (retx)
+ hx509_err(context, 1, retx, "hx509_certs_append %s", sn);
+ free(sn);
+ argv++;
+ }
+
+ ret = hx509_certs_store(context, certs, 0, outlock);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_store");
+
+ hx509_certs_free(&certs);
+ hx509_lock_free(inlock);
+ hx509_lock_free(outlock);
+
+ return 0;
+}
+
+struct verify {
+ hx509_verify_ctx ctx;
+ hx509_certs chain;
+ const char *hostname;
+ int errors;
+ int count;
+};
+
+static int HX509_LIB_CALL
+verify_f(hx509_context hxcontext, void *ctx, hx509_cert c)
+{
+ struct verify *v = ctx;
+ int ret;
+
+ ret = hx509_verify_path(hxcontext, v->ctx, c, v->chain);
+ if (ret) {
+ char *s = hx509_get_error_string(hxcontext, ret);
+ printf("verify_path: %s: %d\n", s, ret);
+ hx509_free_error_string(s);
+ v->errors++;
+ } else {
+ v->count++;
+ printf("path ok\n");
+ }
+
+ if (v->hostname) {
+ ret = hx509_verify_hostname(hxcontext, c, 0, HX509_HN_HOSTNAME,
+ v->hostname, NULL, 0);
+ if (ret) {
+ printf("verify_hostname: %d\n", ret);
+ v->errors++;
+ }
+ }
+
+ return 0;
+}
+
+int
+pcert_verify(struct verify_options *opt, int argc, char **argv)
+{
+ hx509_certs anchors, chain, certs;
+ hx509_revoke_ctx revoke_ctx;
+ hx509_verify_ctx ctx;
+ struct verify v;
+ int ret;
+
+ memset(&v, 0, sizeof(v));
+
+ if (opt->missing_revoke_flag)
+ hx509_context_set_missing_revoke(context, 1);
+
+ ret = hx509_verify_init_ctx(context, &ctx);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_verify_init_ctx");
+ ret = hx509_certs_init(context, "MEMORY:anchors", 0, NULL, &anchors);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+ ret = hx509_certs_init(context, "MEMORY:chain", 0, NULL, &chain);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+ ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &certs);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ if (opt->allow_proxy_certificate_flag)
+ hx509_verify_set_proxy_certificate(ctx, 1);
+
+ if (opt->time_string) {
+ const char *p;
+ struct tm tm;
+ time_t t;
+
+ memset(&tm, 0, sizeof(tm));
+
+ p = strptime (opt->time_string, "%Y-%m-%d", &tm);
+ if (p == NULL)
+ errx(1, "Failed to parse time %s, need to be on format %%Y-%%m-%%d",
+ opt->time_string);
+
+ t = tm2time (tm, 0);
+
+ hx509_verify_set_time(ctx, t);
+ }
+
+ if (opt->hostname_string)
+ v.hostname = opt->hostname_string;
+ if (opt->max_depth_integer)
+ hx509_verify_set_max_depth(ctx, opt->max_depth_integer);
+
+ ret = hx509_revoke_init(context, &revoke_ctx);
+ if (ret)
+ errx(1, "hx509_revoke_init: %d", ret);
+
+ while(argc--) {
+ const char *s = *argv++;
+ char *sn = NULL;
+
+ if (strncmp(s, "chain:", 6) == 0) {
+ s += 6;
+
+ sn = fix_store_name(context, s, "FILE");
+ ret = hx509_certs_append(context, chain, NULL, sn);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_append: chain: %s: %d",
+ sn, ret);
+
+ } else if (strncmp(s, "anchor:", 7) == 0) {
+ s += 7;
+
+ sn = fix_store_name(context, s, "FILE");
+ ret = hx509_certs_append(context, anchors, NULL, sn);
+ if (ret)
+ hx509_err(context, 1, ret,
+ "hx509_certs_append: anchor: %s: %d", sn, ret);
+
+ } else if (strncmp(s, "cert:", 5) == 0) {
+ s += 5;
+
+ sn = fix_store_name(context, s, "FILE");
+ ret = hx509_certs_append(context, certs, NULL, sn);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_append: certs: %s: %d",
+ sn, ret);
+
+ } else if (strncmp(s, "crl:", 4) == 0) {
+ s += 4;
+
+ ret = hx509_revoke_add_crl(context, revoke_ctx, s);
+ if (ret)
+ errx(1, "hx509_revoke_add_crl: %s: %d", s, ret);
+
+ } else if (strncmp(s, "ocsp:", 5) == 0) {
+ s += 5;
+
+ ret = hx509_revoke_add_ocsp(context, revoke_ctx, s);
+ if (ret)
+ errx(1, "hx509_revoke_add_ocsp: %s: %d", s, ret);
+
+ } else {
+ errx(1, "unknown option to verify: `%s'\n", s);
+ }
+ free(sn);
+ }
+
+ hx509_verify_attach_anchors(ctx, anchors);
+ hx509_verify_attach_revoke(ctx, revoke_ctx);
+
+ v.ctx = ctx;
+ v.chain = chain;
+
+ hx509_certs_iter_f(context, certs, verify_f, &v);
+
+ hx509_verify_destroy_ctx(ctx);
+
+ hx509_certs_free(&certs);
+ hx509_certs_free(&chain);
+ hx509_certs_free(&anchors);
+
+ hx509_revoke_free(&revoke_ctx);
+
+
+ if (v.count == 0) {
+ printf("no certs verify at all\n");
+ return 1;
+ }
+
+ if (v.errors) {
+ printf("failed verifing %d checks\n", v.errors);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+query(struct query_options *opt, int argc, char **argv)
+{
+ hx509_lock lock;
+ hx509_query *q;
+ hx509_certs certs;
+ hx509_cert c;
+ int ret;
+
+ ret = hx509_query_alloc(context, &q);
+ if (ret)
+ errx(1, "hx509_query_alloc: %d", ret);
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &certs);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ while (argc > 0) {
+ char *sn = fix_store_name(context, argv[0], "FILE");
+
+ ret = hx509_certs_append(context, certs, lock, sn);
+ if (ret)
+ errx(1, "hx509_certs_append: %s: %d", sn, ret);
+ free(sn);
+
+ argc--;
+ argv++;
+ }
+
+ if (opt->friendlyname_string)
+ hx509_query_match_friendly_name(q, opt->friendlyname_string);
+
+ if (opt->eku_string) {
+ heim_oid oid;
+
+ parse_oid(opt->eku_string, NULL, &oid);
+
+ ret = hx509_query_match_eku(q, &oid);
+ if (ret)
+ errx(1, "hx509_query_match_eku: %d", ret);
+ der_free_oid(&oid);
+ }
+
+ if (opt->private_key_flag)
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+
+ if (opt->keyEncipherment_flag)
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_ENCIPHERMENT);
+
+ if (opt->digitalSignature_flag)
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ if (opt->expr_string)
+ hx509_query_match_expr(context, q, opt->expr_string);
+
+ ret = hx509_certs_find(context, certs, q, &c);
+ hx509_query_free(context, q);
+ if (ret)
+ printf("no match found (%d)\n", ret);
+ else {
+ printf("match found\n");
+ if (opt->print_flag)
+ print_certificate(context, c, 0);
+ }
+
+ hx509_cert_free(c);
+ hx509_certs_free(&certs);
+
+ hx509_lock_free(lock);
+
+ return ret;
+}
+
+int
+ocsp_fetch(struct ocsp_fetch_options *opt, int argc, char **argv)
+{
+ hx509_certs reqcerts, pool;
+ heim_octet_string req, nonce_data, *nonce = &nonce_data;
+ hx509_lock lock;
+ int i, ret;
+ char *file;
+ const char *url = "/";
+
+ memset(&nonce, 0, sizeof(nonce));
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ /* no nonce */
+ if (!opt->nonce_flag)
+ nonce = NULL;
+
+ if (opt->url_path_string)
+ url = opt->url_path_string;
+
+ ret = hx509_certs_init(context, "MEMORY:ocsp-pool", 0, NULL, &pool);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ certs_strings(context, "ocsp-pool", pool, lock, &opt->pool_strings);
+
+ file = argv[0];
+
+ ret = hx509_certs_init(context, "MEMORY:ocsp-req", 0, NULL, &reqcerts);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ for (i = 1; i < argc; i++) {
+ char *sn = fix_store_name(context, argv[i], "FILE");
+
+ ret = hx509_certs_append(context, reqcerts, lock, sn);
+ if (ret)
+ errx(1, "hx509_certs_append: req: %s: %d", sn, ret);
+ free(sn);
+ }
+
+ ret = hx509_ocsp_request(context, reqcerts, pool, NULL, NULL, &req, nonce);
+ if (ret)
+ errx(1, "hx509_ocsp_request: req: %d", ret);
+
+ {
+ FILE *f;
+
+ f = fopen(file, "w");
+ if (f == NULL)
+ abort();
+
+ fprintf(f,
+ "POST %s HTTP/1.0\r\n"
+ "Content-Type: application/ocsp-request\r\n"
+ "Content-Length: %ld\r\n"
+ "\r\n",
+ url,
+ (unsigned long)req.length);
+ fwrite(req.data, req.length, 1, f);
+ fclose(f);
+ }
+
+ if (nonce)
+ der_free_octet_string(nonce);
+
+ hx509_certs_free(&reqcerts);
+ hx509_certs_free(&pool);
+
+ return 0;
+}
+
+int
+ocsp_print(struct ocsp_print_options *opt, int argc, char **argv)
+{
+ hx509_revoke_ocsp_print(context, argv[0], stdout);
+ return 0;
+}
+
+int
+revoke_print(struct revoke_print_options *opt, int argc, char **argv)
+{
+ hx509_revoke_ctx revoke_ctx;
+ int ret;
+
+ ret = hx509_revoke_init(context, &revoke_ctx);
+ if (ret)
+ errx(1, "hx509_revoke_init: %d", ret);
+
+ while(argc--) {
+ char *s = *argv++;
+
+ if (strncmp(s, "crl:", 4) == 0) {
+ s += 4;
+
+ ret = hx509_revoke_add_crl(context, revoke_ctx, s);
+ if (ret)
+ errx(1, "hx509_revoke_add_crl: %s: %d", s, ret);
+
+ } else if (strncmp(s, "ocsp:", 5) == 0) {
+ s += 5;
+
+ ret = hx509_revoke_add_ocsp(context, revoke_ctx, s);
+ if (ret)
+ errx(1, "hx509_revoke_add_ocsp: %s: %d", s, ret);
+
+ } else {
+ errx(1, "unknown option to verify: `%s'\n", s);
+ }
+ }
+
+ ret = hx509_revoke_print(context, revoke_ctx, stdout);
+ if (ret)
+ warnx("hx509_revoke_print: %d", ret);
+
+ hx509_revoke_free(&revoke_ctx);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int HX509_LIB_CALL
+verify_o(hx509_context hxcontext, void *ctx, hx509_cert c)
+{
+ heim_octet_string *os = ctx;
+ time_t expiration;
+ int ret;
+
+ ret = hx509_ocsp_verify(context, 0, c, 0,
+ os->data, os->length, &expiration);
+ if (ret) {
+ char *s = hx509_get_error_string(hxcontext, ret);
+ printf("ocsp_verify: %s: %d\n", s, ret);
+ hx509_free_error_string(s);
+ } else
+ printf("expire: %d\n", (int)expiration);
+
+ return ret;
+}
+
+
+int
+ocsp_verify(struct ocsp_verify_options *opt, int argc, char **argv)
+{
+ hx509_lock lock;
+ hx509_certs certs;
+ int ret, i;
+ heim_octet_string os;
+
+ hx509_lock_init(context, &lock);
+
+ if (opt->ocsp_file_string == NULL)
+ errx(1, "no ocsp file given");
+
+ ret = _hx509_map_file_os(opt->ocsp_file_string, &os);
+ if (ret)
+ err(1, "map_file: %s: %d", argv[0], ret);
+
+ ret = hx509_certs_init(context, "MEMORY:test-certs", 0, NULL, &certs);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ for (i = 0; i < argc; i++) {
+ char *sn = fix_store_name(context, argv[i], "FILE");
+
+ ret = hx509_certs_append(context, certs, lock, sn);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_append: %s", sn);
+ free(sn);
+ }
+
+ ret = hx509_certs_iter_f(context, certs, verify_o, &os);
+
+ hx509_certs_free(&certs);
+ _hx509_unmap_file_os(&os);
+ hx509_lock_free(lock);
+
+ return ret;
+}
+
+static int
+read_private_key(const char *fn, hx509_private_key *key)
+{
+ hx509_private_key *keys;
+ hx509_certs certs;
+ char *sn = fix_store_name(context, fn, "FILE");
+ int ret;
+
+ *key = NULL;
+
+ ret = hx509_certs_init(context, sn, 0, NULL, &certs);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: %s", sn);
+
+ ret = _hx509_certs_keys_get(context, certs, &keys);
+ hx509_certs_free(&certs);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_keys_get");
+ if (keys[0] == NULL)
+ errx(1, "no keys in key store: %s", sn);
+ free(sn);
+
+ *key = _hx509_private_key_ref(keys[0]);
+ _hx509_certs_keys_free(context, keys);
+
+ return 0;
+}
+
+static void
+get_key(const char *fn, const char *type, int optbits,
+ hx509_private_key *signer)
+{
+ int ret = 0;
+
+ if (type) {
+ struct hx509_generate_private_context *gen_ctx = NULL;
+
+ if (strcasecmp(type, "rsa") != 0)
+ errx(1, "can only handle rsa keys for now");
+
+ ret = _hx509_generate_private_key_init(context,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &gen_ctx);
+ if (ret == 0)
+ ret = _hx509_generate_private_key_bits(context, gen_ctx, optbits);
+ if (ret == 0)
+ ret = _hx509_generate_private_key(context, gen_ctx, signer);
+ _hx509_generate_private_key_free(&gen_ctx);
+ if (ret)
+ hx509_err(context, 1, ret, "failed to generate private key of type %s", type);
+
+ if (fn) {
+ char *sn = fix_store_name(context, fn, "FILE");
+ hx509_certs certs = NULL;
+ hx509_cert cert = NULL;
+
+ cert = hx509_cert_init_private_key(context, *signer, NULL);
+ if (cert)
+ ret = hx509_certs_init(context, sn,
+ HX509_CERTS_CREATE |
+ HX509_CERTS_UNPROTECT_ALL,
+ NULL, &certs);
+ if (ret == 0)
+ ret = hx509_certs_add(context, certs, cert);
+ if (ret == 0)
+ ret = hx509_certs_store(context, certs, 0, NULL);
+ if (ret)
+ hx509_err(context, 1, ret, "failed to store generated private "
+ "key in %s", sn);
+
+ if (certs)
+ hx509_certs_free(&certs);
+ if (cert)
+ hx509_cert_free(cert);
+ free(sn);
+ }
+ } else {
+ if (fn == NULL)
+ err(1, "no private key");
+ ret = read_private_key(fn, signer);
+ if (ret)
+ hx509_err(context, 1, ret, "failed to read private key from %s",
+ fn);
+ }
+}
+
+int
+generate_key(struct generate_key_options *opt, int argc, char **argv)
+{
+ hx509_private_key signer;
+ const char *type = opt->type_string ? opt->type_string : "rsa";
+ int bits = opt->key_bits_integer ? opt->key_bits_integer : 2048;
+
+ memset(&signer, 0, sizeof(signer));
+ get_key(argv[0], type, bits, &signer);
+ hx509_private_key_free(&signer);
+ return 0;
+}
+
+int
+request_create(struct request_create_options *opt, int argc, char **argv)
+{
+ heim_octet_string request;
+ hx509_request req;
+ int ret, i;
+ hx509_private_key signer;
+ SubjectPublicKeyInfo key;
+ const char *outfile = argv[0];
+
+ memset(&key, 0, sizeof(key));
+ memset(&signer, 0, sizeof(signer));
+
+ get_key(opt->key_string,
+ opt->generate_key_string,
+ opt->key_bits_integer,
+ &signer);
+
+ hx509_request_init(context, &req);
+
+ if (opt->subject_string) {
+ hx509_name name = NULL;
+
+ ret = hx509_parse_name(context, opt->subject_string, &name);
+ if (ret)
+ errx(1, "hx509_parse_name: %d\n", ret);
+ hx509_request_set_name(context, req, name);
+
+ if (opt->verbose_flag) {
+ char *s;
+ hx509_name_to_string(name, &s);
+ printf("%s\n", s);
+ free(s);
+ }
+ hx509_name_free(&name);
+ }
+
+ for (i = 0; i < opt->email_strings.num_strings; i++) {
+ ret = hx509_request_add_email(context, req,
+ opt->email_strings.strings[i]);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_email");
+ }
+
+ for (i = 0; i < opt->jid_strings.num_strings; i++) {
+ ret = hx509_request_add_xmpp_name(context, req,
+ opt->jid_strings.strings[i]);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_xmpp_name");
+ }
+
+ for (i = 0; i < opt->dnsname_strings.num_strings; i++) {
+ ret = hx509_request_add_dns_name(context, req,
+ opt->dnsname_strings.strings[i]);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_dns_name");
+ }
+
+ for (i = 0; i < opt->kerberos_strings.num_strings; i++) {
+ ret = hx509_request_add_pkinit(context, req,
+ opt->kerberos_strings.strings[i]);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_pkinit");
+ }
+
+ for (i = 0; i < opt->ms_kerberos_strings.num_strings; i++) {
+ ret = hx509_request_add_ms_upn_name(context, req,
+ opt->ms_kerberos_strings.strings[i]);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_ms_upn_name");
+ }
+
+ for (i = 0; i < opt->registered_strings.num_strings; i++) {
+ heim_oid oid;
+
+ parse_oid(opt->registered_strings.strings[i], NULL, &oid);
+ ret = hx509_request_add_registered(context, req, &oid);
+ der_free_oid(&oid);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_registered");
+ }
+
+ for (i = 0; i < opt->eku_strings.num_strings; i++) {
+ heim_oid oid;
+
+ parse_oid(opt->eku_strings.strings[i], NULL, &oid);
+ ret = hx509_request_add_eku(context, req, &oid);
+ der_free_oid(&oid);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_eku");
+ }
+
+
+ ret = hx509_private_key2SPKI(context, signer, &key);
+ if (ret)
+ errx(1, "hx509_private_key2SPKI: %d\n", ret);
+
+ ret = hx509_request_set_SubjectPublicKeyInfo(context,
+ req,
+ &key);
+ free_SubjectPublicKeyInfo(&key);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_set_SubjectPublicKeyInfo");
+
+ ret = hx509_request_to_pkcs10(context,
+ req,
+ signer,
+ &request);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_to_pkcs10");
+
+ hx509_private_key_free(&signer);
+ hx509_request_free(&req);
+
+ if (ret == 0)
+ rk_dumpdata(outfile, request.data, request.length);
+ der_free_octet_string(&request);
+
+ return 0;
+}
+
+int
+request_print(struct request_print_options *opt, int argc, char **argv)
+{
+ int ret, i;
+
+ printf("request print\n");
+
+ for (i = 0; i < argc; i++) {
+ hx509_request req;
+ char *cn = fix_csr_name(argv[i], "PKCS10");
+
+ ret = hx509_request_parse(context, cn, &req);
+ if (ret)
+ hx509_err(context, 1, ret, "parse_request: %s", cn);
+
+ ret = hx509_request_print(context, req, stdout);
+ hx509_request_free(&req);
+ if (ret)
+ hx509_err(context, 1, ret, "Failed to print file %s", cn);
+ free(cn);
+ }
+
+ return 0;
+}
+
+int
+info(void *opt, int argc, char **argv)
+{
+
+ ENGINE_add_conf_module();
+
+ {
+ const RSA_METHOD *m = RSA_get_default_method();
+ if (m != NULL)
+ printf("rsa: %s\n", m->name);
+ }
+ {
+ const DH_METHOD *m = DH_get_default_method();
+ if (m != NULL)
+ printf("dh: %s\n", m->name);
+ }
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ {
+ printf("ecdsa: ECDSA_METHOD-not-export\n");
+ }
+#else
+ {
+ printf("ecdsa: hcrypto null\n");
+ }
+#endif
+ {
+ int ret = RAND_status();
+ printf("rand: %s\n", ret == 1 ? "ok" : "not available");
+ }
+
+ return 0;
+}
+
+int
+random_data(void *opt, int argc, char **argv)
+{
+ void *ptr;
+ ssize_t len;
+ int ret;
+
+ len = parse_bytes(argv[0], "byte");
+ if (len <= 0) {
+ fprintf(stderr, "bad argument to random-data\n");
+ return 1;
+ }
+
+ ptr = malloc(len);
+ if (ptr == NULL) {
+ fprintf(stderr, "out of memory\n");
+ return 1;
+ }
+
+ ret = RAND_bytes(ptr, len);
+ if (ret != 1) {
+ free(ptr);
+ fprintf(stderr, "did not get cryptographic strong random\n");
+ return 1;
+ }
+
+ fwrite(ptr, len, 1, stdout);
+ fflush(stdout);
+
+ free(ptr);
+
+ return 0;
+}
+
+int
+crypto_available(struct crypto_available_options *opt, int argc, char **argv)
+{
+ AlgorithmIdentifier *val;
+ unsigned int len, i;
+ int ret, type = HX509_SELECT_ALL;
+
+ if (opt->type_string) {
+ if (strcmp(opt->type_string, "all") == 0)
+ type = HX509_SELECT_ALL;
+ else if (strcmp(opt->type_string, "digest") == 0)
+ type = HX509_SELECT_DIGEST;
+ else if (strcmp(opt->type_string, "public-sig") == 0)
+ type = HX509_SELECT_PUBLIC_SIG;
+ else if (strcmp(opt->type_string, "secret") == 0)
+ type = HX509_SELECT_SECRET_ENC;
+ else
+ errx(1, "unknown type: %s", opt->type_string);
+ }
+
+ ret = hx509_crypto_available(context, type, NULL, &val, &len);
+ if (ret)
+ errx(1, "hx509_crypto_available");
+
+ for (i = 0; i < len; i++) {
+ char *s;
+ if (opt->oid_syms_flag)
+ der_print_heim_oid_sym(&val[i].algorithm, '.', &s);
+ else
+ der_print_heim_oid(&val[i].algorithm, '.', &s);
+ printf("%s\n", s);
+ free(s);
+ }
+
+ hx509_crypto_free_algs(val, len);
+
+ return 0;
+}
+
+int
+crypto_select(struct crypto_select_options *opt, int argc, char **argv)
+{
+ hx509_peer_info peer = NULL;
+ AlgorithmIdentifier selected;
+ int ret, type = HX509_SELECT_DIGEST;
+ char *s;
+
+ if (opt->type_string) {
+ if (strcmp(opt->type_string, "digest") == 0)
+ type = HX509_SELECT_DIGEST;
+ else if (strcmp(opt->type_string, "public-sig") == 0)
+ type = HX509_SELECT_PUBLIC_SIG;
+ else if (strcmp(opt->type_string, "secret") == 0)
+ type = HX509_SELECT_SECRET_ENC;
+ else
+ errx(1, "unknown type: %s", opt->type_string);
+ }
+
+ if (opt->peer_cmstype_strings.num_strings)
+ peer_strings(context, &peer, &opt->peer_cmstype_strings);
+
+ ret = hx509_crypto_select(context, type, NULL, peer, &selected);
+ if (ret)
+ errx(1, "hx509_crypto_available");
+
+ if (opt->oid_sym_flag)
+ der_print_heim_oid_sym(&selected.algorithm, '.', &s);
+ else
+ der_print_heim_oid(&selected.algorithm, '.', &s);
+ printf("%s\n", s);
+ free(s);
+ free_AlgorithmIdentifier(&selected);
+
+ hx509_peer_info_free(peer);
+
+ return 0;
+}
+
+int
+hxtool_hex(struct hex_options *opt, int argc, char **argv)
+{
+
+ if (opt->decode_flag) {
+ char buf[1024], buf2[1024], *p;
+ ssize_t len;
+
+ while(fgets(buf, sizeof(buf), stdin) != NULL) {
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace(*(unsigned char *)p))
+ p++;
+ len = hex_decode(p, buf2, strlen(p));
+ if (len < 0)
+ errx(1, "hex_decode failed");
+ if (fwrite(buf2, 1, len, stdout) != (size_t)len)
+ errx(1, "fwrite failed");
+ }
+ } else {
+ char buf[28], *p;
+ ssize_t len;
+
+ while((len = fread(buf, 1, sizeof(buf), stdin)) != 0) {
+ len = hex_encode(buf, len, &p);
+ if (len < 0)
+ continue;
+ fprintf(stdout, "%s\n", p);
+ free(p);
+ }
+ }
+ return 0;
+}
+
+struct cert_type_opt {
+ int pkinit;
+};
+
+
+static int
+https_server(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt)
+{
+ return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_serverAuth);
+}
+
+static int
+https_negotiate_server(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt)
+{
+ int ret = hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkekuoid);
+ if (ret == 0)
+ ret = hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_serverAuth);
+ opt->pkinit++;
+ return ret;
+}
+
+static int
+https_client(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt)
+{
+ return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_clientAuth);
+}
+
+static int
+peap_server(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt)
+{
+ return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_serverAuth);
+}
+
+static int
+pkinit_kdc(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt)
+{
+ opt->pkinit++;
+ return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkkdcekuoid);
+}
+
+static int
+pkinit_client(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt)
+{
+ int ret;
+
+ opt->pkinit++;
+
+ ret = hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkekuoid);
+ if (ret)
+ return ret;
+
+ ret = hx509_ca_tbs_add_eku(context, tbs, &asn1_oid_id_pkix_kp_clientAuth);
+ if (ret)
+ return ret;
+
+ return hx509_ca_tbs_add_eku(context, tbs, &asn1_oid_id_pkinit_ms_eku);
+}
+
+static int
+email_client(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt)
+{
+ return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_emailProtection);
+}
+
+struct {
+ const char *type;
+ const char *desc;
+ int (*eval)(hx509_context, hx509_ca_tbs, struct cert_type_opt *);
+} certtypes[] = {
+ {
+ "https-server",
+ "Used for HTTPS server and many other TLS server certificate types",
+ https_server
+ },
+ {
+ "https-client",
+ "Used for HTTPS client certificates",
+ https_client
+ },
+ {
+ "email-client",
+ "Certificate will be use for email",
+ email_client
+ },
+ {
+ "pkinit-client",
+ "Certificate used for Kerberos PK-INIT client certificates",
+ pkinit_client
+ },
+ {
+ "pkinit-kdc",
+ "Certificates used for Kerberos PK-INIT KDC certificates",
+ pkinit_kdc
+ },
+ {
+ "https-negotiate-server",
+ "Used for HTTPS server and many other TLS server certificate types",
+ https_negotiate_server
+ },
+ {
+ "peap-server",
+ "Certificate used for Radius PEAP (Protected EAP)",
+ peap_server
+ }
+};
+
+static void
+print_eval_types(FILE *out)
+{
+ rtbl_t table;
+ unsigned i;
+
+ table = rtbl_create();
+ rtbl_add_column_by_id (table, 0, "Name", 0);
+ rtbl_add_column_by_id (table, 1, "Description", 0);
+
+ for (i = 0; i < sizeof(certtypes)/sizeof(certtypes[0]); i++) {
+ rtbl_add_column_entry_by_id(table, 0, certtypes[i].type);
+ rtbl_add_column_entry_by_id(table, 1, certtypes[i].desc);
+ }
+
+ rtbl_format (table, out);
+ rtbl_destroy (table);
+}
+
+static int
+eval_types(hx509_context contextp,
+ hx509_ca_tbs tbs,
+ const struct certificate_sign_options *opt)
+{
+ struct cert_type_opt ctopt;
+ int i;
+ size_t j;
+ int ret;
+
+ memset(&ctopt, 0, sizeof(ctopt));
+
+ for (i = 0; i < opt->type_strings.num_strings; i++) {
+ const char *type = opt->type_strings.strings[i];
+
+ for (j = 0; j < sizeof(certtypes)/sizeof(certtypes[0]); j++) {
+ if (strcasecmp(type, certtypes[j].type) == 0) {
+ ret = (*certtypes[j].eval)(contextp, tbs, &ctopt);
+ if (ret)
+ hx509_err(contextp, 1, ret,
+ "Failed to evaluate cert type %s", type);
+ break;
+ }
+ }
+ if (j >= sizeof(certtypes)/sizeof(certtypes[0])) {
+ fprintf(stderr, "Unknown certificate type %s\n\n", type);
+ fprintf(stderr, "Available types:\n");
+ print_eval_types(stderr);
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < opt->pk_init_principal_strings.num_strings; i++) {
+ const char *pk_init_princ = opt->pk_init_principal_strings.strings[i];
+
+ if (!ctopt.pkinit)
+ errx(1, "pk-init principal given but no pk-init oid");
+
+ ret = hx509_ca_tbs_add_san_pkinit(contextp, tbs, pk_init_princ);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_pkinit");
+ }
+
+ if (opt->ms_upn_string) {
+ if (!ctopt.pkinit)
+ errx(1, "MS upn given but no pk-init oid");
+
+ ret = hx509_ca_tbs_add_san_ms_upn(contextp, tbs, opt->ms_upn_string);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_ms_upn");
+ }
+
+
+ for (i = 0; i < opt->hostname_strings.num_strings; i++) {
+ const char *hostname = opt->hostname_strings.strings[i];
+
+ ret = hx509_ca_tbs_add_san_hostname(contextp, tbs, hostname);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_hostname");
+ }
+
+ for (i = 0; i < opt->dnssrv_strings.num_strings; i++) {
+ const char *dnssrv = opt->dnssrv_strings.strings[i];
+
+ ret = hx509_ca_tbs_add_san_dnssrv(contextp, tbs, dnssrv);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_dnssrv");
+ }
+
+ for (i = 0; i < opt->email_strings.num_strings; i++) {
+ const char *email = opt->email_strings.strings[i];
+
+ ret = hx509_ca_tbs_add_san_rfc822name(contextp, tbs, email);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_hostname");
+
+ ret = hx509_ca_tbs_add_eku(contextp, tbs,
+ &asn1_oid_id_pkix_kp_emailProtection);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_eku");
+ }
+
+ if (opt->jid_string) {
+ ret = hx509_ca_tbs_add_san_jid(contextp, tbs, opt->jid_string);
+ if (ret)
+ hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_jid");
+ }
+
+ return 0;
+}
+
+int
+hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
+{
+ int ret;
+ hx509_ca_tbs tbs;
+ hx509_cert signer = NULL, cert = NULL;
+ hx509_private_key private_key = NULL;
+ hx509_private_key cert_key = NULL;
+ hx509_name subject = NULL;
+ SubjectPublicKeyInfo spki;
+ heim_oid oid;
+ size_t i;
+ int delta = 0;
+
+ memset(&oid, 0, sizeof(oid));
+ memset(&spki, 0, sizeof(spki));
+
+ if (opt->ca_certificate_string == NULL && !opt->self_signed_flag)
+ errx(1, "--ca-certificate argument missing (not using --self-signed)");
+ if (opt->ca_private_key_string == NULL && opt->generate_key_string == NULL && opt->self_signed_flag)
+ errx(1, "--ca-private-key argument missing (using --self-signed)");
+ if (opt->certificate_string == NULL)
+ errx(1, "--certificate argument missing");
+
+ if (opt->template_certificate_string && opt->template_fields_string == NULL)
+ errx(1, "--template-certificate used but no --template-fields given");
+
+ if (opt->lifetime_string) {
+ delta = parse_time(opt->lifetime_string, "day");
+ if (delta < 0)
+ errx(1, "Invalid lifetime: %s", opt->lifetime_string);
+ }
+
+ if (opt->ca_certificate_string) {
+ hx509_certs cacerts = NULL;
+ hx509_query *q;
+ char *sn = fix_store_name(context, opt->ca_certificate_string, "FILE");
+
+ ret = hx509_certs_init(context, sn, 0, NULL, &cacerts);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: %s", sn);
+
+ ret = hx509_query_alloc(context, &q);
+ if (ret)
+ errx(1, "hx509_query_alloc: %d", ret);
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ if (!opt->issue_proxy_flag)
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN);
+
+ ret = hx509_certs_find(context, cacerts, q, &signer);
+ hx509_query_free(context, q);
+ hx509_certs_free(&cacerts);
+ if (ret)
+ hx509_err(context, 1, ret, "no CA certificate found");
+ free(sn);
+ } else if (opt->self_signed_flag) {
+ if (opt->generate_key_string == NULL
+ && opt->ca_private_key_string == NULL)
+ errx(1, "no signing private key");
+
+ if (opt->req_string)
+ errx(1, "can't be self-signing and have a request at the same time");
+ } else
+ errx(1, "missing ca key");
+
+ if (opt->ca_private_key_string) {
+
+ ret = read_private_key(opt->ca_private_key_string, &private_key);
+ if (ret)
+ err(1, "read_private_key");
+
+ ret = hx509_private_key2SPKI(context, private_key, &spki);
+ if (ret)
+ errx(1, "hx509_private_key2SPKI: %d\n", ret);
+
+ if (opt->self_signed_flag)
+ cert_key = private_key;
+ }
+
+ if (opt->req_string) {
+ hx509_request req;
+ char *cn = fix_csr_name(opt->req_string, "PKCS10");
+
+ /*
+ * Extract the CN and other attributes we want to preserve from the
+ * requested subjectName and then set them in the hx509_env for the
+ * template.
+ */
+ ret = hx509_request_parse(context, cn, &req);
+ if (ret)
+ hx509_err(context, 1, ret, "parse_request: %s", cn);
+ ret = hx509_request_get_name(context, req, &subject);
+ if (ret)
+ hx509_err(context, 1, ret, "get name");
+ ret = hx509_request_get_SubjectPublicKeyInfo(context, req, &spki);
+ if (ret)
+ hx509_err(context, 1, ret, "get spki");
+ hx509_request_free(&req);
+ free(cn);
+ }
+
+ if (opt->generate_key_string) {
+ struct hx509_generate_private_context *keyctx;
+
+ ret = _hx509_generate_private_key_init(context,
+ &asn1_oid_id_pkcs1_rsaEncryption,
+ &keyctx);
+ if (ret)
+ hx509_err(context, 1, ret, "generate private key");
+
+ if (opt->issue_ca_flag)
+ _hx509_generate_private_key_is_ca(context, keyctx);
+
+ if (opt->key_bits_integer)
+ _hx509_generate_private_key_bits(context, keyctx,
+ opt->key_bits_integer);
+
+ ret = _hx509_generate_private_key(context, keyctx,
+ &cert_key);
+ _hx509_generate_private_key_free(&keyctx);
+ if (ret)
+ hx509_err(context, 1, ret, "generate private key");
+
+ ret = hx509_private_key2SPKI(context, cert_key, &spki);
+ if (ret)
+ errx(1, "hx509_private_key2SPKI: %d\n", ret);
+
+ if (opt->self_signed_flag)
+ private_key = cert_key;
+ }
+
+ if (opt->certificate_private_key_string) {
+ ret = read_private_key(opt->certificate_private_key_string, &cert_key);
+ if (ret)
+ err(1, "read_private_key for certificate");
+ }
+
+ if (opt->subject_string) {
+ if (subject)
+ hx509_name_free(&subject);
+ ret = hx509_parse_name(context, opt->subject_string, &subject);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_parse_name");
+ }
+
+ /*
+ *
+ */
+
+ ret = hx509_ca_tbs_init(context, &tbs);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_init");
+
+ for (i = 0; i < opt->eku_strings.num_strings; i++) {
+ parse_oid(opt->eku_strings.strings[i], NULL, &oid);
+ ret = hx509_ca_tbs_add_eku(context, tbs, &oid);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_eku");
+ der_free_oid(&oid);
+ }
+ if (opt->ku_strings.num_strings) {
+ const struct units *kus = asn1_KeyUsage_units();
+ const struct units *kup;
+ uint64_t n = 0;
+
+ for (i = 0; i < opt->ku_strings.num_strings; i++) {
+ for (kup = kus; kup->name; kup++) {
+ if (strcmp(kup->name, opt->ku_strings.strings[i]))
+ continue;
+ n |= kup->mult;
+ break;
+ }
+ }
+ ret = hx509_ca_tbs_add_ku(context, tbs, int2KeyUsage(n));
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_request_add_ku");
+ }
+ if (opt->signature_algorithm_string) {
+ const AlgorithmIdentifier *sigalg;
+ if (strcasecmp(opt->signature_algorithm_string, "rsa-with-sha1") == 0)
+ sigalg = hx509_signature_rsa_with_sha1();
+ else if (strcasecmp(opt->signature_algorithm_string, "rsa-with-sha256") == 0)
+ sigalg = hx509_signature_rsa_with_sha256();
+ else
+ errx(1, "unsupported sigature algorithm");
+ hx509_ca_tbs_set_signature_algorithm(context, tbs, sigalg);
+ }
+
+ if (opt->template_certificate_string) {
+ hx509_cert template;
+ hx509_certs tcerts;
+ char *sn = fix_store_name(context, opt->template_certificate_string,
+ "FILE");
+ int flags;
+
+ ret = hx509_certs_init(context, sn, 0, NULL, &tcerts);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: %s", sn);
+
+ ret = hx509_get_one_cert(context, tcerts, &template);
+
+ hx509_certs_free(&tcerts);
+ if (ret)
+ hx509_err(context, 1, ret, "no template certificate found");
+
+ flags = parse_units(opt->template_fields_string,
+ hx509_ca_tbs_template_units(), "");
+
+ ret = hx509_ca_tbs_set_template(context, tbs, flags, template);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_set_template");
+
+ hx509_cert_free(template);
+ free(sn);
+ }
+
+ if (opt->serial_number_string) {
+ heim_integer serialNumber;
+
+ ret = der_parse_hex_heim_integer(opt->serial_number_string,
+ &serialNumber);
+ if (ret)
+ err(1, "der_parse_hex_heim_integer");
+ ret = hx509_ca_tbs_set_serialnumber(context, tbs, &serialNumber);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_init");
+ der_free_heim_integer(&serialNumber);
+ }
+
+ if (spki.subjectPublicKey.length) {
+ ret = hx509_ca_tbs_set_spki(context, tbs, &spki);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_set_spki");
+ }
+
+ if (subject) {
+ ret = hx509_ca_tbs_set_subject(context, tbs, subject);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_set_subject");
+ }
+
+ if (opt->crl_uri_string) {
+ ret = hx509_ca_tbs_add_crl_dp_uri(context, tbs,
+ opt->crl_uri_string, NULL);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_add_crl_dp_uri");
+ }
+
+ eval_types(context, tbs, opt);
+
+ if (opt->permanent_id_string) {
+ ret = hx509_ca_tbs_add_san_permanentIdentifier_string(context, tbs,
+ opt->permanent_id_string);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_add_san_permanentIdentifier");
+ }
+
+ if (opt->hardware_module_name_string) {
+ ret = hx509_ca_tbs_add_san_hardwareModuleName_string(context, tbs,
+ opt->hardware_module_name_string);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_add_san_hardwareModuleName_string");
+ }
+
+ for (i = 0; ret == 0 && i < opt->policy_strings.num_strings; i++) {
+ char *oidstr, *uri, *dt;
+
+ if ((oidstr = strdup(opt->policy_strings.strings[i])) == NULL)
+ hx509_err(context, 1, ENOMEM, "out of memory");
+ uri = strchr(oidstr, ':');
+ if (uri)
+ *(uri++) = '\0';
+ dt = strchr(uri ? uri : "", ' ');
+ if (dt)
+ *(dt++) = '\0';
+
+ parse_oid(oidstr, NULL, &oid);
+ ret = hx509_ca_tbs_add_pol(context, tbs, &oid, uri, dt);
+ der_free_oid(&oid);
+ free(oidstr);
+ }
+
+ for (i = 0; ret == 0 && i < opt->policy_mapping_strings.num_strings; i++) {
+ char *issuer_oidstr, *subject_oidstr;
+ heim_oid issuer_oid, subject_oid;
+
+ if ((issuer_oidstr =
+ strdup(opt->policy_mapping_strings.strings[i])) == NULL)
+ hx509_err(context, 1, ENOMEM, "out of memory");
+ subject_oidstr = strchr(issuer_oidstr, ':');
+ if (subject_oidstr == NULL)
+ subject_oidstr = issuer_oidstr;
+ else
+ *(subject_oidstr++) = '\0';
+
+ parse_oid(issuer_oidstr, NULL, &issuer_oid);
+ parse_oid(subject_oidstr, NULL, &subject_oid);
+ ret = hx509_ca_tbs_add_pol_mapping(context, tbs, &issuer_oid,
+ &subject_oid);
+ if (ret)
+ hx509_err(context, 1, ret, "failed to add policy mapping");
+ der_free_oid(&issuer_oid);
+ der_free_oid(&subject_oid);
+ free(issuer_oidstr);
+ }
+
+ if (opt->issue_ca_flag) {
+ ret = hx509_ca_tbs_set_ca(context, tbs, opt->path_length_integer);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_set_ca");
+ }
+ if (opt->issue_proxy_flag) {
+ ret = hx509_ca_tbs_set_proxy(context, tbs, opt->path_length_integer);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_set_proxy");
+ }
+ if (opt->domain_controller_flag) {
+ hx509_ca_tbs_set_domaincontroller(context, tbs);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_set_domaincontroller");
+ }
+
+ if (delta) {
+ ret = hx509_ca_tbs_set_notAfter_lifetime(context, tbs, delta);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_set_notAfter_lifetime");
+ }
+ if (opt->pkinit_max_life_string) {
+ time_t t = parse_time(opt->pkinit_max_life_string, "s");
+
+ ret = hx509_ca_tbs_set_pkinit_max_life(context, tbs, t);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_tbs_set_pkinit_max_life");
+ }
+
+ if (opt->self_signed_flag) {
+ ret = hx509_ca_sign_self(context, tbs, private_key, &cert);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_sign_self");
+ } else {
+ ret = hx509_ca_sign(context, tbs, signer, &cert);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_ca_sign");
+ }
+
+ if (cert_key) {
+ ret = _hx509_cert_assign_key(cert, cert_key);
+ if (ret)
+ hx509_err(context, 1, ret, "_hx509_cert_assign_key");
+ }
+
+ {
+ hx509_certs certs;
+ char *sn = fix_store_name(context, opt->certificate_string, "FILE");
+
+ ret = hx509_certs_init(context, sn, HX509_CERTS_CREATE, NULL, &certs);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init");
+
+ ret = hx509_certs_add(context, certs, cert);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_add");
+
+ ret = hx509_certs_store(context, certs, 0, NULL);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_store");
+
+ hx509_certs_free(&certs);
+ free(sn);
+ }
+
+ if (subject)
+ hx509_name_free(&subject);
+ if (signer)
+ hx509_cert_free(signer);
+ hx509_cert_free(cert);
+ free_SubjectPublicKeyInfo(&spki);
+
+ if (private_key != cert_key)
+ hx509_private_key_free(&private_key);
+ hx509_private_key_free(&cert_key);
+
+ hx509_ca_tbs_free(&tbs);
+
+ return 0;
+}
+
+static int HX509_LIB_CALL
+test_one_cert(hx509_context hxcontext, void *ctx, hx509_cert cert)
+{
+ heim_octet_string sd, c;
+ hx509_verify_ctx vctx = ctx;
+ hx509_certs signer = NULL;
+ heim_oid type;
+ int ret;
+
+ if (_hx509_cert_private_key(cert) == NULL)
+ return 0;
+
+ ret = hx509_cms_create_signed_1(context, 0, NULL, NULL, 0,
+ NULL, cert, NULL, NULL, NULL, &sd);
+ if (ret)
+ errx(1, "hx509_cms_create_signed_1");
+
+ ret = hx509_cms_verify_signed(context, vctx, 0, sd.data, sd.length,
+ NULL, NULL, &type, &c, &signer);
+ free(sd.data);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_cms_verify_signed");
+
+ printf("create-signature verify-sigature done\n");
+
+ free(c.data);
+
+ return 0;
+}
+
+int
+test_crypto(struct test_crypto_options *opt, int argc, char ** argv)
+{
+ hx509_verify_ctx vctx;
+ hx509_certs certs;
+ hx509_lock lock;
+ int i, ret;
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ ret = hx509_certs_init(context, "MEMORY:test-crypto", 0, NULL, &certs);
+ if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY");
+
+ for (i = 0; i < argc; i++) {
+ char *sn = fix_store_name(context, argv[i], "FILE");
+ ret = hx509_certs_append(context, certs, lock, sn);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_append %s", sn);
+ free(sn);
+ }
+
+ ret = hx509_verify_init_ctx(context, &vctx);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_verify_init_ctx");
+
+ hx509_verify_attach_anchors(vctx, certs);
+
+ ret = hx509_certs_iter_f(context, certs, test_one_cert, vctx);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_cert_iter");
+
+ hx509_certs_free(&certs);
+ hx509_verify_destroy_ctx(vctx);
+
+ return 0;
+}
+
+int
+statistic_print(struct statistic_print_options*opt, int argc, char **argv)
+{
+ int type = 0;
+
+ if (stat_file_string == NULL)
+ errx(1, "no stat file");
+
+ if (opt->type_integer)
+ type = opt->type_integer;
+
+ hx509_query_unparse_stats(context, type, stdout);
+ return 0;
+}
+
+/*
+ *
+ */
+
+int
+crl_sign(struct crl_sign_options *opt, int argc, char **argv)
+{
+ hx509_crl crl;
+ heim_octet_string os;
+ hx509_cert signer = NULL;
+ hx509_lock lock;
+ int ret;
+
+ hx509_lock_init(context, &lock);
+ lock_strings(lock, &opt->pass_strings);
+
+ ret = hx509_crl_alloc(context, &crl);
+ if (ret)
+ errx(1, "crl alloc");
+
+ if (opt->signer_string == NULL)
+ errx(1, "signer missing");
+
+ {
+ hx509_certs certs = NULL;
+ hx509_query *q;
+ char *sn = fix_store_name(context, opt->signer_string, "FILE");
+
+ ret = hx509_certs_init(context, sn, 0, NULL, &certs);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_init: %s", sn);
+
+ ret = hx509_query_alloc(context, &q);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_query_alloc: %d", ret);
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+
+ ret = hx509_certs_find(context, certs, q, &signer);
+ hx509_query_free(context, q);
+ hx509_certs_free(&certs);
+ if (ret)
+ hx509_err(context, 1, ret, "no signer certificate found");
+ free(sn);
+ }
+
+ if (opt->lifetime_string) {
+ int delta;
+
+ delta = parse_time(opt->lifetime_string, "day");
+ if (delta < 0)
+ errx(1, "Invalid lifetime: %s", opt->lifetime_string);
+
+ hx509_crl_lifetime(context, crl, delta);
+ }
+
+ {
+ hx509_certs revoked = NULL;
+ int i;
+
+ ret = hx509_certs_init(context, "MEMORY:revoked-certs", 0,
+ NULL, &revoked);
+ if (ret)
+ hx509_err(context, 1, ret,
+ "hx509_certs_init: MEMORY cert");
+
+ for (i = 0; i < argc; i++) {
+ char *sn = fix_store_name(context, argv[i], "FILE");
+
+ ret = hx509_certs_append(context, revoked, lock, sn);
+ if (ret)
+ hx509_err(context, 1, ret, "hx509_certs_append: %s", sn);
+ free(sn);
+ }
+
+ hx509_crl_add_revoked_certs(context, crl, revoked);
+ hx509_certs_free(&revoked);
+ }
+
+ hx509_crl_sign(context, signer, crl, &os);
+
+ if (opt->crl_file_string)
+ rk_dumpdata(opt->crl_file_string, os.data, os.length);
+
+ free(os.data);
+
+ hx509_crl_free(context, &crl);
+ hx509_cert_free(signer);
+ hx509_lock_free(lock);
+
+ return 0;
+}
+
+int
+hxtool_list_oids(void *opt, int argc, char **argv)
+{
+ const heim_oid *oid;
+ int cursor = -1;
+
+ while (der_match_heim_oid_by_name("", &cursor, &oid) == 0) {
+ char *s = NULL;
+
+ if ((errno = der_print_heim_oid_sym(oid, '.', &s)) > 0)
+ err(1, "der_print_heim_oid_sym");
+ printf("%s\n", s);
+ free(s);
+ }
+ return 0;
+}
+
+static int
+acert1_sans_utf8_other(struct acert_options *opt,
+ struct getarg_strings *wanted,
+ const char *type,
+ heim_any *san,
+ size_t *count)
+{
+ size_t k, len;
+
+ if (!wanted->num_strings)
+ return 0;
+ for (k = 0; k < wanted->num_strings; k++) {
+ len = strlen(wanted->strings[k]);
+ if (len == san->length &&
+ strncmp(san->data, wanted->strings[k], len) == 0) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Matched OtherName SAN %s (%s)\n",
+ wanted->strings[k], type);
+ (*count)++;
+ return 0;
+ }
+ }
+ if (opt->verbose_flag)
+ fprintf(stderr, "Did not match OtherName SAN %s (%s)\n",
+ wanted->strings[k], type);
+ return -1;
+}
+
+static int
+acert1_sans_other(struct acert_options *opt,
+ heim_oid *type_id,
+ heim_any *value,
+ size_t *count)
+{
+ heim_any pkinit;
+ size_t k, match;
+ const char *type_str = NULL;
+ char *s = NULL;
+ int ret;
+
+ (void) der_print_heim_oid_sym(type_id, '.', &s);
+ type_str = s ? s : "<unknown>";
+ if (der_heim_oid_cmp(type_id, &asn1_oid_id_pkix_on_xmppAddr) == 0) {
+ ret = acert1_sans_utf8_other(opt, &opt->has_xmpp_san_strings,
+ s ? s : "xmpp", value, count);
+ free(s);
+ return ret;
+ }
+ if (der_heim_oid_cmp(type_id, &asn1_oid_id_pkinit_san) != 0) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Ignoring OtherName SAN of type %s\n", type_str);
+ free(s);
+ return -1;
+ }
+
+ free(s);
+ type_str = s = NULL;
+
+ if (opt->has_pkinit_san_strings.num_strings == 0)
+ return 0;
+
+ for (k = 0; k < opt->has_pkinit_san_strings.num_strings; k++) {
+ const char *s2 = opt->has_pkinit_san_strings.strings[k];
+
+ if ((ret = _hx509_make_pkinit_san(context, s2, &pkinit)))
+ return ret;
+ match = (pkinit.length == value->length &&
+ memcmp(pkinit.data, value->data, pkinit.length) == 0);
+ free(pkinit.data);
+ if (match) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Matched PKINIT SAN %s\n", s2);
+ (*count)++;
+ return 0;
+ }
+ }
+ if (opt->verbose_flag)
+ fprintf(stderr, "Unexpected PKINIT SAN\n");
+ return -1;
+}
+
+static int
+acert1_sans(struct acert_options *opt,
+ Extension *e,
+ size_t *count,
+ size_t *found)
+{
+ heim_printable_string hps;
+ GeneralNames gns;
+ size_t i, k, sz;
+ size_t unwanted = 0;
+ int ret = 0;
+
+ memset(&gns, 0, sizeof(gns));
+ decode_GeneralNames(e->extnValue.data, e->extnValue.length, &gns, &sz);
+ for (i = 0; (ret == -1 || ret == 0) && i < gns.len; i++) {
+ GeneralName *gn = &gns.val[i];
+ const char *s;
+
+ (*found)++;
+ if (gn->element == choice_GeneralName_rfc822Name) {
+ for (k = 0; k < opt->has_email_san_strings.num_strings; k++) {
+ s = opt->has_email_san_strings.strings[k];
+ hps.data = rk_UNCONST(s);
+ hps.length = strlen(s);
+ if (der_printable_string_cmp(&gn->u.rfc822Name, &hps) == 0) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Matched e-mail address SAN %s\n", s);
+ (*count)++;
+ break;
+ }
+ }
+ if (k && k == opt->has_email_san_strings.num_strings) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Unexpected e-mail address SAN %.*s\n",
+ (int)gn->u.rfc822Name.length,
+ (const char *)gn->u.rfc822Name.data);
+ unwanted++;
+ }
+ } else if (gn->element == choice_GeneralName_dNSName) {
+ for (k = 0; k < opt->has_dnsname_san_strings.num_strings; k++) {
+ s = opt->has_dnsname_san_strings.strings[k];
+ hps.data = rk_UNCONST(s);
+ hps.length = strlen(s);
+ if (der_printable_string_cmp(&gn->u.dNSName, &hps) == 0) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Matched dNSName SAN %s\n", s);
+ (*count)++;
+ break;
+ }
+ }
+ if (k && k == opt->has_dnsname_san_strings.num_strings) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Unexpected e-mail address SAN %.*s\n",
+ (int)gn->u.dNSName.length,
+ (const char *)gn->u.dNSName.data);
+ unwanted++;
+ }
+ } else if (gn->element == choice_GeneralName_registeredID) {
+ for (k = 0; k < opt->has_registeredID_san_strings.num_strings; k++) {
+ heim_oid oid;
+
+ s = opt->has_registeredID_san_strings.strings[k];
+ memset(&oid, 0, sizeof(oid));
+ parse_oid(s, NULL, &oid);
+ if (der_heim_oid_cmp(&gn->u.registeredID, &oid) == 0) {
+ der_free_oid(&oid);
+ if (opt->verbose_flag)
+ fprintf(stderr, "Matched registeredID SAN %s\n", s);
+ (*count)++;
+ break;
+ }
+ der_free_oid(&oid);
+ }
+ if (k && k == opt->has_dnsname_san_strings.num_strings) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Unexpected registeredID SAN\n");
+ unwanted++;
+ }
+ } else if (gn->element == choice_GeneralName_otherName) {
+ ret = acert1_sans_other(opt, &gn->u.otherName.type_id,
+ &gn->u.otherName.value, count);
+ } else if (opt->verbose_flag) {
+ fprintf(stderr, "Unexpected unsupported SAN\n");
+ unwanted++;
+ }
+ }
+ free_GeneralNames(&gns);
+ if (ret == 0 && unwanted && opt->exact_flag)
+ return -1;
+ return ret;
+}
+
+static int
+acert1_ekus(struct acert_options *opt,
+ Extension *e,
+ size_t *count,
+ size_t *found)
+{
+ ExtKeyUsage eku;
+ size_t i, k, sz;
+ size_t unwanted = 0;
+ int ret = 0;
+
+ memset(&eku, 0, sizeof(eku));
+ decode_ExtKeyUsage(e->extnValue.data, e->extnValue.length, &eku, &sz);
+ for (i = 0; (ret == -1 || ret == 0) && i < eku.len; i++) {
+ (*found)++;
+ for (k = 0; k < opt->has_eku_strings.num_strings; k++) {
+ const char *s = opt->has_eku_strings.strings[k];
+ heim_oid oid;
+
+ memset(&oid, 0, sizeof(oid));
+ parse_oid(s, NULL, &oid);
+ if (der_heim_oid_cmp(&eku.val[i], &oid) == 0) {
+ der_free_oid(&oid);
+ if (opt->verbose_flag)
+ fprintf(stderr, "Matched EKU OID %s\n", s);
+ (*count)++;
+ break;
+ }
+ der_free_oid(&oid);
+ }
+ if (k && k == opt->has_eku_strings.num_strings) {
+ char *oids = NULL;
+
+ (void) der_print_heim_oid_sym(&eku.val[i], '.', &oids);
+ if (opt->verbose_flag)
+ fprintf(stderr, "Unexpected EKU OID %s\n",
+ oids ? oids : "<could-not-format-OID>");
+ unwanted++;
+ }
+ }
+ free_ExtKeyUsage(&eku);
+ if (ret == 0 && unwanted && opt->exact_flag)
+ return -1;
+ return ret;
+}
+
+static int
+acert1_kus(struct acert_options *opt,
+ Extension *e,
+ size_t *count,
+ size_t *found)
+{
+ const struct units *u = asn1_KeyUsage_units();
+ uint64_t ku_num;
+ KeyUsage ku;
+ size_t unwanted = 0;
+ size_t wanted = opt->has_ku_strings.num_strings;
+ size_t i, k, sz;
+ int ret;
+
+ memset(&ku, 0, sizeof(ku));
+ ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, &ku, &sz);
+ if (ret)
+ return ret;
+ ku_num = KeyUsage2int(ku);
+
+ /* Validate requested key usage values */
+ for (k = 0; k < wanted; k++) {
+ const char *s = opt->has_ku_strings.strings[k];
+
+ for (i = 0; u[i].name; i++)
+ if (strcmp(s, u[i].name) == 0)
+ break;
+
+ if (u[i].name == NULL)
+ warnx("Warning: requested key usage %s unknown", s);
+ }
+
+ for (i = 0; u[i].name; i++) {
+ if ((u[i].mult & ku_num))
+ (*found)++;
+ for (k = 0; k < wanted; k++) {
+ const char *s = opt->has_ku_strings.strings[k];
+
+ if (!(u[i].mult & ku_num) || strcmp(s, u[i].name) != 0)
+ continue;
+
+ if (opt->verbose_flag)
+ fprintf(stderr, "Matched key usage %s\n", s);
+ (*count)++;
+ break;
+ }
+ if ((u[i].mult & ku_num) && k == wanted) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Unexpected key usage %s\n", u[i].name);
+ unwanted++;
+ }
+ }
+
+ return (unwanted && opt->exact_flag) ? -1 : 0;
+}
+
+static time_t
+ptime(const char *s)
+{
+ struct tm at_tm;
+ char *rest;
+ int at_s;
+
+ if ((rest = strptime(s, "%Y-%m-%dT%H:%M:%S", &at_tm)) != NULL &&
+ rest[0] == '\0')
+ return mktime(&at_tm);
+ if ((rest = strptime(s, "%Y%m%d%H%M%S", &at_tm)) != NULL && rest[0] == '\0')
+ return mktime(&at_tm);
+ if ((at_s = parse_time(s, "s")) != -1)
+ return time(NULL) + at_s;
+ errx(1, "Could not parse time spec %s", s);
+}
+
+static int
+acert1_validity(struct acert_options *opt, hx509_cert cert)
+{
+ time_t not_before_eq = 0;
+ time_t not_before_lt = 0;
+ time_t not_before_gt = 0;
+ time_t not_after_eq = 0;
+ time_t not_after_lt = 0;
+ time_t not_after_gt = 0;
+ int ret = 0;
+
+ if (opt->valid_now_flag) {
+ time_t now = time(NULL);
+
+ if (hx509_cert_get_notBefore(cert) > now) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate not valid yet\n");
+ ret = -1;
+ }
+ if (hx509_cert_get_notAfter(cert) < now) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate currently expired\n");
+ ret = -1;
+ }
+ }
+ if (opt->valid_at_string) {
+ time_t at = ptime(opt->valid_at_string);
+
+ if (hx509_cert_get_notBefore(cert) > at) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate not valid yet at %s\n",
+ opt->valid_at_string);
+ ret = -1;
+ }
+ if (hx509_cert_get_notAfter(cert) < at) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate expired before %s\n",
+ opt->valid_at_string);
+ ret = -1;
+ }
+ }
+
+ if (opt->not_before_eq_string)
+ not_before_eq = ptime(opt->not_before_eq_string);
+ if (opt->not_before_lt_string)
+ not_before_lt = ptime(opt->not_before_lt_string);
+ if (opt->not_before_gt_string)
+ not_before_gt = ptime(opt->not_before_gt_string);
+ if (opt->not_after_eq_string)
+ not_after_eq = ptime(opt->not_after_eq_string);
+ if (opt->not_after_lt_string)
+ not_after_lt = ptime(opt->not_after_lt_string);
+ if (opt->not_after_gt_string)
+ not_after_gt = ptime(opt->not_after_gt_string);
+
+ if ((not_before_eq && hx509_cert_get_notBefore(cert) != not_before_eq) ||
+ (not_before_lt && hx509_cert_get_notBefore(cert) >= not_before_lt) ||
+ (not_before_gt && hx509_cert_get_notBefore(cert) <= not_before_gt)) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate notBefore not as requested\n");
+ ret = -1;
+ }
+ if ((not_after_eq && hx509_cert_get_notAfter(cert) != not_after_eq) ||
+ (not_after_lt && hx509_cert_get_notAfter(cert) >= not_after_lt) ||
+ (not_after_gt && hx509_cert_get_notAfter(cert) <= not_after_gt)) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate notAfter not as requested\n");
+ ret = -1;
+ }
+
+ if (opt->has_private_key_flag && !hx509_cert_have_private_key(cert)) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate does not have a private key\n");
+ ret = -1;
+ }
+
+ if (opt->lacks_private_key_flag && hx509_cert_have_private_key(cert)) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate does not have a private key\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int
+acert1(struct acert_options *opt, size_t cert_num, hx509_cert cert, int *matched)
+{
+ const heim_oid *misc_exts [] = {
+ &asn1_oid_id_x509_ce_authorityKeyIdentifier,
+ &asn1_oid_id_x509_ce_subjectKeyIdentifier,
+ &asn1_oid_id_x509_ce_basicConstraints,
+ &asn1_oid_id_x509_ce_nameConstraints,
+ &asn1_oid_id_x509_ce_certificatePolicies,
+ &asn1_oid_id_x509_ce_policyMappings,
+ &asn1_oid_id_x509_ce_issuerAltName,
+ &asn1_oid_id_x509_ce_subjectDirectoryAttributes,
+ &asn1_oid_id_x509_ce_policyConstraints,
+ &asn1_oid_id_x509_ce_cRLDistributionPoints,
+ &asn1_oid_id_x509_ce_deltaCRLIndicator,
+ &asn1_oid_id_x509_ce_issuingDistributionPoint,
+ &asn1_oid_id_x509_ce_inhibitAnyPolicy,
+ &asn1_oid_id_x509_ce_cRLNumber,
+ &asn1_oid_id_x509_ce_freshestCRL,
+ NULL
+ };
+ const Certificate *c;
+ const Extensions *e;
+ KeyUsage ku;
+ size_t matched_elements = 0;
+ size_t wanted, sans_wanted, ekus_wanted, kus_wanted;
+ size_t found, sans_found, ekus_found, kus_found;
+ size_t i, k;
+ int ret;
+
+ if ((c = _hx509_get_cert(cert)) == NULL)
+ errx(1, "Could not get Certificate");
+ e = c->tbsCertificate.extensions;
+
+ ret = _hx509_cert_get_keyusage(context, cert, &ku);
+ if (ret && ret != HX509_KU_CERT_MISSING)
+ hx509_err(context, 1, ret, "Could not get key usage of certificate");
+ if (ret == HX509_KU_CERT_MISSING && opt->ca_flag)
+ return 0; /* want CA cert; this isn't it */
+ if (ret == 0 && opt->ca_flag && !ku.keyCertSign)
+ return 0; /* want CA cert; this isn't it */
+ if (ret == 0 && opt->end_entity_flag && ku.keyCertSign)
+ return 0; /* want EE cert; this isn't it */
+
+ if (opt->cert_num_integer != -1 && cert_num <= INT_MAX &&
+ opt->cert_num_integer != (int)cert_num)
+ return 0;
+ if (opt->cert_num_integer == -1 || opt->cert_num_integer == (int)cert_num)
+ *matched = 1;
+
+ if (_hx509_cert_get_version(c) < 3) {
+ warnx("Certificate with version %d < 3 ignored",
+ _hx509_cert_get_version(c));
+ return 0;
+ }
+
+ sans_wanted = opt->has_email_san_strings.num_strings
+ + opt->has_xmpp_san_strings.num_strings
+ + opt->has_ms_upn_san_strings.num_strings
+ + opt->has_dnsname_san_strings.num_strings
+ + opt->has_pkinit_san_strings.num_strings
+ + opt->has_registeredID_san_strings.num_strings;
+ ekus_wanted = opt->has_eku_strings.num_strings;
+ kus_wanted = opt->has_ku_strings.num_strings;
+ wanted = sans_wanted + ekus_wanted + kus_wanted;
+ sans_found = ekus_found = kus_found = 0;
+
+ if (e == NULL) {
+ if (wanted)
+ return -1;
+ return acert1_validity(opt, cert);
+ }
+
+ for (i = 0; i < e->len; i++) {
+ if (der_heim_oid_cmp(&e->val[i].extnID,
+ &asn1_oid_id_x509_ce_subjectAltName) == 0) {
+ ret = acert1_sans(opt, &e->val[i], &matched_elements, &sans_found);
+ if (ret == -1 && sans_wanted == 0 &&
+ (!opt->exact_flag || sans_found == 0))
+ ret = 0;
+ } else if (der_heim_oid_cmp(&e->val[i].extnID,
+ &asn1_oid_id_x509_ce_extKeyUsage) == 0) {
+ ret = acert1_ekus(opt, &e->val[i], &matched_elements, &ekus_found);
+ if (ret == -1 && ekus_wanted == 0 &&
+ (!opt->exact_flag || ekus_found == 0))
+ ret = 0;
+ } else if (der_heim_oid_cmp(&e->val[i].extnID,
+ &asn1_oid_id_x509_ce_keyUsage) == 0) {
+ ret = acert1_kus(opt, &e->val[i], &matched_elements, &kus_found);
+ if (ret == -1 && kus_wanted == 0 &&
+ (!opt->exact_flag || kus_found == 0))
+ ret = 0;
+ } else {
+ char *oids = NULL;
+
+ for (k = 0; misc_exts[k]; k++) {
+ if (der_heim_oid_cmp(&e->val[i].extnID, misc_exts[k]) == 0)
+ break;
+ }
+ if (misc_exts[k])
+ continue;
+
+ (void) der_print_heim_oid(&e->val[i].extnID, '.', &oids);
+ warnx("Matching certificate has unexpected certificate "
+ "extension %s", oids ? oids : "<could not display OID>");
+ free(oids);
+ ret = -1;
+ }
+ if (ret && ret != -1)
+ hx509_err(context, 1, ret, "Error checking matching certificate");
+ if (ret == -1)
+ break;
+ }
+ if (matched_elements != wanted)
+ return -1;
+ found = sans_found + ekus_found + kus_found;
+ if (matched_elements != found && opt->exact_flag)
+ return -1;
+ if (ret)
+ return ret;
+ return acert1_validity(opt, cert);
+}
+
+int
+acert(struct acert_options *opt, int argc, char **argv)
+{
+ hx509_cursor cursor = NULL;
+ hx509_query *q = NULL;
+ hx509_certs certs = NULL;
+ hx509_cert cert = NULL;
+ char *sn = fix_store_name(context, argv[0], "FILE");
+ size_t n = 0;
+ int matched = 0;
+ int ret;
+
+ if (opt->not_after_eq_string &&
+ (opt->not_after_lt_string || opt->not_after_gt_string))
+ errx(1, "--not-after-eq should not be given with --not-after-lt/gt");
+ if (opt->not_before_eq_string &&
+ (opt->not_before_lt_string || opt->not_before_gt_string))
+ errx(1, "--not-before-eq should not be given with --not-before-lt/gt");
+
+ if ((ret = hx509_certs_init(context, sn, 0, NULL, &certs)))
+ hx509_err(context, 1, ret, "Could not load certificates from %s", sn);
+
+ if (opt->expr_string) {
+ if ((ret = hx509_query_alloc(context, &q)) ||
+ (ret = hx509_query_match_expr(context, q, opt->expr_string)))
+ hx509_err(context, 1, ret, "Could not initialize query");
+ if ((ret = hx509_certs_find(context, certs, q, &cert)) || !cert)
+ hx509_err(context, 1, ret, "No matching certificate");
+ ret = acert1(opt, -1, cert, &matched);
+ matched = 1;
+ } else {
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ while (ret == 0 &&
+ (ret = hx509_certs_next_cert(context, certs,
+ cursor, &cert)) == 0 &&
+ cert) {
+ ret = acert1(opt, n++, cert, &matched);
+ if (matched)
+ break;
+ hx509_cert_free(cert);
+ cert = NULL;
+ }
+ if (cursor)
+ (void) hx509_certs_end_seq(context, certs, cursor);
+ }
+ if (!matched && ret)
+ hx509_err(context, 1, ret, "Could not find certificate");
+ if (!matched)
+ errx(1, "Could not find certificate");
+ if (ret == -1)
+ errx(1, "Matching certificate did not meet requirements");
+ if (ret)
+ hx509_err(context, 1, ret, "Matching certificate did not meet "
+ "requirements");
+ hx509_cert_free(cert);
+ free(sn);
+ return 0;
+}
+
+/*
+ *
+ */
+
+int
+help(void *opt, int argc, char **argv)
+{
+ sl_slc_help(commands, argc, argv);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int ret, optidx = 0;
+
+ setprogname (argv[0]);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+ if(help_flag)
+ usage(0);
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+ argv += optidx;
+ argc -= optidx;
+
+ if (argc == 0)
+ usage(1);
+
+ ret = hx509_context_init(&context);
+ if (ret)
+ errx(1, "hx509_context_init failed with %d", ret);
+
+ if (stat_file_string)
+ hx509_query_statistic_file(context, stat_file_string);
+
+ ret = sl_command(commands, argc, argv);
+ if(ret == -1)
+ warnx ("unrecognized command: %s", argv[0]);
+
+ hx509_context_free(&context);
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/hx509/keyset.c b/third_party/heimdal/lib/hx509/keyset.c
new file mode 100644
index 0000000..f25cdf4
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/keyset.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/**
+ * @page page_keyset Certificate store operations
+ *
+ * Type of certificates store:
+ * - MEMORY
+ * In memory based format. Doesn't support storing.
+ * - FILE
+ * FILE supports raw DER certicates and PEM certicates. When PEM is
+ * used the file can contain may certificates and match private
+ * keys. Support storing the certificates. DER format only supports
+ * on certificate and no private key.
+ * - PEM-FILE
+ * Same as FILE, defaulting to PEM encoded certificates.
+ * - PEM-FILE
+ * Same as FILE, defaulting to DER encoded certificates.
+ * - PKCS11
+ * - PKCS12
+ * - DIR
+ * - KEYCHAIN
+ * Apple Mac OS X KeyChain backed keychain object.
+ *
+ * See the library functions here: @ref hx509_keyset
+ */
+
+struct hx509_certs_data {
+ unsigned int ref;
+ struct hx509_keyset_ops *ops;
+ void *ops_data;
+ int flags;
+};
+
+struct hx509_keyset_ops *
+_hx509_ks_type(hx509_context context, const char *type)
+{
+ int i;
+
+ for (i = 0; i < context->ks_num_ops; i++)
+ if (strcasecmp(type, context->ks_ops[i]->name) == 0)
+ return context->ks_ops[i];
+
+ return NULL;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_ks_register(hx509_context context, struct hx509_keyset_ops *ops)
+{
+ struct hx509_keyset_ops **val;
+
+ if (_hx509_ks_type(context, ops->name))
+ return;
+
+ val = realloc(context->ks_ops,
+ (context->ks_num_ops + 1) * sizeof(context->ks_ops[0]));
+ if (val == NULL)
+ return;
+ val[context->ks_num_ops] = ops;
+ context->ks_ops = val;
+ context->ks_num_ops++;
+}
+
+/**
+ * Open or creates a new hx509 certificate store.
+ *
+ * @param context A hx509 context
+ * @param name name of the store, format is TYPE:type-specific-string,
+ * if NULL is used the MEMORY store is used.
+ * @param flags list of flags:
+ * - HX509_CERTS_CREATE create a new keystore of the specific TYPE.
+ * - HX509_CERTS_UNPROTECT_ALL fails if any private key failed to be extracted.
+ * - HX509_CERTS_NO_PRIVATE_KEYS does not load or permit adding private keys
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ * @param certs return pointer, free with hx509_certs_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_init(hx509_context context,
+ const char *name, int flags,
+ hx509_lock lock, hx509_certs *certs)
+{
+ struct hx509_keyset_ops *ops;
+ const char *residue;
+ hx509_certs c;
+ char *type;
+ int ret;
+
+ *certs = NULL;
+
+ if (name == NULL)
+ name = "";
+
+ residue = strchr(name, ':');
+ if (residue) {
+ type = malloc(residue - name + 1);
+ if (type)
+ strlcpy(type, name, residue - name + 1);
+ residue++;
+ if (residue[0] == '\0')
+ residue = NULL;
+ } else {
+ type = strdup("MEMORY");
+ residue = name;
+ }
+ if (type == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ops = _hx509_ks_type(context, type);
+ if (ops == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Keyset type %s is not supported", type);
+ free(type);
+ return ENOENT;
+ }
+ free(type);
+ c = calloc(1, sizeof(*c));
+ if (c == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ c->flags = flags;
+ c->ops = ops;
+ c->ref = 1;
+
+ ret = (*ops->init)(context, c, &c->ops_data, flags, residue, lock);
+ if (ret) {
+ free(c);
+ return ret;
+ }
+
+ *certs = c;
+ return 0;
+}
+
+/**
+ * Destroys and frees a hx509 certificate store.
+ *
+ * @param context A hx509 context
+ * @param certs A store to destroy
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_destroy(hx509_context context,
+ hx509_certs *certs)
+{
+ int ret = 0;
+
+ if (*certs) {
+ if ((*certs)->ops->destroy)
+ ret = ((*certs)->ops->destroy)(context, *certs, (*certs)->ops_data);
+ else
+ ret = ENOTSUP;
+ }
+ hx509_certs_free(certs);
+ return ret;
+}
+
+/**
+ * Write the certificate store to stable storage.
+ *
+ * Use the HX509_CERTS_STORE_NO_PRIVATE_KEYS flag to ensure that no private
+ * keys are stored, even if added.
+ *
+ * @param context A hx509 context.
+ * @param certs a certificate store to store.
+ * @param flags currently one flag is defined: HX509_CERTS_STORE_NO_PRIVATE_KEYS
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ *
+ * @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION if
+ * the certificate store doesn't support the store operation.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_store(hx509_context context,
+ hx509_certs certs,
+ int flags,
+ hx509_lock lock)
+{
+ if (certs->ops->store == NULL) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "keystore if type %s doesn't support "
+ "store operation",
+ certs->ops->name);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+ return (*certs->ops->store)(context, certs, certs->ops_data, flags, lock);
+}
+
+
+HX509_LIB_FUNCTION hx509_certs HX509_LIB_CALL
+hx509_certs_ref(hx509_certs certs)
+{
+ if (certs == NULL)
+ return NULL;
+ if (certs->ref == 0)
+ _hx509_abort("certs refcount == 0 on ref");
+ if (certs->ref == UINT_MAX)
+ _hx509_abort("certs refcount == UINT_MAX on ref");
+ certs->ref++;
+ return certs;
+}
+
+/**
+ * Free a certificate store.
+ *
+ * @param certs certificate store to free.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_certs_free(hx509_certs *certs)
+{
+ if (*certs) {
+ if ((*certs)->ref == 0)
+ _hx509_abort("cert refcount == 0 on free");
+ if (--(*certs)->ref > 0)
+ return;
+
+ (*(*certs)->ops->free)(*certs, (*certs)->ops_data);
+ free(*certs);
+ *certs = NULL;
+ }
+}
+
+/**
+ * Start the integration
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over
+ * @param cursor cursor that will keep track of progress, free with
+ * hx509_certs_end_seq().
+ *
+ * @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION is
+ * returned if the certificate store doesn't support the iteration
+ * operation.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_start_seq(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor *cursor)
+{
+ int ret;
+
+ if (certs->ops->iter_start == NULL) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "Keyset type %s doesn't support iteration",
+ certs->ops->name);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+ ret = (*certs->ops->iter_start)(context, certs, certs->ops_data, cursor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * Get next ceritificate from the certificate keystore pointed out by
+ * cursor.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param cursor cursor that keeps track of progress.
+ * @param cert return certificate next in store, NULL if the store
+ * contains no more certificates. Free with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_next_cert(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor cursor,
+ hx509_cert *cert)
+{
+ *cert = NULL;
+ return (*certs->ops->iter)(context, certs, certs->ops_data, cursor, cert);
+}
+
+/**
+ * End the iteration over certificates.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param cursor cursor that will keep track of progress, freed.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_end_seq(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor cursor)
+{
+ (*certs->ops->iter_end)(context, certs, certs->ops_data, cursor);
+ return 0;
+}
+
+/**
+ * Iterate over all certificates in a keystore and call a function
+ * for each of them.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param func function to call for each certificate. The function
+ * should return non-zero to abort the iteration, that value is passed
+ * back to the caller of hx509_certs_iter_f().
+ * @param ctx context variable that will passed to the function.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_iter_f(hx509_context context,
+ hx509_certs certs,
+ int (HX509_LIB_CALL *func)(hx509_context, void *, hx509_cert),
+ void *ctx)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret;
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL) {
+ ret = 0;
+ break;
+ }
+ ret = (*func)(context, ctx, c);
+ hx509_cert_free(c);
+ if (ret)
+ break;
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+
+ return ret;
+}
+
+#ifdef __BLOCKS__
+
+static int
+certs_iter(hx509_context context, void *ctx, hx509_cert cert)
+{
+ int (^func)(hx509_cert) = ctx;
+ return func(cert);
+}
+
+/**
+ * Iterate over all certificates in a keystore and call a block
+ * for each of them.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param func block to call for each certificate. The function
+ * should return non-zero to abort the iteration, that value is passed
+ * back to the caller of hx509_certs_iter().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_iter(hx509_context context,
+ hx509_certs certs,
+ int (^func)(hx509_cert))
+{
+ return hx509_certs_iter_f(context, certs, certs_iter, func);
+}
+#endif
+
+
+/**
+ * Function to use to hx509_certs_iter_f() as a function argument, the
+ * ctx variable to hx509_certs_iter_f() should be a FILE file descriptor.
+ *
+ * @param context a hx509 context.
+ * @param ctx used by hx509_certs_iter_f().
+ * @param c a certificate
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ci_print_names(hx509_context context, void *ctx, hx509_cert c)
+{
+ Certificate *cert;
+ hx509_name n;
+ char *s, *i;
+
+ cert = _hx509_get_cert(c);
+
+ _hx509_name_from_Name(&cert->tbsCertificate.subject, &n);
+ hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ _hx509_name_from_Name(&cert->tbsCertificate.issuer, &n);
+ hx509_name_to_string(n, &i);
+ hx509_name_free(&n);
+ fprintf(ctx, "subject: %s\nissuer: %s\n", s, i);
+ free(s);
+ free(i);
+ return 0;
+}
+
+/**
+ * Add a certificate to the certificiate store.
+ *
+ * The receiving keyset certs will either increase reference counter
+ * of the cert or make a deep copy, either way, the caller needs to
+ * free the cert itself.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to add the certificate to.
+ * @param cert certificate to add.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert)
+{
+ hx509_cert copy = NULL;
+ int ret;
+
+ if (certs->ops->add == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Keyset type %s doesn't support add operation",
+ certs->ops->name);
+ return ENOENT;
+ }
+
+ if ((certs->flags & HX509_CERTS_NO_PRIVATE_KEYS) &&
+ hx509_cert_have_private_key(cert)) {
+ if ((copy = hx509_cert_copy_no_private_key(context, cert,
+ NULL)) == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Could not add certificate to store");
+ return ENOMEM;
+ }
+ cert = copy;
+ }
+
+ ret = (*certs->ops->add)(context, certs, certs->ops_data, cert);
+ hx509_cert_free(copy);
+ return ret;
+}
+
+/**
+ * Find a certificate matching the query.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to search.
+ * @param q query allocated with @ref hx509_query functions.
+ * @param r return certificate (or NULL on error), should be freed
+ * with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_find(hx509_context context,
+ hx509_certs certs,
+ const hx509_query *q,
+ hx509_cert *r)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret;
+
+ *r = NULL;
+
+ _hx509_query_statistic(context, 0, q);
+
+ if (certs->ops->query)
+ return (*certs->ops->query)(context, certs, certs->ops_data, q, r);
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ c = NULL;
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL)
+ break;
+ if (_hx509_query_match_cert(context, q, c)) {
+ *r = c;
+ c = NULL;
+ break;
+ }
+ hx509_cert_free(c);
+ c = NULL;
+ }
+
+ hx509_cert_free(c);
+ hx509_certs_end_seq(context, certs, cursor);
+ if (ret)
+ return ret;
+ /**
+ * Return HX509_CERT_NOT_FOUND if no certificate in certs matched
+ * the query.
+ */
+ if (*r == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_CERT_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+/**
+ * Filter certificate matching the query.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to search.
+ * @param q query allocated with @ref hx509_query functions.
+ * @param result the filtered certificate store, caller must free with
+ * hx509_certs_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_filter(hx509_context context,
+ hx509_certs certs,
+ const hx509_query *q,
+ hx509_certs *result)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret, found = 0;
+
+ _hx509_query_statistic(context, 0, q);
+
+ ret = hx509_certs_init(context, "MEMORY:filter-certs", 0,
+ NULL, result);
+ if (ret)
+ return ret;
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret) {
+ hx509_certs_free(result);
+ return ret;
+ }
+
+ c = NULL;
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL)
+ break;
+ if (_hx509_query_match_cert(context, q, c)) {
+ hx509_certs_add(context, *result, c);
+ found = 1;
+ }
+ hx509_cert_free(c);
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+ if (ret) {
+ hx509_certs_free(result);
+ return ret;
+ }
+
+ /**
+ * Return HX509_CERT_NOT_FOUND if no certificate in certs matched
+ * the query.
+ */
+ if (!found) {
+ hx509_certs_free(result);
+ hx509_clear_error_string(context);
+ return HX509_CERT_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+
+static int HX509_LIB_CALL
+certs_merge_func(hx509_context context, void *ctx, hx509_cert c)
+{
+ return hx509_certs_add(context, (hx509_certs)ctx, c);
+}
+
+/**
+ * Merge one certificate store into another. The from store is kept intact.
+ *
+ * @param context a hx509 context.
+ * @param to the store to merge into.
+ * @param from the store to copy the object from.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_merge(hx509_context context, hx509_certs to, hx509_certs from)
+{
+ if (from == NULL)
+ return 0;
+ return hx509_certs_iter_f(context, from, certs_merge_func, to);
+}
+
+/**
+ * Same a hx509_certs_merge() but use a lock and name to describe the
+ * from source.
+ *
+ * @param context a hx509 context.
+ * @param to the store to merge into.
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ * @param name name of the source store
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_append(hx509_context context,
+ hx509_certs to,
+ hx509_lock lock,
+ const char *name)
+{
+ hx509_certs s;
+ int ret;
+
+ ret = hx509_certs_init(context, name, 0, lock, &s);
+ if (ret)
+ return ret;
+ ret = hx509_certs_merge(context, to, s);
+ hx509_certs_free(&s);
+ return ret;
+}
+
+/**
+ * Get one random certificate from the certificate store.
+ *
+ * @param context a hx509 context.
+ * @param certs a certificate store to get the certificate from.
+ * @param c return certificate, should be freed with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_get_one_cert(hx509_context context, hx509_certs certs, hx509_cert *c)
+{
+ hx509_cursor cursor;
+ int ret;
+
+ *c = NULL;
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ ret = hx509_certs_next_cert(context, certs, cursor, c);
+ if (ret)
+ return ret;
+
+ hx509_certs_end_seq(context, certs, cursor);
+ return 0;
+}
+
+static int
+certs_info_stdio(void *ctx, const char *str)
+{
+ FILE *f = ctx;
+ fprintf(f, "%s\n", str);
+ return 0;
+}
+
+/**
+ * Print some info about the certificate store.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to print information about.
+ * @param func function that will get each line of the information, if
+ * NULL is used the data is printed on a FILE descriptor that should
+ * be passed in ctx, if ctx also is NULL, stdout is used.
+ * @param ctx parameter to func.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_certs_info(hx509_context context,
+ hx509_certs certs,
+ int (*func)(void *, const char *),
+ void *ctx)
+{
+ if (func == NULL) {
+ func = certs_info_stdio;
+ if (ctx == NULL)
+ ctx = stdout;
+ }
+ if (certs->ops->printinfo == NULL) {
+ (*func)(ctx, "No info function for certs");
+ return 0;
+ }
+ return (*certs->ops->printinfo)(context, certs, certs->ops_data,
+ func, ctx);
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_pi_printf(int (*func)(void *, const char *), void *ctx,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *str;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret == -1 || str == NULL)
+ return;
+ (*func)(ctx, str);
+ free(str);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_certs_keys_get(hx509_context context,
+ hx509_certs certs,
+ hx509_private_key **keys)
+{
+ if (certs->ops->getkeys == NULL) {
+ *keys = NULL;
+ return 0;
+ }
+ return (*certs->ops->getkeys)(context, certs, certs->ops_data, keys);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_certs_keys_add(hx509_context context,
+ hx509_certs certs,
+ hx509_private_key key)
+{
+ if (certs->ops->addkey == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "keystore if type %s doesn't support "
+ "key add operation",
+ certs->ops->name);
+ return EINVAL;
+ }
+ return (*certs->ops->addkey)(context, certs, certs->ops_data, key);
+}
+
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_certs_keys_free(hx509_context context,
+ hx509_private_key *keys)
+{
+ size_t i;
+
+ if (keys == NULL)
+ return;
+ for (i = 0; keys[i]; i++)
+ hx509_private_key_free(&keys[i]);
+ free(keys);
+}
diff --git a/third_party/heimdal/lib/hx509/ks_dir.c b/third_party/heimdal/lib/hx509/ks_dir.c
new file mode 100644
index 0000000..3bc99f2
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ks_dir.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include <dirent.h>
+
+/*
+ * The DIR keyset module is strange compared to the other modules
+ * since it does lazy evaluation and really doesn't keep any local
+ * state except for the directory iteration and cert iteration of
+ * files. DIR ignores most errors so that the consumer doesn't get
+ * failes for stray files in directories.
+ */
+
+struct dircursor {
+ DIR *dir;
+ hx509_certs certs;
+ void *iter;
+};
+
+/*
+ *
+ */
+
+static int
+dir_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ *data = NULL;
+
+ if (residue == NULL || residue[0] == '\0') {
+ hx509_set_error_string(context, 0, EINVAL,
+ "DIR file name not specified");
+ return EINVAL;
+ }
+
+ {
+ struct stat sb;
+ int ret;
+
+ ret = stat(residue, &sb);
+ if (ret == -1) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "No such file %s", residue);
+ return ENOENT;
+ }
+
+ if (!S_ISDIR(sb.st_mode)) {
+ hx509_set_error_string(context, 0, ENOTDIR,
+ "%s is not a directory", residue);
+ return ENOTDIR;
+ }
+ }
+
+ *data = strdup(residue);
+ if (*data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+static int
+dir_free(hx509_certs certs, void *data)
+{
+ free(data);
+ return 0;
+}
+
+static int
+dir_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct dircursor *d;
+
+ *cursor = NULL;
+
+ d = calloc(1, sizeof(*d));
+ if (d == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ d->dir = opendir(data);
+ if (d->dir == NULL) {
+ hx509_clear_error_string(context);
+ free(d);
+ return errno;
+ }
+ rk_cloexec_dir(d->dir);
+ d->certs = NULL;
+ d->iter = NULL;
+
+ *cursor = d;
+ return 0;
+}
+
+static int
+dir_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ struct dircursor *d = iter;
+ int ret = 0;
+
+ *cert = NULL;
+
+ do {
+ struct dirent *dir;
+ char *fn;
+
+ if (d->certs) {
+ ret = hx509_certs_next_cert(context, d->certs, d->iter, cert);
+ if (ret) {
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ return ret;
+ }
+ if (*cert) {
+ ret = 0;
+ break;
+ }
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ }
+
+ dir = readdir(d->dir);
+ if (dir == NULL) {
+ ret = 0;
+ break;
+ }
+ if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
+ continue;
+
+ if (asprintf(&fn, "FILE:%s/%s", (char *)data, dir->d_name) == -1)
+ return ENOMEM;
+
+ ret = hx509_certs_init(context, fn, 0, NULL, &d->certs);
+ if (ret == 0) {
+
+ ret = hx509_certs_start_seq(context, d->certs, &d->iter);
+ if (ret)
+ hx509_certs_free(&d->certs);
+ }
+ /* ignore errors */
+ if (ret) {
+ d->certs = NULL;
+ ret = 0;
+ }
+
+ free(fn);
+ } while(ret == 0);
+
+ return ret;
+}
+
+
+static int
+dir_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct dircursor *d = cursor;
+
+ if (d->certs) {
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ }
+ closedir(d->dir);
+ free(d);
+ return 0;
+}
+
+
+static struct hx509_keyset_ops keyset_dir = {
+ "DIR",
+ 0,
+ dir_init,
+ NULL,
+ dir_free,
+ NULL,
+ NULL,
+ dir_iter_start,
+ dir_iter,
+ dir_iter_end,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_ks_dir_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_dir);
+}
diff --git a/third_party/heimdal/lib/hx509/ks_file.c b/third_party/heimdal/lib/hx509/ks_file.c
new file mode 100644
index 0000000..880668b
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ks_file.c
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#ifndef WIN32
+#include <libgen.h>
+#endif
+
+typedef enum { USE_PEM, USE_DER } outformat;
+
+struct ks_file {
+ hx509_certs certs;
+ char *fn;
+ outformat format;
+};
+
+/*
+ *
+ */
+
+static int
+parse_certificate(hx509_context context, const char *fn, int flags,
+ struct hx509_collector *c,
+ const hx509_pem_header *headers,
+ const void *data, size_t len,
+ const AlgorithmIdentifier *ai)
+{
+ heim_error_t error = NULL;
+ hx509_cert cert;
+ int ret;
+
+ cert = hx509_cert_init_data(context, data, len, &error);
+ if (cert == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
+ return ret;
+ }
+
+ ret = _hx509_collector_certs_add(context, c, cert);
+ hx509_cert_free(cert);
+ return ret;
+}
+
+static int
+try_decrypt(hx509_context context,
+ struct hx509_collector *collector,
+ int flags,
+ const AlgorithmIdentifier *alg,
+ const EVP_CIPHER *c,
+ const void *ivdata,
+ const void *password,
+ size_t passwordlen,
+ const void *cipher,
+ size_t len)
+{
+ heim_octet_string clear;
+ size_t keylen;
+ void *key;
+ int ret;
+
+ keylen = EVP_CIPHER_key_length(c);
+
+ key = malloc(keylen);
+ if (key == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = EVP_BytesToKey(c, EVP_md5(), ivdata,
+ password, passwordlen,
+ 1, key, NULL);
+ if (ret <= 0) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to do string2key for private key");
+ goto out;
+ }
+
+ clear.data = malloc(len);
+ if (clear.data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Out of memory to decrypt for private key");
+ ret = ENOMEM;
+ goto out;
+ }
+ clear.length = len;
+
+ {
+ EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX_init(&ctx);
+ EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0);
+ EVP_Cipher(&ctx, clear.data, cipher, len);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ }
+
+ if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS))
+ ret = _hx509_collector_private_key_add(context, collector, alg, NULL,
+ &clear, NULL);
+
+ memset_s(clear.data, clear.length, 0, clear.length);
+ free(clear.data);
+out:
+ memset_s(key, keylen, 0, keylen);
+ free(key);
+ return ret;
+}
+
+static int
+parse_pkcs8_private_key(hx509_context context, const char *fn, int flags,
+ struct hx509_collector *c,
+ const hx509_pem_header *headers,
+ const void *data, size_t length,
+ const AlgorithmIdentifier *ai)
+{
+ PKCS8PrivateKeyInfo ki;
+ heim_octet_string keydata;
+ int ret;
+
+ ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
+ if (ret)
+ return ret;
+
+ if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
+ keydata.data = rk_UNCONST(data);
+ keydata.length = length;
+ ret = _hx509_collector_private_key_add(context,
+ c,
+ &ki.privateKeyAlgorithm,
+ NULL,
+ &ki.privateKey,
+ &keydata);
+ }
+ free_PKCS8PrivateKeyInfo(&ki);
+ return ret;
+}
+
+static int
+parse_pem_private_key(hx509_context context, const char *fn, int flags,
+ struct hx509_collector *c,
+ const hx509_pem_header *headers,
+ const void *data, size_t len,
+ const AlgorithmIdentifier *ai)
+{
+ int ret = 0;
+ const char *enc;
+
+ enc = hx509_pem_find_header(headers, "Proc-Type");
+ if (enc) {
+ const char *dek;
+ char *type, *iv;
+ ssize_t ssize, size;
+ void *ivdata;
+ const EVP_CIPHER *cipher;
+ const struct _hx509_password *pw;
+ hx509_lock lock;
+ int decrypted = 0;
+ size_t i;
+
+ lock = _hx509_collector_get_lock(c);
+ if (lock == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "Failed to get password for "
+ "password protected file %s", fn);
+ return HX509_ALG_NOT_SUPP;
+ }
+
+ if (strcmp(enc, "4,ENCRYPTED") != 0) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Private key encrypted in unknown method %s "
+ "in file",
+ enc, fn);
+ hx509_clear_error_string(context);
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ dek = hx509_pem_find_header(headers, "DEK-Info");
+ if (dek == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Encrypted private key missing DEK-Info");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ type = strdup(dek);
+ if (type == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ iv = strchr(type, ',');
+ if (iv == NULL) {
+ free(type);
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "IV missing");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ *iv++ = '\0';
+
+ size = strlen(iv);
+ ivdata = malloc(size);
+ if (ivdata == NULL) {
+ hx509_clear_error_string(context);
+ free(type);
+ return ENOMEM;
+ }
+
+ cipher = EVP_get_cipherbyname(type);
+ if (cipher == NULL) {
+ free(ivdata);
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "Private key encrypted with "
+ "unsupported cipher: %s",
+ type);
+ free(type);
+ return HX509_ALG_NOT_SUPP;
+ }
+
+#define PKCS5_SALT_LEN 8
+
+ ssize = hex_decode(iv, ivdata, size);
+ free(type);
+ type = NULL;
+ iv = NULL;
+
+ if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {
+ free(ivdata);
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Salt have wrong length in "
+ "private key file");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ pw = _hx509_lock_get_passwords(lock);
+ if (pw != NULL) {
+ const void *password;
+ size_t passwordlen;
+
+ for (i = 0; i < pw->len; i++) {
+ password = pw->val[i];
+ passwordlen = strlen(password);
+
+ ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
+ password, passwordlen, data, len);
+ if (ret == 0) {
+ decrypted = 1;
+ break;
+ }
+ }
+ }
+ if (!decrypted) {
+ hx509_prompt prompt;
+ char password[128];
+
+ memset(&prompt, 0, sizeof(prompt));
+
+ prompt.prompt = "Password for keyfile: ";
+ prompt.type = HX509_PROMPT_TYPE_PASSWORD;
+ prompt.reply.data = password;
+ prompt.reply.length = sizeof(password);
+
+ ret = hx509_lock_prompt(lock, &prompt);
+ if (ret == 0)
+ ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
+ password, strlen(password), data, len);
+ /* XXX add password to lock password collection ? */
+ memset_s(password, sizeof(password), 0, sizeof(password));
+ }
+ free(ivdata);
+
+ } else if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
+ heim_octet_string keydata;
+
+ keydata.data = rk_UNCONST(data);
+ keydata.length = len;
+
+ ret = _hx509_collector_private_key_add(context, c, ai, NULL,
+ &keydata, NULL);
+ }
+
+ return ret;
+}
+
+
+struct pem_formats {
+ const char *name;
+ int (*func)(hx509_context, const char *, int, struct hx509_collector *,
+ const hx509_pem_header *, const void *, size_t,
+ const AlgorithmIdentifier *);
+ const AlgorithmIdentifier *(*ai)(void);
+} formats[] = {
+ { "CERTIFICATE", parse_certificate, NULL },
+ { "PRIVATE KEY", parse_pkcs8_private_key, NULL },
+ { "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa },
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ { "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey }
+#endif
+};
+
+
+struct pem_ctx {
+ int flags;
+ struct hx509_collector *c;
+};
+
+static int
+pem_func(hx509_context context, const char *type,
+ const hx509_pem_header *header,
+ const void *data, size_t len, void *ctx)
+{
+ struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx;
+ int ret = 0;
+ size_t j;
+
+ for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {
+ const char *q = formats[j].name;
+ if (strcasecmp(type, q) == 0) {
+ const AlgorithmIdentifier *ai = NULL;
+
+ if (formats[j].ai != NULL)
+ ai = (*formats[j].ai)();
+
+ ret = (*formats[j].func)(context, NULL, pem_ctx->flags, pem_ctx->c,
+ header, data, len, ai);
+ if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed parseing PEM format %s", type);
+ return ret;
+ }
+ break;
+ }
+ }
+ if (j == sizeof(formats)/sizeof(formats[0])) {
+ ret = HX509_UNSUPPORTED_OPERATION;
+ hx509_set_error_string(context, 0, ret,
+ "Found no matching PEM format for %s", type);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+file_init_common(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock, outformat format)
+{
+ char *p, *pnext;
+ struct ks_file *ksf = NULL;
+ hx509_private_key *keys = NULL;
+ int ret;
+ struct pem_ctx pem_ctx;
+
+ pem_ctx.flags = flags;
+ pem_ctx.c = NULL;
+
+ if (residue == NULL || residue[0] == '\0') {
+ hx509_set_error_string(context, 0, EINVAL,
+ "PEM file name not specified");
+ return EINVAL;
+ }
+
+ *data = NULL;
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ ksf = calloc(1, sizeof(*ksf));
+ if (ksf == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ ksf->format = format;
+
+ ksf->fn = strdup(residue);
+ if (ksf->fn == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * XXX this is broken, the function should parse the file before
+ * overwriting it
+ */
+
+ if (flags & HX509_CERTS_CREATE) {
+ /*
+ * Note that the file creation is deferred until file_store() is
+ * called.
+ */
+ ret = hx509_certs_init(context, "MEMORY:ks-file-create",
+ 0, lock, &ksf->certs);
+ if (ret)
+ goto out;
+ *data = ksf;
+ return 0;
+ }
+
+ ret = _hx509_collector_alloc(context, lock, &pem_ctx.c);
+ if (ret)
+ goto out;
+
+ for (p = ksf->fn; p != NULL; p = pnext) {
+ FILE *f;
+
+ pnext = strchr(p, ',');
+ if (pnext)
+ *pnext++ = '\0';
+
+
+ if ((f = fopen(p, "r")) == NULL) {
+ ret = ENOENT;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to open PEM file \"%s\": %s",
+ p, strerror(errno));
+ goto out;
+ }
+ rk_cloexec_file(f);
+
+ ret = hx509_pem_read(context, f, pem_func, &pem_ctx);
+ fclose(f);
+ if (ret != 0 && ret != HX509_PARSING_KEY_FAILED)
+ goto out;
+ else if (ret == HX509_PARSING_KEY_FAILED) {
+ size_t length;
+ void *ptr;
+ size_t i;
+
+ ret = rk_undumpdata(p, &ptr, &length);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
+ const AlgorithmIdentifier *ai = NULL;
+
+ if (formats[i].ai != NULL)
+ ai = (*formats[i].ai)();
+
+ ret = (*formats[i].func)(context, p, pem_ctx.flags, pem_ctx.c,
+ NULL, ptr, length, ai);
+ if (ret == 0)
+ break;
+ }
+ rk_xfree(ptr);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+ }
+
+ ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs);
+ if (ret)
+ goto out;
+
+ ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys);
+ if (ret == 0) {
+ int i;
+
+ for (i = 0; keys[i]; i++)
+ _hx509_certs_keys_add(context, ksf->certs, keys[i]);
+ _hx509_certs_keys_free(context, keys);
+ }
+
+out:
+ if (ret == 0)
+ *data = ksf;
+ else {
+ if (ksf->fn)
+ free(ksf->fn);
+ free(ksf);
+ }
+ if (pem_ctx.c)
+ _hx509_collector_free(pem_ctx.c);
+
+ return ret;
+}
+
+static int
+file_init_pem(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ return file_init_common(context, certs, data, flags, residue, lock, USE_PEM);
+}
+
+static int
+file_init_der(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ return file_init_common(context, certs, data, flags, residue, lock, USE_DER);
+}
+
+static int
+file_free(hx509_certs certs, void *data)
+{
+ struct ks_file *ksf = data;
+ hx509_certs_free(&ksf->certs);
+ free(ksf->fn);
+ free(ksf);
+ return 0;
+}
+
+struct store_ctx {
+ FILE *f;
+ outformat format;
+ int store_flags;
+};
+
+static int HX509_LIB_CALL
+store_func(hx509_context context, void *ctx, hx509_cert c)
+{
+ struct store_ctx *sc = ctx;
+ heim_octet_string data;
+ int ret = 0;
+
+ if (hx509_cert_have_private_key_only(c)) {
+ data.length = 0;
+ data.data = NULL;
+ } else {
+ ret = hx509_cert_binary(context, c, &data);
+ if (ret)
+ return ret;
+ }
+
+ switch (sc->format) {
+ case USE_DER:
+ /* Can't store both. Well, we could, but nothing will support it */
+ if (data.data) {
+ fwrite(data.data, data.length, 1, sc->f);
+ } else if (_hx509_cert_private_key_exportable(c) &&
+ !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
+ hx509_private_key key = _hx509_cert_private_key(c);
+
+ free(data.data);
+ data.length = 0;
+ data.data = NULL;
+ ret = _hx509_private_key_export(context, key,
+ HX509_KEY_FORMAT_DER, &data);
+ if (ret == 0 && data.length)
+ fwrite(data.data, data.length, 1, sc->f);
+ }
+ break;
+ case USE_PEM:
+ if (_hx509_cert_private_key_exportable(c) &&
+ !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
+ heim_octet_string priv_key;
+ hx509_private_key key = _hx509_cert_private_key(c);
+
+ ret = _hx509_private_key_export(context, key,
+ HX509_KEY_FORMAT_DER, &priv_key);
+ if (ret == 0)
+ ret = hx509_pem_write(context, _hx509_private_pem_name(key), NULL,
+ sc->f, priv_key.data, priv_key.length);
+ free(priv_key.data);
+ }
+ if (ret == 0 && data.data) {
+ ret = hx509_pem_write(context, "CERTIFICATE", NULL, sc->f,
+ data.data, data.length);
+ }
+ break;
+ }
+
+ free(data.data);
+ return ret;
+}
+
+static int
+mk_temp(const char *fn, char **tfn)
+{
+ char *ds;
+ int ret = -1;
+
+#ifdef WIN32
+ char buf[PATH_MAX];
+ char *p;
+
+ *tfn = NULL;
+
+ if ((ds = _fullpath(buf, fn, sizeof(buf))) == NULL) {
+ errno = errno ? errno : ENAMETOOLONG;
+ return -1;
+ }
+
+ if ((p = strrchr(ds, '\\')) == NULL) {
+ ret = asprintf(tfn, ".%s-XXXXXX", ds); /* XXX can't happen */
+ } else {
+ *(p++) = '\0';
+ ret = asprintf(tfn, "%s/.%s-XXXXXX", ds, p);
+ }
+#else
+ *tfn = NULL;
+ if ((ds = strdup(fn)))
+ ret = asprintf(tfn, "%s/.%s-XXXXXX", dirname(ds), basename(ds));
+ free(ds);
+#endif
+
+ /*
+ * Using mkostemp() risks leaving garbage files lying around. To do better
+ * without resorting to file locks (which have their own problems) we need
+ * O_TMPFILE and linkat(2), which only Linux has.
+ */
+ return (ret == -1 || *tfn == NULL) ? -1 : mkostemp(*tfn, O_CLOEXEC);
+}
+
+static int
+file_store(hx509_context context,
+ hx509_certs certs, void *data, int flags, hx509_lock lock)
+{
+ struct ks_file *ksf = data;
+ struct store_ctx sc;
+ char *tfn;
+ int ret;
+ int fd;
+
+ sc.f = NULL;
+ fd = mk_temp(ksf->fn, &tfn);
+ if (fd > -1)
+ sc.f = fdopen(fd, "w");
+ if (sc.f == NULL) {
+ hx509_set_error_string(context, 0, ret = errno,
+ "Failed to open file %s for writing", ksf->fn);
+ if (fd > -1)
+ (void) close(fd);
+ return ret;
+ }
+ rk_cloexec_file(sc.f);
+ sc.store_flags = flags;
+ sc.format = ksf->format;
+
+ ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc);
+ if (ret == 0)
+ ret = fclose(sc.f);
+ else
+ (void) fclose(sc.f);
+ if (ret)
+ (void) unlink(tfn);
+ else
+ (void) rename(tfn, ksf->fn);
+ free(tfn);
+ return ret;
+}
+
+static int
+file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct ks_file *ksf = data;
+ return hx509_certs_add(context, ksf->certs, c);
+}
+
+static int
+file_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct ks_file *ksf = data;
+ return hx509_certs_start_seq(context, ksf->certs, cursor);
+}
+
+static int
+file_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ struct ks_file *ksf = data;
+ return hx509_certs_next_cert(context, ksf->certs, iter, cert);
+}
+
+static int
+file_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct ks_file *ksf = data;
+ return hx509_certs_end_seq(context, ksf->certs, cursor);
+}
+
+static int
+file_getkeys(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key **keys)
+{
+ struct ks_file *ksf = data;
+ return _hx509_certs_keys_get(context, ksf->certs, keys);
+}
+
+static int
+file_addkey(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key key)
+{
+ struct ks_file *ksf = data;
+ return _hx509_certs_keys_add(context, ksf->certs, key);
+}
+
+static int
+file_destroy(hx509_context context,
+ hx509_certs certs,
+ void *data)
+{
+ struct ks_file *ksf = data;
+ return _hx509_erase_file(context, ksf->fn);
+}
+
+static struct hx509_keyset_ops keyset_file = {
+ "FILE",
+ 0,
+ file_init_pem,
+ file_store,
+ file_free,
+ file_add,
+ NULL,
+ file_iter_start,
+ file_iter,
+ file_iter_end,
+ NULL,
+ file_getkeys,
+ file_addkey,
+ file_destroy
+};
+
+static struct hx509_keyset_ops keyset_pemfile = {
+ "PEM-FILE",
+ 0,
+ file_init_pem,
+ file_store,
+ file_free,
+ file_add,
+ NULL,
+ file_iter_start,
+ file_iter,
+ file_iter_end,
+ NULL,
+ file_getkeys,
+ file_addkey,
+ file_destroy
+};
+
+static struct hx509_keyset_ops keyset_derfile = {
+ "DER-FILE",
+ 0,
+ file_init_der,
+ file_store,
+ file_free,
+ file_add,
+ NULL,
+ file_iter_start,
+ file_iter,
+ file_iter_end,
+ NULL,
+ file_getkeys,
+ file_addkey,
+ file_destroy
+};
+
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_ks_file_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_file);
+ _hx509_ks_register(context, &keyset_pemfile);
+ _hx509_ks_register(context, &keyset_derfile);
+}
diff --git a/third_party/heimdal/lib/hx509/ks_keychain.c b/third_party/heimdal/lib/hx509/ks_keychain.c
new file mode 100644
index 0000000..3243ee8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ks_keychain.c
@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+#ifdef HAVE_FRAMEWORK_SECURITY
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#include <Security/Security.h>
+
+/* Missing function decls in pre Leopard */
+#ifdef NEED_SECKEYGETCSPHANDLE_PROTO
+OSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *);
+OSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG,
+ int, const CSSM_ACCESS_CREDENTIALS **);
+#define kSecCredentialTypeDefault 0
+#define CSSM_SIZE uint32_t
+#endif
+
+
+static int
+getAttribute(SecKeychainItemRef itemRef, SecItemAttr item,
+ SecKeychainAttributeList **attrs)
+{
+ SecKeychainAttributeInfo attrInfo;
+ UInt32 attrFormat = 0;
+ OSStatus ret;
+
+ *attrs = NULL;
+
+ attrInfo.count = 1;
+ attrInfo.tag = &item;
+ attrInfo.format = &attrFormat;
+
+ ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
+ attrs, NULL, NULL);
+ if (ret)
+ return EINVAL;
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+struct kc_rsa {
+ SecKeychainItemRef item;
+ size_t keysize;
+};
+
+
+static int
+kc_rsa_public_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+static int
+kc_rsa_public_decrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+
+static int
+kc_rsa_private_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ struct kc_rsa *kc = RSA_get_app_data(rsa);
+
+ CSSM_RETURN cret;
+ OSStatus ret;
+ const CSSM_ACCESS_CREDENTIALS *creds;
+ SecKeyRef privKeyRef = (SecKeyRef)kc->item;
+ CSSM_CSP_HANDLE cspHandle;
+ const CSSM_KEY *cssmKey;
+ CSSM_CC_HANDLE sigHandle = 0;
+ CSSM_DATA sig, in;
+ int fret = 0;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
+ if(cret) abort();
+
+ cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
+ if(cret) abort();
+
+ ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN,
+ kSecCredentialTypeDefault, &creds);
+ if(ret) abort();
+
+ ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA,
+ creds, cssmKey, &sigHandle);
+ if(ret) abort();
+
+ in.Data = (uint8 *)from;
+ in.Length = flen;
+
+ sig.Data = (uint8 *)to;
+ sig.Length = kc->keysize;
+
+ cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig);
+ if(cret) {
+ /* cssmErrorString(cret); */
+ fret = -1;
+ } else
+ fret = sig.Length;
+
+ if(sigHandle)
+ CSSM_DeleteContext(sigHandle);
+
+ return fret;
+}
+
+static int
+kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
+ RSA * rsa, int padding)
+{
+ struct kc_rsa *kc = RSA_get_app_data(rsa);
+
+ CSSM_RETURN cret;
+ OSStatus ret;
+ const CSSM_ACCESS_CREDENTIALS *creds;
+ SecKeyRef privKeyRef = (SecKeyRef)kc->item;
+ CSSM_CSP_HANDLE cspHandle;
+ const CSSM_KEY *cssmKey;
+ CSSM_CC_HANDLE handle = 0;
+ CSSM_DATA out, in, rem;
+ int fret = 0;
+ CSSM_SIZE outlen = 0;
+ char remdata[1024];
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
+ if(cret) abort();
+
+ cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
+ if(cret) abort();
+
+ ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_DECRYPT,
+ kSecCredentialTypeDefault, &creds);
+ if(ret) abort();
+
+
+ ret = CSSM_CSP_CreateAsymmetricContext (cspHandle,
+ CSSM_ALGID_RSA,
+ creds,
+ cssmKey,
+ CSSM_PADDING_PKCS1,
+ &handle);
+ if(ret) abort();
+
+ in.Data = (uint8 *)from;
+ in.Length = flen;
+
+ out.Data = (uint8 *)to;
+ out.Length = kc->keysize;
+
+ rem.Data = (uint8 *)remdata;
+ rem.Length = sizeof(remdata);
+
+ cret = CSSM_DecryptData(handle, &in, 1, &out, 1, &outlen, &rem);
+ if(cret) {
+ /* cssmErrorString(cret); */
+ fret = -1;
+ } else
+ fret = out.Length;
+
+ if(handle)
+ CSSM_DeleteContext(handle);
+
+ return fret;
+}
+
+static int
+kc_rsa_init(RSA *rsa)
+{
+ return 1;
+}
+
+static int
+kc_rsa_finish(RSA *rsa)
+{
+ struct kc_rsa *kc_rsa = RSA_get_app_data(rsa);
+ CFRelease(kc_rsa->item);
+ memset(kc_rsa, 0, sizeof(*kc_rsa));
+ free(kc_rsa);
+ return 1;
+}
+
+static const RSA_METHOD kc_rsa_pkcs1_method = {
+ "hx509 Keychain PKCS#1 RSA",
+ kc_rsa_public_encrypt,
+ kc_rsa_public_decrypt,
+ kc_rsa_private_encrypt,
+ kc_rsa_private_decrypt,
+ NULL,
+ NULL,
+ kc_rsa_init,
+ kc_rsa_finish,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static int
+set_private_key(hx509_context context,
+ SecKeychainItemRef itemRef,
+ hx509_cert cert)
+{
+ struct kc_rsa *kc;
+ hx509_private_key key;
+ RSA *rsa;
+ int ret;
+
+ ret = hx509_private_key_init(&key, NULL, NULL);
+ if (ret)
+ return ret;
+
+ kc = calloc(1, sizeof(*kc));
+ if (kc == NULL)
+ _hx509_abort("out of memory");
+
+ kc->item = itemRef;
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ _hx509_abort("out of memory");
+
+ /* Argh, fake modulus since OpenSSL API is on crack */
+ {
+ SecKeychainAttributeList *attrs = NULL;
+ uint32_t size;
+ void *data;
+
+ rsa->n = BN_new();
+ if (rsa->n == NULL) abort();
+
+ ret = getAttribute(itemRef, kSecKeyKeySizeInBits, &attrs);
+ if (ret) abort();
+
+ size = *(uint32_t *)attrs->attr[0].data;
+ SecKeychainItemFreeAttributesAndData(attrs, NULL);
+
+ kc->keysize = (size + 7) / 8;
+
+ data = malloc(kc->keysize);
+ memset(data, 0xe0, kc->keysize);
+ BN_bin2bn(data, kc->keysize, rsa->n);
+ free(data);
+ }
+ rsa->e = NULL;
+
+ RSA_set_method(rsa, &kc_rsa_pkcs1_method);
+ ret = RSA_set_app_data(rsa, kc);
+ if (ret != 1)
+ _hx509_abort("RSA_set_app_data");
+
+ hx509_private_key_assign_rsa(key, rsa);
+ _hx509_cert_assign_key(cert, key);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct ks_keychain {
+ int anchors;
+ SecKeychainRef keychain;
+};
+
+static int
+keychain_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ struct ks_keychain *ctx;
+
+ if (flags & HX509_CERTS_NO_PRIVATE_KEYS) {
+ hx509_set_error_string(context, 0, ENOTSUP,
+ "KEYCHAIN store does not support not reading "
+ "private keys");
+ return ENOTSUP;
+ }
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ if (residue) {
+ if (strcasecmp(residue, "system-anchors") == 0) {
+ ctx->anchors = 1;
+ } else if (strncasecmp(residue, "FILE:", 5) == 0) {
+ OSStatus ret;
+
+ ret = SecKeychainOpen(residue + 5, &ctx->keychain);
+ if (ret != noErr) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Failed to open %s", residue);
+ free(ctx);
+ return ENOENT;
+ }
+ } else {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Unknown subtype %s", residue);
+ free(ctx);
+ return ENOENT;
+ }
+ }
+
+ *data = ctx;
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+keychain_free(hx509_certs certs, void *data)
+{
+ struct ks_keychain *ctx = data;
+ if (ctx->keychain)
+ CFRelease(ctx->keychain);
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct iter {
+ hx509_certs certs;
+ void *cursor;
+ SecKeychainSearchRef searchRef;
+};
+
+static int
+keychain_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct ks_keychain *ctx = data;
+ struct iter *iter;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ if (ctx->anchors) {
+ CFArrayRef anchors;
+ int ret;
+ int i;
+
+ ret = hx509_certs_init(context, "MEMORY:ks-file-create",
+ 0, NULL, &iter->certs);
+ if (ret) {
+ free(iter);
+ return ret;
+ }
+
+ ret = SecTrustCopyAnchorCertificates(&anchors);
+ if (ret != 0) {
+ hx509_certs_free(&iter->certs);
+ free(iter);
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Can't get trust anchors from Keychain");
+ return ENOMEM;
+ }
+ for (i = 0; i < CFArrayGetCount(anchors); i++) {
+ SecCertificateRef cr;
+ hx509_cert cert;
+ CSSM_DATA cssm;
+
+ cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i);
+
+ SecCertificateGetData(cr, &cssm);
+
+ cert = hx509_cert_init_data(context, cssm.Data, cssm.Length, NULL);
+ if (cert == NULL)
+ continue;
+
+ ret = hx509_certs_add(context, iter->certs, cert);
+ hx509_cert_free(cert);
+ }
+ CFRelease(anchors);
+ }
+
+ if (iter->certs) {
+ int ret;
+ ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor);
+ if (ret) {
+ hx509_certs_free(&iter->certs);
+ free(iter);
+ return ret;
+ }
+ } else {
+ OSStatus ret;
+
+ ret = SecKeychainSearchCreateFromAttributes(ctx->keychain,
+ kSecCertificateItemClass,
+ NULL,
+ &iter->searchRef);
+ if (ret) {
+ free(iter);
+ hx509_set_error_string(context, 0, ret,
+ "Failed to start search for attributes");
+ return ENOMEM;
+ }
+ }
+
+ *cursor = iter;
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+keychain_iter(hx509_context context,
+ hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
+{
+ SecKeychainAttributeList *attrs = NULL;
+ SecKeychainAttributeInfo attrInfo;
+ UInt32 attrFormat[1] = { 0 };
+ SecKeychainItemRef itemRef;
+ SecItemAttr item[1];
+ heim_error_t error = NULL;
+ struct iter *iter = cursor;
+ OSStatus ret;
+ UInt32 len;
+ void *ptr = NULL;
+
+ if (iter->certs)
+ return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert);
+
+ *cert = NULL;
+
+ ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef);
+ if (ret == errSecItemNotFound)
+ return 0;
+ else if (ret != 0)
+ return EINVAL;
+
+ /*
+ * Pick out certificate and matching "keyid"
+ */
+
+ item[0] = kSecPublicKeyHashItemAttr;
+
+ attrInfo.count = 1;
+ attrInfo.tag = item;
+ attrInfo.format = attrFormat;
+
+ ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
+ &attrs, &len, &ptr);
+ if (ret)
+ return EINVAL;
+
+ *cert = hx509_cert_init_data(context, ptr, len, &error);
+ if (*cert == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
+ goto out;
+ }
+
+ /*
+ * Find related private key if there is one by looking at
+ * kSecPublicKeyHashItemAttr == kSecKeyLabel
+ */
+ {
+ SecKeychainSearchRef search;
+ SecKeychainAttribute attrKeyid;
+ SecKeychainAttributeList attrList;
+
+ attrKeyid.tag = kSecKeyLabel;
+ attrKeyid.length = attrs->attr[0].length;
+ attrKeyid.data = attrs->attr[0].data;
+
+ attrList.count = 1;
+ attrList.attr = &attrKeyid;
+
+ ret = SecKeychainSearchCreateFromAttributes(NULL,
+ CSSM_DL_DB_RECORD_PRIVATE_KEY,
+ &attrList,
+ &search);
+ if (ret) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = SecKeychainSearchCopyNext(search, &itemRef);
+ CFRelease(search);
+ if (ret == errSecItemNotFound) {
+ ret = 0;
+ goto out;
+ } else if (ret) {
+ ret = EINVAL;
+ goto out;
+ }
+ set_private_key(context, itemRef, *cert);
+ }
+
+out:
+ SecKeychainItemFreeAttributesAndData(attrs, ptr);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+keychain_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct iter *iter = cursor;
+
+ if (iter->certs) {
+ hx509_certs_end_seq(context, iter->certs, iter->cursor);
+ hx509_certs_free(&iter->certs);
+ } else {
+ CFRelease(iter->searchRef);
+ }
+
+ memset(iter, 0, sizeof(*iter));
+ free(iter);
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct hx509_keyset_ops keyset_keychain = {
+ "KEYCHAIN",
+ 0,
+ keychain_init,
+ NULL,
+ keychain_free,
+ NULL,
+ NULL,
+ keychain_iter_start,
+ keychain_iter,
+ keychain_iter_end,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+#pragma clang diagnostic pop
+
+#endif /* HAVE_FRAMEWORK_SECURITY */
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_ks_keychain_register(hx509_context context)
+{
+#ifdef HAVE_FRAMEWORK_SECURITY
+ _hx509_ks_register(context, &keyset_keychain);
+#endif
+}
diff --git a/third_party/heimdal/lib/hx509/ks_mem.c b/third_party/heimdal/lib/hx509/ks_mem.c
new file mode 100644
index 0000000..f325d12
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ks_mem.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/*
+ * Should use two hash/tree certificates intead of a array. Criteria
+ * should be subject and subjectKeyIdentifier since those two are
+ * commonly seached on in CMS and path building.
+ */
+
+struct mem_data {
+ char *name;
+ struct {
+ unsigned long len;
+ hx509_cert *val;
+ } certs;
+ hx509_private_key *keys;
+};
+
+static int
+mem_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ struct mem_data *mem;
+ mem = calloc(1, sizeof(*mem));
+ if (mem == NULL)
+ return ENOMEM;
+ if (residue == NULL || residue[0] == '\0')
+ residue = "anonymous";
+ mem->name = strdup(residue);
+ if (mem->name == NULL) {
+ free(mem);
+ return ENOMEM;
+ }
+ *data = mem;
+ return 0;
+}
+
+static int
+mem_free(hx509_certs certs, void *data)
+{
+ struct mem_data *mem = data;
+ unsigned long i;
+
+ for (i = 0; i < mem->certs.len; i++)
+ hx509_cert_free(mem->certs.val[i]);
+ free(mem->certs.val);
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ hx509_private_key_free(&mem->keys[i]);
+ free(mem->keys);
+ free(mem->name);
+ free(mem);
+
+ return 0;
+}
+
+static int
+mem_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct mem_data *mem = data;
+ hx509_cert *val;
+
+ val = realloc(mem->certs.val,
+ (mem->certs.len + 1) * sizeof(mem->certs.val[0]));
+ if (val == NULL)
+ return ENOMEM;
+
+ mem->certs.val = val;
+ mem->certs.val[mem->certs.len] = hx509_cert_ref(c);
+ mem->certs.len++;
+
+ return 0;
+}
+
+static int
+mem_iter_start(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void **cursor)
+{
+ unsigned long *iter = malloc(sizeof(*iter));
+
+ if (iter == NULL)
+ return ENOMEM;
+
+ *iter = 0;
+ *cursor = iter;
+
+ return 0;
+}
+
+static int
+mem_iter(hx509_context contexst,
+ hx509_certs certs,
+ void *data,
+ void *cursor,
+ hx509_cert *cert)
+{
+ unsigned long *iter = cursor;
+ struct mem_data *mem = data;
+
+ if (*iter >= mem->certs.len) {
+ *cert = NULL;
+ return 0;
+ }
+
+ *cert = hx509_cert_ref(mem->certs.val[*iter]);
+ (*iter)++;
+ return 0;
+}
+
+static int
+mem_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ free(cursor);
+ return 0;
+}
+
+static int
+mem_getkeys(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key **keys)
+{
+ struct mem_data *mem = data;
+ int i;
+
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ ;
+ *keys = calloc(i + 1, sizeof(**keys));
+ for (i = 0; mem->keys && mem->keys[i]; i++) {
+ (*keys)[i] = _hx509_private_key_ref(mem->keys[i]);
+ if ((*keys)[i] == NULL) {
+ while (--i >= 0)
+ hx509_private_key_free(&(*keys)[i]);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ }
+ (*keys)[i] = NULL;
+ return 0;
+}
+
+static int
+mem_addkey(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key key)
+{
+ struct mem_data *mem = data;
+ void *ptr;
+ int i;
+
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ ;
+ ptr = realloc(mem->keys, (i + 2) * sizeof(*mem->keys));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ mem->keys = ptr;
+ mem->keys[i] = _hx509_private_key_ref(key);
+ mem->keys[i + 1] = NULL;
+ return 0;
+}
+
+
+static struct hx509_keyset_ops keyset_mem = {
+ "MEMORY",
+ 0,
+ mem_init,
+ NULL,
+ mem_free,
+ mem_add,
+ NULL,
+ mem_iter_start,
+ mem_iter,
+ mem_iter_end,
+ NULL,
+ mem_getkeys,
+ mem_addkey,
+ NULL
+};
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_ks_mem_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_mem);
+}
diff --git a/third_party/heimdal/lib/hx509/ks_null.c b/third_party/heimdal/lib/hx509/ks_null.c
new file mode 100644
index 0000000..c241d30
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ks_null.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+
+static int
+null_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ *data = NULL;
+ return 0;
+}
+
+static int
+null_free(hx509_certs certs, void *data)
+{
+ assert(data == NULL);
+ return 0;
+}
+
+static int
+null_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ *cursor = NULL;
+ return 0;
+}
+
+static int
+null_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ *cert = NULL;
+ return ENOENT;
+}
+
+static int
+null_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ assert(cursor == NULL);
+ return 0;
+}
+
+
+struct hx509_keyset_ops keyset_null = {
+ "NULL",
+ 0,
+ null_init,
+ NULL,
+ null_free,
+ NULL,
+ NULL,
+ null_iter_start,
+ null_iter,
+ null_iter_end,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_ks_null_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_null);
+}
diff --git a/third_party/heimdal/lib/hx509/ks_p11.c b/third_party/heimdal/lib/hx509/ks_p11.c
new file mode 100644
index 0000000..265523b
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ks_p11.c
@@ -0,0 +1,1230 @@
+/*
+ * Copyright (c) 2004 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+#ifdef HAVE_DLOPEN
+
+#include "ref/pkcs11.h"
+
+struct p11_slot {
+ uint64_t flags;
+#define P11_SESSION 1
+#define P11_SESSION_IN_USE 2
+#define P11_LOGIN_REQ 4
+#define P11_LOGIN_DONE 8
+#define P11_TOKEN_PRESENT 16
+ CK_SESSION_HANDLE session;
+ CK_SLOT_ID id;
+ CK_BBOOL token;
+ char *name;
+ hx509_certs certs;
+ char *pin;
+ struct {
+ CK_MECHANISM_TYPE_PTR list;
+ CK_ULONG num;
+ CK_MECHANISM_INFO_PTR *infos;
+ } mechs;
+};
+
+struct p11_module {
+ void *dl_handle;
+ CK_FUNCTION_LIST_PTR funcs;
+ CK_ULONG num_slots;
+ unsigned int ref;
+ unsigned int selected_slot;
+ struct p11_slot *slot;
+};
+
+#define P11FUNC(module,f,args) (*(module)->funcs->C_##f)args
+
+static int p11_get_session(hx509_context,
+ struct p11_module *,
+ struct p11_slot *,
+ hx509_lock,
+ CK_SESSION_HANDLE *);
+static int p11_put_session(struct p11_module *,
+ struct p11_slot *,
+ CK_SESSION_HANDLE);
+static void p11_release_module(struct p11_module *);
+
+static int p11_list_keys(hx509_context,
+ struct p11_module *,
+ struct p11_slot *,
+ CK_SESSION_HANDLE,
+ hx509_lock,
+ hx509_certs *);
+
+/*
+ *
+ */
+
+struct p11_rsa {
+ struct p11_module *p;
+ struct p11_slot *slot;
+ CK_OBJECT_HANDLE private_key;
+ CK_OBJECT_HANDLE public_key;
+};
+
+static int
+p11_rsa_public_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+static int
+p11_rsa_public_decrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+
+static int
+p11_rsa_private_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ CK_OBJECT_HANDLE key = p11rsa->private_key;
+ CK_SESSION_HANDLE session;
+ CK_MECHANISM mechanism;
+ CK_ULONG ck_sigsize;
+ int ret;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ memset(&mechanism, 0, sizeof(mechanism));
+ mechanism.mechanism = CKM_RSA_PKCS;
+
+ ck_sigsize = RSA_size(rsa);
+
+ ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session);
+ if (ret)
+ return -1;
+
+ ret = P11FUNC(p11rsa->p, SignInit, (session, &mechanism, key));
+ if (ret != CKR_OK) {
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ return -1;
+ }
+
+ ret = P11FUNC(p11rsa->p, Sign,
+ (session, (CK_BYTE *)(intptr_t)from, flen, to, &ck_sigsize));
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ if (ret != CKR_OK)
+ return -1;
+
+ return ck_sigsize;
+}
+
+static int
+p11_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
+ RSA * rsa, int padding)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ CK_OBJECT_HANDLE key = p11rsa->private_key;
+ CK_SESSION_HANDLE session;
+ CK_MECHANISM mechanism;
+ CK_ULONG ck_sigsize;
+ int ret;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ memset(&mechanism, 0, sizeof(mechanism));
+ mechanism.mechanism = CKM_RSA_PKCS;
+
+ ck_sigsize = RSA_size(rsa);
+
+ ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session);
+ if (ret)
+ return -1;
+
+ ret = P11FUNC(p11rsa->p, DecryptInit, (session, &mechanism, key));
+ if (ret != CKR_OK) {
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ return -1;
+ }
+
+ ret = P11FUNC(p11rsa->p, Decrypt,
+ (session, (CK_BYTE *)(intptr_t)from, flen, to, &ck_sigsize));
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ if (ret != CKR_OK)
+ return -1;
+
+ return ck_sigsize;
+}
+
+static int
+p11_rsa_init(RSA *rsa)
+{
+ return 1;
+}
+
+static int
+p11_rsa_finish(RSA *rsa)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ p11_release_module(p11rsa->p);
+ free(p11rsa);
+ return 1;
+}
+
+static const RSA_METHOD p11_rsa_pkcs1_method = {
+ "hx509 PKCS11 PKCS#1 RSA",
+ p11_rsa_public_encrypt,
+ p11_rsa_public_decrypt,
+ p11_rsa_private_encrypt,
+ p11_rsa_private_decrypt,
+ NULL,
+ NULL,
+ p11_rsa_init,
+ p11_rsa_finish,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ *
+ */
+
+static int
+p11_mech_info(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ int num)
+{
+ CK_ULONG i;
+ int ret;
+
+ ret = P11FUNC(p, GetMechanismList, (slot->id, NULL_PTR, &i));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech list count for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ if (i == 0) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "no mech supported for slot %d", num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ slot->mechs.list = calloc(i, sizeof(slot->mechs.list[0]));
+ if (slot->mechs.list == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+ slot->mechs.num = i;
+ ret = P11FUNC(p, GetMechanismList, (slot->id, slot->mechs.list, &i));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech list for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ assert(i == slot->mechs.num);
+
+ slot->mechs.infos = calloc(i, sizeof(*slot->mechs.infos));
+ if (slot->mechs.list == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+
+ for (i = 0; i < slot->mechs.num; i++) {
+ slot->mechs.infos[i] = calloc(1, sizeof(*(slot->mechs.infos[0])));
+ if (slot->mechs.infos[i] == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+ ret = P11FUNC(p, GetMechanismInfo, (slot->id, slot->mechs.list[i],
+ slot->mechs.infos[i]));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech info for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ }
+
+ return 0;
+}
+
+static int
+p11_init_slot(hx509_context context,
+ struct p11_module *p,
+ hx509_lock lock,
+ CK_SLOT_ID id,
+ int num,
+ struct p11_slot *slot)
+{
+ CK_SESSION_HANDLE session;
+ CK_SLOT_INFO slot_info;
+ CK_TOKEN_INFO token_info;
+ size_t i;
+ int ret;
+
+ slot->certs = NULL;
+ slot->id = id;
+
+ ret = P11FUNC(p, GetSlotInfo, (slot->id, &slot_info));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
+ "Failed to init PKCS11 slot %d",
+ num);
+ return HX509_PKCS11_TOKEN_CONFUSED;
+ }
+
+ for (i = sizeof(slot_info.slotDescription) - 1; i > 0; i--) {
+ char c = slot_info.slotDescription[i];
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0')
+ continue;
+ i++;
+ break;
+ }
+
+ ret = asprintf(&slot->name, "%.*s", (int)i,
+ slot_info.slotDescription);
+ if (ret == -1)
+ return ENOMEM;
+
+ if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0)
+ return 0;
+
+ ret = P11FUNC(p, GetTokenInfo, (slot->id, &token_info));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_TOKEN,
+ "Failed to init PKCS11 slot %d "
+ "with error 0x%08x",
+ num, ret);
+ return HX509_PKCS11_NO_TOKEN;
+ }
+ slot->flags |= P11_TOKEN_PRESENT;
+
+ if (token_info.flags & CKF_LOGIN_REQUIRED)
+ slot->flags |= P11_LOGIN_REQ;
+
+ ret = p11_get_session(context, p, slot, lock, &session);
+ if (ret)
+ return ret;
+
+ ret = p11_mech_info(context, p, slot, num);
+ if (ret)
+ goto out;
+
+ ret = p11_list_keys(context, p, slot, session, lock, &slot->certs);
+ out:
+ p11_put_session(p, slot, session);
+
+ return ret;
+}
+
+static int
+p11_get_session(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ hx509_lock lock,
+ CK_SESSION_HANDLE *psession)
+{
+ CK_RV ret;
+
+ if (slot->flags & P11_SESSION_IN_USE)
+ _hx509_abort("slot already in session");
+
+ if (slot->flags & P11_SESSION) {
+ slot->flags |= P11_SESSION_IN_USE;
+ *psession = slot->session;
+ return 0;
+ }
+
+ ret = P11FUNC(p, OpenSession, (slot->id,
+ CKF_SERIAL_SESSION,
+ NULL,
+ NULL,
+ &slot->session));
+ if (ret != CKR_OK) {
+ if (context)
+ hx509_set_error_string(context, 0, HX509_PKCS11_OPEN_SESSION,
+ "Failed to OpenSession for slot id %d "
+ "with error: 0x%08x",
+ (int)slot->id, ret);
+ return HX509_PKCS11_OPEN_SESSION;
+ }
+
+ slot->flags |= P11_SESSION;
+
+ /*
+ * If we have have to login, and haven't tried before and have a
+ * prompter or known to work pin code.
+ *
+ * This code is very conversative and only uses the prompter in
+ * the hx509_lock, the reason is that it's bad to try many
+ * passwords on a pkcs11 token, it might lock up and have to be
+ * unlocked by a administrator.
+ *
+ * XXX try harder to not use pin several times on the same card.
+ */
+
+ if ( (slot->flags & P11_LOGIN_REQ)
+ && (slot->flags & P11_LOGIN_DONE) == 0
+ && (lock || slot->pin))
+ {
+ hx509_prompt prompt;
+ char pin[20];
+ char *str;
+
+ if (slot->pin == NULL) {
+
+ memset(&prompt, 0, sizeof(prompt));
+
+ ret = asprintf(&str, "PIN code for %s: ", slot->name);
+ if (ret == -1 || str == NULL) {
+ if (context)
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ prompt.prompt = str;
+ prompt.type = HX509_PROMPT_TYPE_PASSWORD;
+ prompt.reply.data = pin;
+ prompt.reply.length = sizeof(pin);
+
+ ret = hx509_lock_prompt(lock, &prompt);
+ if (ret) {
+ free(str);
+ if (context)
+ hx509_set_error_string(context, 0, ret,
+ "Failed to get pin code for slot "
+ "id %d with error: %d",
+ (int)slot->id, ret);
+ return ret;
+ }
+ free(str);
+ } else {
+ strlcpy(pin, slot->pin, sizeof(pin));
+ }
+
+ ret = P11FUNC(p, Login, (slot->session, CKU_USER,
+ (unsigned char*)pin, strlen(pin)));
+ if (ret != CKR_OK) {
+ if (context)
+ hx509_set_error_string(context, 0, HX509_PKCS11_LOGIN,
+ "Failed to login on slot id %d "
+ "with error: 0x%08x",
+ (int)slot->id, ret);
+ switch(ret) {
+ case CKR_PIN_LOCKED:
+ return HX509_PKCS11_PIN_LOCKED;
+ case CKR_PIN_EXPIRED:
+ return HX509_PKCS11_PIN_EXPIRED;
+ case CKR_PIN_INCORRECT:
+ return HX509_PKCS11_PIN_INCORRECT;
+ case CKR_USER_PIN_NOT_INITIALIZED:
+ return HX509_PKCS11_PIN_NOT_INITIALIZED;
+ default:
+ return HX509_PKCS11_LOGIN;
+ }
+ } else
+ slot->flags |= P11_LOGIN_DONE;
+
+ if (slot->pin == NULL) {
+ slot->pin = strdup(pin);
+ if (slot->pin == NULL) {
+ if (context)
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+ }
+ } else
+ slot->flags |= P11_LOGIN_DONE;
+
+ slot->flags |= P11_SESSION_IN_USE;
+
+ *psession = slot->session;
+
+ return 0;
+}
+
+static int
+p11_put_session(struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session)
+{
+ if ((slot->flags & P11_SESSION_IN_USE) == 0)
+ _hx509_abort("slot not in session");
+ slot->flags &= ~P11_SESSION_IN_USE;
+
+ return 0;
+}
+
+static int
+iterate_entries(hx509_context context,
+ struct p11_module *p, struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_ATTRIBUTE *search_data, int num_search_data,
+ CK_ATTRIBUTE *query, int num_query,
+ int (*func)(hx509_context,
+ struct p11_module *, struct p11_slot *,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ void *, CK_ATTRIBUTE *, int), void *ptr)
+{
+ CK_OBJECT_HANDLE object;
+ CK_ULONG object_count;
+ int ret, ret2, i;
+
+ ret = P11FUNC(p, FindObjectsInit, (session, search_data, num_search_data));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ while (1) {
+ ret = P11FUNC(p, FindObjects, (session, &object, 1, &object_count));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ if (object_count == 0)
+ break;
+
+ for (i = 0; i < num_query; i++)
+ query[i].pValue = NULL;
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, query, num_query));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ for (i = 0; i < num_query; i++) {
+ query[i].pValue = malloc(query[i].ulValueLen);
+ if (query[i].pValue == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, query, num_query));
+ if (ret != CKR_OK) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = (*func)(context, p, slot, session, object, ptr, query, num_query);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < num_query; i++) {
+ if (query[i].pValue)
+ free(query[i].pValue);
+ query[i].pValue = NULL;
+ }
+ }
+ out:
+
+ for (i = 0; i < num_query; i++) {
+ if (query[i].pValue)
+ free(query[i].pValue);
+ query[i].pValue = NULL;
+ }
+
+ ret2 = P11FUNC(p, FindObjectsFinal, (session));
+ if (ret2 != CKR_OK) {
+ return ret2;
+ }
+
+ return ret;
+}
+
+static BIGNUM *
+getattr_bn(struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ unsigned int type)
+{
+ CK_ATTRIBUTE query;
+ BIGNUM *bn;
+ int ret;
+
+ query.type = type;
+ query.pValue = NULL;
+ query.ulValueLen = 0;
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, &query, 1));
+ if (ret != CKR_OK)
+ return NULL;
+
+ query.pValue = malloc(query.ulValueLen);
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, &query, 1));
+ if (ret != CKR_OK) {
+ free(query.pValue);
+ return NULL;
+ }
+ bn = BN_bin2bn(query.pValue, query.ulValueLen, NULL);
+ free(query.pValue);
+
+ return bn;
+}
+
+static int
+collect_private_key(hx509_context context,
+ struct p11_module *p, struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ void *ptr, CK_ATTRIBUTE *query, int num_query)
+{
+ struct hx509_collector *collector = ptr;
+ hx509_private_key key;
+ heim_octet_string localKeyId;
+ int ret;
+ RSA *rsa;
+ struct p11_rsa *p11rsa;
+
+ localKeyId.data = query[0].pValue;
+ localKeyId.length = query[0].ulValueLen;
+
+ ret = hx509_private_key_init(&key, NULL, NULL);
+ if (ret)
+ return ret;
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ _hx509_abort("out of memory");
+
+ /*
+ * The exponent and modulus should always be present according to
+ * the pkcs11 specification, but some smartcards leaves it out,
+ * let ignore any failure to fetch it.
+ */
+ rsa->n = getattr_bn(p, slot, session, object, CKA_MODULUS);
+ rsa->e = getattr_bn(p, slot, session, object, CKA_PUBLIC_EXPONENT);
+
+ p11rsa = calloc(1, sizeof(*p11rsa));
+ if (p11rsa == NULL)
+ _hx509_abort("out of memory");
+
+ p11rsa->p = p;
+ p11rsa->slot = slot;
+ p11rsa->private_key = object;
+
+ if (p->ref == 0)
+ _hx509_abort("pkcs11 ref == 0 on alloc");
+ p->ref++;
+ if (p->ref == UINT_MAX)
+ _hx509_abort("pkcs11 ref == UINT_MAX on alloc");
+
+ RSA_set_method(rsa, &p11_rsa_pkcs1_method);
+ ret = RSA_set_app_data(rsa, p11rsa);
+ if (ret != 1)
+ _hx509_abort("RSA_set_app_data");
+
+ hx509_private_key_assign_rsa(key, rsa);
+
+ ret = _hx509_collector_private_key_add(context,
+ collector,
+ hx509_signature_rsa(),
+ key,
+ NULL,
+ &localKeyId);
+
+ if (ret) {
+ hx509_private_key_free(&key);
+ return ret;
+ }
+ return 0;
+}
+
+static void
+p11_cert_release(hx509_cert cert, void *ctx)
+{
+ struct p11_module *p = ctx;
+ p11_release_module(p);
+}
+
+
+static int
+collect_cert(hx509_context context,
+ struct p11_module *p, struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ void *ptr, CK_ATTRIBUTE *query, int num_query)
+{
+ struct hx509_collector *collector = ptr;
+ heim_error_t error = NULL;
+ hx509_cert cert;
+ int ret;
+
+ if ((CK_LONG)query[0].ulValueLen == -1 ||
+ (CK_LONG)query[1].ulValueLen == -1)
+ {
+ return 0;
+ }
+
+ cert = hx509_cert_init_data(context, query[1].pValue,
+ query[1].ulValueLen, &error);
+ if (cert == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
+ return ret;
+ }
+
+ if (p->ref == 0)
+ _hx509_abort("pkcs11 ref == 0 on alloc");
+ p->ref++;
+ if (p->ref == UINT_MAX)
+ _hx509_abort("pkcs11 ref to high");
+
+ _hx509_cert_set_release(cert, p11_cert_release, p);
+
+ {
+ heim_octet_string data;
+
+ data.data = query[0].pValue;
+ data.length = query[0].ulValueLen;
+
+ _hx509_set_cert_attribute(context,
+ cert,
+ &asn1_oid_id_pkcs_9_at_localKeyId,
+ &data);
+ }
+
+ if ((CK_LONG)query[2].ulValueLen != -1) {
+ char *str;
+
+ ret = asprintf(&str, "%.*s",
+ (int)query[2].ulValueLen, (char *)query[2].pValue);
+ if (ret != -1 && str) {
+ hx509_cert_set_friendly_name(cert, str);
+ free(str);
+ }
+ }
+
+ ret = _hx509_collector_certs_add(context, collector, cert);
+ hx509_cert_free(cert);
+
+ return ret;
+}
+
+
+static int
+p11_list_keys(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ hx509_lock lock,
+ hx509_certs *certs)
+{
+ struct hx509_collector *collector;
+ CK_OBJECT_CLASS key_class;
+ CK_ATTRIBUTE search_data[] = {
+ {CKA_CLASS, NULL, 0},
+ };
+ CK_ATTRIBUTE query_data[3] = {
+ {CKA_ID, NULL, 0},
+ {CKA_VALUE, NULL, 0},
+ {CKA_LABEL, NULL, 0}
+ };
+ int ret;
+
+ search_data[0].pValue = &key_class;
+ search_data[0].ulValueLen = sizeof(key_class);
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ ret = _hx509_collector_alloc(context, lock, &collector);
+ if (ret)
+ return ret;
+
+ key_class = CKO_PRIVATE_KEY;
+ ret = iterate_entries(context, p, slot, session,
+ search_data, 1,
+ query_data, 1,
+ collect_private_key, collector);
+ if (ret)
+ goto out;
+
+ key_class = CKO_CERTIFICATE;
+ ret = iterate_entries(context, p, slot, session,
+ search_data, 1,
+ query_data, 3,
+ collect_cert, collector);
+ if (ret)
+ goto out;
+
+ ret = _hx509_collector_collect_certs(context, collector, &slot->certs);
+
+out:
+ _hx509_collector_free(collector);
+
+ return ret;
+}
+
+
+static int
+p11_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ CK_C_GetFunctionList getFuncs;
+ struct p11_module *p;
+ char *list, *str;
+ int ret;
+
+ *data = NULL;
+
+ if (flags & HX509_CERTS_NO_PRIVATE_KEYS) {
+ hx509_set_error_string(context, 0, ENOTSUP,
+ "PKCS#11 store does not support "
+ "HX509_CERTS_NO_PRIVATE_KEYS flag");
+ return ENOTSUP;
+ }
+
+ if (residue == NULL || residue[0] == '\0') {
+ hx509_set_error_string(context, 0, EINVAL,
+ "PKCS#11 store not specified");
+ return EINVAL;
+ }
+ list = strdup(residue);
+ if (list == NULL)
+ return ENOMEM;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ free(list);
+ return ENOMEM;
+ }
+
+ p->ref = 1;
+ p->selected_slot = 0;
+
+ str = strchr(list, ',');
+ if (str)
+ *str++ = '\0';
+ while (str) {
+ char *strnext;
+ strnext = strchr(str, ',');
+ if (strnext)
+ *strnext++ = '\0';
+ if (strncasecmp(str, "slot=", 5) == 0)
+ p->selected_slot = atoi(str + 5);
+ str = strnext;
+ }
+
+ p->dl_handle = dlopen(list, RTLD_NOW | RTLD_LOCAL | RTLD_GROUP);
+ if (p->dl_handle == NULL) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to open %s: %s", list, dlerror());
+ goto out;
+ }
+
+ getFuncs = (CK_C_GetFunctionList) dlsym(p->dl_handle, "C_GetFunctionList");
+ if (getFuncs == NULL) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "C_GetFunctionList missing in %s: %s",
+ list, dlerror());
+ goto out;
+ }
+
+ ret = (*getFuncs)(&p->funcs);
+ if (ret) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "C_GetFunctionList failed in %s", list);
+ goto out;
+ }
+
+ ret = P11FUNC(p, Initialize, (NULL_PTR));
+ if (ret != CKR_OK) {
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ hx509_set_error_string(context, 0, ret,
+ "Failed initialize the PKCS11 module");
+ goto out;
+ }
+
+ ret = P11FUNC(p, GetSlotList, (FALSE, NULL, &p->num_slots));
+ if (ret) {
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to get number of PKCS11 slots");
+ goto out;
+ }
+
+ if (p->num_slots == 0) {
+ ret = HX509_PKCS11_NO_SLOT;
+ hx509_set_error_string(context, 0, ret,
+ "Selected PKCS11 module have no slots");
+ goto out;
+ }
+
+
+ {
+ CK_SLOT_ID_PTR slot_ids;
+ int num_tokens = 0;
+ size_t i;
+
+ slot_ids = malloc(p->num_slots * sizeof(*slot_ids));
+ if (slot_ids == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = P11FUNC(p, GetSlotList, (FALSE, slot_ids, &p->num_slots));
+ if (ret) {
+ free(slot_ids);
+ hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
+ "Failed getting slot-list from "
+ "PKCS11 module");
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ goto out;
+ }
+
+ p->slot = calloc(p->num_slots, sizeof(p->slot[0]));
+ if (p->slot == NULL) {
+ free(slot_ids);
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Failed to get memory for slot-list");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < p->num_slots; i++) {
+ if ((p->selected_slot != 0) && (slot_ids[i] != (p->selected_slot - 1)))
+ continue;
+ ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]);
+ if (!ret) {
+ if (p->slot[i].flags & P11_TOKEN_PRESENT)
+ num_tokens++;
+ }
+ }
+ free(slot_ids);
+ if (ret)
+ goto out;
+ if (num_tokens == 0) {
+ ret = HX509_PKCS11_NO_TOKEN;
+ goto out;
+ }
+ }
+
+ free(list);
+
+ *data = p;
+
+ return 0;
+ out:
+ if (list)
+ free(list);
+ p11_release_module(p);
+ return ret;
+}
+
+static void
+p11_release_module(struct p11_module *p)
+{
+ size_t i;
+
+ if (p->ref == 0)
+ _hx509_abort("pkcs11 ref to low");
+ if (--p->ref > 0)
+ return;
+
+ for (i = 0; i < p->num_slots; i++) {
+ if (p->slot[i].flags & P11_SESSION_IN_USE)
+ _hx509_abort("pkcs11 module release while session in use");
+ if (p->slot[i].flags & P11_SESSION) {
+ P11FUNC(p, CloseSession, (p->slot[i].session));
+ }
+
+ if (p->slot[i].name)
+ free(p->slot[i].name);
+ if (p->slot[i].pin) {
+ memset(p->slot[i].pin, 0, strlen(p->slot[i].pin));
+ free(p->slot[i].pin);
+ }
+ if (p->slot[i].mechs.num) {
+ free(p->slot[i].mechs.list);
+
+ if (p->slot[i].mechs.infos) {
+ size_t j;
+
+ for (j = 0 ; j < p->slot[i].mechs.num ; j++)
+ free(p->slot[i].mechs.infos[j]);
+ free(p->slot[i].mechs.infos);
+ }
+ }
+ }
+ free(p->slot);
+
+ if (p->funcs)
+ P11FUNC(p, Finalize, (NULL));
+
+ if (p->dl_handle)
+ dlclose(p->dl_handle);
+
+ memset(p, 0, sizeof(*p));
+ free(p);
+}
+
+static int
+p11_free(hx509_certs certs, void *data)
+{
+ struct p11_module *p = data;
+ size_t i;
+
+ for (i = 0; i < p->num_slots; i++) {
+ if (p->slot[i].certs)
+ hx509_certs_free(&p->slot[i].certs);
+ }
+ p11_release_module(p);
+ return 0;
+}
+
+struct p11_cursor {
+ hx509_certs certs;
+ void *cursor;
+};
+
+static int
+p11_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct p11_module *p = data;
+ struct p11_cursor *c;
+ int ret;
+ size_t i;
+
+ c = malloc(sizeof(*c));
+ if (c == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ ret = hx509_certs_init(context, "MEMORY:pkcs11-iter", 0, NULL, &c->certs);
+ if (ret) {
+ free(c);
+ return ret;
+ }
+
+ for (i = 0 ; i < p->num_slots; i++) {
+ if (p->slot[i].certs == NULL)
+ continue;
+ ret = hx509_certs_merge(context, c->certs, p->slot[i].certs);
+ if (ret) {
+ hx509_certs_free(&c->certs);
+ free(c);
+ return ret;
+ }
+ }
+
+ ret = hx509_certs_start_seq(context, c->certs, &c->cursor);
+ if (ret) {
+ hx509_certs_free(&c->certs);
+ free(c);
+ return 0;
+ }
+ *cursor = c;
+
+ return 0;
+}
+
+static int
+p11_iter(hx509_context context,
+ hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
+{
+ struct p11_cursor *c = cursor;
+ return hx509_certs_next_cert(context, c->certs, c->cursor, cert);
+}
+
+static int
+p11_iter_end(hx509_context context,
+ hx509_certs certs, void *data, void *cursor)
+{
+ struct p11_cursor *c = cursor;
+ int ret;
+ ret = hx509_certs_end_seq(context, c->certs, c->cursor);
+ hx509_certs_free(&c->certs);
+ free(c);
+ return ret;
+}
+
+#define MECHFLAG(x) { "unknown-flag-" #x, x }
+static struct units mechflags[] = {
+ MECHFLAG(0x80000000),
+ MECHFLAG(0x40000000),
+ MECHFLAG(0x20000000),
+ MECHFLAG(0x10000000),
+ MECHFLAG(0x08000000),
+ MECHFLAG(0x04000000),
+ {"ec-compress", 0x2000000 },
+ {"ec-uncompress", 0x1000000 },
+ {"ec-namedcurve", 0x0800000 },
+ {"ec-ecparameters", 0x0400000 },
+ {"ec-f-2m", 0x0200000 },
+ {"ec-f-p", 0x0100000 },
+ {"derive", 0x0080000 },
+ {"unwrap", 0x0040000 },
+ {"wrap", 0x0020000 },
+ {"genereate-key-pair", 0x0010000 },
+ {"generate", 0x0008000 },
+ {"verify-recover", 0x0004000 },
+ {"verify", 0x0002000 },
+ {"sign-recover", 0x0001000 },
+ {"sign", 0x0000800 },
+ {"digest", 0x0000400 },
+ {"decrypt", 0x0000200 },
+ {"encrypt", 0x0000100 },
+ MECHFLAG(0x00080),
+ MECHFLAG(0x00040),
+ MECHFLAG(0x00020),
+ MECHFLAG(0x00010),
+ MECHFLAG(0x00008),
+ MECHFLAG(0x00004),
+ MECHFLAG(0x00002),
+ {"hw", 0x0000001 },
+ { NULL, 0x0000000 }
+};
+#undef MECHFLAG
+
+static int
+p11_printinfo(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ int (*func)(void *, const char *),
+ void *ctx)
+{
+ struct p11_module *p = data;
+ size_t i, j;
+
+ _hx509_pi_printf(func, ctx, "pkcs11 driver with %d slot%s",
+ p->num_slots, p->num_slots > 1 ? "s" : "");
+
+ for (i = 0; i < p->num_slots; i++) {
+ struct p11_slot *s = &p->slot[i];
+
+ _hx509_pi_printf(func, ctx, "slot %d: id: %d name: %s flags: %08x",
+ i, (int)s->id, s->name, s->flags);
+
+ _hx509_pi_printf(func, ctx, "number of supported mechanisms: %lu",
+ (unsigned long)s->mechs.num);
+ for (j = 0; j < s->mechs.num; j++) {
+ const char *mechname = "unknown";
+ char flags[256], unknownname[40];
+#define MECHNAME(s,n) case s: mechname = n; break
+ switch(s->mechs.list[j]) {
+ MECHNAME(CKM_RSA_PKCS_KEY_PAIR_GEN, "rsa-pkcs-key-pair-gen");
+ MECHNAME(CKM_RSA_PKCS, "rsa-pkcs");
+ MECHNAME(CKM_RSA_X_509, "rsa-x-509");
+ MECHNAME(CKM_MD5_RSA_PKCS, "md5-rsa-pkcs");
+ MECHNAME(CKM_SHA1_RSA_PKCS, "sha1-rsa-pkcs");
+ MECHNAME(CKM_SHA256_RSA_PKCS, "sha256-rsa-pkcs");
+ MECHNAME(CKM_SHA384_RSA_PKCS, "sha384-rsa-pkcs");
+ MECHNAME(CKM_SHA512_RSA_PKCS, "sha512-rsa-pkcs");
+ MECHNAME(CKM_RIPEMD160_RSA_PKCS, "ripemd160-rsa-pkcs");
+ MECHNAME(CKM_RSA_PKCS_OAEP, "rsa-pkcs-oaep");
+ MECHNAME(CKM_SHA512_HMAC, "sha512-hmac");
+ MECHNAME(CKM_SHA512, "sha512");
+ MECHNAME(CKM_SHA384_HMAC, "sha384-hmac");
+ MECHNAME(CKM_SHA384, "sha384");
+ MECHNAME(CKM_SHA256_HMAC, "sha256-hmac");
+ MECHNAME(CKM_SHA256, "sha256");
+ MECHNAME(CKM_SHA_1, "sha1");
+ MECHNAME(CKM_MD5, "md5");
+ MECHNAME(CKM_RIPEMD160, "ripemd-160");
+ MECHNAME(CKM_DES_ECB, "des-ecb");
+ MECHNAME(CKM_DES_CBC, "des-cbc");
+ MECHNAME(CKM_AES_ECB, "aes-ecb");
+ MECHNAME(CKM_AES_CBC, "aes-cbc");
+ MECHNAME(CKM_DH_PKCS_PARAMETER_GEN, "dh-pkcs-parameter-gen");
+ default:
+ snprintf(unknownname, sizeof(unknownname),
+ "unknown-mech-%lu",
+ (unsigned long)s->mechs.list[j]);
+ mechname = unknownname;
+ break;
+ }
+#undef MECHNAME
+ unparse_flags(s->mechs.infos[j]->flags, mechflags,
+ flags, sizeof(flags));
+
+ _hx509_pi_printf(func, ctx, " %s: %s", mechname, flags);
+ }
+ }
+
+ return 0;
+}
+
+static struct hx509_keyset_ops keyset_pkcs11 = {
+ "PKCS11",
+ 0,
+ p11_init,
+ NULL,
+ p11_free,
+ NULL,
+ NULL,
+ p11_iter_start,
+ p11_iter,
+ p11_iter_end,
+ p11_printinfo,
+ NULL,
+ NULL,
+ NULL
+};
+
+#endif /* HAVE_DLOPEN */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_ks_pkcs11_register(hx509_context context)
+{
+#ifdef HAVE_DLOPEN
+ _hx509_ks_register(context, &keyset_pkcs11);
+#endif
+}
diff --git a/third_party/heimdal/lib/hx509/ks_p12.c b/third_party/heimdal/lib/hx509/ks_p12.c
new file mode 100644
index 0000000..6fd7cd1
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ks_p12.c
@@ -0,0 +1,756 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+struct ks_pkcs12 {
+ hx509_certs certs;
+ char *fn;
+ unsigned int store_no_priv_keys;
+};
+
+typedef int (*collector_func)(hx509_context,
+ struct hx509_collector *,
+ int,
+ const void *, size_t,
+ const PKCS12_Attributes *);
+
+struct type {
+ const heim_oid *oid;
+ collector_func func;
+};
+
+static void
+parse_pkcs12_type(hx509_context, struct hx509_collector *, int,
+ const heim_oid *, const void *, size_t,
+ const PKCS12_Attributes *);
+
+
+static const PKCS12_Attribute *
+find_attribute(const PKCS12_Attributes *attrs, const heim_oid *oid)
+{
+ size_t i;
+ if (attrs == NULL)
+ return NULL;
+ for (i = 0; i < attrs->len; i++)
+ if (der_heim_oid_cmp(oid, &attrs->val[i].attrId) == 0)
+ return &attrs->val[i];
+ return NULL;
+}
+
+static int
+keyBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ int flags,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ const PKCS12_Attribute *attr;
+ PKCS8PrivateKeyInfo ki;
+ const heim_octet_string *os = NULL;
+ int ret;
+
+ if (flags & HX509_CERTS_NO_PRIVATE_KEYS)
+ return 0;
+
+ attr = find_attribute(attrs, &asn1_oid_id_pkcs_9_at_localKeyId);
+ if (attr)
+ os = &attr->attrValues;
+
+ ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
+ if (ret)
+ return ret;
+
+ _hx509_collector_private_key_add(context,
+ c,
+ &ki.privateKeyAlgorithm,
+ NULL,
+ &ki.privateKey,
+ os);
+ free_PKCS8PrivateKeyInfo(&ki);
+ return 0;
+}
+
+static int
+ShroudedKeyBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ int flags,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ PKCS8EncryptedPrivateKeyInfo pk;
+ heim_octet_string content;
+ int ret;
+
+ memset(&pk, 0, sizeof(pk));
+
+ ret = decode_PKCS8EncryptedPrivateKeyInfo(data, length, &pk, NULL);
+ if (ret)
+ return ret;
+
+ ret = _hx509_pbe_decrypt(context,
+ _hx509_collector_get_lock(c),
+ &pk.encryptionAlgorithm,
+ &pk.encryptedData,
+ &content);
+ free_PKCS8EncryptedPrivateKeyInfo(&pk);
+ if (ret)
+ return ret;
+
+ ret = keyBag_parser(context, c, flags, content.data, content.length,
+ attrs);
+ der_free_octet_string(&content);
+ return ret;
+}
+
+static int
+certBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ int flags,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_error_t error = NULL;
+ heim_octet_string os;
+ hx509_cert cert;
+ PKCS12_CertBag cb;
+ int ret;
+
+ ret = decode_PKCS12_CertBag(data, length, &cb, NULL);
+ if (ret)
+ return ret;
+
+ if (der_heim_oid_cmp(&asn1_oid_id_pkcs_9_at_certTypes_x509, &cb.certType)) {
+ free_PKCS12_CertBag(&cb);
+ return 0;
+ }
+
+ ret = decode_PKCS12_OctetString(cb.certValue.data,
+ cb.certValue.length,
+ &os,
+ NULL);
+ free_PKCS12_CertBag(&cb);
+ if (ret)
+ return ret;
+
+ cert = hx509_cert_init_data(context, os.data, os.length, &error);
+ der_free_octet_string(&os);
+ if (cert == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
+ return ret;
+ }
+
+ ret = _hx509_collector_certs_add(context, c, cert);
+ if (ret) {
+ hx509_cert_free(cert);
+ return ret;
+ }
+
+ {
+ const PKCS12_Attribute *attr;
+ const heim_oid *oids[] = {
+ &asn1_oid_id_pkcs_9_at_localKeyId, &asn1_oid_id_pkcs_9_at_friendlyName
+ };
+ size_t i;
+
+ for (i = 0; i < sizeof(oids)/sizeof(oids[0]); i++) {
+ const heim_oid *oid = oids[i];
+ attr = find_attribute(attrs, oid);
+ if (attr)
+ _hx509_set_cert_attribute(context, cert, oid,
+ &attr->attrValues);
+ }
+ }
+
+ hx509_cert_free(cert);
+
+ return 0;
+}
+
+static int
+parse_safe_content(hx509_context context,
+ struct hx509_collector *c,
+ int flags,
+ const unsigned char *p, size_t len)
+{
+ PKCS12_SafeContents sc;
+ int ret;
+ size_t i;
+
+ memset(&sc, 0, sizeof(sc));
+
+ ret = decode_PKCS12_SafeContents(p, len, &sc, NULL);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < sc.len ; i++)
+ parse_pkcs12_type(context,
+ c,
+ flags,
+ &sc.val[i].bagId,
+ sc.val[i].bagValue.data,
+ sc.val[i].bagValue.length,
+ sc.val[i].bagAttributes);
+
+ free_PKCS12_SafeContents(&sc);
+ return 0;
+}
+
+static int
+safeContent_parser(hx509_context context,
+ struct hx509_collector *c,
+ int flags,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string os;
+ int ret;
+
+ ret = decode_PKCS12_OctetString(data, length, &os, NULL);
+ if (ret)
+ return ret;
+ ret = parse_safe_content(context, c, flags, os.data, os.length);
+ der_free_octet_string(&os);
+ return ret;
+}
+
+static int
+encryptedData_parser(hx509_context context,
+ struct hx509_collector *c,
+ int flags,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string content;
+ heim_oid contentType;
+ int ret;
+
+ memset(&contentType, 0, sizeof(contentType));
+
+ ret = hx509_cms_decrypt_encrypted(context,
+ _hx509_collector_get_lock(c),
+ data, length,
+ &contentType,
+ &content);
+ if (ret)
+ return ret;
+
+ if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) == 0)
+ ret = parse_safe_content(context, c, flags,
+ content.data, content.length);
+
+ der_free_octet_string(&content);
+ der_free_oid(&contentType);
+ return ret;
+}
+
+static int
+envelopedData_parser(hx509_context context,
+ struct hx509_collector *c,
+ int flags,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string content;
+ heim_oid contentType;
+ hx509_lock lock;
+ int ret;
+
+ memset(&contentType, 0, sizeof(contentType));
+
+ lock = _hx509_collector_get_lock(c);
+
+ ret = hx509_cms_unenvelope(context,
+ _hx509_lock_unlock_certs(lock),
+ 0,
+ data, length,
+ NULL,
+ 0,
+ &contentType,
+ &content);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "PKCS12 failed to unenvelope");
+ return ret;
+ }
+
+ if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) == 0)
+ ret = parse_safe_content(context, c, flags,
+ content.data, content.length);
+
+ der_free_octet_string(&content);
+ der_free_oid(&contentType);
+
+ return ret;
+}
+
+
+struct type bagtypes[] = {
+ { &asn1_oid_id_pkcs12_keyBag, keyBag_parser },
+ { &asn1_oid_id_pkcs12_pkcs8ShroudedKeyBag, ShroudedKeyBag_parser },
+ { &asn1_oid_id_pkcs12_certBag, certBag_parser },
+ { &asn1_oid_id_pkcs7_data, safeContent_parser },
+ { &asn1_oid_id_pkcs7_encryptedData, encryptedData_parser },
+ { &asn1_oid_id_pkcs7_envelopedData, envelopedData_parser }
+};
+
+static void
+parse_pkcs12_type(hx509_context context,
+ struct hx509_collector *c,
+ int flags,
+ const heim_oid *oid,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(bagtypes)/sizeof(bagtypes[0]); i++)
+ if (der_heim_oid_cmp(bagtypes[i].oid, oid) == 0)
+ (*bagtypes[i].func)(context, c, flags, data, length, attrs);
+}
+
+static int
+p12_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ struct ks_pkcs12 *p12;
+ size_t len;
+ void *buf;
+ PKCS12_PFX pfx;
+ PKCS12_AuthenticatedSafe as;
+ int ret;
+ size_t i;
+ struct hx509_collector *c;
+
+ *data = NULL;
+
+ if (residue == NULL || residue[0] == '\0') {
+ hx509_set_error_string(context, 0, EINVAL,
+ "PKCS#12 file not specified");
+ return EINVAL;
+ }
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ ret = _hx509_collector_alloc(context, lock, &c);
+ if (ret)
+ return ret;
+
+ p12 = calloc(1, sizeof(*p12));
+ if (p12 == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ p12->fn = strdup(residue);
+ if (p12->fn == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ if (flags & HX509_CERTS_CREATE) {
+ ret = hx509_certs_init(context, "MEMORY:ks-file-create",
+ 0, lock, &p12->certs);
+ if (ret == 0)
+ *data = p12;
+ goto out;
+ }
+
+ ret = rk_undumpdata(residue, &buf, &len);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = decode_PKCS12_PFX(buf, len, &pfx, NULL);
+ rk_xfree(buf);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode the PFX in %s", residue);
+ goto out;
+ }
+
+ if (der_heim_oid_cmp(&pfx.authSafe.contentType, &asn1_oid_id_pkcs7_data) != 0) {
+ free_PKCS12_PFX(&pfx);
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "PKCS PFX isn't a pkcs7-data container");
+ goto out;
+ }
+
+ if (pfx.authSafe.content == NULL) {
+ free_PKCS12_PFX(&pfx);
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "PKCS PFX missing data");
+ goto out;
+ }
+
+ {
+ heim_octet_string asdata;
+
+ ret = decode_PKCS12_OctetString(pfx.authSafe.content->data,
+ pfx.authSafe.content->length,
+ &asdata,
+ NULL);
+ free_PKCS12_PFX(&pfx);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ ret = decode_PKCS12_AuthenticatedSafe(asdata.data,
+ asdata.length,
+ &as,
+ NULL);
+ der_free_octet_string(&asdata);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ for (i = 0; i < as.len; i++)
+ parse_pkcs12_type(context,
+ c,
+ flags,
+ &as.val[i].contentType,
+ as.val[i].content->data,
+ as.val[i].content->length,
+ NULL);
+
+ free_PKCS12_AuthenticatedSafe(&as);
+
+ ret = _hx509_collector_collect_certs(context, c, &p12->certs);
+ if (ret == 0)
+ *data = p12;
+
+out:
+ _hx509_collector_free(c);
+
+ if (ret && p12) {
+ if (p12->fn)
+ free(p12->fn);
+ if (p12->certs)
+ hx509_certs_free(&p12->certs);
+ free(p12);
+ }
+
+ return ret;
+}
+
+static int
+addBag(hx509_context context,
+ PKCS12_AuthenticatedSafe *as,
+ const heim_oid *oid,
+ void *data,
+ size_t length)
+{
+ void *ptr;
+ int ret;
+
+ ptr = realloc(as->val, sizeof(as->val[0]) * (as->len + 1));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ as->val = ptr;
+
+ ret = der_copy_oid(oid, &as->val[as->len].contentType);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+
+ as->val[as->len].content = calloc(1, sizeof(*as->val[0].content));
+ if (as->val[as->len].content == NULL) {
+ der_free_oid(&as->val[as->len].contentType);
+ hx509_set_error_string(context, 0, ENOMEM, "malloc out of memory");
+ return ENOMEM;
+ }
+
+ as->val[as->len].content->data = data;
+ as->val[as->len].content->length = length;
+
+ as->len++;
+
+ return 0;
+}
+
+struct store_func_ctx {
+ PKCS12_AuthenticatedSafe as;
+ int store_flags;
+};
+
+static int HX509_LIB_CALL
+store_func(hx509_context context, void *d, hx509_cert c)
+{
+ struct store_func_ctx *ctx = d;
+ PKCS12_OctetString os;
+ PKCS12_CertBag cb;
+ size_t size;
+ int ret;
+
+ memset(&os, 0, sizeof(os));
+ memset(&cb, 0, sizeof(cb));
+
+ os.data = NULL;
+ os.length = 0;
+
+ ret = hx509_cert_binary(context, c, &os);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(PKCS12_OctetString,
+ cb.certValue.data,cb.certValue.length,
+ &os, &size, ret);
+ free(os.data);
+ if (ret)
+ goto out;
+ ret = der_copy_oid(&asn1_oid_id_pkcs_9_at_certTypes_x509, &cb.certType);
+ if (ret) {
+ free_PKCS12_CertBag(&cb);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(PKCS12_CertBag, os.data, os.length,
+ &cb, &size, ret);
+ free_PKCS12_CertBag(&cb);
+ if (ret)
+ goto out;
+
+ ret = addBag(context, &ctx->as, &asn1_oid_id_pkcs12_certBag, os.data,
+ os.length);
+
+ if (_hx509_cert_private_key_exportable(c) &&
+ !(ctx->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
+ hx509_private_key key = _hx509_cert_private_key(c);
+ PKCS8PrivateKeyInfo pki;
+
+ memset(&pki, 0, sizeof(pki));
+
+ ret = der_parse_hex_heim_integer("00", &pki.version);
+ if (ret)
+ return ret;
+ ret = _hx509_private_key_oid(context, key,
+ &pki.privateKeyAlgorithm.algorithm);
+ if (ret) {
+ free_PKCS8PrivateKeyInfo(&pki);
+ return ret;
+ }
+ ret = _hx509_private_key_export(context,
+ _hx509_cert_private_key(c),
+ HX509_KEY_FORMAT_DER,
+ &pki.privateKey);
+ if (ret) {
+ free_PKCS8PrivateKeyInfo(&pki);
+ return ret;
+ }
+ /* set attribute, asn1_oid_id_pkcs_9_at_localKeyId */
+
+ ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, os.data, os.length,
+ &pki, &size, ret);
+ free_PKCS8PrivateKeyInfo(&pki);
+ if (ret)
+ return ret;
+
+ ret = addBag(context, &ctx->as, &asn1_oid_id_pkcs12_keyBag, os.data,
+ os.length);
+ if (ret)
+ return ret;
+ }
+
+out:
+ return ret;
+}
+
+static int
+p12_store(hx509_context context,
+ hx509_certs certs, void *data, int flags, hx509_lock lock)
+{
+ struct ks_pkcs12 *p12 = data;
+ PKCS12_PFX pfx;
+ struct store_func_ctx ctx;
+ PKCS12_OctetString asdata;
+ size_t size;
+ int ret;
+
+ memset(&ctx, 0, sizeof(ctx));
+ memset(&pfx, 0, sizeof(pfx));
+ ctx.store_flags = flags;
+
+ ret = hx509_certs_iter_f(context, p12->certs, store_func, &ctx);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(PKCS12_AuthenticatedSafe, asdata.data, asdata.length,
+ &ctx.as, &size, ret);
+ free_PKCS12_AuthenticatedSafe(&ctx.as);
+ if (ret)
+ return ret;
+
+ ret = der_parse_hex_heim_integer("03", &pfx.version);
+ if (ret) {
+ free(asdata.data);
+ goto out;
+ }
+
+ pfx.authSafe.content = calloc(1, sizeof(*pfx.authSafe.content));
+
+ ASN1_MALLOC_ENCODE(PKCS12_OctetString,
+ pfx.authSafe.content->data,
+ pfx.authSafe.content->length,
+ &asdata, &size, ret);
+ free(asdata.data);
+ if (ret)
+ goto out;
+
+ ret = der_copy_oid(&asn1_oid_id_pkcs7_data, &pfx.authSafe.contentType);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(PKCS12_PFX, asdata.data, asdata.length,
+ &pfx, &size, ret);
+ if (ret)
+ goto out;
+
+#if 0
+ const struct _hx509_password *pw;
+
+ pw = _hx509_lock_get_passwords(lock);
+ if (pw != NULL) {
+ pfx.macData = calloc(1, sizeof(*pfx.macData));
+ if (pfx.macData == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+ }
+ if (pfx.macData == NULL) {
+ free(asdata.data);
+ goto out;
+ }
+ }
+ ret = calculate_hash(&aspath, pw, pfx.macData);
+#endif
+
+ rk_dumpdata(p12->fn, asdata.data, asdata.length);
+ free(asdata.data);
+
+out:
+ free_PKCS12_AuthenticatedSafe(&ctx.as);
+ free_PKCS12_PFX(&pfx);
+
+ return ret;
+}
+
+
+static int
+p12_free(hx509_certs certs, void *data)
+{
+ struct ks_pkcs12 *p12 = data;
+ hx509_certs_free(&p12->certs);
+ free(p12->fn);
+ free(p12);
+ return 0;
+}
+
+static int
+p12_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_add(context, p12->certs, c);
+}
+
+static int
+p12_iter_start(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void **cursor)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_start_seq(context, p12->certs, cursor);
+}
+
+static int
+p12_iter(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor,
+ hx509_cert *cert)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_next_cert(context, p12->certs, cursor, cert);
+}
+
+static int
+p12_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_end_seq(context, p12->certs, cursor);
+}
+
+static int
+p12_destroy(hx509_context context, hx509_certs certs, void *data)
+{
+ struct ks_pkcs12 *p12 = data;
+ return _hx509_erase_file(context, p12->fn);
+}
+
+static struct hx509_keyset_ops keyset_pkcs12 = {
+ "PKCS12",
+ 0,
+ p12_init,
+ p12_store,
+ p12_free,
+ p12_add,
+ NULL,
+ p12_iter_start,
+ p12_iter,
+ p12_iter_end,
+ NULL,
+ NULL,
+ NULL,
+ p12_destroy
+};
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_ks_pkcs12_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_pkcs12);
+}
diff --git a/third_party/heimdal/lib/hx509/libhx509-exports.def b/third_party/heimdal/lib/hx509/libhx509-exports.def
new file mode 100644
index 0000000..745ad5a
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/libhx509-exports.def
@@ -0,0 +1,288 @@
+
+EXPORTS
+ _hx509_cert_assign_key
+ _hx509_cert_get_keyusage
+ _hx509_cert_get_version
+ _hx509_cert_private_key
+ _hx509_certs_keys_free
+ _hx509_certs_keys_get
+ _hx509_expr_eval
+ _hx509_expr_free
+ _hx509_expr_parse
+ _hx509_generate_private_key
+ _hx509_generate_private_key_bits
+ _hx509_generate_private_key_free
+ _hx509_generate_private_key_init
+ _hx509_generate_private_key_is_ca
+ _hx509_get_cert
+ _hx509_ks_type
+ _hx509_make_pkinit_san
+ _hx509_map_file_os
+ _hx509_name_from_Name
+ hx509_private_key2SPKI
+ hx509_private_key_free
+ _hx509_private_key_ref
+ hx509_request_add_GeneralName
+ hx509_request_add_dns_name
+ hx509_request_add_dns_srv
+ hx509_request_add_eku
+ hx509_request_add_email
+ hx509_request_add_ms_upn_name
+ hx509_request_add_pkinit
+ hx509_request_add_registered
+ hx509_request_add_xmpp_name
+ hx509_request_authorize_ku
+ hx509_request_authorize_eku
+ hx509_request_authorize_san
+ hx509_request_count_unsupported
+ hx509_request_count_unauthorized
+ _hx509_private_key_export
+ _hx509_private_key_exportable
+ _hx509_private_key_get_internal
+ _hx509_private_key_oid
+ _hx509_private_key_ref
+ hx509_request_free
+ hx509_request_get_eku
+ hx509_request_get_exts
+ hx509_request_get_ku
+ hx509_request_get_name
+ hx509_request_get_san
+ hx509_request_get_SubjectPublicKeyInfo
+ hx509_request_get_name
+ hx509_request_init
+ hx509_request_parse
+ hx509_request_parse_der
+ hx509_request_print
+ hx509_request_set_SubjectPublicKeyInfo
+ hx509_request_add_email
+ hx509_request_set_name
+ hx509_request_set_ku
+ hx509_request_to_pkcs10
+ _hx509_unmap_file_os
+ _hx509_write_file
+ hx509_bitstring_print
+ _hx509_ca_issue_certificate
+ hx509_ca_sign
+ hx509_ca_sign_self
+ hx509_ca_tbs_add_crl_dp_uri
+ hx509_ca_tbs_add_eku
+ hx509_ca_tbs_add_ku
+ hx509_ca_tbs_add_pol
+ hx509_ca_tbs_add_pol_mapping
+ hx509_ca_tbs_add_san
+ hx509_ca_tbs_add_san_dnssrv
+ hx509_ca_tbs_add_san_hardwareModuleName
+ hx509_ca_tbs_add_san_hardwareModuleName_string
+ hx509_ca_tbs_add_san_hostname
+ hx509_ca_tbs_add_san_jid
+ hx509_ca_tbs_add_san_ms_upn
+ hx509_ca_tbs_add_san_otherName
+ hx509_ca_tbs_add_san_permanentIdentifier
+ hx509_ca_tbs_add_san_permanentIdentifier_string
+ hx509_ca_tbs_add_san_pkinit
+ hx509_ca_tbs_add_san_rfc822name
+ hx509_ca_tbs_free
+ hx509_ca_tbs_get_name
+ hx509_ca_tbs_init
+ hx509_ca_tbs_set_ca
+ hx509_ca_tbs_set_domaincontroller
+ hx509_ca_tbs_set_from_csr
+ hx509_ca_tbs_set_notAfter
+ hx509_ca_tbs_set_notAfter_lifetime
+ hx509_ca_tbs_set_notBefore
+ hx509_ca_tbs_set_pkinit_max_life
+ hx509_ca_tbs_set_proxy
+ hx509_ca_tbs_set_serialnumber
+ hx509_ca_tbs_set_signature_algorithm
+ hx509_ca_tbs_set_spki
+ hx509_ca_tbs_set_subject
+ hx509_ca_tbs_set_template
+ hx509_ca_tbs_subject_expand
+ hx509_ca_tbs_template_units
+; hx509_cert
+; hx509_cert_attribute
+ hx509_cert_binary
+ hx509_cert_check_eku
+ hx509_cert_cmp
+ hx509_cert_find_subjectAltName_otherName
+ hx509_cert_free
+ hx509_cert_get_SPKI
+ hx509_cert_get_SPKI_AlgorithmIdentifier
+ hx509_cert_get_attribute
+ hx509_cert_get_base_subject
+ hx509_cert_get_friendly_name
+ hx509_cert_get_issuer
+ hx509_cert_get_notAfter
+ hx509_cert_get_notBefore
+ hx509_cert_get_pkinit_max_life
+ hx509_cert_get_serialnumber
+ hx509_cert_get_subject
+ hx509_cert_have_private_key
+ hx509_cert_have_private_key_only
+ hx509_cert_init
+ hx509_cert_init_data
+ hx509_cert_init_private_key
+ hx509_cert_keyusage_print
+ hx509_cert_ref
+ hx509_cert_set_friendly_name
+ hx509_certs_add
+ hx509_certs_append
+ hx509_certs_destroy
+ hx509_certs_end_seq
+ hx509_certs_ref
+ hx509_certs_filter
+ hx509_certs_find
+ hx509_certs_free
+ hx509_certs_info
+ hx509_certs_init
+; hx509_certs_iter
+ hx509_certs_iter_f
+ hx509_certs_merge
+ hx509_certs_next_cert
+ hx509_certs_start_seq
+ hx509_certs_store
+ hx509_ci_print_names
+ hx509_clear_error_string
+ hx509_cms_create_signed
+ hx509_cms_create_signed_1
+ hx509_cms_decrypt_encrypted
+ hx509_cms_envelope_1
+ hx509_cms_unenvelope
+ hx509_cms_unwrap_ContentInfo
+ hx509_cms_verify_signed
+ hx509_cms_verify_signed_ext
+ hx509_cms_wrap_ContentInfo
+ hx509_context_free
+ hx509_context_init
+ hx509_context_set_missing_revoke
+ hx509_crl_add_revoked_certs
+ hx509_crl_alloc
+ hx509_crl_free
+ hx509_crl_lifetime
+ hx509_crl_sign
+ hx509_crypto_aes128_cbc
+ hx509_crypto_aes256_cbc
+ hx509_crypto_allow_weak
+ hx509_crypto_available
+ hx509_crypto_decrypt
+ hx509_crypto_des_rsdi_ede3_cbc
+ hx509_crypto_destroy
+ hx509_crypto_encrypt
+ hx509_crypto_enctype_by_name
+ hx509_crypto_free_algs
+ hx509_crypto_get_params
+ hx509_crypto_init
+ hx509_crypto_provider
+ hx509_crypto_select
+ hx509_crypto_set_key_data
+ hx509_crypto_set_key_name
+ hx509_crypto_set_padding
+ hx509_crypto_set_params
+ hx509_crypto_set_random_key
+ hx509_empty_name
+ hx509_env_add
+ hx509_env_add_binding
+ hx509_env_find
+ hx509_env_find_binding
+ hx509_env_free
+; hx509_env_init
+ hx509_env_lfind
+ hx509_err
+ hx509_free_error_string
+ hx509_free_octet_string_list
+ hx509_general_name_unparse
+ hx509_get_error_string
+ hx509_get_instance
+ hx509_get_one_cert
+ hx509_lock_add_cert
+ hx509_lock_add_certs
+ hx509_lock_add_password
+ hx509_lock_command_string
+ hx509_lock_free
+ hx509_lock_init
+ hx509_lock_prompt
+ hx509_lock_reset_certs
+ hx509_lock_reset_passwords
+ hx509_lock_reset_promper
+ hx509_lock_set_prompter
+ hx509_name_binary
+ hx509_name_cmp
+ hx509_name_copy
+ hx509_name_expand
+ hx509_name_free
+ hx509_name_is_null_p
+ hx509_name_normalize
+ hx509_name_to_Name
+ hx509_name_to_string
+ hx509_ocsp_request
+ hx509_ocsp_verify
+ hx509_oid_print
+ hx509_oid_sprint
+ hx509_parse_name
+ hx509_parse_private_key
+ hx509_peer_info_add_cms_alg
+ hx509_peer_info_alloc
+ hx509_peer_info_free
+ hx509_peer_info_set_cert
+ hx509_peer_info_set_cms_algs
+ hx509_pem_add_header
+ hx509_pem_find_header
+ hx509_pem_free_header
+ hx509_pem_read
+ hx509_pem_write
+ hx509_print_stdout
+ hx509_print_cert
+ hx509_prompt_hidden
+ hx509_query_alloc
+ hx509_query_free
+ hx509_query_match_cmp_func
+ hx509_query_match_eku
+ hx509_query_match_expr
+ hx509_query_match_friendly_name
+ hx509_query_match_issuer_serial
+ hx509_query_match_option
+ hx509_query_statistic_file
+ hx509_query_unparse_stats
+ hx509_revoke_add_crl
+ hx509_revoke_add_ocsp
+ hx509_revoke_free
+ hx509_revoke_init
+ hx509_revoke_ocsp_print
+ hx509_revoke_print
+ hx509_revoke_verify
+ hx509_set_error_string
+ hx509_set_error_stringv
+ hx509_signature_md5
+ hx509_signature_rsa
+ hx509_signature_rsa_with_md5
+ hx509_signature_rsa_with_sha1
+ hx509_signature_rsa_with_sha256
+ hx509_signature_rsa_with_sha384
+ hx509_signature_rsa_with_sha512
+ hx509_signature_sha1
+ hx509_signature_sha256
+ hx509_signature_sha384
+ hx509_signature_sha512
+ hx509_unparse_der_name
+ hx509_validate_cert
+ hx509_validate_ctx_add_flags
+ hx509_validate_ctx_free
+ hx509_validate_ctx_init
+ hx509_validate_ctx_set_print
+ hx509_verify_attach_anchors
+ hx509_verify_attach_revoke
+ hx509_verify_ctx_f_allow_default_trustanchors
+ hx509_verify_destroy_ctx
+ hx509_verify_hostname
+ hx509_verify_init_ctx
+ hx509_verify_path
+ hx509_verify_set_max_depth
+ hx509_verify_set_proxy_certificate
+ hx509_verify_set_strict_rfc3280_verification
+ hx509_verify_set_time
+ hx509_verify_signature
+ hx509_xfree
+ initialize_hx_error_table_r
+
+; pkcs11 symbols
+ C_GetFunctionList
diff --git a/third_party/heimdal/lib/hx509/lock.c b/third_party/heimdal/lib/hx509/lock.c
new file mode 100644
index 0000000..7f767d2
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/lock.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/**
+ * @page page_lock Locking and unlocking certificates and encrypted data.
+ *
+ * See the library functions here: @ref hx509_lock
+ */
+
+struct hx509_lock_data {
+ struct _hx509_password password;
+ hx509_certs certs;
+ hx509_prompter_fct prompt;
+ void *prompt_data;
+};
+
+static struct hx509_lock_data empty_lock_data = {
+ { 0, NULL },
+ NULL,
+ NULL,
+ NULL
+};
+
+hx509_lock _hx509_empty_lock = &empty_lock_data;
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_lock_init(hx509_context context, hx509_lock *lock)
+{
+ hx509_lock l;
+ int ret;
+
+ *lock = NULL;
+
+ l = calloc(1, sizeof(*l));
+ if (l == NULL)
+ return ENOMEM;
+
+ ret = hx509_certs_init(context,
+ "MEMORY:locks-internal",
+ 0,
+ NULL,
+ &l->certs);
+ if (ret) {
+ free(l);
+ return ret;
+ }
+
+ *lock = l;
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_lock_add_password(hx509_lock lock, const char *password)
+{
+ void *d;
+ char *s;
+
+ s = strdup(password);
+ if (s == NULL)
+ return ENOMEM;
+
+ d = realloc(lock->password.val,
+ (lock->password.len + 1) * sizeof(lock->password.val[0]));
+ if (d == NULL) {
+ free(s);
+ return ENOMEM;
+ }
+ lock->password.val = d;
+ lock->password.val[lock->password.len] = s;
+ lock->password.len++;
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION const struct _hx509_password * HX509_LIB_CALL
+_hx509_lock_get_passwords(hx509_lock lock)
+{
+ return &lock->password;
+}
+
+HX509_LIB_FUNCTION hx509_certs HX509_LIB_CALL
+_hx509_lock_unlock_certs(hx509_lock lock)
+{
+ return lock->certs;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_lock_reset_passwords(hx509_lock lock)
+{
+ size_t i;
+ for (i = 0; i < lock->password.len; i++)
+ free(lock->password.val[i]);
+ free(lock->password.val);
+ lock->password.val = NULL;
+ lock->password.len = 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_lock_add_cert(hx509_context context, hx509_lock lock, hx509_cert cert)
+{
+ return hx509_certs_add(context, lock->certs, cert);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_lock_add_certs(hx509_context context, hx509_lock lock, hx509_certs certs)
+{
+ return hx509_certs_merge(context, lock->certs, certs);
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_lock_reset_certs(hx509_context context, hx509_lock lock)
+{
+ hx509_certs certs = lock->certs;
+ int ret;
+
+ ret = hx509_certs_init(context,
+ "MEMORY:locks-internal",
+ 0,
+ NULL,
+ &lock->certs);
+ if (ret == 0)
+ hx509_certs_free(&certs);
+ else
+ lock->certs = certs;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_lock_find_cert(hx509_lock lock, const hx509_query *q, hx509_cert *c)
+{
+ *c = NULL;
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_lock_set_prompter(hx509_lock lock, hx509_prompter_fct prompt, void *data)
+{
+ lock->prompt = prompt;
+ lock->prompt_data = data;
+ return 0;
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_lock_reset_promper(hx509_lock lock)
+{
+ lock->prompt = NULL;
+ lock->prompt_data = NULL;
+}
+
+static int
+default_prompter(void *data, const hx509_prompt *prompter)
+{
+ if (hx509_prompt_hidden(prompter->type)) {
+ if(UI_UTIL_read_pw_string(prompter->reply.data,
+ prompter->reply.length,
+ prompter->prompt,
+ 0))
+ return 1;
+ } else {
+ char *s = prompter->reply.data;
+
+ fputs (prompter->prompt, stdout);
+ fflush (stdout);
+ if(fgets(prompter->reply.data,
+ prompter->reply.length,
+ stdin) == NULL)
+ return 1;
+ s[strcspn(s, "\n")] = '\0';
+ }
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_lock_prompt(hx509_lock lock, hx509_prompt *prompt)
+{
+ if (lock->prompt == NULL)
+ return HX509_CRYPTO_NO_PROMPTER;
+ return (*lock->prompt)(lock->prompt_data, prompt);
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_lock_free(hx509_lock lock)
+{
+ if (lock) {
+ hx509_certs_free(&lock->certs);
+ hx509_lock_reset_passwords(lock);
+ memset(lock, 0, sizeof(*lock));
+ free(lock);
+ }
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_prompt_hidden(hx509_prompt_type type)
+{
+ /* default to hidden if unknown */
+
+ switch (type) {
+ case HX509_PROMPT_TYPE_QUESTION:
+ case HX509_PROMPT_TYPE_INFO:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_lock_command_string(hx509_lock lock, const char *string)
+{
+ if (strncasecmp(string, "PASS:", 5) == 0) {
+ hx509_lock_add_password(lock, string + 5);
+ } else if (strcasecmp(string, "PROMPT") == 0) {
+ hx509_lock_set_prompter(lock, default_prompter, NULL);
+ } else
+ return HX509_UNKNOWN_LOCK_COMMAND;
+ return 0;
+}
diff --git a/third_party/heimdal/lib/hx509/name.c b/third_party/heimdal/lib/hx509/name.c
new file mode 100644
index 0000000..7d67716
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/name.c
@@ -0,0 +1,1529 @@
+/*
+ * Copyright (c) 2004 - 2009 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include <wind.h>
+#include "char_map.h"
+
+/**
+ * @page page_name PKIX/X.509 Names
+ *
+ * There are several names in PKIX/X.509, GeneralName and Name.
+ *
+ * A Name consists of an ordered list of Relative Distinguished Names
+ * (RDN). Each RDN consists of an unordered list of typed strings. The
+ * types are defined by OID and have long and short description. For
+ * example id-at-commonName (2.5.4.3) have the long name CommonName
+ * and short name CN. The string itself can be of several encoding,
+ * UTF8, UTF16, Teltex string, etc. The type limit what encoding
+ * should be used.
+ *
+ * GeneralName is a broader nametype that can contains al kind of
+ * stuff like Name, IP addresses, partial Name, etc.
+ *
+ * Name is mapped into a hx509_name object.
+ *
+ * Parse and string name into a hx509_name object with hx509_parse_name(),
+ * make it back into string representation with hx509_name_to_string().
+ *
+ * Name string are defined rfc2253, rfc1779 and X.501.
+ *
+ * See the library functions here: @ref hx509_name
+ */
+
+static const struct {
+ const char *n;
+ const heim_oid *o;
+ int type_choice; /* Preference for DirectoryString choice; 0 -> no pref */
+ wind_profile_flags flags;
+ /*
+ * RFC52380 imposes maximum lengths for some strings in Names. These are
+ * ASN.1 size limits. We should implement these in our copy of the PKIX
+ * ASN.1 module. For now we treat them as maximum byte counts rather than
+ * maximum character counts, and we encode and enforce them here.
+ *
+ * 0 -> no max
+ *
+ * Some of these attributes aren't of type DirectoryString, so our
+ * type_choice isn't really correct. We're not really set up for
+ * attributes whose types aren't DirectoryString or one of its choice arms'
+ * type, much less are we set up for non-string attribute value types.
+ */
+ size_t max_bytes;
+} no[] = {
+ { "C", &asn1_oid_id_at_countryName,
+ choice_DirectoryString_printableString, 0, 2 },
+ { "CN", &asn1_oid_id_at_commonName, 0, 0, ub_common_name },
+ { "DC", &asn1_oid_id_domainComponent, choice_DirectoryString_ia5String,
+ 0, 63 }, /* DNS label */
+ { "L", &asn1_oid_id_at_localityName, 0, 0, ub_locality_name },
+ { "O", &asn1_oid_id_at_organizationName, 0, 0, ub_organization_name },
+ { "OU", &asn1_oid_id_at_organizationalUnitName, 0, 0,
+ ub_organizational_unit_name },
+ { "S", &asn1_oid_id_at_stateOrProvinceName, 0, 0, ub_state_name },
+ { "STREET", &asn1_oid_id_at_streetAddress, 0, 0, 0 }, /* ENOTSUP */
+ { "UID", &asn1_oid_id_Userid, 0, 0, ub_numeric_user_id_length },
+ { "emailAddress", &asn1_oid_id_pkcs9_emailAddress,
+ choice_DirectoryString_ia5String, 0, ub_emailaddress_length },
+ /* This is for DevID certificates and maybe others */
+ { "serialNumber", &asn1_oid_id_at_serialNumber, 0, 0, ub_serial_number },
+ /* These are for TPM 2.0 Endorsement Key Certificates (EKCerts) */
+ { "TPMManufacturer", &asn1_oid_tcg_at_tpmManufacturer, 0, 0,
+ ub_emailaddress_length },
+ { "TPMModel", &asn1_oid_tcg_at_tpmModel, 0, 0, ub_emailaddress_length },
+ { "TPMVersion", &asn1_oid_tcg_at_tpmVersion, 0, 0, ub_emailaddress_length },
+};
+
+static char *
+quote_string(const char *f, size_t len, int flags, size_t *rlen)
+{
+ size_t i, j, tolen;
+ const unsigned char *from = (const unsigned char *)f;
+ unsigned char *to;
+
+ tolen = len * 3 + 1;
+ to = malloc(tolen);
+ if (to == NULL)
+ return NULL;
+
+ for (i = 0, j = 0; i < len; i++) {
+ unsigned char map = char_map[from[i]] & flags;
+ if (i == 0 && (map & Q_RFC2253_QUOTE_FIRST)) {
+ to[j++] = '\\';
+ to[j++] = from[i];
+ } else if ((i + 1) == len && (map & Q_RFC2253_QUOTE_LAST)) {
+
+ to[j++] = '\\';
+ to[j++] = from[i];
+ } else if (map & Q_RFC2253_QUOTE) {
+ to[j++] = '\\';
+ to[j++] = from[i];
+ } else if (map & Q_RFC2253_HEX) {
+ int l = snprintf((char *)&to[j], tolen - j - 1,
+ "#%02x", (unsigned char)from[i]);
+ j += l;
+ } else {
+ to[j++] = from[i];
+ }
+ }
+ to[j] = '\0';
+ assert(j < tolen);
+ *rlen = j;
+ return (char *)to;
+}
+
+
+static int
+append_string(char **str, size_t *total_len, const char *ss,
+ size_t len, int quote)
+{
+ char *s, *qs;
+
+ if (quote)
+ qs = quote_string(ss, len, Q_RFC2253, &len);
+ else
+ qs = rk_UNCONST(ss);
+
+ s = realloc(*str, len + *total_len + 1);
+ if (s == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ memcpy(s + *total_len, qs, len);
+ if (qs != ss)
+ free(qs);
+ s[*total_len + len] = '\0';
+ *str = s;
+ *total_len += len;
+ return 0;
+}
+
+static char *
+oidtostring(const heim_oid *type, int *type_choice)
+{
+ char *s;
+ size_t i;
+
+ if (type_choice)
+ *type_choice = choice_DirectoryString_utf8String;
+
+ for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
+ if (der_heim_oid_cmp(no[i].o, type) == 0) {
+ if (type_choice && no[i].type_choice)
+ *type_choice = no[i].type_choice;
+ return strdup(no[i].n);
+ }
+ }
+ if (der_print_heim_oid(type, '.', &s) != 0)
+ return NULL;
+ return s;
+}
+
+static size_t
+oidtomaxlen(const heim_oid *type)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
+ if (der_heim_oid_cmp(no[i].o, type) == 0)
+ return no[i].max_bytes;
+ }
+ return 0;
+}
+
+static int
+stringtooid(const char *name, size_t len, heim_oid *oid)
+{
+ int ret;
+ size_t i;
+ char *s;
+
+ memset(oid, 0, sizeof(*oid));
+
+ for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
+ if (strncasecmp(no[i].n, name, len) == 0)
+ return der_copy_oid(no[i].o, oid);
+ }
+ s = malloc(len + 1);
+ if (s == NULL)
+ return ENOMEM;
+ memcpy(s, name, len);
+ s[len] = '\0';
+ ret = der_parse_heim_oid(s, ".", oid);
+ free(s);
+ return ret;
+}
+
+/**
+ * Convert the hx509 name object into a printable string.
+ * The resulting string should be freed with free().
+ *
+ * @param name name to print
+ * @param str the string to return
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_name_to_string(const hx509_name name, char **str)
+{
+ return _hx509_Name_to_string(&name->der_name, str);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_Name_to_string(const Name *n, char **str)
+{
+ size_t total_len = 0;
+ size_t i, j, m;
+ int ret;
+
+ *str = strdup("");
+ if (*str == NULL)
+ return ENOMEM;
+
+ for (m = n->u.rdnSequence.len; m > 0; m--) {
+ size_t len;
+ i = m - 1;
+
+ for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
+ DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
+ char *oidname;
+ char *ss;
+
+ oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type, NULL);
+
+ switch(ds->element) {
+ case choice_DirectoryString_ia5String:
+ ss = ds->u.ia5String.data;
+ len = ds->u.ia5String.length;
+ break;
+ case choice_DirectoryString_printableString:
+ ss = ds->u.printableString.data;
+ len = ds->u.printableString.length;
+ break;
+ case choice_DirectoryString_utf8String:
+ ss = ds->u.utf8String;
+ len = strlen(ss);
+ break;
+ case choice_DirectoryString_bmpString: {
+ const uint16_t *bmp = ds->u.bmpString.data;
+ size_t bmplen = ds->u.bmpString.length;
+ size_t k;
+
+ ret = wind_ucs2utf8_length(bmp, bmplen, &k);
+ if (ret) {
+ free(oidname);
+ free(*str);
+ *str = NULL;
+ return ret;
+ }
+
+ ss = malloc(k + 1);
+ if (ss == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ ret = wind_ucs2utf8(bmp, bmplen, ss, NULL);
+ if (ret) {
+ free(oidname);
+ free(ss);
+ free(*str);
+ *str = NULL;
+ return ret;
+ }
+ ss[k] = '\0';
+ len = k;
+ break;
+ }
+ case choice_DirectoryString_teletexString:
+ ss = ds->u.teletexString;
+ len = strlen(ss);
+ break;
+ case choice_DirectoryString_universalString: {
+ const uint32_t *uni = ds->u.universalString.data;
+ size_t unilen = ds->u.universalString.length;
+ size_t k;
+
+ ret = wind_ucs4utf8_length(uni, unilen, &k);
+ if (ret) {
+ free(oidname);
+ free(*str);
+ *str = NULL;
+ return ret;
+ }
+
+ ss = malloc(k + 1);
+ if (ss == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ ret = wind_ucs4utf8(uni, unilen, ss, NULL);
+ if (ret) {
+ free(ss);
+ free(oidname);
+ free(*str);
+ *str = NULL;
+ return ret;
+ }
+ ss[k] = '\0';
+ len = k;
+ break;
+ }
+ default:
+ _hx509_abort("unknown directory type: %d", ds->element);
+ exit(1);
+ }
+ append_string(str, &total_len, oidname, strlen(oidname), 0);
+ free(oidname);
+ append_string(str, &total_len, "=", 1, 0);
+ append_string(str, &total_len, ss, len, 1);
+ if (ds->element == choice_DirectoryString_bmpString ||
+ ds->element == choice_DirectoryString_universalString)
+ {
+ free(ss);
+ }
+ if (j + 1 < n->u.rdnSequence.val[i].len)
+ append_string(str, &total_len, "+", 1, 0);
+ }
+
+ if (i > 0)
+ append_string(str, &total_len, ",", 1, 0);
+ }
+ return 0;
+}
+
+#define COPYCHARARRAY(_ds,_el,_l,_n) \
+ (_l) = strlen(_ds->u._el); \
+ (_n) = malloc((_l + 1) * sizeof((_n)[0])); \
+ if ((_n) == NULL) \
+ return ENOMEM; \
+ for (i = 0; i < (_l); i++) \
+ (_n)[i] = _ds->u._el[i]
+
+
+#define COPYVALARRAY(_ds,_el,_l,_n) \
+ (_l) = _ds->u._el.length; \
+ (_n) = malloc((_l + 1) * sizeof((_n)[0])); \
+ if ((_n) == NULL) \
+ return ENOMEM; \
+ for (i = 0; i < (_l); i++) \
+ (_n)[i] = _ds->u._el.data[i]
+
+#define COPYVOIDARRAY(_ds,_el,_l,_n) \
+ (_l) = _ds->u._el.length; \
+ (_n) = malloc((_l + 1) * sizeof((_n)[0])); \
+ if ((_n) == NULL) \
+ return ENOMEM; \
+ for (i = 0; i < (_l); i++) \
+ (_n)[i] = ((unsigned char *)_ds->u._el.data)[i]
+
+
+
+static int
+dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen)
+{
+ wind_profile_flags flags;
+ size_t i, len;
+ int ret = 0;
+ uint32_t *name;
+
+ *rname = NULL;
+ *rlen = 0;
+
+ switch(ds->element) {
+ case choice_DirectoryString_ia5String:
+ flags = WIND_PROFILE_LDAP;
+ COPYVOIDARRAY(ds, ia5String, len, name);
+ break;
+ case choice_DirectoryString_printableString:
+ flags = WIND_PROFILE_LDAP;
+ flags |= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE;
+ COPYVOIDARRAY(ds, printableString, len, name);
+ break;
+ case choice_DirectoryString_teletexString:
+ flags = WIND_PROFILE_LDAP_CASE;
+ COPYCHARARRAY(ds, teletexString, len, name);
+ break;
+ case choice_DirectoryString_bmpString:
+ flags = WIND_PROFILE_LDAP;
+ COPYVALARRAY(ds, bmpString, len, name);
+ break;
+ case choice_DirectoryString_universalString:
+ flags = WIND_PROFILE_LDAP;
+ COPYVALARRAY(ds, universalString, len, name);
+ break;
+ case choice_DirectoryString_utf8String:
+ flags = WIND_PROFILE_LDAP;
+ ret = wind_utf8ucs4_length(ds->u.utf8String, &len);
+ if (ret)
+ return ret;
+ name = malloc((len + 1) * sizeof(name[0]));
+ if (name == NULL)
+ return ENOMEM;
+ ret = wind_utf8ucs4(ds->u.utf8String, name, &len);
+ if (ret) {
+ free(name);
+ return ret;
+ }
+ break;
+ default:
+ _hx509_abort("unknown directory type: %d", ds->element);
+ }
+
+ *rlen = len;
+ /* try a couple of times to get the length right, XXX gross */
+ for (i = 0; i < 4; i++) {
+ *rlen = *rlen * 2;
+ if ((*rname = malloc((rlen[0] + 1) * sizeof((*rname)[0]))) == NULL) {
+ ret = ENOMEM;
+ break;
+ }
+
+ ret = wind_stringprep(name, len, *rname, rlen, flags);
+ if (ret == WIND_ERR_OVERRUN) {
+ free(*rname);
+ *rname = NULL;
+ continue;
+ } else
+ break;
+ }
+ free(name);
+ if (ret) {
+ if (*rname)
+ free(*rname);
+ *rname = NULL;
+ *rlen = 0;
+ return ret;
+ }
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_name_ds_cmp(const DirectoryString *ds1,
+ const DirectoryString *ds2,
+ int *diff)
+{
+ uint32_t *ds1lp, *ds2lp;
+ size_t ds1len, ds2len, i;
+ int ret;
+
+ ret = dsstringprep(ds1, &ds1lp, &ds1len);
+ if (ret)
+ return ret;
+ ret = dsstringprep(ds2, &ds2lp, &ds2len);
+ if (ret) {
+ free(ds1lp);
+ return ret;
+ }
+
+ if (ds1len != ds2len)
+ *diff = ds1len - ds2len;
+ else {
+ for (i = 0; i < ds1len; i++) {
+ *diff = ds1lp[i] - ds2lp[i];
+ if (*diff)
+ break;
+ }
+ }
+ free(ds1lp);
+ free(ds2lp);
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_name_cmp(const Name *n1, const Name *n2, int *c)
+{
+ int ret;
+ size_t i, j;
+
+ *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
+ if (*c)
+ return 0;
+
+ for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
+ *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
+ if (*c)
+ return 0;
+
+ for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
+ *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
+ &n1->u.rdnSequence.val[i].val[j].type);
+ if (*c)
+ return 0;
+
+ ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
+ &n2->u.rdnSequence.val[i].val[j].value,
+ c);
+ if (ret)
+ return ret;
+ if (*c)
+ return 0;
+ }
+ }
+ *c = 0;
+ return 0;
+}
+
+/**
+ * Compare to hx509 name object, useful for sorting.
+ *
+ * @param n1 a hx509 name object.
+ * @param n2 a hx509 name object.
+ *
+ * @return 0 the objects are the same, returns > 0 is n2 is "larger"
+ * then n2, < 0 if n1 is "smaller" then n2.
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_name_cmp(hx509_name n1, hx509_name n2)
+{
+ int ret, diff;
+ ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff);
+ if (ret)
+ return ret;
+ return diff;
+}
+
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_name_from_Name(const Name *n, hx509_name *name)
+{
+ int ret;
+ *name = calloc(1, sizeof(**name));
+ if (*name == NULL)
+ return ENOMEM;
+ ret = copy_Name(n, &(*name)->der_name);
+ if (ret) {
+ free(*name);
+ *name = NULL;
+ }
+ return ret;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_name_modify(hx509_context context,
+ Name *name,
+ int append,
+ const heim_oid *oid,
+ const char *str)
+{
+ RelativeDistinguishedName rdn;
+ size_t max_len = oidtomaxlen(oid);
+ char *s = NULL;
+ int type_choice = choice_DirectoryString_printableString;
+ int ret;
+
+ /*
+ * Check string length upper bounds.
+ *
+ * Because we don't have these bounds in our copy of the PKIX ASN.1 module,
+ * and because we might like to catch these early anyways, we enforce them
+ * here.
+ */
+ if (max_len && strlen(str) > max_len) {
+ char *a = oidtostring(oid, &type_choice);
+
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, "RDN attribute %s value too "
+ "long (max %llu): %s", a ? a : "<unknown>",
+ max_len, str);
+ free(a);
+ return ret;
+ }
+
+ memset(&rdn, 0, sizeof(rdn));
+ if ((rdn.val = malloc(sizeof(rdn.val[0]))) == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
+ return ENOMEM;
+ }
+ rdn.len = 1;
+
+ /*
+ * How best to pick a type for this attribute value?
+ *
+ * Options:
+ *
+ * 1) the API deals only in UTF-8, let the callers convert to/from UTF-8
+ * and whatever the current locale wants
+ *
+ * 2) use the best type for the codeset of the current locale.
+ *
+ * We choose (1).
+ *
+ * However, for some cases we really should prefer other types when the
+ * input string is all printable ASCII.
+ */
+ rdn.val[0].value.element = type_choice;
+ if ((s = strdup(str)) == NULL ||
+ der_copy_oid(oid, &rdn.val[0].type)) {
+ free(rdn.val);
+ free(s);
+ return hx509_enomem(context);
+ }
+ switch (rdn.val[0].value.element) {
+ /* C strings: */
+ case choice_DirectoryString_utf8String:
+ rdn.val[0].value.u.utf8String = s;
+ break;
+ case choice_DirectoryString_teletexString:
+ rdn.val[0].value.u.teletexString = s;
+ break;
+
+ /* Length and pointer */
+ case choice_DirectoryString_ia5String:
+ rdn.val[0].value.u.ia5String.data = s;
+ rdn.val[0].value.u.ia5String.length = strlen(s);
+ break;
+ case choice_DirectoryString_printableString:
+ rdn.val[0].value.u.printableString.data = s;
+ rdn.val[0].value.u.printableString.length = strlen(s);
+ break;
+ case choice_DirectoryString_universalString:
+ free(s);
+ free(rdn.val);
+ hx509_set_error_string(context, 0, ENOTSUP, "UniversalString not supported");
+ return ENOTSUP;
+ case choice_DirectoryString_bmpString:
+ free(s);
+ free(rdn.val);
+ hx509_set_error_string(context, 0, ENOTSUP, "BMPString not supported");
+ return ENOTSUP;
+ default:
+ free(s);
+ free(rdn.val);
+ hx509_set_error_string(context, 0, ENOTSUP,
+ "Internal error; unknown DirectoryString choice");
+ return ENOTSUP;
+ }
+
+ /* Append RDN. If the caller wanted to prepend instead, we'll rotate. */
+ ret = add_RDNSequence(&name->u.rdnSequence, &rdn);
+ free_RelativeDistinguishedName(&rdn);
+
+ if (ret || append || name->u.rdnSequence.len < 2)
+ return ret;
+
+ /* Rotate */
+ rdn = name->u.rdnSequence.val[name->u.rdnSequence.len - 1];
+ memmove(&name->u.rdnSequence.val[1],
+ &name->u.rdnSequence.val[0],
+ (name->u.rdnSequence.len - 1) *
+ sizeof(name->u.rdnSequence.val[0]));
+ name->u.rdnSequence.val[0] = rdn;
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_empty_name(hx509_context context, hx509_name *name)
+{
+ if ((*name = calloc(1, sizeof(**name))) == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ (*name)->der_name.element = choice_Name_rdnSequence;
+ (*name)->der_name.u.rdnSequence.val = 0;
+ (*name)->der_name.u.rdnSequence.len = 0;
+ return 0;
+}
+
+/**
+ * Parse a string into a hx509 name object.
+ *
+ * @param context A hx509 context.
+ * @param str a string to parse.
+ * @param name the resulting object, NULL in case of error.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
+{
+ const char *p, *q;
+ size_t len;
+ hx509_name n;
+ int ret;
+
+ *name = NULL;
+
+ n = calloc(1, sizeof(*n));
+ if (n == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ n->der_name.element = choice_Name_rdnSequence;
+
+ p = str;
+
+ while (p != NULL && *p != '\0') {
+ heim_oid oid;
+ int last;
+
+ q = strchr(p, ',');
+ if (q) {
+ len = (q - p);
+ last = 1;
+ } else {
+ len = strlen(p);
+ last = 0;
+ }
+
+ q = strchr(p, '=');
+ if (q == NULL) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, "missing = in %s", p);
+ goto out;
+ }
+ if (q == p) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "missing name before = in %s", p);
+ goto out;
+ }
+
+ if ((size_t)(q - p) > len) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, " = after , in %s", p);
+ goto out;
+ }
+
+ ret = stringtooid(p, q - p, &oid);
+ if (ret) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "unknown type: %.*s", (int)(q - p), p);
+ goto out;
+ }
+
+ {
+ size_t pstr_len = len - (q - p) - 1;
+ const char *pstr = p + (q - p) + 1;
+ char *r;
+
+ r = malloc(pstr_len + 1);
+ if (r == NULL) {
+ der_free_oid(&oid);
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+ memcpy(r, pstr, pstr_len);
+ r[pstr_len] = '\0';
+
+ ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
+ free(r);
+ der_free_oid(&oid);
+ if(ret)
+ goto out;
+ }
+ p += len + last;
+ }
+
+ *name = n;
+
+ return 0;
+out:
+ hx509_name_free(&n);
+ return HX509_NAME_MALFORMED;
+}
+
+/**
+ * Copy a hx509 name object.
+ *
+ * @param context A hx509 cotext.
+ * @param from the name to copy from
+ * @param to the name to copy to
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
+{
+ int ret;
+
+ *to = calloc(1, sizeof(**to));
+ if (*to == NULL)
+ return ENOMEM;
+ ret = copy_Name(&from->der_name, &(*to)->der_name);
+ if (ret) {
+ free(*to);
+ *to = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * Convert a hx509_name into a Name.
+ *
+ * @param from the name to copy from
+ * @param to the name to copy to
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_name_to_Name(const hx509_name from, Name *to)
+{
+ return copy_Name(&from->der_name, to);
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_name_normalize(hx509_context context, hx509_name name)
+{
+ return 0;
+}
+
+/**
+ * Expands variables in the name using env. Variables are on the form
+ * ${name}. Useful when dealing with certificate templates.
+ *
+ * @param context A hx509 cotext.
+ * @param name the name to expand.
+ * @param env environment variable to expand.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_name_expand(hx509_context context,
+ hx509_name name,
+ hx509_env env)
+{
+ Name *n = &name->der_name;
+ size_t i, j;
+ int bounds_check = 1;
+
+ if (env == NULL)
+ return 0;
+
+ if (n->element != choice_Name_rdnSequence) {
+ hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type");
+ return EINVAL;
+ }
+
+ for (i = 0 ; i < n->u.rdnSequence.len; i++) {
+ for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
+ /** Only UTF8String rdnSequence names are allowed */
+ /*
+ THIS SHOULD REALLY BE:
+ COMP = n->u.rdnSequence.val[i].val[j];
+ normalize COMP to utf8
+ check if there are variables
+ expand variables
+ convert back to orignal format, store in COMP
+ free normalized utf8 string
+ */
+ DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
+ heim_oid *type = &n->u.rdnSequence.val[i].val[j].type;
+ const char *sval = NULL;
+ char *p, *p2;
+ char *s = NULL;
+ struct rk_strpool *strpool = NULL;
+
+ switch (ds->element) {
+ case choice_DirectoryString_utf8String:
+ sval = ds->u.utf8String;
+ break;
+ case choice_DirectoryString_teletexString:
+ sval = ds->u.utf8String;
+ break;
+ case choice_DirectoryString_ia5String:
+ s = strndup(ds->u.ia5String.data,
+ ds->u.ia5String.length);
+ break;
+ case choice_DirectoryString_printableString:
+ s = strndup(ds->u.printableString.data,
+ ds->u.printableString.length);
+ break;
+ case choice_DirectoryString_universalString:
+ hx509_set_error_string(context, 0, ENOTSUP, "UniversalString not supported");
+ return ENOTSUP;
+ case choice_DirectoryString_bmpString:
+ hx509_set_error_string(context, 0, ENOTSUP, "BMPString not supported");
+ return ENOTSUP;
+ }
+ if (sval == NULL && s == NULL)
+ return hx509_enomem(context);
+ if (s)
+ sval = s;
+
+ p = strstr(sval, "${");
+ if (p) {
+ strpool = rk_strpoolprintf(strpool, "%.*s", (int)(p - sval), sval);
+ if (strpool == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ free(s);
+ return ENOMEM;
+ }
+ }
+
+ while (p != NULL) {
+ /* expand variables */
+ const char *value;
+ p2 = strchr(p, '}');
+ if (p2 == NULL) {
+ hx509_set_error_string(context, 0, EINVAL, "missing }");
+ rk_strpoolfree(strpool);
+ free(s);
+ return EINVAL;
+ }
+ p += 2;
+ value = hx509_env_lfind(context, env, p, p2 - p);
+ if (value == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "variable %.*s missing",
+ (int)(p2 - p), p);
+ rk_strpoolfree(strpool);
+ free(s);
+ return EINVAL;
+ }
+ strpool = rk_strpoolprintf(strpool, "%s", value);
+ if (strpool == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ free(s);
+ return ENOMEM;
+ }
+ p2++;
+
+ p = strstr(p2, "${");
+ if (p)
+ strpool = rk_strpoolprintf(strpool, "%.*s",
+ (int)(p - p2), p2);
+ else
+ strpool = rk_strpoolprintf(strpool, "%s", p2);
+ if (strpool == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ free(s);
+ return ENOMEM;
+ }
+ }
+
+ free(s);
+ s = NULL;
+
+ if (strpool) {
+ size_t max_bytes;
+
+ if ((s = rk_strpoolcollect(strpool)) == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ /* Check upper bounds! */
+ if ((max_bytes = oidtomaxlen(type)) && strlen(s) > max_bytes)
+ bounds_check = 0;
+
+ switch (ds->element) {
+ /* C strings: */
+ case choice_DirectoryString_utf8String:
+ free(ds->u.utf8String);
+ ds->u.utf8String = s;
+ break;
+ case choice_DirectoryString_teletexString:
+ free(ds->u.teletexString);
+ ds->u.teletexString = s;
+ break;
+
+ /* Length and pointer */
+ case choice_DirectoryString_ia5String:
+ free(ds->u.ia5String.data);
+ ds->u.ia5String.data = s;
+ ds->u.ia5String.length = strlen(s);
+ break;
+ case choice_DirectoryString_printableString:
+ free(ds->u.printableString.data);
+ ds->u.printableString.data = s;
+ ds->u.printableString.length = strlen(s);
+ break;
+ default:
+ break; /* Handled above */
+ }
+ }
+ }
+ }
+
+ if (!bounds_check) {
+ hx509_set_error_string(context, 0, HX509_PARSING_NAME_FAILED,
+ "some expanded RDNs are too long");
+ return HX509_PARSING_NAME_FAILED;
+ }
+ return 0;
+}
+
+/**
+ * Free a hx509 name object, upond return *name will be NULL.
+ *
+ * @param name a hx509 name object to be freed.
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_name_free(hx509_name *name)
+{
+ free_Name(&(*name)->der_name);
+ memset(*name, 0, sizeof(**name));
+ free(*name);
+ *name = NULL;
+}
+
+/**
+ * Convert a DER encoded name info a string.
+ *
+ * @param data data to a DER/BER encoded name
+ * @param length length of data
+ * @param str the resulting string, is NULL on failure.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_unparse_der_name(const void *data, size_t length, char **str)
+{
+ Name name;
+ int ret;
+
+ *str = NULL;
+
+ ret = decode_Name(data, length, &name, NULL);
+ if (ret)
+ return ret;
+ ret = _hx509_Name_to_string(&name, str);
+ free_Name(&name);
+ return ret;
+}
+
+/**
+ * Convert a hx509_name object to DER encoded name.
+ *
+ * @param name name to concert
+ * @param os data to a DER encoded name, free the resulting octet
+ * string with hx509_xfree(os->data).
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_name_binary(const hx509_name name, heim_octet_string *os)
+{
+ size_t size;
+ int ret;
+
+ ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret);
+ if (ret)
+ return ret;
+ if (os->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_unparse_Name(const Name *aname, char **str)
+{
+ hx509_name name;
+ int ret;
+
+ ret = _hx509_name_from_Name(aname, &name);
+ if (ret)
+ return ret;
+
+ ret = hx509_name_to_string(name, str);
+ hx509_name_free(&name);
+ return ret;
+}
+
+/**
+ * Check if a name is empty.
+ *
+ * @param name the name to check if its empty/null.
+ *
+ * @return non zero if the name is empty/null.
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_name_is_null_p(const hx509_name name)
+{
+ return name->der_name.element == choice_Name_rdnSequence &&
+ name->der_name.u.rdnSequence.len == 0;
+}
+
+int
+_hx509_unparse_PermanentIdentifier(hx509_context context,
+ struct rk_strpool **strpool,
+ heim_any *value)
+{
+ PermanentIdentifier pi;
+ size_t len;
+ const char *pid = "";
+ char *s = NULL;
+ int ret;
+
+ ret = decode_PermanentIdentifier(value->data, value->length, &pi, &len);
+ if (ret == 0 && pi.assigner &&
+ der_print_heim_oid(pi.assigner, '.', &s) != 0)
+ ret = hx509_enomem(context);
+ if (pi.identifierValue && *pi.identifierValue)
+ pid = *pi.identifierValue;
+ if (ret == 0 &&
+ (*strpool = rk_strpoolprintf(*strpool, "%s:%s", s ? s : "", pid)) == NULL)
+ ret = hx509_enomem(context);
+ free_PermanentIdentifier(&pi);
+ free(s);
+ if (ret) {
+ rk_strpoolfree(*strpool);
+ *strpool = rk_strpoolprintf(NULL,
+ "<error-decoding-PermanentIdentifier");
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode PermanentIdentifier");
+ }
+ return ret;
+}
+
+int
+_hx509_unparse_HardwareModuleName(hx509_context context,
+ struct rk_strpool **strpool,
+ heim_any *value)
+{
+ HardwareModuleName hm;
+ size_t len;
+ char *s = NULL;
+ int ret;
+
+ ret = decode_HardwareModuleName(value->data, value->length, &hm, &len);
+ if (ret == 0 && hm.hwSerialNum.length > 256)
+ hm.hwSerialNum.length = 256;
+ if (ret == 0)
+ ret = der_print_heim_oid(&hm.hwType, '.', &s);
+ if (ret == 0) {
+ *strpool = rk_strpoolprintf(*strpool, "%s:%.*s%s", s,
+ (int)hm.hwSerialNum.length,
+ (char *)hm.hwSerialNum.data,
+ value->length == len ? "" : ", <garbage>");
+ if (*strpool == NULL)
+ ret = hx509_enomem(context);
+ }
+ free_HardwareModuleName(&hm);
+ free(s);
+ if (ret) {
+ rk_strpoolfree(*strpool);
+ *strpool = rk_strpoolprintf(NULL,
+ "<error-decoding-HardwareModuleName");
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode HardwareModuleName");
+ }
+ return ret;
+}
+
+/*
+ * This necessarily duplicates code from libkrb5, and has to unless we move
+ * common code here or to lib/roken for it. We do have slightly different
+ * needs (e.g., we want space quoted, and we want to indicate whether we saw
+ * trailing garbage, we have no need for flags, no special realm treatment,
+ * etc) than the corresponding code in libkrb5, so for now we duplicate this
+ * code.
+ *
+ * The relevant RFCs here are RFC1964 for the string representation of Kerberos
+ * principal names, and RFC4556 for the KRB5PrincipalName ASN.1 type (Kerberos
+ * lacks such a type because on the wire the name and realm are sent
+ * separately as a form of cheap compression).
+ *
+ * Note that we cannot handle embedded NULs because of Heimdal's representation
+ * of ASN.1 strings as C strings.
+ */
+int
+_hx509_unparse_KRB5PrincipalName(hx509_context context,
+ struct rk_strpool **strpool,
+ heim_any *value)
+{
+ KRB5PrincipalName kn;
+ size_t len;
+ int ret;
+
+ ret = decode_KRB5PrincipalName(value->data, value->length, &kn, &len);
+ if (ret == 0 &&
+ (*strpool = _hx509_unparse_kerberos_name(*strpool, &kn)) == NULL)
+ ret = hx509_enomem(context);
+ free_KRB5PrincipalName(&kn);
+ if (ret == 0 && (value->length != len) &&
+ (*strpool = rk_strpoolprintf(*strpool, " <garbage>")) == NULL)
+ ret = hx509_enomem(context);
+ if (ret) {
+ rk_strpoolfree(*strpool);
+ *strpool = rk_strpoolprintf(NULL,
+ "<error-decoding-PrincipalName");
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode PermanentIdentifier");
+ }
+ return ret;
+}
+
+struct rk_strpool *
+_hx509_unparse_kerberos_name(struct rk_strpool *strpool, KRB5PrincipalName *kn)
+{
+ static const char comp_quotable_chars[] = " \n\t\b\\/@";
+ static const char realm_quotable_chars[] = " \n\t\b\\@";
+ const char *s;
+ size_t i, k, len, plen;
+ int need_slash = 0;
+
+ for (i = 0; i < kn->principalName.name_string.len; i++) {
+ s = kn->principalName.name_string.val[i];
+ len = strlen(s);
+
+ if (need_slash)
+ strpool = rk_strpoolprintf(strpool, "/");
+ need_slash = 1;
+
+ for (k = 0; k < len; s += plen, k += plen) {
+ char c;
+
+ plen = strcspn(s, comp_quotable_chars);
+ if (plen)
+ strpool = rk_strpoolprintf(strpool, "%.*s", (int)plen, s);
+ if (k + plen >= len)
+ continue;
+ switch ((c = s[plen++])) {
+ case '\n': strpool = rk_strpoolprintf(strpool, "\\n"); break;
+ case '\t': strpool = rk_strpoolprintf(strpool, "\\t"); break;
+ case '\b': strpool = rk_strpoolprintf(strpool, "\\b"); break;
+ /* default -> '@', ' ', '\\', or '/' */
+ default: strpool = rk_strpoolprintf(strpool, "\\%c", c); break;
+ }
+ }
+ }
+ if (!kn->realm)
+ return strpool;
+ strpool = rk_strpoolprintf(strpool, "@");
+
+ s = kn->realm;
+ len = strlen(kn->realm);
+ for (k = 0; k < len; s += plen, k += plen) {
+ char c;
+
+ plen = strcspn(s, realm_quotable_chars);
+ if (plen)
+ strpool = rk_strpoolprintf(strpool, "%.*s", (int)plen, s);
+ if (k + plen >= len)
+ continue;
+ switch ((c = s[plen++])) {
+ case '\n': strpool = rk_strpoolprintf(strpool, "\\n"); break;
+ case '\t': strpool = rk_strpoolprintf(strpool, "\\t"); break;
+ case '\b': strpool = rk_strpoolprintf(strpool, "\\b"); break;
+ /* default -> '@', ' ', or '\\' */
+ default: strpool = rk_strpoolprintf(strpool, "\\%c", c); break;
+ }
+ }
+ return strpool;
+}
+
+int
+_hx509_unparse_utf8_string_name(hx509_context context,
+ struct rk_strpool **strpool,
+ heim_any *value)
+{
+ PKIXXmppAddr us;
+ size_t size;
+ int ret;
+
+ ret = decode_PKIXXmppAddr(value->data, value->length, &us, &size);
+ if (ret == 0 &&
+ (*strpool = rk_strpoolprintf(*strpool, "%s", us)) == NULL)
+ ret = hx509_enomem(context);
+ if (ret) {
+ rk_strpoolfree(*strpool);
+ *strpool = rk_strpoolprintf(NULL,
+ "<error-decoding-UTF8String-SAN>");
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode UTF8String SAN");
+ }
+ free_PKIXXmppAddr(&us);
+ return ret;
+}
+
+int
+_hx509_unparse_ia5_string_name(hx509_context context,
+ struct rk_strpool **strpool,
+ heim_any *value)
+{
+ SRVName us;
+ size_t size;
+ int ret;
+
+ ret = decode_SRVName(value->data, value->length, &us, &size);
+ if (ret == 0) {
+ rk_strpoolfree(*strpool);
+ *strpool = rk_strpoolprintf(NULL,
+ "<error-decoding-IA5String-SAN>");
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode UTF8String SAN");
+ return ret;
+ }
+ *strpool = rk_strpoolprintf(*strpool, "%.*s",
+ (int)us.length, (char *)us.data);
+ free_SRVName(&us);
+ return ret;
+}
+
+typedef int (*other_unparser_f)(hx509_context,
+ struct rk_strpool **,
+ heim_any *);
+
+struct {
+ const heim_oid *oid;
+ const char *friendly_name;
+ other_unparser_f f;
+} o_unparsers[] = {
+ { &asn1_oid_id_pkinit_san,
+ "KerberosPrincipalName",
+ _hx509_unparse_KRB5PrincipalName },
+ { &asn1_oid_id_pkix_on_permanentIdentifier,
+ "PermanentIdentifier",
+ _hx509_unparse_PermanentIdentifier },
+ { &asn1_oid_id_on_hardwareModuleName,
+ "HardwareModuleName",
+ _hx509_unparse_HardwareModuleName },
+ { &asn1_oid_id_pkix_on_xmppAddr,
+ "XMPPName",
+ _hx509_unparse_utf8_string_name },
+ { &asn1_oid_id_pkinit_ms_san,
+ "MSFTKerberosPrincipalName",
+ _hx509_unparse_utf8_string_name },
+ { &asn1_oid_id_pkix_on_dnsSRV,
+ "SRVName",
+ _hx509_unparse_ia5_string_name },
+};
+
+/**
+ * Unparse the hx509 name in name into a string.
+ *
+ * @param name the name to print
+ * @param str an allocated string returns the name in string form
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_general_name_unparse(GeneralName *name, char **str)
+{
+ hx509_context context;
+ int ret;
+
+ if ((ret = hx509_context_init(&context)))
+ return ret;
+ ret = hx509_general_name_unparse2(context, name, str);
+ hx509_context_free(&context);
+ return ret;
+}
+
+/**
+ * Unparse the hx509 name in name into a string.
+ *
+ * @param context hx509 library context
+ * @param name the name to print
+ * @param str an allocated string returns the name in string form
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_general_name_unparse2(hx509_context context,
+ GeneralName *name,
+ char **str)
+{
+ struct rk_strpool *strpool = NULL;
+ int ret = 0;
+
+ *str = NULL;
+
+ switch (name->element) {
+ case choice_GeneralName_otherName: {
+ size_t i;
+ char *oid;
+
+ ret = hx509_oid_sprint(&name->u.otherName.type_id, &oid);
+ if (ret == 0)
+ strpool = rk_strpoolprintf(strpool, "otherName: %s ", oid);
+ if (strpool == NULL)
+ ret = ENOMEM;
+
+ for (i = 0; ret == 0 && i < sizeof(o_unparsers)/sizeof(o_unparsers[0]); i++) {
+ if (der_heim_oid_cmp(&name->u.otherName.type_id,
+ o_unparsers[i].oid))
+ continue;
+ strpool = rk_strpoolprintf(strpool, "%s ",o_unparsers[i].friendly_name);
+ if (strpool == NULL)
+ ret = ENOMEM;
+ if (ret == 0)
+ ret = o_unparsers[i].f(context, &strpool, &name->u.otherName.value);
+ break;
+ }
+ if (ret == 0 && i == sizeof(o_unparsers)/sizeof(o_unparsers[0])) {
+ strpool = rk_strpoolprintf(strpool, "<unknown-other-name-type>");
+ ret = ENOTSUP;
+ }
+ free(oid);
+ break;
+ }
+ case choice_GeneralName_rfc822Name:
+ strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s",
+ (int)name->u.rfc822Name.length,
+ (char *)name->u.rfc822Name.data);
+ break;
+ case choice_GeneralName_dNSName:
+ strpool = rk_strpoolprintf(strpool, "dNSName: %.*s",
+ (int)name->u.dNSName.length,
+ (char *)name->u.dNSName.data);
+ break;
+ case choice_GeneralName_directoryName: {
+ Name dir;
+ char *s;
+ memset(&dir, 0, sizeof(dir));
+ dir.element = (enum Name_enum)name->u.directoryName.element;
+ dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
+ ret = _hx509_unparse_Name(&dir, &s);
+ if (ret)
+ return ret;
+ strpool = rk_strpoolprintf(strpool, "directoryName: %s", s);
+ free(s);
+ break;
+ }
+ case choice_GeneralName_uniformResourceIdentifier:
+ strpool = rk_strpoolprintf(strpool, "URI: %.*s",
+ (int)name->u.uniformResourceIdentifier.length,
+ (char *)name->u.uniformResourceIdentifier.data);
+ break;
+ case choice_GeneralName_iPAddress: {
+ unsigned char *a = name->u.iPAddress.data;
+
+ strpool = rk_strpoolprintf(strpool, "IPAddress: ");
+ if (strpool == NULL)
+ break;
+ if (name->u.iPAddress.length == 4)
+ strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d",
+ a[0], a[1], a[2], a[3]);
+ else if (name->u.iPAddress.length == 16)
+ strpool = rk_strpoolprintf(strpool,
+ "%02X:%02X:%02X:%02X:"
+ "%02X:%02X:%02X:%02X:"
+ "%02X:%02X:%02X:%02X:"
+ "%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3],
+ a[4], a[5], a[6], a[7],
+ a[8], a[9], a[10], a[11],
+ a[12], a[13], a[14], a[15]);
+ else
+ strpool = rk_strpoolprintf(strpool,
+ "unknown IP address of length %lu",
+ (unsigned long)name->u.iPAddress.length);
+ break;
+ }
+ case choice_GeneralName_registeredID: {
+ char *oid;
+ hx509_oid_sprint(&name->u.registeredID, &oid);
+ if (oid == NULL)
+ return ENOMEM;
+ strpool = rk_strpoolprintf(strpool, "registeredID: %s", oid);
+ free(oid);
+ break;
+ }
+ default:
+ return EINVAL;
+ }
+ if (ret)
+ rk_strpoolfree(strpool);
+ else if (strpool == NULL || (*str = rk_strpoolcollect(strpool)) == NULL)
+ return ENOMEM;
+ return ret;
+}
diff --git a/third_party/heimdal/lib/hx509/peer.c b/third_party/heimdal/lib/hx509/peer.c
new file mode 100644
index 0000000..2501f01
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/peer.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+/**
+ * @page page_peer Hx509 crypto selecting functions
+ *
+ * Peer info structures are used togeter with hx509_crypto_select() to
+ * select the best avaible crypto algorithm to use.
+ *
+ * See the library functions here: @ref hx509_peer
+ */
+
+/**
+ * Allocate a new peer info structure an init it to default values.
+ *
+ * @param context A hx509 context.
+ * @param peer return an allocated peer, free with hx509_peer_info_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_peer_info_alloc(hx509_context context, hx509_peer_info *peer)
+{
+ *peer = calloc(1, sizeof(**peer));
+ if (*peer == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+static void
+free_cms_alg(hx509_peer_info peer)
+{
+ if (peer->val) {
+ size_t i;
+ for (i = 0; i < peer->len; i++)
+ free_AlgorithmIdentifier(&peer->val[i]);
+ free(peer->val);
+ peer->val = NULL;
+ peer->len = 0;
+ }
+}
+
+/**
+ * Free a peer info structure.
+ *
+ * @param peer peer info to be freed.
+ *
+ * @ingroup hx509_peer
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_peer_info_free(hx509_peer_info peer)
+{
+ if (peer == NULL)
+ return;
+ if (peer->cert)
+ hx509_cert_free(peer->cert);
+ free_cms_alg(peer);
+ memset(peer, 0, sizeof(*peer));
+ free(peer);
+}
+
+/**
+ * Set the certificate that remote peer is using.
+ *
+ * @param peer peer info to update
+ * @param cert cerificate of the remote peer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_peer_info_set_cert(hx509_peer_info peer,
+ hx509_cert cert)
+{
+ if (peer->cert)
+ hx509_cert_free(peer->cert);
+ peer->cert = hx509_cert_ref(cert);
+ return 0;
+}
+
+/**
+ * Add an additional algorithm that the peer supports.
+ *
+ * @param context A hx509 context.
+ * @param peer the peer to set the new algorithms for
+ * @param val an AlgorithmsIdentier to add
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_peer_info_add_cms_alg(hx509_context context,
+ hx509_peer_info peer,
+ const AlgorithmIdentifier *val)
+{
+ void *ptr;
+ int ret;
+
+ ptr = realloc(peer->val, sizeof(peer->val[0]) * (peer->len + 1));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ peer->val = ptr;
+ ret = copy_AlgorithmIdentifier(val, &peer->val[peer->len]);
+ if (ret == 0)
+ peer->len += 1;
+ else
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+}
+
+/**
+ * Set the algorithms that the peer supports.
+ *
+ * @param context A hx509 context.
+ * @param peer the peer to set the new algorithms for
+ * @param val array of supported AlgorithmsIdentiers
+ * @param len length of array val.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_peer_info_set_cms_algs(hx509_context context,
+ hx509_peer_info peer,
+ const AlgorithmIdentifier *val,
+ size_t len)
+{
+ size_t i;
+
+ free_cms_alg(peer);
+
+ peer->val = calloc(len, sizeof(*peer->val));
+ if (peer->val == NULL) {
+ peer->len = 0;
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ peer->len = len;
+ for (i = 0; i < len; i++) {
+ int ret;
+ ret = copy_AlgorithmIdentifier(&val[i], &peer->val[i]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ free_cms_alg(peer);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+#if 0
+
+/*
+ * S/MIME
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_peer_info_parse_smime(hx509_peer_info peer,
+ const heim_octet_string *data)
+{
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_peer_info_unparse_smime(hx509_peer_info peer,
+ heim_octet_string *data)
+{
+ return 0;
+}
+
+/*
+ * For storing hx509_peer_info to be able to cache them.
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_peer_info_parse(hx509_peer_info peer,
+ const heim_octet_string *data)
+{
+ return 0;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_peer_info_unparse(hx509_peer_info peer,
+ heim_octet_string *data)
+{
+ return 0;
+}
+#endif
diff --git a/third_party/heimdal/lib/hx509/print.c b/third_party/heimdal/lib/hx509/print.c
new file mode 100644
index 0000000..3309913
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/print.c
@@ -0,0 +1,1144 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include <vis.h>
+#include <vis-extras.h>
+
+/**
+ * @page page_print Hx509 printing functions
+ *
+ * See the library functions here: @ref hx509_print
+ */
+
+struct hx509_validate_ctx_data {
+ hx509_context context;
+ int flags;
+ hx509_vprint_func vprint_func;
+ void *ctx;
+};
+
+struct cert_status {
+ unsigned int selfsigned:1;
+ unsigned int isca:1;
+ unsigned int isproxy:1;
+ unsigned int haveSAN:1;
+ unsigned int haveIAN:1;
+ unsigned int haveSKI:1;
+ unsigned int haveAKI:1;
+ unsigned int haveCRLDP:1;
+};
+
+
+/*
+ *
+ */
+
+static int
+Time2string(const Time *T, char **str)
+{
+ time_t t;
+ char *s;
+ struct tm *tm;
+
+ *str = NULL;
+ t = _hx509_Time2time_t(T);
+ tm = gmtime (&t);
+ s = malloc(30);
+ if (s == NULL)
+ return ENOMEM;
+ strftime(s, 30, "%Y-%m-%d %H:%M:%S", tm);
+ *str = s;
+ return 0;
+}
+
+/**
+ * Helper function to print on stdout for:
+ * - hx509_oid_print(),
+ * - hx509_bitstring_print(),
+ * - hx509_validate_ctx_set_print().
+ *
+ * @param ctx the context to the print function. If the ctx is NULL,
+ * stdout is used.
+ * @param fmt the printing format.
+ * @param va the argumet list.
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION void
+hx509_print_stdout(void *ctx, const char *fmt, va_list va)
+{
+ FILE *f = ctx;
+ if (f == NULL)
+ f = stdout;
+ vfprintf(f, fmt, va);
+}
+
+static void
+print_func(hx509_vprint_func func, void *ctx, const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ (*func)(ctx, fmt, va);
+ va_end(va);
+}
+
+/**
+ * Print a oid to a string.
+ *
+ * @param oid oid to print
+ * @param str allocated string, free with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_oid_sprint(const heim_oid *oid, char **str)
+{
+ return der_print_heim_oid(oid, '.', str);
+}
+
+/**
+ * Print a oid using a hx509_vprint_func function. To print to stdout
+ * use hx509_print_stdout().
+ *
+ * @param oid oid to print
+ * @param func hx509_vprint_func to print with.
+ * @param ctx context variable to hx509_vprint_func function.
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_oid_print(const heim_oid *oid, hx509_vprint_func func, void *ctx)
+{
+ char *str;
+ hx509_oid_sprint(oid, &str);
+ print_func(func, ctx, "%s", str);
+ free(str);
+}
+
+/**
+ * Print a bitstring using a hx509_vprint_func function. To print to
+ * stdout use hx509_print_stdout().
+ *
+ * @param b bit string to print.
+ * @param func hx509_vprint_func to print with.
+ * @param ctx context variable to hx509_vprint_func function.
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_bitstring_print(const heim_bit_string *b,
+ hx509_vprint_func func, void *ctx)
+{
+ size_t i;
+ print_func(func, ctx, "\tlength: %d\n\t", b->length);
+ for (i = 0; i < (b->length + 7) / 8; i++)
+ print_func(func, ctx, "%02x%s%s",
+ ((unsigned char *)b->data)[i],
+ i < (b->length - 7) / 8
+ && (i == 0 || (i % 16) != 15) ? ":" : "",
+ i != 0 && (i % 16) == 15 ?
+ (i <= ((b->length + 7) / 8 - 2) ? "\n\t" : "\n"):"");
+}
+
+/**
+ * Print certificate usage for a certificate to a string.
+ *
+ * @param context A hx509 context.
+ * @param c a certificate print the keyusage for.
+ * @param s the return string with the keysage printed in to, free
+ * with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_cert_keyusage_print(hx509_context context, hx509_cert c, char **s)
+{
+ KeyUsage ku;
+ char buf[256];
+ int ret;
+
+ *s = NULL;
+
+ ret = _hx509_cert_get_keyusage(context, c, &ku);
+ if (ret)
+ return ret;
+ unparse_flags(KeyUsage2int(ku), asn1_KeyUsage_units(), buf, sizeof(buf));
+ *s = strdup(buf);
+ if (*s == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+validate_vprint(void *c, const char *fmt, va_list va)
+{
+ hx509_validate_ctx ctx = c;
+ if (ctx->vprint_func == NULL)
+ return;
+ (ctx->vprint_func)(ctx->ctx, fmt, va);
+}
+
+static void
+validate_print(hx509_validate_ctx ctx, int flags, const char *fmt, ...)
+{
+ va_list va;
+ if ((ctx->flags & flags) == 0)
+ return;
+ va_start(va, fmt);
+ validate_vprint(ctx, fmt, va);
+ va_end(va);
+}
+
+/*
+ * Don't Care, SHOULD critical, SHOULD NOT critical, MUST critical,
+ * MUST NOT critical
+ */
+enum critical_flag { D_C = 0, S_C, S_N_C, M_C, M_N_C };
+
+static int
+check_Null(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf, const Extension *e)
+{
+ switch(cf) {
+ case D_C:
+ break;
+ case S_C:
+ if (!e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical not set on SHOULD\n");
+ break;
+ case S_N_C:
+ if (e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical set on SHOULD NOT\n");
+ break;
+ case M_C:
+ if (!e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical not set on MUST\n");
+ break;
+ case M_N_C:
+ if (e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical set on MUST NOT\n");
+ break;
+ default:
+ _hx509_abort("internal check_Null state error");
+ }
+ return 0;
+}
+
+static int
+check_subjectKeyIdentifier(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ SubjectKeyIdentifier si;
+ size_t size;
+ int ret;
+
+ status->haveSKI = 1;
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_SubjectKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ &si, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding SubjectKeyIdentifier failed: %d", ret);
+ return 1;
+ }
+ if (size != e->extnValue.length) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding SKI ahve extra bits on the end");
+ return 1;
+ }
+ if (si.length == 0)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "SKI is too short (0 bytes)");
+ if (si.length > 20)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "SKI is too long");
+
+ {
+ char *id;
+ hex_encode(si.data, si.length, &id);
+ if (id) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tsubject key id: %s\n", id);
+ free(id);
+ }
+ }
+
+ free_SubjectKeyIdentifier(&si);
+
+ return 0;
+}
+
+static int
+check_authorityKeyIdentifier(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ AuthorityKeyIdentifier ai;
+ size_t size;
+ int ret;
+
+ status->haveAKI = 1;
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_AuthorityKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ &ai, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding AuthorityKeyIdentifier failed: %d", ret);
+ return 1;
+ }
+ if (size != e->extnValue.length) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding SKI ahve extra bits on the end");
+ return 1;
+ }
+
+ if (ai.keyIdentifier) {
+ char *id;
+ hex_encode(ai.keyIdentifier->data, ai.keyIdentifier->length, &id);
+ if (id) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tauthority key id: %s\n", id);
+ free(id);
+ }
+ }
+
+ free_AuthorityKeyIdentifier(&ai);
+ return 0;
+}
+
+static int
+check_extKeyUsage(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ ExtKeyUsage eku;
+ size_t size, i;
+ int ret;
+
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_ExtKeyUsage(e->extnValue.data,
+ e->extnValue.length,
+ &eku, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding ExtKeyUsage failed: %d", ret);
+ return 1;
+ }
+ if (size != e->extnValue.length) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Padding data in EKU");
+ free_ExtKeyUsage(&eku);
+ return 1;
+ }
+ if (eku.len == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "ExtKeyUsage length is 0");
+ return 1;
+ }
+
+ for (i = 0; i < eku.len; i++) {
+ char *str;
+ ret = der_print_heim_oid (&eku.val[i], '.', &str);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tEKU: failed to print oid %d", i);
+ free_ExtKeyUsage(&eku);
+ return 1;
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\teku-%d: %s\n", i, str);;
+ free(str);
+ }
+
+ free_ExtKeyUsage(&eku);
+
+ return 0;
+}
+
+static int
+check_CRLDistributionPoints(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ CRLDistributionPoints dp;
+ size_t size;
+ int ret;
+ size_t i;
+
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_CRLDistributionPoints(e->extnValue.data,
+ e->extnValue.length,
+ &dp, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding CRL Distribution Points failed: %d\n", ret);
+ return 1;
+ }
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "CRL Distribution Points:\n");
+ for (i = 0 ; i < dp.len; i++) {
+ if (dp.val[i].distributionPoint) {
+ DistributionPointName dpname = dp.val[i].distributionPoint[0];
+ size_t j;
+
+ switch (dpname.element) {
+ case choice_DistributionPointName_fullName:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "Fullname:\n");
+
+ for (j = 0 ; j < dpname.u.fullName.len; j++) {
+ char *s;
+ GeneralName *name = &dpname.u.fullName.val[j];
+
+ ret = hx509_general_name_unparse2(ctx->context, name, &s);
+ if (ret) {
+ s = hx509_get_error_string(ctx->context, ret);
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Unknown DistributionPointName: %s", s);
+ hx509_free_error_string(s);
+ } else {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " %s\n", s);
+ free(s);
+ }
+ }
+ break;
+ case choice_DistributionPointName_nameRelativeToCRLIssuer:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Unknown nameRelativeToCRLIssuer");
+ break;
+ default:
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Unknown DistributionPointName");
+ break;
+ }
+ }
+ }
+ free_CRLDistributionPoints(&dp);
+
+ status->haveCRLDP = 1;
+
+ return 0;
+}
+
+static int
+check_altName(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ const char *name,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ GeneralNames gn;
+ size_t size;
+ int ret;
+ size_t i;
+
+ check_Null(ctx, status, cf, e);
+
+ if (e->extnValue.length == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "%sAltName empty, not allowed", name);
+ return 1;
+ }
+ ret = decode_GeneralNames(e->extnValue.data, e->extnValue.length,
+ &gn, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tret = %d while decoding %s GeneralNames\n",
+ ret, name);
+ return 1;
+ }
+ if (gn.len == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "%sAltName generalName empty, not allowed\n", name);
+ return 1;
+ }
+
+ for (i = 0; i < gn.len; i++) {
+ char *s;
+
+ ret = hx509_general_name_unparse2(ctx->context, &gn.val[i], &s);
+ if (ret) {
+ s = hx509_get_error_string(ctx->context, ret);
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Error unparsing GeneralName: %s\n", s);
+ hx509_free_error_string(s);
+ return 1;
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\t%s\n", s);
+ free(s);
+ }
+
+ free_GeneralNames(&gn);
+ return 0;
+}
+
+static int
+check_subjectAltName(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ status->haveSAN = 1;
+ return check_altName(ctx, status, "subject", cf, e);
+}
+
+static int
+check_issuerAltName(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ status->haveIAN = 1;
+ return check_altName(ctx, status, "issuer", cf, e);
+}
+
+
+static int
+check_basicConstraints(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ BasicConstraints b;
+ size_t size;
+ int ret;
+
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_BasicConstraints(e->extnValue.data, e->extnValue.length,
+ &b, &size);
+ if (ret) {
+ printf("\tret = %d while decoding BasicConstraints\n", ret);
+ return 0;
+ }
+ if (size != e->extnValue.length)
+ printf("\tlength of der data isn't same as extension\n");
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tis %sa CA\n", b.cA ? "" : "NOT ");
+ if (b.pathLenConstraint)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tpathLenConstraint: %d\n", *b.pathLenConstraint);
+
+ if (b.cA) {
+ if (!e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Is a CA and not BasicConstraints CRITICAL\n");
+ status->isca = 1;
+ }
+ free_BasicConstraints(&b);
+
+ return 0;
+}
+
+static int
+check_proxyCertInfo(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ check_Null(ctx, status, cf, e);
+ status->isproxy = 1;
+ return 0;
+}
+
+static int
+check_authorityInfoAccess(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ AuthorityInfoAccessSyntax aia;
+ size_t size;
+ int ret;
+ size_t i;
+
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_AuthorityInfoAccessSyntax(e->extnValue.data,
+ e->extnValue.length,
+ &aia, &size);
+ if (ret) {
+ printf("\tret = %d while decoding AuthorityInfoAccessSyntax\n", ret);
+ return 0;
+ }
+
+ for (i = 0; i < aia.len; i++) {
+ char *str;
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\ttype: ");
+ hx509_oid_print(&aia.val[i].accessMethod, validate_vprint, ctx);
+ ret = hx509_general_name_unparse2(ctx->context,
+ &aia.val[i].accessLocation, &str);
+ if (ret) {
+ str = hx509_get_error_string(ctx->context, ret);
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Error unparsing AuthorityInfoAccessSyntax "
+ "accessLocation: %s", str);
+ hx509_free_error_string(str);
+ } else {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\n\tdirname: %s\n", str);
+ free(str);
+ }
+ }
+ free_AuthorityInfoAccessSyntax(&aia);
+
+ return ret;
+}
+
+static int
+get_display_text(DisplayText *dt, char **out)
+{
+ int r = -1;
+
+ *out = NULL;
+
+ /*
+ * XXX We're cheating with various string types here.
+ *
+ * Proper support for IA5String is a real pain, and we don't have it.
+ *
+ * We also don't have support for BMPString.
+ */
+ switch (dt->element) {
+ case choice_DisplayText_ia5String:
+ r = rk_strasvisx(out, dt->u.ia5String.data, dt->u.ia5String.length,
+ VIS_CSTYLE | VIS_TAB | VIS_NL, "");
+ break;
+ case choice_DisplayText_visibleString:
+ r = rk_strasvis(out, dt->u.visibleString,
+ VIS_CSTYLE | VIS_TAB | VIS_NL, "");
+ break;
+ case choice_DisplayText_bmpString:
+ errno = ENOTSUP; /* XXX Need a UTF-16 -> UTF-8 conversion */
+ break;
+ case choice_DisplayText_utf8String:
+ r = rk_strasvis(out, dt->u.visibleString,
+ VIS_CSTYLE | VIS_TAB | VIS_NL, "");
+ break;
+ default:
+ errno = EINVAL;
+ }
+ return r < 0 ? errno : 0;
+}
+
+static int
+check_certificatePolicies(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ CertificatePolicies cp;
+ size_t i, size;
+ int ret = 0;
+
+ check_Null(ctx, status, cf, e);
+
+ if (e->extnValue.length == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "CertificatePolicies empty, not allowed");
+ return 1;
+ }
+ ret = decode_CertificatePolicies(e->extnValue.data, e->extnValue.length,
+ &cp, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tret = %d while decoding CertificatePolicies\n", ret);
+ return 1;
+ }
+ if (cp.len == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "CertificatePolicies empty, not allowed\n");
+ return 1;
+ }
+
+ for (i = 0; ret == 0 && i < cp.len; i++) {
+ size_t k;
+ char *poid = NULL;
+ char *qoid = NULL;
+ char *dt = NULL;
+
+ ret = der_print_heim_oid(&cp.val[i].policyIdentifier, '.', &poid);
+ if (ret == 0)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tPolicy: %s", poid);
+
+ for (k = 0;
+ ret == 0 && cp.val[i].policyQualifiers &&
+ k < cp.val[i].policyQualifiers->len;
+ k++) {
+ PolicyQualifierInfo *pi = &cp.val[i].policyQualifiers->val[k];
+
+ if (der_heim_oid_cmp(&pi->policyQualifierId,
+ &asn1_oid_id_pkix_qt_cps) == 0) {
+ CPSuri cps;
+
+ ret = decode_CPSuri(pi->qualifier.data, pi->qualifier.length,
+ &cps, &size);
+ if (ret == 0) {
+ if (cps.length > 4096)
+ cps.length = 4096;
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ ":CPSuri:%.*s",
+ (int)cps.length, (char *)cps.data);
+ free_CPSuri(&cps);
+ }
+ } else if (der_heim_oid_cmp(&pi->policyQualifierId,
+ &asn1_oid_id_pkix_qt_unotice) == 0) {
+ UserNotice un;
+
+ ret = decode_UserNotice(pi->qualifier.data,
+ pi->qualifier.length, &un, &size);
+ if (ret == 0) {
+ if (un.explicitText) {
+ /*
+ * get_display_text() will strvis to make it safer to
+ * print.
+ */
+ ret = get_display_text(un.explicitText, &dt);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ " UserNotice:DistplayText:%s", dt);
+ } else if (un.noticeRef) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ " UserNotice:NoticeRef:<noticeRef-not-supported>",
+ qoid);
+ } else {
+ ret = der_print_heim_oid(&pi->policyQualifierId, '.',
+ &qoid);
+ if (ret)
+ break;
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ " Unknown:%s", qoid);
+ }
+ free_UserNotice(&un);
+ }
+ } else {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ ", qualifier %s:<unknown>", qoid);
+ }
+ free(qoid);
+ free(dt);
+ qoid = dt = 0;
+ }
+ if (ret == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\n");
+ } else {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\nOut of memory formatting certificate policy");
+ ret = ENOMEM;
+ }
+ free(poid);
+ free(qoid);
+ free(dt);
+ poid = qoid = dt = 0;
+ }
+
+ free_CertificatePolicies(&cp);
+
+ return ret ? 1 : 0;
+}
+
+static int
+check_policyMappings(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ PolicyMappings pm;
+ size_t i, size;
+ int ret = 0;
+
+ check_Null(ctx, status, cf, e);
+
+ if (e->extnValue.length == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "PolicyMappings empty, not allowed");
+ return 1;
+ }
+ ret = decode_PolicyMappings(e->extnValue.data, e->extnValue.length,
+ &pm, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tret = %d while decoding PolicyMappings\n", ret);
+ return 1;
+ }
+ if (pm.len == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "PolicyMappings empty, not allowed\n");
+ return 1;
+ }
+
+ for (i = 0; ret == 0 && i < pm.len; i++) {
+ char *idpoid = NULL;
+ char *sdpoid = NULL;
+
+ ret = der_print_heim_oid(&pm.val[i].issuerDomainPolicy, '.', &idpoid);
+ if (ret == 0)
+ ret = der_print_heim_oid(&pm.val[i].subjectDomainPolicy, '.',
+ &sdpoid);
+ if (ret == 0)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tPolicy mapping %s -> %s\n", idpoid, sdpoid);
+ else
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "ret=%d while decoding PolicyMappings\n", ret);
+ free(sdpoid);
+ free(idpoid);
+ }
+
+ free_PolicyMappings(&pm);
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct {
+ const char *name;
+ const heim_oid *oid;
+ int (*func)(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *);
+ enum critical_flag cf;
+} check_extension[] = {
+#define ext(name, checkname) #name, &asn1_oid_id_x509_ce_##name, check_##checkname
+ { ext(subjectDirectoryAttributes, Null), M_N_C },
+ { ext(subjectKeyIdentifier, subjectKeyIdentifier), M_N_C },
+ { ext(keyUsage, Null), S_C },
+ { ext(subjectAltName, subjectAltName), M_N_C },
+ { ext(issuerAltName, issuerAltName), S_N_C },
+ { ext(basicConstraints, basicConstraints), D_C },
+ { ext(cRLNumber, Null), M_N_C },
+ { ext(cRLReason, Null), M_N_C },
+ { ext(holdInstructionCode, Null), M_N_C },
+ { ext(invalidityDate, Null), M_N_C },
+ { ext(deltaCRLIndicator, Null), M_C },
+ { ext(issuingDistributionPoint, Null), M_C },
+ { ext(certificateIssuer, Null), M_C },
+ { ext(nameConstraints, Null), M_C },
+ { ext(cRLDistributionPoints, CRLDistributionPoints), S_N_C },
+ { ext(certificatePolicies, certificatePolicies), 0 },
+ { ext(policyMappings, policyMappings), M_N_C },
+ { ext(authorityKeyIdentifier, authorityKeyIdentifier), M_N_C },
+ { ext(policyConstraints, Null), D_C },
+ { ext(extKeyUsage, extKeyUsage), D_C },
+ { ext(freshestCRL, Null), M_N_C },
+ { ext(inhibitAnyPolicy, Null), M_C },
+#undef ext
+#define ext(name, checkname) #name, &asn1_oid_id_pkix_pe_##name, check_##checkname
+ { ext(proxyCertInfo, proxyCertInfo), M_C },
+ { ext(authorityInfoAccess, authorityInfoAccess), M_C },
+#undef ext
+ { "US Fed PKI - PIV Interim", &asn1_oid_id_uspkicommon_piv_interim,
+ check_Null, D_C },
+ { "Netscape cert comment", &asn1_oid_id_netscape_cert_comment,
+ check_Null, D_C },
+ { NULL, NULL, NULL, 0 }
+};
+
+/**
+ * Allocate a hx509 validation/printing context.
+ *
+ * @param context A hx509 context.
+ * @param ctx a new allocated hx509 validation context, free with
+ * hx509_validate_ctx_free().
+
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_validate_ctx_init(hx509_context context, hx509_validate_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL)
+ return hx509_enomem(context);
+ (*ctx)->context = context;
+ return 0;
+}
+
+/**
+ * Set the printing functions for the validation context.
+ *
+ * @param ctx a hx509 valication context.
+ * @param func the printing function to usea.
+ * @param c the context variable to the printing function.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_validate_ctx_set_print(hx509_validate_ctx ctx,
+ hx509_vprint_func func,
+ void *c)
+{
+ ctx->vprint_func = func;
+ ctx->ctx = c;
+}
+
+/**
+ * Add flags to control the behaivor of the hx509_validate_cert()
+ * function.
+ *
+ * @param ctx A hx509 validation context.
+ * @param flags flags to add to the validation context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_validate_ctx_add_flags(hx509_validate_ctx ctx, int flags)
+{
+ ctx->flags |= flags;
+}
+
+/**
+ * Free an hx509 validate context.
+ *
+ * @param ctx the hx509 validate context to free.
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_validate_ctx_free(hx509_validate_ctx ctx)
+{
+ free(ctx);
+}
+
+/**
+ * Validate/Print the status of the certificate.
+ *
+ * @param context A hx509 context.
+ * @param ctx A hx509 validation context.
+ * @param cert the cerificate to validate/print.
+
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_validate_cert(hx509_context context,
+ hx509_validate_ctx ctx,
+ hx509_cert cert)
+{
+ Certificate *c = _hx509_get_cert(cert);
+ TBSCertificate *t = &c->tbsCertificate;
+ hx509_name issuer, subject;
+ char *str;
+ struct cert_status status;
+ int ret;
+
+ memset(&status, 0, sizeof(status));
+
+ if (_hx509_cert_get_version(c) != 3)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Not version 3 certificate\n");
+
+ if ((t->version == NULL || *t->version < 2) && t->extensions)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Not version 3 certificate with extensions\n");
+
+ if (_hx509_cert_get_version(c) >= 3 && t->extensions == NULL)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Version 3 certificate without extensions\n");
+
+ ret = hx509_cert_get_subject(cert, &subject);
+ if (ret) abort();
+ hx509_name_to_string(subject, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "subject name: %s\n", str);
+ free(str);
+
+ ret = hx509_cert_get_issuer(cert, &issuer);
+ if (ret) abort();
+ hx509_name_to_string(issuer, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "issuer name: %s\n", str);
+ free(str);
+
+ if (hx509_name_cmp(subject, issuer) == 0) {
+ status.selfsigned = 1;
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tis a self-signed certificate\n");
+ }
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Validity:\n");
+
+ Time2string(&t->validity.notBefore, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotBefore %s\n", str);
+ free(str);
+ Time2string(&t->validity.notAfter, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotAfter %s\n", str);
+ free(str);
+
+ if (t->extensions) {
+ size_t i, j;
+
+ if (t->extensions->len == 0) {
+ validate_print(ctx,
+ HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE,
+ "The empty extensions list is not "
+ "allowed by PKIX\n");
+ }
+
+ for (i = 0; i < t->extensions->len; i++) {
+
+ for (j = 0; check_extension[j].name; j++)
+ if (der_heim_oid_cmp(check_extension[j].oid,
+ &t->extensions->val[i].extnID) == 0)
+ break;
+ if (check_extension[j].name == NULL) {
+ int flags = HX509_VALIDATE_F_VERBOSE;
+ if (t->extensions->val[i].critical)
+ flags |= HX509_VALIDATE_F_VALIDATE;
+ validate_print(ctx, flags, "don't know what ");
+ if (t->extensions->val[i].critical)
+ validate_print(ctx, flags, "and is CRITICAL ");
+ if (ctx->flags & flags)
+ hx509_oid_print(&t->extensions->val[i].extnID,
+ validate_vprint, ctx);
+ validate_print(ctx, flags, " is\n");
+ continue;
+ }
+ validate_print(ctx,
+ HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE,
+ "checking extension: %s\n",
+ check_extension[j].name);
+ (*check_extension[j].func)(ctx,
+ &status,
+ check_extension[j].cf,
+ &t->extensions->val[i]);
+ }
+ } else
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "no extensions\n");
+
+ if (status.isca) {
+ if (!status.haveSKI)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "CA certificate have no SubjectKeyIdentifier\n");
+
+ } else {
+ if (!status.haveAKI)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Is not CA and doesn't have "
+ "AuthorityKeyIdentifier\n");
+ }
+
+
+ if (!status.haveSKI)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Doesn't have SubjectKeyIdentifier\n");
+
+ if (status.isproxy && status.isca)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Proxy and CA at the same time!\n");
+
+ if (status.isproxy) {
+ if (status.haveSAN)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Proxy and have SAN\n");
+ if (status.haveIAN)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Proxy and have IAN\n");
+ }
+
+ if (hx509_name_is_null_p(subject) && !status.haveSAN)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "NULL subject DN and doesn't have a SAN\n");
+
+ if (!status.selfsigned && !status.haveCRLDP)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Not a CA nor PROXY and doesn't have"
+ "CRL Dist Point\n");
+
+ if (status.selfsigned) {
+ ret = _hx509_verify_signature_bitstring(context,
+ cert,
+ &c->signatureAlgorithm,
+ &c->tbsCertificate._save,
+ &c->signatureValue);
+ if (ret == 0)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Self-signed certificate was self-signed\n");
+ else
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Self-signed certificate NOT really self-signed!\n");
+ }
+
+ hx509_name_free(&subject);
+ hx509_name_free(&issuer);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/hx509/quote.py b/third_party/heimdal/lib/hx509/quote.py
new file mode 100644
index 0000000..41887e5
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/quote.py
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2010 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+CONTROL_CHAR = 1
+PRINTABLE = 2
+RFC2253_QUOTE_FIRST = 4
+RFC2253_QUOTE_LAST = 8
+RFC2253_QUOTE = 16
+RFC2253_HEX = 32
+
+chars = []
+
+for i in range(0, 256):
+ chars.append(0);
+
+for i in range(0, 256):
+ if (i < 32 or i > 126):
+ chars[i] |= CONTROL_CHAR | RFC2253_HEX;
+
+for i in range(ord("A"), ord("Z") + 1):
+ chars[i] |= PRINTABLE
+for i in range(ord("a"), ord("z") + 1):
+ chars[i] |= PRINTABLE
+for i in range(ord("0"), ord("9") + 1):
+ chars[i] |= PRINTABLE
+
+chars[ord(' ')] |= PRINTABLE
+chars[ord('+')] |= PRINTABLE
+chars[ord(',')] |= PRINTABLE
+chars[ord('-')] |= PRINTABLE
+chars[ord('.')] |= PRINTABLE
+chars[ord('/')] |= PRINTABLE
+chars[ord(':')] |= PRINTABLE
+chars[ord('=')] |= PRINTABLE
+chars[ord('?')] |= PRINTABLE
+
+chars[ord(' ')] |= RFC2253_QUOTE_FIRST | RFC2253_QUOTE_FIRST
+
+chars[ord(',')] |= RFC2253_QUOTE
+chars[ord('=')] |= RFC2253_QUOTE
+chars[ord('+')] |= RFC2253_QUOTE
+chars[ord('<')] |= RFC2253_QUOTE
+chars[ord('>')] |= RFC2253_QUOTE
+chars[ord('#')] |= RFC2253_QUOTE
+chars[ord(';')] |= RFC2253_QUOTE
+
+print "#define Q_CONTROL_CHAR 1"
+print "#define Q_PRINTABLE 2"
+print "#define Q_RFC2253_QUOTE_FIRST 4"
+print "#define Q_RFC2253_QUOTE_LAST 8"
+print "#define Q_RFC2253_QUOTE 16"
+print "#define Q_RFC2253_HEX 32"
+print ""
+print "#define Q_RFC2253 (Q_RFC2253_QUOTE_FIRST|Q_RFC2253_QUOTE_LAST|Q_RFC2253_QUOTE|Q_RFC2253_HEX)"
+print "\n" * 2
+
+
+
+
+print "unsigned char char_map[] = {\n\t",
+for x in range(0, 256):
+ if (x % 8) == 0 and x != 0:
+ print "\n\t",
+ print "0x%(char)02x" % { 'char' : chars[x] },
+ if x < 255:
+ print ", ",
+ else:
+ print ""
+print "};"
diff --git a/third_party/heimdal/lib/hx509/ref/pkcs11.h b/third_party/heimdal/lib/hx509/ref/pkcs11.h
new file mode 100644
index 0000000..a294c5e
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/ref/pkcs11.h
@@ -0,0 +1,1722 @@
+/* pkcs11.h
+ Copyright 2006, 2007 g10 Code GmbH
+ Copyright 2006 Andreas Jellinghaus
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. */
+
+/* Please submit changes back to the Scute project at
+ http://www.scute.org/ (or send them to marcus@g10code.com), so that
+ they can be picked up by other projects from there as well. */
+
+/* This file is a modified implementation of the PKCS #11 standard by
+ RSA Security Inc. It is mostly a drop-in replacement, with the
+ following change:
+
+ This header file does not require any macro definitions by the user
+ (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros
+ for you (if useful, some are missing, let me know if you need
+ more).
+
+ There is an additional API available that does comply better to the
+ GNU coding standard. It can be switched on by defining
+ CRYPTOKI_GNU before including this header file. For this, the
+ following changes are made to the specification:
+
+ All structure types are changed to a "struct ck_foo" where CK_FOO
+ is the type name in PKCS #11.
+
+ All non-structure types are changed to ck_foo_t where CK_FOO is the
+ lowercase version of the type name in PKCS #11. The basic types
+ (CK_ULONG et al.) are removed without substitute.
+
+ All members of structures are modified in the following way: Type
+ indication prefixes are removed, and underscore characters are
+ inserted before words. Then the result is lowercased.
+
+ Note that function names are still in the original case, as they
+ need for ABI compatibility.
+
+ CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use
+ <stdbool.h>.
+
+ If CRYPTOKI_COMPAT is defined before including this header file,
+ then none of the API changes above take place, and the API is the
+ one defined by the PKCS #11 standard. */
+
+#ifndef PKCS11_H
+#define PKCS11_H 1
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* The version of cryptoki we implement. The revision is changed with
+ each modification of this file. If you do not use the "official"
+ version of this file, please consider deleting the revision macro
+ (you may use a macro with a different name to keep track of your
+ versions). */
+#define CRYPTOKI_VERSION_MAJOR 2
+#define CRYPTOKI_VERSION_MINOR 30
+#define CRYPTOKI_VERSION_REVISION 0
+#define CRYPTOKI_VERSION_AMENDMENT 0
+
+
+/* Compatibility interface is default, unless CRYPTOKI_GNU is
+ given. */
+#ifndef CRYPTOKI_GNU
+#ifndef CRYPTOKI_COMPAT
+#define CRYPTOKI_COMPAT 1
+#endif
+#endif
+
+/* System dependencies. */
+
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+
+/* There is a matching pop below. */
+#pragma pack(push, cryptoki, 1)
+
+#ifdef CRYPTOKI_EXPORTS
+#define CK_SPEC __declspec(dllexport)
+#else
+#define CK_SPEC __declspec(dllimport)
+#endif
+
+#else
+
+#if defined(CRYPTOKI_VISIBILITY) && defined(CRYPTOKI_EXPORTS)
+#define CK_SPEC __attribute__((visibility("default")))
+#else
+#define CK_SPEC
+#endif
+
+#endif
+
+
+#ifdef CRYPTOKI_COMPAT
+ /* If we are in compatibility mode, switch all exposed names to the
+ PKCS #11 variant. There are corresponding #undefs below. */
+
+#define ck_flags_t CK_FLAGS
+#define ck_version _CK_VERSION
+
+#define ck_info _CK_INFO
+#define cryptoki_version cryptokiVersion
+#define manufacturer_id manufacturerID
+#define library_description libraryDescription
+#define library_version libraryVersion
+
+#define ck_notification_t CK_NOTIFICATION
+#define ck_slot_id_t CK_SLOT_ID
+
+#define ck_slot_info _CK_SLOT_INFO
+#define slot_description slotDescription
+#define hardware_version hardwareVersion
+#define firmware_version firmwareVersion
+
+#define ck_token_info _CK_TOKEN_INFO
+#define serial_number serialNumber
+#define max_session_count ulMaxSessionCount
+#define session_count ulSessionCount
+#define max_rw_session_count ulMaxRwSessionCount
+#define rw_session_count ulRwSessionCount
+#define max_pin_len ulMaxPinLen
+#define min_pin_len ulMinPinLen
+#define total_public_memory ulTotalPublicMemory
+#define free_public_memory ulFreePublicMemory
+#define total_private_memory ulTotalPrivateMemory
+#define free_private_memory ulFreePrivateMemory
+#define utc_time utcTime
+
+#define ck_session_handle_t CK_SESSION_HANDLE
+#define ck_user_type_t CK_USER_TYPE
+#define ck_state_t CK_STATE
+
+#define ck_session_info _CK_SESSION_INFO
+#define slot_id slotID
+#define device_error ulDeviceError
+
+#define ck_object_handle_t CK_OBJECT_HANDLE
+#define ck_object_class_t CK_OBJECT_CLASS
+#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE
+#define ck_key_type_t CK_KEY_TYPE
+#define ck_certificate_type_t CK_CERTIFICATE_TYPE
+#define ck_attribute_type_t CK_ATTRIBUTE_TYPE
+
+#define ck_attribute _CK_ATTRIBUTE
+#define value pValue
+#define value_len ulValueLen
+
+#define ck_date _CK_DATE
+
+#define ck_mechanism_type_t CK_MECHANISM_TYPE
+
+#define ck_mechanism _CK_MECHANISM
+#define parameter pParameter
+#define parameter_len ulParameterLen
+
+#define ck_mechanism_info _CK_MECHANISM_INFO
+#define min_key_size ulMinKeySize
+#define max_key_size ulMaxKeySize
+
+#define hash_alg hashAlg
+#define source_data pSourceData
+#define source_data_len ulSourceDataLen
+
+#define slen sLen
+
+#define ck_ec_kdf_type_t CK_EC_KDF_TYPE
+
+#define shared_data_len ulSharedDataLen
+#define shared_data pSharedData
+#define public_data_len ulPublicDataLen
+#define public_data pPublicData
+
+#define private_data_len ulPrivateDataLen
+#define private_data hPrivateData
+#define public_data_len2 ulPublicDataLen2
+#define public_data2 pPublicData2
+
+#define public_key publicKey
+
+#define ck_x9_42_dh_kdf_type_t CK_X9_42_DH_KDF_TYPE
+
+#define other_info_len ulOtherInfoLen
+#define other_info pOtherInfo
+
+#define data pData
+#define len ulLen
+
+#define ck_rv_t CK_RV
+#define ck_notify_t CK_NOTIFY
+
+#define ck_function_list _CK_FUNCTION_LIST
+
+#define ck_createmutex_t CK_CREATEMUTEX
+#define ck_destroymutex_t CK_DESTROYMUTEX
+#define ck_lockmutex_t CK_LOCKMUTEX
+#define ck_unlockmutex_t CK_UNLOCKMUTEX
+
+#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS
+#define create_mutex CreateMutex
+#define destroy_mutex DestroyMutex
+#define lock_mutex LockMutex
+#define unlock_mutex UnlockMutex
+#define reserved pReserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+
+typedef unsigned long ck_flags_t;
+
+struct ck_version
+{
+ unsigned char major;
+ unsigned char minor;
+};
+
+
+struct ck_info
+{
+ struct ck_version cryptoki_version;
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ unsigned char library_description[32];
+ struct ck_version library_version;
+};
+
+
+typedef unsigned long ck_notification_t;
+
+#define CKN_SURRENDER (0)
+#define CKN_OTP_CHANGED (1)
+
+typedef unsigned long ck_slot_id_t;
+
+
+struct ck_slot_info
+{
+ unsigned char slot_description[64];
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+};
+
+
+#define CKF_TOKEN_PRESENT (1 << 0)
+#define CKF_REMOVABLE_DEVICE (1 << 1)
+#define CKF_HW_SLOT (1 << 2)
+#define CKF_ARRAY_ATTRIBUTE (1 << 30)
+
+
+struct ck_token_info
+{
+ unsigned char label[32];
+ unsigned char manufacturer_id[32];
+ unsigned char model[16];
+ unsigned char serial_number[16];
+ ck_flags_t flags;
+ unsigned long max_session_count;
+ unsigned long session_count;
+ unsigned long max_rw_session_count;
+ unsigned long rw_session_count;
+ unsigned long max_pin_len;
+ unsigned long min_pin_len;
+ unsigned long total_public_memory;
+ unsigned long free_public_memory;
+ unsigned long total_private_memory;
+ unsigned long free_private_memory;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+ unsigned char utc_time[16];
+};
+
+
+#define CKF_RNG (1 << 0)
+#define CKF_WRITE_PROTECTED (1 << 1)
+#define CKF_LOGIN_REQUIRED (1 << 2)
+#define CKF_USER_PIN_INITIALIZED (1 << 3)
+#define CKF_RESTORE_KEY_NOT_NEEDED (1 << 5)
+#define CKF_CLOCK_ON_TOKEN (1 << 6)
+#define CKF_PROTECTED_AUTHENTICATION_PATH (1 << 8)
+#define CKF_DUAL_CRYPTO_OPERATIONS (1 << 9)
+#define CKF_TOKEN_INITIALIZED (1 << 10)
+#define CKF_SECONDARY_AUTHENTICATION (1 << 11)
+#define CKF_USER_PIN_COUNT_LOW (1 << 16)
+#define CKF_USER_PIN_FINAL_TRY (1 << 17)
+#define CKF_USER_PIN_LOCKED (1 << 18)
+#define CKF_USER_PIN_TO_BE_CHANGED (1 << 19)
+#define CKF_SO_PIN_COUNT_LOW (1 << 20)
+#define CKF_SO_PIN_FINAL_TRY (1 << 21)
+#define CKF_SO_PIN_LOCKED (1 << 22)
+#define CKF_SO_PIN_TO_BE_CHANGED (1 << 23)
+#define CKF_ERROR_STATE (1 << 24)
+
+#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1)
+#define CK_EFFECTIVELY_INFINITE (0)
+
+
+typedef unsigned long ck_session_handle_t;
+
+#define CK_INVALID_HANDLE (0)
+
+
+typedef unsigned long ck_user_type_t;
+
+#define CKU_SO (0)
+#define CKU_USER (1)
+#define CKU_CONTEXT_SPECIFIC (2)
+
+
+typedef unsigned long ck_state_t;
+
+#define CKS_RO_PUBLIC_SESSION (0)
+#define CKS_RO_USER_FUNCTIONS (1)
+#define CKS_RW_PUBLIC_SESSION (2)
+#define CKS_RW_USER_FUNCTIONS (3)
+#define CKS_RW_SO_FUNCTIONS (4)
+
+
+struct ck_session_info
+{
+ ck_slot_id_t slot_id;
+ ck_state_t state;
+ ck_flags_t flags;
+ unsigned long device_error;
+};
+
+#define CKF_RW_SESSION (1 << 1)
+#define CKF_SERIAL_SESSION (1 << 2)
+
+
+typedef unsigned long ck_object_handle_t;
+
+
+typedef unsigned long ck_object_class_t;
+
+#define CKO_DATA (0)
+#define CKO_CERTIFICATE (1)
+#define CKO_PUBLIC_KEY (2)
+#define CKO_PRIVATE_KEY (3)
+#define CKO_SECRET_KEY (4)
+#define CKO_HW_FEATURE (5)
+#define CKO_DOMAIN_PARAMETERS (6)
+#define CKO_MECHANISM (7)
+#define CKO_OTP_KEY (8)
+#define CKO_VENDOR_DEFINED ((unsigned long) (1ul << 31))
+
+
+typedef unsigned long ck_hw_feature_type_t;
+
+#define CKH_MONOTONIC_COUNTER (1)
+#define CKH_CLOCK (2)
+#define CKH_USER_INTERFACE (3)
+#define CKH_VENDOR_DEFINED ((unsigned long) (1ul << 31))
+
+
+typedef unsigned long ck_key_type_t;
+
+#define CKK_RSA (0)
+#define CKK_DSA (1)
+#define CKK_DH (2)
+#define CKK_ECDSA (3)
+#define CKK_EC (3)
+#define CKK_X9_42_DH (4)
+#define CKK_KEA (5)
+#define CKK_GENERIC_SECRET (0x10)
+#define CKK_RC2 (0x11)
+#define CKK_RC4 (0x12)
+#define CKK_DES (0x13)
+#define CKK_DES2 (0x14)
+#define CKK_DES3 (0x15)
+#define CKK_CAST (0x16)
+#define CKK_CAST3 (0x17)
+#define CKK_CAST5 (0x18)
+#define CKK_CAST128 (0x18)
+#define CKK_RC5 (0x19)
+#define CKK_IDEA (0x1a)
+#define CKK_SKIPJACK (0x1b)
+#define CKK_BATON (0x1c)
+#define CKK_JUNIPER (0x1d)
+#define CKK_CDMF (0x1e)
+#define CKK_AES (0x1f)
+#define CKK_BLOWFISH (0x20)
+#define CKK_TWOFISH (0x21)
+#define CKK_SECURID (0x22)
+#define CKK_HOTP (0x23)
+#define CKK_ACTI (0x24)
+#define CKK_CAMELLIA (0x25)
+#define CKK_ARIA (0x26)
+#define CKK_MD5_HMAC (0x27)
+#define CKK_SHA_1_HMAC (0x28)
+#define CKK_RIPEMD128_HMAC (0x29)
+#define CKK_RIPEMD160_HMAC (0x2A)
+#define CKK_SHA256_HMAC (0x2B)
+#define CKK_SHA384_HMAC (0x2C)
+#define CKK_SHA512_HMAC (0x2D)
+#define CKK_SHA224_HMAC (0x2E)
+#define CKK_SEED (0x2F)
+#define CKK_GOSTR3410 (0x30)
+#define CKK_GOSTR3411 (0x31)
+#define CKK_GOST28147 (0x32)
+#define CKK_VENDOR_DEFINED ((unsigned long) (1ul << 31))
+
+
+typedef unsigned long ck_certificate_type_t;
+
+#define CKC_X_509 (0)
+#define CKC_X_509_ATTR_CERT (1)
+#define CKC_WTLS (2)
+#define CKC_VENDOR_DEFINED ((unsigned long) (1ul << 31))
+#define CKC_OPENPGP (CKC_VENDOR_DEFINED|0x00504750)
+
+#define CK_OTP_FORMAT_DECIMAL (0)
+#define CK_OTP_FORMAT_HEXADECIMAL (1)
+#define CK_OTP_FORMAT_ALPHANUMERIC (2)
+#define CK_OTP_FORMAT_BINARY (3)
+
+#define CK_OTP_PARAM_IGNORED (0)
+#define CK_OTP_PARAM_OPTIONAL (1)
+#define CK_OTP_PARAM_MANDATORY (2)
+
+typedef unsigned long ck_attribute_type_t;
+
+#define CKA_CLASS (0)
+#define CKA_TOKEN (1)
+#define CKA_PRIVATE (2)
+#define CKA_LABEL (3)
+#define CKA_APPLICATION (0x10)
+#define CKA_VALUE (0x11)
+#define CKA_OBJECT_ID (0x12)
+#define CKA_CERTIFICATE_TYPE (0x80)
+#define CKA_ISSUER (0x81)
+#define CKA_SERIAL_NUMBER (0x82)
+#define CKA_AC_ISSUER (0x83)
+#define CKA_OWNER (0x84)
+#define CKA_ATTR_TYPES (0x85)
+#define CKA_TRUSTED (0x86)
+#define CKA_CERTIFICATE_CATEGORY (0x87)
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88)
+#define CKA_URL (0x89)
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8a)
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8b)
+#define CKA_NAME_HASH_ALGORITHM (0x8c)
+#define CKA_CHECK_VALUE (0x90)
+#define CKA_KEY_TYPE (0x100)
+#define CKA_SUBJECT (0x101)
+#define CKA_ID (0x102)
+#define CKA_SENSITIVE (0x103)
+#define CKA_ENCRYPT (0x104)
+#define CKA_DECRYPT (0x105)
+#define CKA_WRAP (0x106)
+#define CKA_UNWRAP (0x107)
+#define CKA_SIGN (0x108)
+#define CKA_SIGN_RECOVER (0x109)
+#define CKA_VERIFY (0x10a)
+#define CKA_VERIFY_RECOVER (0x10b)
+#define CKA_DERIVE (0x10c)
+#define CKA_START_DATE (0x110)
+#define CKA_END_DATE (0x111)
+#define CKA_MODULUS (0x120)
+#define CKA_MODULUS_BITS (0x121)
+#define CKA_PUBLIC_EXPONENT (0x122)
+#define CKA_PRIVATE_EXPONENT (0x123)
+#define CKA_PRIME_1 (0x124)
+#define CKA_PRIME_2 (0x125)
+#define CKA_EXPONENT_1 (0x126)
+#define CKA_EXPONENT_2 (0x127)
+#define CKA_COEFFICIENT (0x128)
+#define CKA_PRIME (0x130)
+#define CKA_SUBPRIME (0x131)
+#define CKA_BASE (0x132)
+#define CKA_PRIME_BITS (0x133)
+#define CKA_SUB_PRIME_BITS (0x134)
+#define CKA_SUBPRIME_BITS (0x134)
+#define CKA_VALUE_BITS (0x160)
+#define CKA_VALUE_LEN (0x161)
+#define CKA_EXTRACTABLE (0x162)
+#define CKA_LOCAL (0x163)
+#define CKA_NEVER_EXTRACTABLE (0x164)
+#define CKA_ALWAYS_SENSITIVE (0x165)
+#define CKA_KEY_GEN_MECHANISM (0x166)
+#define CKA_MODIFIABLE (0x170)
+#define CKA_COPYABLE (0x171)
+#define CKA_ECDSA_PARAMS (0x180)
+#define CKA_EC_PARAMS (0x180)
+#define CKA_EC_POINT (0x181)
+#define CKA_SECONDARY_AUTH (0x200)
+#define CKA_AUTH_PIN_FLAGS (0x201)
+#define CKA_ALWAYS_AUTHENTICATE (0x202)
+#define CKA_WRAP_WITH_TRUSTED (0x210)
+#define CKA_OTP_FORMAT (0x220)
+#define CKA_OTP_LENGTH (0x221)
+#define CKA_OTP_TIME_INTERVAL (0x222)
+#define CKA_OTP_USER_FRIENDLY_MODE (0x223)
+#define CKA_OTP_CHALLENGE_REQUIREMENT (0x224)
+#define CKA_OTP_TIME_REQUIREMENT (0x225)
+#define CKA_OTP_COUNTER_REQUIREMENT (0x226)
+#define CKA_OTP_PIN_REQUIREMENT (0x227)
+#define CKA_OTP_COUNTER (0x22E)
+#define CKA_OTP_TIME (0x22F)
+#define CKA_OTP_USER_IDENTIFIER (0x22A)
+#define CKA_OTP_SERVICE_IDENTIFIER (0x22B)
+#define CKA_OTP_SERVICE_LOGO (0x22C)
+#define CKA_OTP_SERVICE_LOGO_TYPE (0x22D)
+#define CKA_GOSTR3410_PARAMS (0x250)
+#define CKA_GOSTR3411_PARAMS (0x251)
+#define CKA_GOST28147_PARAMS (0x252)
+#define CKA_HW_FEATURE_TYPE (0x300)
+#define CKA_RESET_ON_INIT (0x301)
+#define CKA_HAS_RESET (0x302)
+#define CKA_PIXEL_X (0x400)
+#define CKA_PIXEL_Y (0x401)
+#define CKA_RESOLUTION (0x402)
+#define CKA_CHAR_ROWS (0x403)
+#define CKA_CHAR_COLUMNS (0x404)
+#define CKA_COLOR (0x405)
+#define CKA_BITS_PER_PIXEL (0x406)
+#define CKA_CHAR_SETS (0x480)
+#define CKA_ENCODING_METHODS (0x481)
+#define CKA_MIME_TYPES (0x482)
+#define CKA_MECHANISM_TYPE (0x500)
+#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501)
+#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502)
+#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503)
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212)
+#define CKA_DERIVE_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x213)
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600)
+#define CKA_VENDOR_DEFINED ((unsigned long) (1ul << 31))
+
+
+struct ck_attribute
+{
+ ck_attribute_type_t type;
+ void *value;
+ unsigned long value_len;
+};
+
+
+struct ck_date
+{
+ unsigned char year[4];
+ unsigned char month[2];
+ unsigned char day[2];
+};
+
+
+typedef unsigned long ck_mechanism_type_t;
+
+#define CKM_RSA_PKCS_KEY_PAIR_GEN (0)
+#define CKM_RSA_PKCS (1)
+#define CKM_RSA_9796 (2)
+#define CKM_RSA_X_509 (3)
+#define CKM_MD2_RSA_PKCS (4)
+#define CKM_MD5_RSA_PKCS (5)
+#define CKM_SHA1_RSA_PKCS (6)
+#define CKM_RIPEMD128_RSA_PKCS (7)
+#define CKM_RIPEMD160_RSA_PKCS (8)
+#define CKM_RSA_PKCS_OAEP (9)
+#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xa)
+#define CKM_RSA_X9_31 (0xb)
+#define CKM_SHA1_RSA_X9_31 (0xc)
+#define CKM_RSA_PKCS_PSS (0xd)
+#define CKM_SHA1_RSA_PKCS_PSS (0xe)
+#define CKM_DSA_KEY_PAIR_GEN (0x10)
+#define CKM_DSA (0x11)
+#define CKM_DSA_SHA1 (0x12)
+#define CKM_DSA_SHA224 (0x13)
+#define CKM_DSA_SHA256 (0x14)
+#define CKM_DSA_SHA384 (0x15)
+#define CKM_DSA_SHA512 (0x16)
+#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20)
+#define CKM_DH_PKCS_DERIVE (0x21)
+#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30)
+#define CKM_X9_42_DH_DERIVE (0x31)
+#define CKM_X9_42_DH_HYBRID_DERIVE (0x32)
+#define CKM_X9_42_MQV_DERIVE (0x33)
+#define CKM_SHA256_RSA_PKCS (0x40)
+#define CKM_SHA384_RSA_PKCS (0x41)
+#define CKM_SHA512_RSA_PKCS (0x42)
+#define CKM_SHA256_RSA_PKCS_PSS (0x43)
+#define CKM_SHA384_RSA_PKCS_PSS (0x44)
+#define CKM_SHA512_RSA_PKCS_PSS (0x45)
+#define CKM_SHA224_RSA_PKCS (0x46)
+#define CKM_SHA224_RSA_PKCS_PSS (0x47)
+#define CKM_RC2_KEY_GEN (0x100)
+#define CKM_RC2_ECB (0x101)
+#define CKM_RC2_CBC (0x102)
+#define CKM_RC2_MAC (0x103)
+#define CKM_RC2_MAC_GENERAL (0x104)
+#define CKM_RC2_CBC_PAD (0x105)
+#define CKM_RC4_KEY_GEN (0x110)
+#define CKM_RC4 (0x111)
+#define CKM_DES_KEY_GEN (0x120)
+#define CKM_DES_ECB (0x121)
+#define CKM_DES_CBC (0x122)
+#define CKM_DES_MAC (0x123)
+#define CKM_DES_MAC_GENERAL (0x124)
+#define CKM_DES_CBC_PAD (0x125)
+#define CKM_DES2_KEY_GEN (0x130)
+#define CKM_DES3_KEY_GEN (0x131)
+#define CKM_DES3_ECB (0x132)
+#define CKM_DES3_CBC (0x133)
+#define CKM_DES3_MAC (0x134)
+#define CKM_DES3_MAC_GENERAL (0x135)
+#define CKM_DES3_CBC_PAD (0x136)
+#define CKM_DES3_CMAC_GENERAL (0x137)
+#define CKM_DES3_CMAC (0x138)
+#define CKM_CDMF_KEY_GEN (0x140)
+#define CKM_CDMF_ECB (0x141)
+#define CKM_CDMF_CBC (0x142)
+#define CKM_CDMF_MAC (0x143)
+#define CKM_CDMF_MAC_GENERAL (0x144)
+#define CKM_CDMF_CBC_PAD (0x145)
+#define CKM_DES_OFB64 (0x150)
+#define CKM_DES_OFB8 (0x151)
+#define CKM_DES_CFB64 (0x152)
+#define CKM_DES_CFB8 (0x153)
+#define CKM_MD2 (0x200)
+#define CKM_MD2_HMAC (0x201)
+#define CKM_MD2_HMAC_GENERAL (0x202)
+#define CKM_MD5 (0x210)
+#define CKM_MD5_HMAC (0x211)
+#define CKM_MD5_HMAC_GENERAL (0x212)
+#define CKM_SHA_1 (0x220)
+#define CKM_SHA_1_HMAC (0x221)
+#define CKM_SHA_1_HMAC_GENERAL (0x222)
+#define CKM_RIPEMD128 (0x230)
+#define CKM_RIPEMD128_HMAC (0x231)
+#define CKM_RIPEMD128_HMAC_GENERAL (0x232)
+#define CKM_RIPEMD160 (0x240)
+#define CKM_RIPEMD160_HMAC (0x241)
+#define CKM_RIPEMD160_HMAC_GENERAL (0x242)
+#define CKM_SHA256 (0x250)
+#define CKM_SHA256_HMAC (0x251)
+#define CKM_SHA256_HMAC_GENERAL (0x252)
+#define CKM_SHA224 (0x255)
+#define CKM_SHA224_HMAC (0x256)
+#define CKM_SHA224_HMAC_GENERAL (0x257)
+#define CKM_SHA384 (0x260)
+#define CKM_SHA384_HMAC (0x261)
+#define CKM_SHA384_HMAC_GENERAL (0x262)
+#define CKM_SHA512 (0x270)
+#define CKM_SHA512_HMAC (0x271)
+#define CKM_SHA512_HMAC_GENERAL (0x272)
+#define CKM_SECURID_KEY_GEN (0x280)
+#define CKM_SECURID (0x282)
+#define CKM_HOTP_KEY_GEN (0x290)
+#define CKM_HOTP (0x291)
+#define CKM_ACTI (0x2A0)
+#define CKM_ACTI_KEY_GEN (0x2A1)
+#define CKM_CAST_KEY_GEN (0x300)
+#define CKM_CAST_ECB (0x301)
+#define CKM_CAST_CBC (0x302)
+#define CKM_CAST_MAC (0x303)
+#define CKM_CAST_MAC_GENERAL (0x304)
+#define CKM_CAST_CBC_PAD (0x305)
+#define CKM_CAST3_KEY_GEN (0x310)
+#define CKM_CAST3_ECB (0x311)
+#define CKM_CAST3_CBC (0x312)
+#define CKM_CAST3_MAC (0x313)
+#define CKM_CAST3_MAC_GENERAL (0x314)
+#define CKM_CAST3_CBC_PAD (0x315)
+#define CKM_CAST5_KEY_GEN (0x320)
+#define CKM_CAST128_KEY_GEN (0x320)
+#define CKM_CAST5_ECB (0x321)
+#define CKM_CAST128_ECB (0x321)
+#define CKM_CAST5_CBC (0x322)
+#define CKM_CAST128_CBC (0x322)
+#define CKM_CAST5_MAC (0x323)
+#define CKM_CAST128_MAC (0x323)
+#define CKM_CAST5_MAC_GENERAL (0x324)
+#define CKM_CAST128_MAC_GENERAL (0x324)
+#define CKM_CAST5_CBC_PAD (0x325)
+#define CKM_CAST128_CBC_PAD (0x325)
+#define CKM_RC5_KEY_GEN (0x330)
+#define CKM_RC5_ECB (0x331)
+#define CKM_RC5_CBC (0x332)
+#define CKM_RC5_MAC (0x333)
+#define CKM_RC5_MAC_GENERAL (0x334)
+#define CKM_RC5_CBC_PAD (0x335)
+#define CKM_IDEA_KEY_GEN (0x340)
+#define CKM_IDEA_ECB (0x341)
+#define CKM_IDEA_CBC (0x342)
+#define CKM_IDEA_MAC (0x343)
+#define CKM_IDEA_MAC_GENERAL (0x344)
+#define CKM_IDEA_CBC_PAD (0x345)
+#define CKM_GENERIC_SECRET_KEY_GEN (0x350)
+#define CKM_CONCATENATE_BASE_AND_KEY (0x360)
+#define CKM_CONCATENATE_BASE_AND_DATA (0x362)
+#define CKM_CONCATENATE_DATA_AND_BASE (0x363)
+#define CKM_XOR_BASE_AND_DATA (0x364)
+#define CKM_EXTRACT_KEY_FROM_KEY (0x365)
+#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370)
+#define CKM_SSL3_MASTER_KEY_DERIVE (0x371)
+#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372)
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373)
+#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374)
+#define CKM_TLS_MASTER_KEY_DERIVE (0x375)
+#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376)
+#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377)
+#define CKM_TLS_PRF (0x378)
+#define CKM_SSL3_MD5_MAC (0x380)
+#define CKM_SSL3_SHA1_MAC (0x381)
+#define CKM_MD5_KEY_DERIVATION (0x390)
+#define CKM_MD2_KEY_DERIVATION (0x391)
+#define CKM_SHA1_KEY_DERIVATION (0x392)
+#define CKM_SHA256_KEY_DERIVATION (0x393)
+#define CKM_SHA384_KEY_DERIVATION (0x394)
+#define CKM_SHA512_KEY_DERIVATION (0x395)
+#define CKM_SHA224_KEY_DERIVATION (0x396)
+#define CKM_PBE_MD2_DES_CBC (0x3a0)
+#define CKM_PBE_MD5_DES_CBC (0x3a1)
+#define CKM_PBE_MD5_CAST_CBC (0x3a2)
+#define CKM_PBE_MD5_CAST3_CBC (0x3a3)
+#define CKM_PBE_MD5_CAST5_CBC (0x3a4)
+#define CKM_PBE_MD5_CAST128_CBC (0x3a4)
+#define CKM_PBE_SHA1_CAST5_CBC (0x3a5)
+#define CKM_PBE_SHA1_CAST128_CBC (0x3a5)
+#define CKM_PBE_SHA1_RC4_128 (0x3a6)
+#define CKM_PBE_SHA1_RC4_40 (0x3a7)
+#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8)
+#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9)
+#define CKM_PBE_SHA1_RC2_128_CBC (0x3aa)
+#define CKM_PBE_SHA1_RC2_40_CBC (0x3ab)
+#define CKM_PKCS5_PBKD2 (0x3b0)
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0)
+#define CKM_WTLS_PRE_MASTER_KEY_GEN (0x3d0)
+#define CKM_WTLS_MASTER_KEY_DERIVE (0x3d1)
+#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC (0x3d2)
+#define CKM_WTLS_PRF (0x3d3)
+#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE (0x3d4)
+#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE (0x3d5)
+#define CKM_KEY_WRAP_LYNKS (0x400)
+#define CKM_KEY_WRAP_SET_OAEP (0x401)
+#define CKM_CMS_SIG (0x500)
+#define CKM_KIP_DERIVE (0x510)
+#define CKM_KIP_WRAP (0x511)
+#define CKM_KIP_MAC (0x512)
+#define CKM_CAMELLIA_KEY_GEN (0x550)
+#define CKM_CAMELLIA_ECB (0x551)
+#define CKM_CAMELLIA_CBC (0x552)
+#define CKM_CAMELLIA_MAC (0x553)
+#define CKM_CAMELLIA_MAC_GENERAL (0x554)
+#define CKM_CAMELLIA_CBC_PAD (0x555)
+#define CKM_CAMELLIA_ECB_ENCRYPT_DATA (0x556)
+#define CKM_CAMELLIA_CBC_ENCRYPT_DATA (0x557)
+#define CKM_CAMELLIA_CTR (0x558)
+#define CKM_ARIA_KEY_GEN (0x560)
+#define CKM_ARIA_ECB (0x561)
+#define CKM_ARIA_CBC (0x562)
+#define CKM_ARIA_MAC (0x563)
+#define CKM_ARIA_MAC_GENERAL (0x564)
+#define CKM_ARIA_CBC_PAD (0x565)
+#define CKM_ARIA_ECB_ENCRYPT_DATA (0x566)
+#define CKM_ARIA_CBC_ENCRYPT_DATA (0x567)
+#define CKM_SEED_KEY_GEN (0x650)
+#define CKM_SEED_ECB (0x651)
+#define CKM_SEED_CBC (0x652)
+#define CKM_SEED_MAC (0x653)
+#define CKM_SEED_MAC_GENERAL (0x654)
+#define CKM_SEED_CBC_PAD (0x655)
+#define CKM_SEED_ECB_ENCRYPT_DATA (0x656)
+#define CKM_SEED_CBC_ENCRYPT_DATA (0x657)
+#define CKM_SKIPJACK_KEY_GEN (0x1000)
+#define CKM_SKIPJACK_ECB64 (0x1001)
+#define CKM_SKIPJACK_CBC64 (0x1002)
+#define CKM_SKIPJACK_OFB64 (0x1003)
+#define CKM_SKIPJACK_CFB64 (0x1004)
+#define CKM_SKIPJACK_CFB32 (0x1005)
+#define CKM_SKIPJACK_CFB16 (0x1006)
+#define CKM_SKIPJACK_CFB8 (0x1007)
+#define CKM_SKIPJACK_WRAP (0x1008)
+#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009)
+#define CKM_SKIPJACK_RELAYX (0x100a)
+#define CKM_KEA_KEY_PAIR_GEN (0x1010)
+#define CKM_KEA_KEY_DERIVE (0x1011)
+#define CKM_FORTEZZA_TIMESTAMP (0x1020)
+#define CKM_BATON_KEY_GEN (0x1030)
+#define CKM_BATON_ECB128 (0x1031)
+#define CKM_BATON_ECB96 (0x1032)
+#define CKM_BATON_CBC128 (0x1033)
+#define CKM_BATON_COUNTER (0x1034)
+#define CKM_BATON_SHUFFLE (0x1035)
+#define CKM_BATON_WRAP (0x1036)
+#define CKM_ECDSA_KEY_PAIR_GEN (0x1040)
+#define CKM_EC_KEY_PAIR_GEN (0x1040)
+#define CKM_ECDSA (0x1041)
+#define CKM_ECDSA_SHA1 (0x1042)
+#define CKM_ECDSA_SHA224 (0x1043)
+#define CKM_ECDSA_SHA256 (0x1044)
+#define CKM_ECDSA_SHA384 (0x1045)
+#define CKM_ECDSA_SHA512 (0x1046)
+#define CKM_ECDH1_DERIVE (0x1050)
+#define CKM_ECDH1_COFACTOR_DERIVE (0x1051)
+#define CKM_ECMQV_DERIVE (0x1052)
+#define CKM_JUNIPER_KEY_GEN (0x1060)
+#define CKM_JUNIPER_ECB128 (0x1061)
+#define CKM_JUNIPER_CBC128 (0x1062)
+#define CKM_JUNIPER_COUNTER (0x1063)
+#define CKM_JUNIPER_SHUFFLE (0x1064)
+#define CKM_JUNIPER_WRAP (0x1065)
+#define CKM_FASTHASH (0x1070)
+#define CKM_AES_KEY_GEN (0x1080)
+#define CKM_AES_ECB (0x1081)
+#define CKM_AES_CBC (0x1082)
+#define CKM_AES_MAC (0x1083)
+#define CKM_AES_MAC_GENERAL (0x1084)
+#define CKM_AES_CBC_PAD (0x1085)
+#define CKM_AES_CTR (0x1086)
+#define CKM_AES_GCM (0x1087)
+#define CKM_AES_CCM (0x1088)
+#define CKM_AES_CTS (0x1089)
+#define CKM_AES_CMAC (0x108a)
+#define CKM_AES_CMAC_GENERAL (0x108b)
+#define CKM_BLOWFISH_KEY_GEN (0x1090)
+#define CKM_BLOWFISH_CBC (0x1091)
+#define CKM_TWOFISH_KEY_GEN (0x1092)
+#define CKM_TWOFISH_CBC (0x1093)
+#define CKM_BLOWFISH_CBC_PAD (0x1094)
+#define CKM_TWOFISH_CBC_PAD (0x1095)
+#define CKM_DES_ECB_ENCRYPT_DATA (0x1100)
+#define CKM_DES_CBC_ENCRYPT_DATA (0x1101)
+#define CKM_DES3_ECB_ENCRYPT_DATA (0x1102)
+#define CKM_DES3_CBC_ENCRYPT_DATA (0x1103)
+#define CKM_AES_ECB_ENCRYPT_DATA (0x1104)
+#define CKM_AES_CBC_ENCRYPT_DATA (0x1105)
+#define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200)
+#define CKM_GOSTR3410 (0x1201)
+#define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202)
+#define CKM_GOSTR3410_KEY_WRAP (0x1203)
+#define CKM_GOSTR3410_DERIVE (0x1204)
+#define CKM_GOSTR3411 (0x1210)
+#define CKM_GOSTR3411_HMAC (0x1211)
+#define CKM_GOST28147_KEY_GEN (0x1220)
+#define CKM_GOST28147_ECB (0x1221)
+#define CKM_GOST28147 (0x1222)
+#define CKM_GOST28147_MAC (0x1223)
+#define CKM_GOST28147_KEY_WRAP (0x1224)
+#define CKM_DSA_PARAMETER_GEN (0x2000)
+#define CKM_DH_PKCS_PARAMETER_GEN (0x2001)
+#define CKM_X9_42_DH_PARAMETER_GEN (0x2002)
+#define CKM_AES_OFB (0x2104)
+#define CKM_AES_CFB64 (0x2105)
+#define CKM_AES_CFB8 (0x2106)
+#define CKM_AES_CFB128 (0x2107)
+#define CKM_AES_KEY_WRAP (0x2109)
+#define CKM_AES_KEY_WRAP_PAD (0x210a)
+#define CKM_RSA_PKCS_TPM_1_1 (0x4001)
+#define CKM_RSA_PKCS_OAEPTPM_1_1 (0x4002)
+#define CKM_VENDOR_DEFINED ((unsigned long) (1ul << 31))
+
+
+struct ck_mechanism
+{
+ ck_mechanism_type_t mechanism;
+ void *parameter;
+ unsigned long parameter_len;
+};
+
+
+struct ck_mechanism_info
+{
+ unsigned long min_key_size;
+ unsigned long max_key_size;
+ ck_flags_t flags;
+};
+
+#define CKF_HW (1 << 0)
+#define CKF_ENCRYPT (1 << 8)
+#define CKF_DECRYPT (1 << 9)
+#define CKF_DIGEST (1 << 10)
+#define CKF_SIGN (1 << 11)
+#define CKF_SIGN_RECOVER (1 << 12)
+#define CKF_VERIFY (1 << 13)
+#define CKF_VERIFY_RECOVER (1 << 14)
+#define CKF_GENERATE (1 << 15)
+#define CKF_GENERATE_KEY_PAIR (1 << 16)
+#define CKF_WRAP (1 << 17)
+#define CKF_UNWRAP (1 << 18)
+#define CKF_DERIVE (1 << 19)
+#define CKF_EC_F_P (1 << 20)
+#define CKF_EC_F_2M (1 << 21)
+#define CKF_EC_ECPARAMETERS (1 << 22)
+#define CKF_EC_NAMEDCURVE (1 << 23)
+#define CKF_EC_UNCOMPRESS (1 << 24)
+#define CKF_EC_COMPRESS (1 << 25)
+#define CKF_EXTENSION ((unsigned long) (1ul << 31))
+
+
+/* The following MGFs are defined */
+#define CKG_MGF1_SHA1 (0x00000001)
+#define CKG_MGF1_SHA256 (0x00000002)
+#define CKG_MGF1_SHA384 (0x00000003)
+#define CKG_MGF1_SHA512 (0x00000004)
+#define CKG_MGF1_SHA224 (0x00000005)
+
+#define CKZ_DATA_SPECIFIED (0x00000001)
+
+struct ck_rsa_pkcs_oaep_params {
+ ck_mechanism_type_t hash_alg;
+ unsigned long mgf;
+ unsigned long source;
+ void *source_data;
+ unsigned long source_data_len;
+};
+
+struct ck_rsa_pkcs_pss_params {
+ ck_mechanism_type_t hash_alg;
+ unsigned long mgf;
+ unsigned long slen;
+};
+
+typedef unsigned long ck_ec_kdf_type_t;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL (0x00000001)
+#define CKD_SHA1_KDF (0x00000002)
+
+struct ck_ecdh1_derive_params {
+ ck_ec_kdf_type_t kdf;
+ unsigned long shared_data_len;
+ unsigned char *shared_data;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+};
+
+struct ck_ecdh2_derive_params {
+ ck_ec_kdf_type_t kdf;
+ unsigned long shared_data_len;
+ unsigned char *shared_data;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+ unsigned long private_data_len;
+ ck_object_handle_t private_data;
+ unsigned long public_data_len2;
+ unsigned char *public_data2;
+};
+
+struct ck_ecmqv_derive_params {
+ ck_ec_kdf_type_t kdf;
+ unsigned long shared_data_len;
+ unsigned char *shared_data;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+ unsigned long private_data_len;
+ ck_object_handle_t private_data;
+ unsigned long public_data_len2;
+ unsigned char *public_data2;
+ ck_object_handle_t public_key;
+};
+
+typedef unsigned long ck_x9_42_dh_kdf_type_t;
+
+/* The following X9.42 DH key derivation functions are defined */
+#define CKD_SHA1_KDF_ASN1 (0x00000003)
+#define CKD_SHA1_KDF_CONCATENATE (0x00000004)
+#define CKD_SHA224_KDF (0x00000005)
+#define CKD_SHA256_KDF (0x00000006)
+#define CKD_SHA384_KDF (0x00000007)
+#define CKD_SHA512_KDF (0x00000008)
+#define CKD_CPDIVERSIFY_KDF (0x00000009)
+
+struct ck_x9_42_dh1_derive_params {
+ ck_x9_42_dh_kdf_type_t kdf;
+ unsigned long other_info_len;
+ unsigned char *other_info;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+};
+
+struct ck_x9_42_dh2_derive_params {
+ ck_x9_42_dh_kdf_type_t kdf;
+ unsigned long other_info_len;
+ unsigned char *other_info;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+ unsigned long private_data_len;
+ ck_object_handle_t private_data;
+ unsigned long public_data_len2;
+ unsigned char *public_data2;
+};
+
+struct ck_x9_42_mqv_derive_params {
+ ck_x9_42_dh_kdf_type_t kdf;
+ unsigned long other_info_len;
+ unsigned char *other_info;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+ unsigned long private_data_len;
+ ck_object_handle_t private_data;
+ unsigned long public_data_len2;
+ unsigned char *public_data2;
+ ck_object_handle_t public_key;
+};
+
+struct ck_des_cbc_encrypt_data_params {
+ unsigned char iv[8];
+ unsigned char *data;
+ unsigned long length;
+};
+
+struct ck_aes_cbc_encrypt_data_params {
+ unsigned char iv[16];
+ unsigned char *data;
+ unsigned long length;
+};
+
+struct ck_key_derivation_string_data {
+ unsigned char *data;
+ unsigned long len;
+};
+
+/* Flags for C_WaitForSlotEvent. */
+#define CKF_DONT_BLOCK (1)
+
+
+typedef unsigned long ck_rv_t;
+
+
+typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session,
+ ck_notification_t event, void *application);
+
+/* Forward reference. */
+struct ck_function_list;
+
+#define _CK_DECLARE_FUNCTION(name, args) \
+typedef ck_rv_t (*CK_ ## name) args; \
+ck_rv_t CK_SPEC name args
+
+_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args));
+_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved));
+_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info));
+_CK_DECLARE_FUNCTION (C_GetFunctionList,
+ (struct ck_function_list **function_list));
+
+_CK_DECLARE_FUNCTION (C_GetSlotList,
+ (unsigned char token_present, ck_slot_id_t *slot_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetSlotInfo,
+ (ck_slot_id_t slot_id, struct ck_slot_info *info));
+_CK_DECLARE_FUNCTION (C_GetTokenInfo,
+ (ck_slot_id_t slot_id, struct ck_token_info *info));
+_CK_DECLARE_FUNCTION (C_WaitForSlotEvent,
+ (ck_flags_t flags, ck_slot_id_t *slot, void *reserved));
+_CK_DECLARE_FUNCTION (C_GetMechanismList,
+ (ck_slot_id_t slot_id,
+ ck_mechanism_type_t *mechanism_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetMechanismInfo,
+ (ck_slot_id_t slot_id, ck_mechanism_type_t type,
+ struct ck_mechanism_info *info));
+_CK_DECLARE_FUNCTION (C_InitToken,
+ (ck_slot_id_t slot_id, unsigned char *pin,
+ unsigned long pin_len, unsigned char *label));
+_CK_DECLARE_FUNCTION (C_InitPIN,
+ (ck_session_handle_t session, unsigned char *pin,
+ unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_SetPIN,
+ (ck_session_handle_t session, unsigned char *old_pin,
+ unsigned long old_len, unsigned char *new_pin,
+ unsigned long new_len));
+
+_CK_DECLARE_FUNCTION (C_OpenSession,
+ (ck_slot_id_t slot_id, ck_flags_t flags,
+ void *application, ck_notify_t notify,
+ ck_session_handle_t *session));
+_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id));
+_CK_DECLARE_FUNCTION (C_GetSessionInfo,
+ (ck_session_handle_t session,
+ struct ck_session_info *info));
+_CK_DECLARE_FUNCTION (C_GetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long *operation_state_len));
+_CK_DECLARE_FUNCTION (C_SetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long operation_state_len,
+ ck_object_handle_t encryption_key,
+ ck_object_handle_t authentiation_key));
+_CK_DECLARE_FUNCTION (C_Login,
+ (ck_session_handle_t session, ck_user_type_t user_type,
+ unsigned char *pin, unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_CreateObject,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count, ck_object_handle_t *object));
+_CK_DECLARE_FUNCTION (C_CopyObject,
+ (ck_session_handle_t session, ck_object_handle_t object,
+ struct ck_attribute *templ, unsigned long count,
+ ck_object_handle_t *new_object));
+_CK_DECLARE_FUNCTION (C_DestroyObject,
+ (ck_session_handle_t session,
+ ck_object_handle_t object));
+_CK_DECLARE_FUNCTION (C_GetObjectSize,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ unsigned long *size));
+_CK_DECLARE_FUNCTION (C_GetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_SetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjectsInit,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjects,
+ (ck_session_handle_t session,
+ ck_object_handle_t *object,
+ unsigned long max_object_count,
+ unsigned long *object_count));
+_CK_DECLARE_FUNCTION (C_FindObjectsFinal,
+ (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_EncryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Encrypt,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *encrypted_data,
+ unsigned long *encrypted_data_len));
+_CK_DECLARE_FUNCTION (C_EncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_EncryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_encrypted_part,
+ unsigned long *last_encrypted_part_len));
+
+_CK_DECLARE_FUNCTION (C_DecryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Decrypt,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_data,
+ unsigned long encrypted_data_len,
+ unsigned char *data, unsigned long *data_len));
+_CK_DECLARE_FUNCTION (C_DecryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part, unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_DecryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_part,
+ unsigned long *last_part_len));
+
+_CK_DECLARE_FUNCTION (C_DigestInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism));
+_CK_DECLARE_FUNCTION (C_Digest,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *digest,
+ unsigned long *digest_len));
+_CK_DECLARE_FUNCTION (C_DigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_DigestKey,
+ (ck_session_handle_t session, ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_DigestFinal,
+ (ck_session_handle_t session,
+ unsigned char *digest,
+ unsigned long *digest_len));
+
+_CK_DECLARE_FUNCTION (C_SignInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Sign,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_SignFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_SignRecover,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+
+_CK_DECLARE_FUNCTION (C_VerifyInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Verify,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_VerifyFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_VerifyRecover,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len,
+ unsigned char *data,
+ unsigned long *data_len));
+
+_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_SignEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+
+_CK_DECLARE_FUNCTION (C_GenerateKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *templ,
+ unsigned long count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_GenerateKeyPair,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *public_key_template,
+ unsigned long public_key_attribute_count,
+ struct ck_attribute *private_key_template,
+ unsigned long private_key_attribute_count,
+ ck_object_handle_t *public_key,
+ ck_object_handle_t *private_key));
+_CK_DECLARE_FUNCTION (C_WrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t wrapping_key,
+ ck_object_handle_t key,
+ unsigned char *wrapped_key,
+ unsigned long *wrapped_key_len));
+_CK_DECLARE_FUNCTION (C_UnwrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t unwrapping_key,
+ unsigned char *wrapped_key,
+ unsigned long wrapped_key_len,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_DeriveKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t base_key,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+
+_CK_DECLARE_FUNCTION (C_SeedRandom,
+ (ck_session_handle_t session, unsigned char *seed,
+ unsigned long seed_len));
+_CK_DECLARE_FUNCTION (C_GenerateRandom,
+ (ck_session_handle_t session,
+ unsigned char *random_data,
+ unsigned long random_len));
+
+_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session));
+
+
+struct ck_function_list
+{
+ struct ck_version version;
+ CK_C_Initialize C_Initialize;
+ CK_C_Finalize C_Finalize;
+ CK_C_GetInfo C_GetInfo;
+ CK_C_GetFunctionList C_GetFunctionList;
+ CK_C_GetSlotList C_GetSlotList;
+ CK_C_GetSlotInfo C_GetSlotInfo;
+ CK_C_GetTokenInfo C_GetTokenInfo;
+ CK_C_GetMechanismList C_GetMechanismList;
+ CK_C_GetMechanismInfo C_GetMechanismInfo;
+ CK_C_InitToken C_InitToken;
+ CK_C_InitPIN C_InitPIN;
+ CK_C_SetPIN C_SetPIN;
+ CK_C_OpenSession C_OpenSession;
+ CK_C_CloseSession C_CloseSession;
+ CK_C_CloseAllSessions C_CloseAllSessions;
+ CK_C_GetSessionInfo C_GetSessionInfo;
+ CK_C_GetOperationState C_GetOperationState;
+ CK_C_SetOperationState C_SetOperationState;
+ CK_C_Login C_Login;
+ CK_C_Logout C_Logout;
+ CK_C_CreateObject C_CreateObject;
+ CK_C_CopyObject C_CopyObject;
+ CK_C_DestroyObject C_DestroyObject;
+ CK_C_GetObjectSize C_GetObjectSize;
+ CK_C_GetAttributeValue C_GetAttributeValue;
+ CK_C_SetAttributeValue C_SetAttributeValue;
+ CK_C_FindObjectsInit C_FindObjectsInit;
+ CK_C_FindObjects C_FindObjects;
+ CK_C_FindObjectsFinal C_FindObjectsFinal;
+ CK_C_EncryptInit C_EncryptInit;
+ CK_C_Encrypt C_Encrypt;
+ CK_C_EncryptUpdate C_EncryptUpdate;
+ CK_C_EncryptFinal C_EncryptFinal;
+ CK_C_DecryptInit C_DecryptInit;
+ CK_C_Decrypt C_Decrypt;
+ CK_C_DecryptUpdate C_DecryptUpdate;
+ CK_C_DecryptFinal C_DecryptFinal;
+ CK_C_DigestInit C_DigestInit;
+ CK_C_Digest C_Digest;
+ CK_C_DigestUpdate C_DigestUpdate;
+ CK_C_DigestKey C_DigestKey;
+ CK_C_DigestFinal C_DigestFinal;
+ CK_C_SignInit C_SignInit;
+ CK_C_Sign C_Sign;
+ CK_C_SignUpdate C_SignUpdate;
+ CK_C_SignFinal C_SignFinal;
+ CK_C_SignRecoverInit C_SignRecoverInit;
+ CK_C_SignRecover C_SignRecover;
+ CK_C_VerifyInit C_VerifyInit;
+ CK_C_Verify C_Verify;
+ CK_C_VerifyUpdate C_VerifyUpdate;
+ CK_C_VerifyFinal C_VerifyFinal;
+ CK_C_VerifyRecoverInit C_VerifyRecoverInit;
+ CK_C_VerifyRecover C_VerifyRecover;
+ CK_C_DigestEncryptUpdate C_DigestEncryptUpdate;
+ CK_C_DecryptDigestUpdate C_DecryptDigestUpdate;
+ CK_C_SignEncryptUpdate C_SignEncryptUpdate;
+ CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate;
+ CK_C_GenerateKey C_GenerateKey;
+ CK_C_GenerateKeyPair C_GenerateKeyPair;
+ CK_C_WrapKey C_WrapKey;
+ CK_C_UnwrapKey C_UnwrapKey;
+ CK_C_DeriveKey C_DeriveKey;
+ CK_C_SeedRandom C_SeedRandom;
+ CK_C_GenerateRandom C_GenerateRandom;
+ CK_C_GetFunctionStatus C_GetFunctionStatus;
+ CK_C_CancelFunction C_CancelFunction;
+ CK_C_WaitForSlotEvent C_WaitForSlotEvent;
+};
+
+
+typedef ck_rv_t (*ck_createmutex_t) (void **mutex);
+typedef ck_rv_t (*ck_destroymutex_t) (void *mutex);
+typedef ck_rv_t (*ck_lockmutex_t) (void *mutex);
+typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex);
+
+
+struct ck_c_initialize_args
+{
+ ck_createmutex_t create_mutex;
+ ck_destroymutex_t destroy_mutex;
+ ck_lockmutex_t lock_mutex;
+ ck_unlockmutex_t unlock_mutex;
+ ck_flags_t flags;
+ void *reserved;
+};
+
+
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1 << 0)
+#define CKF_OS_LOCKING_OK (1 << 1)
+
+#define CKR_OK (0)
+#define CKR_CANCEL (1)
+#define CKR_HOST_MEMORY (2)
+#define CKR_SLOT_ID_INVALID (3)
+#define CKR_GENERAL_ERROR (5)
+#define CKR_FUNCTION_FAILED (6)
+#define CKR_ARGUMENTS_BAD (7)
+#define CKR_NO_EVENT (8)
+#define CKR_NEED_TO_CREATE_THREADS (9)
+#define CKR_CANT_LOCK (0xa)
+#define CKR_ATTRIBUTE_READ_ONLY (0x10)
+#define CKR_ATTRIBUTE_SENSITIVE (0x11)
+#define CKR_ATTRIBUTE_TYPE_INVALID (0x12)
+#define CKR_ATTRIBUTE_VALUE_INVALID (0x13)
+#define CKR_COPY_PROHIBITED (0x1A)
+#define CKR_DATA_INVALID (0x20)
+#define CKR_DATA_LEN_RANGE (0x21)
+#define CKR_DEVICE_ERROR (0x30)
+#define CKR_DEVICE_MEMORY (0x31)
+#define CKR_DEVICE_REMOVED (0x32)
+#define CKR_ENCRYPTED_DATA_INVALID (0x40)
+#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41)
+#define CKR_FUNCTION_CANCELED (0x50)
+#define CKR_FUNCTION_NOT_PARALLEL (0x51)
+#define CKR_FUNCTION_NOT_SUPPORTED (0x54)
+#define CKR_KEY_HANDLE_INVALID (0x60)
+#define CKR_KEY_SIZE_RANGE (0x62)
+#define CKR_KEY_TYPE_INCONSISTENT (0x63)
+#define CKR_KEY_NOT_NEEDED (0x64)
+#define CKR_KEY_CHANGED (0x65)
+#define CKR_KEY_NEEDED (0x66)
+#define CKR_KEY_INDIGESTIBLE (0x67)
+#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68)
+#define CKR_KEY_NOT_WRAPPABLE (0x69)
+#define CKR_KEY_UNEXTRACTABLE (0x6a)
+#define CKR_MECHANISM_INVALID (0x70)
+#define CKR_MECHANISM_PARAM_INVALID (0x71)
+#define CKR_OBJECT_HANDLE_INVALID (0x82)
+#define CKR_OPERATION_ACTIVE (0x90)
+#define CKR_OPERATION_NOT_INITIALIZED (0x91)
+#define CKR_PIN_INCORRECT (0xa0)
+#define CKR_PIN_INVALID (0xa1)
+#define CKR_PIN_LEN_RANGE (0xa2)
+#define CKR_PIN_EXPIRED (0xa3)
+#define CKR_PIN_LOCKED (0xa4)
+#define CKR_SESSION_CLOSED (0xb0)
+#define CKR_SESSION_COUNT (0xb1)
+#define CKR_SESSION_HANDLE_INVALID (0xb3)
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4)
+#define CKR_SESSION_READ_ONLY (0xb5)
+#define CKR_SESSION_EXISTS (0xb6)
+#define CKR_SESSION_READ_ONLY_EXISTS (0xb7)
+#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8)
+#define CKR_SIGNATURE_INVALID (0xc0)
+#define CKR_SIGNATURE_LEN_RANGE (0xc1)
+#define CKR_TEMPLATE_INCOMPLETE (0xd0)
+#define CKR_TEMPLATE_INCONSISTENT (0xd1)
+#define CKR_TOKEN_NOT_PRESENT (0xe0)
+#define CKR_TOKEN_NOT_RECOGNIZED (0xe1)
+#define CKR_TOKEN_WRITE_PROTECTED (0xe2)
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0)
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1)
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2)
+#define CKR_USER_ALREADY_LOGGED_IN (0x100)
+#define CKR_USER_NOT_LOGGED_IN (0x101)
+#define CKR_USER_PIN_NOT_INITIALIZED (0x102)
+#define CKR_USER_TYPE_INVALID (0x103)
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104)
+#define CKR_USER_TOO_MANY_TYPES (0x105)
+#define CKR_WRAPPED_KEY_INVALID (0x110)
+#define CKR_WRAPPED_KEY_LEN_RANGE (0x112)
+#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113)
+#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114)
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115)
+#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120)
+#define CKR_RANDOM_NO_RNG (0x121)
+#define CKR_DOMAIN_PARAMS_INVALID (0x130)
+#define CKR_BUFFER_TOO_SMALL (0x150)
+#define CKR_SAVED_STATE_INVALID (0x160)
+#define CKR_INFORMATION_SENSITIVE (0x170)
+#define CKR_STATE_UNSAVEABLE (0x180)
+#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190)
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191)
+#define CKR_MUTEX_BAD (0x1a0)
+#define CKR_MUTEX_NOT_LOCKED (0x1a1)
+#define CKR_NEW_PIN_MODE (0x1b0)
+#define CKR_NEXT_OTP (0x1b1)
+#define CKR_EXCEEDED_MAX_ITERATIONS (0x1b5)
+#define CKR_FIPS_SELF_TEST_FAILED (0x1b6)
+#define CKR_LIBRARY_LOAD_FAILED (0x1b7)
+#define CKR_PIN_TOO_WEAK (0x1b8)
+#define CKR_PUBLIC_KEY_INVALID (0x1b9)
+#define CKR_FUNCTION_REJECTED (0x200)
+#define CKR_VENDOR_DEFINED ((unsigned long) (1ul << 31))
+
+
+
+/* Compatibility layer. */
+
+#ifdef CRYPTOKI_COMPAT
+
+#undef CK_DEFINE_FUNCTION
+#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name
+
+/* For NULL. */
+#include <stddef.h>
+
+typedef unsigned char CK_BYTE;
+typedef unsigned char CK_CHAR;
+typedef unsigned char CK_UTF8CHAR;
+typedef unsigned char CK_BBOOL;
+typedef unsigned long int CK_ULONG;
+typedef long int CK_LONG;
+typedef CK_BYTE *CK_BYTE_PTR;
+typedef CK_CHAR *CK_CHAR_PTR;
+typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR;
+typedef CK_ULONG *CK_ULONG_PTR;
+typedef void *CK_VOID_PTR;
+typedef void **CK_VOID_PTR_PTR;
+#define CK_FALSE 0
+#define CK_TRUE 1
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#endif
+
+typedef struct ck_version CK_VERSION;
+typedef struct ck_version *CK_VERSION_PTR;
+
+typedef struct ck_info CK_INFO;
+typedef struct ck_info *CK_INFO_PTR;
+
+typedef ck_slot_id_t *CK_SLOT_ID_PTR;
+
+typedef struct ck_slot_info CK_SLOT_INFO;
+typedef struct ck_slot_info *CK_SLOT_INFO_PTR;
+
+typedef struct ck_token_info CK_TOKEN_INFO;
+typedef struct ck_token_info *CK_TOKEN_INFO_PTR;
+
+typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR;
+
+typedef struct ck_session_info CK_SESSION_INFO;
+typedef struct ck_session_info *CK_SESSION_INFO_PTR;
+
+typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR;
+
+typedef ck_object_class_t *CK_OBJECT_CLASS_PTR;
+
+typedef struct ck_attribute CK_ATTRIBUTE;
+typedef struct ck_attribute *CK_ATTRIBUTE_PTR;
+
+typedef struct ck_date CK_DATE;
+typedef struct ck_date *CK_DATE_PTR;
+
+typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR;
+
+typedef struct ck_mechanism CK_MECHANISM;
+typedef struct ck_mechanism *CK_MECHANISM_PTR;
+
+typedef struct ck_mechanism_info CK_MECHANISM_INFO;
+typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR;
+
+typedef struct ck_rsa_pkcs_oaep_params CK_RSA_PKCS_OAEP_PARAMS;
+typedef struct ck_rsa_pkcs_oaep_params *CK_RSA_PKCS_OAEP_PARAMS_PTR;
+
+typedef struct ck_rsa_pkcs_pss_params CK_RSA_PKCS_PSS_PARAMS;
+typedef struct ck_rsa_pkcs_pss_params *CK_RSA_PKCS_PSS_PARAMS_PTR;
+
+typedef struct ck_ecdh1_derive_params CK_ECDH1_DERIVE_PARAMS;
+typedef struct ck_ecdh1_derive_params *CK_ECDH1_DERIVE_PARAMS_PTR;
+
+typedef struct ck_des_cbc_encrypt_data_params CK_DES_CBC_ENCRYPT_DATA_PARAMS;
+typedef struct ck_des_cbc_encrypt_data_params *CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct ck_aes_cbc_encrypt_data_params CK_AES_CBC_ENCRYPT_DATA_PARAMS;
+typedef struct ck_aes_cbc_encrypt_data_params *CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct ck_key_derivation_string_data CK_KEY_DERIVATION_STRING_DATA;
+typedef struct ck_key_derivation_string_data *CK_KEY_DERIVATION_STRING_DATA_PTR;
+
+typedef struct ck_function_list CK_FUNCTION_LIST;
+typedef struct ck_function_list *CK_FUNCTION_LIST_PTR;
+typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR;
+
+typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS;
+typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR;
+
+#define NULL_PTR NULL
+
+/* Delete the helper macros defined at the top of the file. */
+#undef ck_flags_t
+#undef ck_version
+
+#undef ck_info
+#undef cryptoki_version
+#undef manufacturer_id
+#undef library_description
+#undef library_version
+
+#undef ck_notification_t
+#undef ck_slot_id_t
+
+#undef ck_slot_info
+#undef slot_description
+#undef hardware_version
+#undef firmware_version
+
+#undef ck_token_info
+#undef serial_number
+#undef max_session_count
+#undef session_count
+#undef max_rw_session_count
+#undef rw_session_count
+#undef max_pin_len
+#undef min_pin_len
+#undef total_public_memory
+#undef free_public_memory
+#undef total_private_memory
+#undef free_private_memory
+#undef utc_time
+
+#undef ck_session_handle_t
+#undef ck_user_type_t
+#undef ck_state_t
+
+#undef ck_session_info
+#undef slot_id
+#undef device_error
+
+#undef ck_object_handle_t
+#undef ck_object_class_t
+#undef ck_hw_feature_type_t
+#undef ck_key_type_t
+#undef ck_certificate_type_t
+#undef ck_attribute_type_t
+
+#undef ck_attribute
+#undef value
+#undef value_len
+
+#undef ck_date
+
+#undef ck_mechanism_type_t
+
+#undef ck_mechanism
+#undef parameter
+#undef parameter_len
+
+#undef ck_mechanism_info
+#undef min_key_size
+#undef max_key_size
+
+#undef ck_rsa_pkcs_oaep_params
+#undef hash_alg
+#undef source_data
+#undef source_data_len
+#undef slen
+
+#undef ck_ec_kdf_type_t
+#undef shared_data_len
+#undef shared_data
+#undef public_data_len
+#undef public_data
+#undef private_data_len
+#undef private_data
+#undef public_data_len2
+#undef public_data2
+#undef public_key
+
+#undef ck_x9_42_dh_kdf_type_t
+#undef other_info_len
+#undef other_info
+
+#undef data
+#undef len
+
+#undef ck_rv_t
+#undef ck_notify_t
+
+#undef ck_function_list
+
+#undef ck_createmutex_t
+#undef ck_destroymutex_t
+#undef ck_lockmutex_t
+#undef ck_unlockmutex_t
+
+#undef ck_c_initialize_args
+#undef create_mutex
+#undef destroy_mutex
+#undef lock_mutex
+#undef unlock_mutex
+#undef reserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+/* System dependencies. */
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+#pragma pack(pop, cryptoki)
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PKCS11_H */
diff --git a/third_party/heimdal/lib/hx509/req.c b/third_party/heimdal/lib/hx509/req.c
new file mode 100644
index 0000000..2b3f46d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/req.c
@@ -0,0 +1,1520 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include <pkcs10_asn1.h>
+
+typedef struct abitstring_s {
+ unsigned char *feats;
+ size_t feat_bytes;
+} *abitstring;
+
+struct hx509_request_data {
+ hx509_context context;
+ hx509_name name;
+ SubjectPublicKeyInfo key;
+ KeyUsage ku;
+ ExtKeyUsage eku;
+ GeneralNames san;
+ struct abitstring_s authorized_EKUs;
+ struct abitstring_s authorized_SANs;
+ uint32_t nunsupported; /* Count of unsupported features requested */
+ uint32_t nauthorized; /* Count of supported features authorized */
+ uint32_t ku_are_authorized:1;
+};
+
+/**
+ * Allocate and initialize an hx509_request structure representing a PKCS#10
+ * certificate signing request.
+ *
+ * @param context An hx509 context.
+ * @param req Where to put the new hx509_request object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_init(hx509_context context, hx509_request *req)
+{
+ *req = calloc(1, sizeof(**req));
+ if (*req == NULL)
+ return ENOMEM;
+
+ (*req)->context = context;
+ return 0;
+}
+
+/**
+ * Free a certificate signing request object.
+ *
+ * @param req A pointer to the hx509_request to free.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_request_free(hx509_request *reqp)
+{
+ hx509_request req = *reqp;
+
+ *reqp = NULL;
+ if (req == NULL)
+ return;
+ if (req->name)
+ hx509_name_free(&req->name);
+ free(req->authorized_EKUs.feats);
+ free(req->authorized_SANs.feats);
+ free_SubjectPublicKeyInfo(&req->key);
+ free_ExtKeyUsage(&req->eku);
+ free_GeneralNames(&req->san);
+ memset(req, 0, sizeof(*req));
+ free(req);
+}
+
+/**
+ * Set the subjectName of the CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request to alter.
+ * @param name The subjectName.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_set_name(hx509_context context,
+ hx509_request req,
+ hx509_name name)
+{
+ if (req->name)
+ hx509_name_free(&req->name);
+ if (name) {
+ int ret = hx509_name_copy(context, name, &req->name);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * Get the subject name requested by a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param name Where to put the name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_get_name(hx509_context context,
+ hx509_request req,
+ hx509_name *name)
+{
+ if (req->name == NULL) {
+ hx509_set_error_string(context, 0, EINVAL, "Request have no name");
+ return EINVAL;
+ }
+ return hx509_name_copy(context, req->name, name);
+}
+
+/**
+ * Set the subject public key requested by a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param key The public key.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_set_SubjectPublicKeyInfo(hx509_context context,
+ hx509_request req,
+ const SubjectPublicKeyInfo *key)
+{
+ free_SubjectPublicKeyInfo(&req->key);
+ return copy_SubjectPublicKeyInfo(key, &req->key);
+}
+
+/**
+ * Get the subject public key requested by a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param key Where to put the key.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_get_SubjectPublicKeyInfo(hx509_context context,
+ hx509_request req,
+ SubjectPublicKeyInfo *key)
+{
+ return copy_SubjectPublicKeyInfo(&req->key, key);
+}
+
+/**
+ * Set the key usage requested by a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param ku The key usage.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_set_ku(hx509_context context, hx509_request req, KeyUsage ku)
+{
+ uint64_t n = KeyUsage2int(ku);
+
+ if ((KeyUsage2int(req->ku) & n) != n)
+ req->ku_are_authorized = 0;
+ req->ku = ku;
+ return 0;
+}
+
+/**
+ * Get the key usage requested by a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param ku Where to put the key usage.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_get_ku(hx509_context context, hx509_request req, KeyUsage *ku)
+{
+ *ku = req->ku;
+ return 0;
+}
+
+/**
+ * Add an extended key usage OID to a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param oid The EKU OID.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_eku(hx509_context context,
+ hx509_request req,
+ const heim_oid *oid)
+{
+ void *val;
+ int ret;
+
+ val = realloc(req->eku.val, sizeof(req->eku.val[0]) * (req->eku.len + 1));
+ if (val == NULL)
+ return ENOMEM;
+ req->eku.val = val;
+
+ ret = der_copy_oid(oid, &req->eku.val[req->eku.len]);
+ if (ret)
+ return ret;
+
+ req->eku.len += 1;
+
+ return 0;
+}
+
+/**
+ * Add a GeneralName (Jabber ID) subject alternative name to a CSR.
+ *
+ * XXX Make this take a heim_octet_string, not a GeneralName*.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param gn The GeneralName object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_GeneralName(hx509_context context,
+ hx509_request req,
+ const GeneralName *gn)
+{
+ return add_GeneralNames(&req->san, gn);
+}
+
+static int
+add_utf8_other_san(hx509_context context,
+ GeneralNames *gns,
+ const heim_oid *oid,
+ const char *s)
+{
+ const PKIXXmppAddr us = (const PKIXXmppAddr)(uintptr_t)s;
+ GeneralName gn;
+ size_t size;
+ int ret;
+
+ gn.element = choice_GeneralName_otherName;
+ gn.u.otherName.type_id.length = 0;
+ gn.u.otherName.type_id.components = 0;
+ gn.u.otherName.value.data = NULL;
+ gn.u.otherName.value.length = 0;
+ ret = der_copy_oid(oid, &gn.u.otherName.type_id);
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(PKIXXmppAddr, gn.u.otherName.value.data,
+ gn.u.otherName.value.length, &us, &size, ret);
+ if (ret == 0 && size != gn.u.otherName.value.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ if (ret == 0)
+ ret = add_GeneralNames(gns, &gn);
+ free_GeneralName(&gn);
+ if (ret)
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ return ret;
+}
+
+/**
+ * Add an xmppAddr (Jabber ID) subject alternative name to a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param jid The XMPP address.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_xmpp_name(hx509_context context,
+ hx509_request req,
+ const char *jid)
+{
+ return add_utf8_other_san(context, &req->san,
+ &asn1_oid_id_pkix_on_xmppAddr, jid);
+}
+
+/**
+ * Add a Microsoft UPN subject alternative name to a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param hostname The XMPP address.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_ms_upn_name(hx509_context context,
+ hx509_request req,
+ const char *upn)
+{
+ return add_utf8_other_san(context, &req->san, &asn1_oid_id_pkinit_ms_san,
+ upn);
+}
+
+/**
+ * Add a dNSName (hostname) subject alternative name to a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param hostname The fully-qualified hostname.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_dns_name(hx509_context context,
+ hx509_request req,
+ const char *hostname)
+{
+ GeneralName name;
+
+ memset(&name, 0, sizeof(name));
+ name.element = choice_GeneralName_dNSName;
+ name.u.dNSName.data = rk_UNCONST(hostname);
+ name.u.dNSName.length = strlen(hostname);
+
+ return add_GeneralNames(&req->san, &name);
+}
+
+/**
+ * Add a dnsSRV (_service.hostname) subject alternative name to a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param dnssrv The DNS SRV name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_dns_srv(hx509_context context,
+ hx509_request req,
+ const char *dnssrv)
+{
+ GeneralName gn;
+ SRVName n;
+ size_t size;
+ int ret;
+
+ memset(&n, 0, sizeof(n));
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_otherName;
+ gn.u.otherName.type_id.length = 0;
+ gn.u.otherName.type_id.components = 0;
+ gn.u.otherName.value.data = NULL;
+ gn.u.otherName.value.length = 0;
+ n.length = strlen(dnssrv);
+ n.data = (void *)(uintptr_t)dnssrv;
+ ASN1_MALLOC_ENCODE(SRVName,
+ gn.u.otherName.value.data,
+ gn.u.otherName.value.length, &n, &size, ret);
+ if (ret == 0)
+ ret = der_copy_oid(&asn1_oid_id_pkix_on_dnsSRV, &gn.u.otherName.type_id);
+ if (ret == 0)
+ ret = add_GeneralNames(&req->san, &gn);
+ free_GeneralName(&gn);
+ return ret;
+}
+
+/**
+ * Add an rfc822Name (e-mail address) subject alternative name to a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param email The e-mail address.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_email(hx509_context context,
+ hx509_request req,
+ const char *email)
+{
+ GeneralName name;
+
+ memset(&name, 0, sizeof(name));
+ name.element = choice_GeneralName_rfc822Name;
+ name.u.rfc822Name.data = rk_UNCONST(email);
+ name.u.rfc822Name.length = strlen(email);
+
+ return add_GeneralNames(&req->san, &name);
+}
+
+/**
+ * Add a registeredID (OID) subject alternative name to a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param oid The OID.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_registered(hx509_context context,
+ hx509_request req,
+ heim_oid *oid)
+{
+ GeneralName name;
+ int ret;
+
+ memset(&name, 0, sizeof(name));
+ name.element = choice_GeneralName_registeredID;
+ ret = der_copy_oid(oid, &name.u.registeredID);
+ if (ret)
+ return ret;
+ ret = add_GeneralNames(&req->san, &name);
+ free_GeneralName(&name);
+ return ret;
+}
+
+/**
+ * Add a Kerberos V5 principal subject alternative name to a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param princ The Kerberos principal name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_add_pkinit(hx509_context context,
+ hx509_request req,
+ const char *princ)
+{
+ KRB5PrincipalName kn;
+ GeneralName gn;
+ int ret;
+
+ memset(&kn, 0, sizeof(kn));
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_otherName;
+ gn.u.otherName.type_id.length = 0;
+ gn.u.otherName.type_id.components = 0;
+ gn.u.otherName.value.data = NULL;
+ gn.u.otherName.value.length = 0;
+ ret = der_copy_oid(&asn1_oid_id_pkinit_san, &gn.u.otherName.type_id);
+ if (ret == 0)
+ ret = _hx509_make_pkinit_san(context, princ, &gn.u.otherName.value);
+ if (ret == 0)
+ ret = add_GeneralNames(&req->san, &gn);
+ free_GeneralName(&gn);
+ return ret;
+}
+
+/* XXX Add DNSSRV and other SANs */
+
+static int
+get_exts(hx509_context context,
+ const hx509_request req,
+ Extensions *exts)
+{
+ size_t size;
+ int ret = 0;
+
+ exts->val = NULL;
+ exts->len = 0;
+
+ if (KeyUsage2int(req->ku)) {
+ Extension e;
+
+ memset(&e, 0, sizeof(e));
+ /* The critical field needs to be made DEFAULT FALSE... */
+ e.critical = 1;
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(KeyUsage, e.extnValue.data, e.extnValue.length,
+ &req->ku, &size, ret);
+ if (ret == 0)
+ ret = der_copy_oid(&asn1_oid_id_x509_ce_keyUsage, &e.extnID);
+ if (ret == 0)
+ ret = add_Extensions(exts, &e);
+ free_Extension(&e);
+ }
+ if (ret == 0 && req->eku.len) {
+ Extension e;
+
+ memset(&e, 0, sizeof(e));
+ e.critical = 1;
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(ExtKeyUsage,
+ e.extnValue.data, e.extnValue.length,
+ &req->eku, &size, ret);
+ if (ret == 0)
+ ret = der_copy_oid(&asn1_oid_id_x509_ce_extKeyUsage, &e.extnID);
+ if (ret == 0)
+ ret = add_Extensions(exts, &e);
+ free_Extension(&e);
+ }
+ if (ret == 0 && req->san.len) {
+ Extension e;
+
+ memset(&e, 0, sizeof(e));
+ /*
+ * SANs are critical when the subject Name is empty.
+ *
+ * The empty DN check could probably stand to be a function we export.
+ */
+ e.critical = FALSE;
+ if (req->name &&
+ req->name->der_name.element == choice_Name_rdnSequence &&
+ req->name->der_name.u.rdnSequence.len == 0)
+ e.critical = 1;
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(GeneralNames,
+ e.extnValue.data, e.extnValue.length,
+ &req->san,
+ &size, ret);
+ if (ret == 0)
+ ret = der_copy_oid(&asn1_oid_id_x509_ce_subjectAltName, &e.extnID);
+ if (ret == 0)
+ ret = add_Extensions(exts, &e);
+ free_Extension(&e);
+ }
+
+ return ret;
+}
+
+/**
+ * Get the KU/EKUs/SANs set on a request as a DER-encoding of Extensions.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param exts_der Where to put the DER-encoded Extensions.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_get_exts(hx509_context context,
+ const hx509_request req,
+ heim_octet_string *exts_der)
+{
+ Extensions exts;
+ size_t size;
+ int ret;
+
+ exts_der->data = NULL;
+ exts_der->length = 0;
+ ret = get_exts(context, req, &exts);
+ if (ret == 0 && exts.len /* Extensions has a min size constraint of 1 */)
+ ASN1_MALLOC_ENCODE(Extensions, exts_der->data, exts_der->length,
+ &exts, &size, ret);
+ free_Extensions(&exts);
+ return ret;
+}
+
+/* XXX Add PEM */
+
+/**
+ * Encode a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param signer The private key corresponding to the CSR's subject public key.
+ * @param request Where to put the DER-encoded CSR.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_to_pkcs10(hx509_context context,
+ const hx509_request req,
+ const hx509_private_key signer,
+ heim_octet_string *request)
+{
+ CertificationRequest r;
+ Extensions exts;
+ heim_octet_string data;
+ size_t size;
+ int ret;
+
+ request->data = NULL;
+ request->length = 0;
+
+ data.length = 0;
+ data.data = NULL;
+
+ if (req->name == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "PKCS10 needs to have a subject");
+ return EINVAL;
+ }
+
+ memset(&r, 0, sizeof(r));
+
+ /* Setup CSR */
+ r.certificationRequestInfo.version = pkcs10_v1;
+ ret = copy_Name(&req->name->der_name,
+ &r.certificationRequestInfo.subject);
+ if (ret == 0)
+ ret = copy_SubjectPublicKeyInfo(&req->key,
+ &r.certificationRequestInfo.subjectPKInfo);
+
+ /* Encode extReq attribute with requested Certificate Extensions */
+
+ if (ret == 0)
+ ret = get_exts(context, req, &exts);
+ if (ret == 0 && exts.len) {
+ Attribute *a = NULL; /* Quiet VC */
+ heim_any extns;
+
+ r.certificationRequestInfo.attributes =
+ calloc(1, sizeof(r.certificationRequestInfo.attributes[0]));
+ if (r.certificationRequestInfo.attributes == NULL)
+ ret = ENOMEM;
+ if (ret == 0) {
+ r.certificationRequestInfo.attributes[0].len = 1;
+ r.certificationRequestInfo.attributes[0].val =
+ calloc(1, sizeof(r.certificationRequestInfo.attributes[0].val[0]));
+ if (r.certificationRequestInfo.attributes[0].val == NULL)
+ ret = ENOMEM;
+ if (ret == 0)
+ a = r.certificationRequestInfo.attributes[0].val;
+ }
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(Extensions, extns.data, extns.length,
+ &exts, &size, ret);
+ if (ret == 0 && a)
+ ret = der_copy_oid(&asn1_oid_id_pkcs9_extReq, &a->type);
+ if (ret == 0)
+ ret = add_AttributeValues(&a->value, &extns);
+ free_heim_any(&extns);
+ }
+
+ /* Encode CSR body for signing */
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(CertificationRequestInfo, data.data, data.length,
+ &r.certificationRequestInfo, &size, ret);
+ if (ret == 0 && data.length != size)
+ abort();
+
+ /* Self-sign CSR body */
+ if (ret == 0) {
+ ret = _hx509_create_signature_bitstring(context, signer,
+ _hx509_crypto_default_sig_alg,
+ &data,
+ &r.signatureAlgorithm,
+ &r.signature);
+ }
+ free(data.data);
+
+ /* Encode CSR */
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(CertificationRequest, request->data, request->length,
+ &r, &size, ret);
+ if (ret == 0 && request->length != size)
+ abort();
+
+ free_CertificationRequest(&r);
+ free_Extensions(&exts);
+ return ret;
+}
+
+/**
+ * Parse an encoded CSR and verify its self-signature.
+ *
+ * @param context An hx509 context.
+ * @param der The DER-encoded CSR.
+ * @param req Where to put request object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_parse_der(hx509_context context,
+ heim_octet_string *der,
+ hx509_request *req)
+{
+ CertificationRequestInfo *rinfo = NULL;
+ CertificationRequest r;
+ hx509_cert signer = NULL;
+ Extensions exts;
+ size_t i, size;
+ int ret;
+
+ memset(&exts, 0, sizeof(exts));
+
+ /* Initial setup and decoding of CSR */
+ ret = hx509_request_init(context, req);
+ if (ret)
+ return ret;
+ ret = decode_CertificationRequest(der->data, der->length, &r, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to decode CSR");
+ free(*req);
+ *req = NULL;
+ return ret;
+ }
+ rinfo = &r.certificationRequestInfo;
+
+ /*
+ * Setup a 'signer' for verifying the self-signature for proof of
+ * possession.
+ *
+ * Sadly we need a "certificate" here because _hx509_verify_signature_*()
+ * functions want one as a signer even though all the verification
+ * functions that use the signer argument only ever use the spki of the
+ * signer certificate.
+ *
+ * FIXME Change struct signature_alg's verify_signature's prototype to use
+ * an spki instead of an hx509_cert as the signer! The we won't have
+ * to do this.
+ */
+ if (ret == 0) {
+ Certificate c;
+ memset(&c, 0, sizeof(c));
+ c.tbsCertificate.subjectPublicKeyInfo = rinfo->subjectPKInfo;
+ if ((signer = hx509_cert_init(context, &c, NULL)) == NULL)
+ ret = ENOMEM;
+ }
+
+ /* Verify the signature */
+ if (ret == 0)
+ ret = _hx509_verify_signature_bitstring(context, signer,
+ &r.signatureAlgorithm,
+ &rinfo->_save,
+ &r.signature);
+ if (ret)
+ hx509_set_error_string(context, 0, ret,
+ "CSR signature verification failed");
+ hx509_cert_free(signer);
+
+ /* Populate the hx509_request */
+ if (ret == 0)
+ ret = hx509_request_set_SubjectPublicKeyInfo(context, *req,
+ &rinfo->subjectPKInfo);
+ if (ret == 0)
+ ret = _hx509_name_from_Name(&rinfo->subject, &(*req)->name);
+
+ /* Extract KUs, EKUs, and SANs from the CSR's attributes */
+ if (ret || !rinfo->attributes || !rinfo->attributes[0].len)
+ goto out;
+
+ for (i = 0; ret == 0 && i < rinfo->attributes[0].len; i++) {
+ Attribute *a = &rinfo->attributes[0].val[i];
+ heim_any *av = NULL;
+
+ /* We only support Extensions request attributes */
+ if (der_heim_oid_cmp(&a->type, &asn1_oid_id_pkcs9_extReq) != 0) {
+ char *oidstr = NULL;
+
+ /*
+ * We need an HX509_TRACE facility for this sort of warning.
+ *
+ * We'd put the warning in the context and then allow the caller to
+ * extract and reset the warning.
+ *
+ * FIXME
+ */
+ der_print_heim_oid(&a->type, '.', &oidstr);
+ warnx("Unknown or unsupported CSR attribute %s",
+ oidstr ? oidstr : "<error decoding OID>");
+ free(oidstr);
+ continue;
+ }
+ if (!a->value.val)
+ continue;
+
+ av = a->value.val;
+ ret = decode_Extensions(av->data, av->length, &exts, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "CSR signature verification failed "
+ "due to invalid extReq attribute");
+ goto out;
+ }
+ }
+ for (i = 0; ret == 0 && i < exts.len; i++) {
+ const char *what = "";
+ Extension *e = &exts.val[i];
+
+ if (der_heim_oid_cmp(&e->extnID,
+ &asn1_oid_id_x509_ce_keyUsage) == 0) {
+ ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length,
+ &(*req)->ku, NULL);
+ what = "keyUsage";
+ /*
+ * Count all KUs as one requested extension to be authorized,
+ * though the caller will have to check the KU values individually.
+ */
+ if (KeyUsage2int((*req)->ku) & ~KeyUsage2int(int2KeyUsage(~0)))
+ (*req)->nunsupported++;
+ } else if (der_heim_oid_cmp(&e->extnID,
+ &asn1_oid_id_x509_ce_extKeyUsage) == 0) {
+ ret = decode_ExtKeyUsage(e->extnValue.data, e->extnValue.length,
+ &(*req)->eku, NULL);
+ what = "extKeyUsage";
+
+ /*
+ * Count each EKU as a separate requested extension to be
+ * authorized.
+ */
+ } else if (der_heim_oid_cmp(&e->extnID,
+ &asn1_oid_id_x509_ce_subjectAltName) == 0) {
+ ret = decode_GeneralNames(e->extnValue.data, e->extnValue.length,
+ &(*req)->san, NULL);
+ what = "subjectAlternativeName";
+
+ /*
+ * Count each SAN as a separate requested extension to be
+ * authorized.
+ */
+ } else {
+ char *oidstr = NULL;
+
+ (*req)->nunsupported++;
+
+ /*
+ * We need an HX509_TRACE facility for this sort of warning.
+ *
+ * We'd put the warning in the context and then allow the caller to
+ * extract and reset the warning.
+ *
+ * FIXME
+ */
+ der_print_heim_oid(&e->extnID, '.', &oidstr);
+ warnx("Unknown or unsupported CSR extension request %s",
+ oidstr ? oidstr : "<error decoding OID>");
+ free(oidstr);
+ }
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "CSR signature verification failed "
+ "due to invalid %s extension", what);
+ break;
+ }
+ }
+
+out:
+ free_CertificationRequest(&r);
+ free_Extensions(&exts);
+ if (ret)
+ hx509_request_free(req);
+ return ret;
+}
+
+/**
+ * Parse an encoded CSR and verify its self-signature.
+ *
+ * @param context An hx509 context.
+ * @param csr The name of a store containing the CSR ("PKCS10:/path/to/file")
+ * @param req Where to put request object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_parse(hx509_context context,
+ const char *csr,
+ hx509_request *req)
+{
+ heim_octet_string d;
+ int ret;
+
+ /* XXX Add support for PEM */
+ if (strncmp(csr, "PKCS10:", 7) != 0) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "CSR location does not start with \"PKCS10:\": %s",
+ csr);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+ ret = rk_undumpdata(csr + 7, &d.data, &d.length);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Could not read %s", csr);
+ return ret;
+ }
+
+ ret = hx509_request_parse_der(context, &d, req);
+ free(d.data);
+ if (ret)
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ " (while parsing CSR from %s)", csr);
+ return ret;
+}
+
+/**
+ * Get some EKU from a CSR. Usable as an iterator.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param idx The index of the EKU (0 for the first) to return
+ * @param out A pointer to a char * variable where the OID will be placed
+ * (caller must free with free())
+ *
+ * @return Zero on success, HX509_NO_ITEM if no such item exists (denoting
+ * iteration end), or an error.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_get_eku(hx509_request req,
+ size_t idx,
+ char **out)
+{
+ *out = NULL;
+ if (idx >= req->eku.len)
+ return HX509_NO_ITEM;
+ return der_print_heim_oid(&req->eku.val[idx], '.', out);
+}
+
+static int
+abitstring_check(abitstring a, size_t n, int idx)
+{
+ size_t bytes;
+
+ if (idx >= n)
+ return EINVAL;
+
+ bytes = (idx + 1) / CHAR_BIT + (((idx + 1) % CHAR_BIT) ? 1 : 0);
+ if (a->feat_bytes < bytes)
+ return 0;
+
+ return !!(a->feats[idx / CHAR_BIT] & (1UL<<(idx % CHAR_BIT)));
+}
+
+/*
+ * Sets and returns 0 if not already set, -1 if already set. Positive return
+ * values are system errors.
+ */
+static int
+abitstring_set(abitstring a, size_t n, int idx)
+{
+ size_t bytes;
+
+ if (idx >= n)
+ return EINVAL;
+
+ bytes = n / CHAR_BIT + ((n % CHAR_BIT) ? 1 : 0);
+ if (a->feat_bytes < bytes) {
+ unsigned char *tmp;
+
+ if ((tmp = realloc(a->feats, bytes)) == NULL)
+ return ENOMEM;
+ memset(tmp + a->feat_bytes, 0, bytes - a->feat_bytes);
+ a->feats = tmp;
+ a->feat_bytes = bytes;
+ }
+
+ if (!(a->feats[idx / CHAR_BIT] & (1UL<<(idx % CHAR_BIT)))) {
+ a->feats[idx / CHAR_BIT] |= 1UL<<(idx % CHAR_BIT);
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * Resets and returns 0 if not already reset, -1 if already reset. Positive
+ * return values are system errors.
+ */
+static int
+abitstring_reset(abitstring a, size_t n, int idx)
+{
+ size_t bytes;
+
+ if (idx >= n)
+ return EINVAL;
+
+ bytes = (idx + 1) / CHAR_BIT + (((idx + 1) % CHAR_BIT) ? 1 : 0);
+ if (a->feat_bytes >= bytes &&
+ (a->feats[idx / CHAR_BIT] & (1UL<<(idx % CHAR_BIT)))) {
+ a->feats[idx / CHAR_BIT] &= ~(1UL<<(idx % CHAR_BIT));
+ return 0;
+ }
+ return -1;
+}
+
+static int
+authorize_feat(hx509_request req, abitstring a, size_t n, int idx)
+{
+ int ret;
+
+ ret = abitstring_set(a, n, idx);
+ switch (ret) {
+ case 0:
+ req->nauthorized++;
+ fallthrough;
+ case -1:
+ return 0;
+ default:
+ return ret;
+ }
+}
+
+static int
+reject_feat(hx509_request req, abitstring a, size_t n, int idx)
+{
+ int ret;
+
+ ret = abitstring_reset(a, n, idx);
+ switch (ret) {
+ case 0:
+ req->nauthorized--;
+ fallthrough;
+ case -1:
+ return 0;
+ default:
+ return ret;
+ }
+}
+
+/**
+ * Filter the requested KeyUsage and mark it authorized.
+ *
+ * @param req The hx509_request object.
+ * @param ku Permitted KeyUsage
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_request_authorize_ku(hx509_request req, KeyUsage ku)
+{
+ (void) hx509_request_set_ku(NULL, req, ku);
+ req->ku = int2KeyUsage(KeyUsage2int(req->ku) & KeyUsage2int(ku));
+ if (KeyUsage2int(ku))
+ req->ku_are_authorized = 1;
+}
+
+/**
+ * Mark a requested EKU as authorized.
+ *
+ * @param req The hx509_request object.
+ * @param idx The index of an EKU that can be fetched with
+ * hx509_request_get_eku()
+ *
+ * @return Zero on success, an error otherwise.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_authorize_eku(hx509_request req, size_t idx)
+{
+ return authorize_feat(req, &req->authorized_EKUs, req->eku.len, idx);
+}
+
+/**
+ * Mark a requested EKU as not authorized.
+ *
+ * @param req The hx509_request object.
+ * @param idx The index of an EKU that can be fetched with
+ * hx509_request_get_eku()
+ *
+ * @return Zero on success, an error otherwise.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_reject_eku(hx509_request req, size_t idx)
+{
+ return reject_feat(req, &req->authorized_EKUs, req->eku.len, idx);
+}
+
+/**
+ * Check if an EKU has been marked authorized.
+ *
+ * @param req The hx509_request object.
+ * @param idx The index of an EKU that can be fetched with
+ * hx509_request_get_eku()
+ *
+ * @return Non-zero if authorized, zero if not.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_eku_authorized_p(hx509_request req, size_t idx)
+{
+ return abitstring_check(&req->authorized_EKUs, req->eku.len, idx);
+}
+
+/**
+ * Mark a requested SAN as authorized.
+ *
+ * @param req The hx509_request object.
+ * @param idx The cursor as modified by a SAN iterator.
+ *
+ * @return Zero on success, an error otherwise.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_authorize_san(hx509_request req, size_t idx)
+{
+ return authorize_feat(req, &req->authorized_SANs, req->san.len, idx);
+}
+
+/**
+ * Mark a requested SAN as not authorized.
+ *
+ * @param req The hx509_request object.
+ * @param idx The cursor as modified by a SAN iterator.
+ *
+ * @return Zero on success, an error otherwise.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_reject_san(hx509_request req, size_t idx)
+{
+ return reject_feat(req, &req->authorized_SANs, req->san.len, idx);
+}
+
+/**
+ * Check if a SAN has been marked authorized.
+ *
+ * @param req The hx509_request object.
+ * @param idx The index of a SAN that can be fetched with
+ * hx509_request_get_san()
+ *
+ * @return Non-zero if authorized, zero if not.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_san_authorized_p(hx509_request req, size_t idx)
+{
+ return abitstring_check(&req->authorized_SANs, req->san.len, idx);
+}
+
+/**
+ * Return the count of unsupported requested certificate extensions.
+ *
+ * @param req The hx509_request object.
+ * @return The number of unsupported certificate extensions requested.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION size_t HX509_LIB_CALL
+hx509_request_count_unsupported(hx509_request req)
+{
+ return req->nunsupported;
+}
+
+/**
+ * Return the count of as-yet unauthorized certificate extensions requested.
+ *
+ * @param req The hx509_request object.
+ * @return The number of as-yet unauthorized certificate extensions requested.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION size_t HX509_LIB_CALL
+hx509_request_count_unauthorized(hx509_request req)
+{
+ size_t nrequested = req->eku.len + req->san.len +
+ (KeyUsage2int(req->ku) ? 1 : 0) + req->nunsupported;
+
+ return nrequested - (req->nauthorized + req->ku_are_authorized);
+}
+
+static hx509_san_type
+san_map_type(GeneralName *san)
+{
+ static const struct {
+ const heim_oid *oid;
+ hx509_san_type type;
+ } map[] = {
+ { &asn1_oid_id_pkix_on_dnsSRV, HX509_SAN_TYPE_DNSSRV },
+ { &asn1_oid_id_pkinit_san, HX509_SAN_TYPE_PKINIT },
+ { &asn1_oid_id_pkix_on_xmppAddr, HX509_SAN_TYPE_XMPP },
+ { &asn1_oid_id_pkinit_ms_san, HX509_SAN_TYPE_MS_UPN },
+ { &asn1_oid_id_pkix_on_permanentIdentifier, HX509_SAN_TYPE_PERMANENT_ID },
+ { &asn1_oid_id_on_hardwareModuleName, HX509_SAN_TYPE_HW_MODULE },
+ };
+ size_t i;
+
+ switch (san->element) {
+ case choice_GeneralName_rfc822Name: return HX509_SAN_TYPE_EMAIL;
+ case choice_GeneralName_dNSName: return HX509_SAN_TYPE_DNSNAME;
+ case choice_GeneralName_directoryName: return HX509_SAN_TYPE_DN;
+ case choice_GeneralName_registeredID: return HX509_SAN_TYPE_REGISTERED_ID;
+ case choice_GeneralName_otherName: {
+ for (i = 0; i < sizeof(map)/sizeof(map[0]); i++)
+ if (der_heim_oid_cmp(&san->u.otherName.type_id, map[i].oid) == 0)
+ return map[i].type;
+ }
+ fallthrough;
+ default: return HX509_SAN_TYPE_UNSUPPORTED;
+ }
+}
+
+/**
+ * Return the count of as-yet unauthorized certificate extensions requested.
+ *
+ * @param req The hx509_request object.
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION size_t HX509_LIB_CALL
+hx509_request_get_san(hx509_request req,
+ size_t idx,
+ hx509_san_type *type,
+ char **out)
+{
+ struct rk_strpool *pool = NULL;
+ GeneralName *san;
+
+ *out = NULL;
+ if (idx >= req->san.len)
+ return HX509_NO_ITEM;
+
+ san = &req->san.val[idx];
+ switch ((*type = san_map_type(san))) {
+ case HX509_SAN_TYPE_UNSUPPORTED: return 0;
+ case HX509_SAN_TYPE_EMAIL:
+ *out = strndup(san->u.rfc822Name.data,
+ san->u.rfc822Name.length);
+ break;
+ case HX509_SAN_TYPE_DNSNAME:
+ *out = strndup(san->u.dNSName.data,
+ san->u.dNSName.length);
+ break;
+ case HX509_SAN_TYPE_DNSSRV: {
+ SRVName name;
+ size_t size;
+ int ret;
+
+ ret = decode_SRVName(san->u.otherName.value.data,
+ san->u.otherName.value.length, &name, &size);
+ if (ret)
+ return ret;
+ *out = strndup(name.data, name.length);
+ break;
+ }
+ case HX509_SAN_TYPE_PERMANENT_ID: {
+ PermanentIdentifier pi;
+ size_t size;
+ char *s = NULL;
+ int ret;
+
+ ret = decode_PermanentIdentifier(san->u.otherName.value.data,
+ san->u.otherName.value.length,
+ &pi, &size);
+ if (ret == 0 && pi.assigner) {
+ ret = der_print_heim_oid(pi.assigner, '.', &s);
+ if (ret == 0 &&
+ (pool = rk_strpoolprintf(NULL, "%s", s)) == NULL)
+ ret = ENOMEM;
+ } else if (ret == 0) {
+ pool = rk_strpoolprintf(NULL, "-");
+ }
+ if (ret == 0 &&
+ (pool = rk_strpoolprintf(pool, "%s%s",
+ *pi.identifierValue ? " " : "",
+ *pi.identifierValue ? *pi.identifierValue : "")) == NULL)
+ ret = ENOMEM;
+ if (ret == 0 && (*out = rk_strpoolcollect(pool)) == NULL)
+ ret = ENOMEM;
+ free_PermanentIdentifier(&pi);
+ free(s);
+ return ret;
+ }
+ case HX509_SAN_TYPE_HW_MODULE: {
+ HardwareModuleName hn;
+ size_t size;
+ char *s = NULL;
+ int ret;
+
+ ret = decode_HardwareModuleName(san->u.otherName.value.data,
+ san->u.otherName.value.length,
+ &hn, &size);
+ if (ret == 0 && hn.hwSerialNum.length > 256)
+ hn.hwSerialNum.length = 256;
+ if (ret == 0)
+ ret = der_print_heim_oid(&hn.hwType, '.', &s);
+ if (ret == 0)
+ pool = rk_strpoolprintf(NULL, "%s", s);
+ if (ret == 0 && pool)
+ pool = rk_strpoolprintf(pool, " %.*s",
+ (int)hn.hwSerialNum.length,
+ (char *)hn.hwSerialNum.data);
+ if (ret == 0 &&
+ (pool == NULL || (*out = rk_strpoolcollect(pool)) == NULL))
+ ret = ENOMEM;
+ free_HardwareModuleName(&hn);
+ return ret;
+ }
+ case HX509_SAN_TYPE_DN: {
+ Name name;
+
+ if (san->u.directoryName.element == choice_Name_rdnSequence) {
+ name.element = choice_Name_rdnSequence;
+ name.u.rdnSequence = san->u.directoryName.u.rdnSequence;
+ return _hx509_Name_to_string(&name, out);
+ }
+ *type = HX509_SAN_TYPE_UNSUPPORTED;
+ return 0;
+ }
+ case HX509_SAN_TYPE_REGISTERED_ID:
+ return der_print_heim_oid(&san->u.registeredID, '.', out);
+ case HX509_SAN_TYPE_XMPP:
+ fallthrough;
+ case HX509_SAN_TYPE_MS_UPN: {
+ int ret;
+
+ ret = _hx509_unparse_utf8_string_name(req->context, &pool,
+ &san->u.otherName.value);
+ if ((*out = rk_strpoolcollect(pool)) == NULL)
+ return hx509_enomem(req->context);
+ return ret;
+ }
+ case HX509_SAN_TYPE_PKINIT: {
+ int ret;
+
+ ret = _hx509_unparse_KRB5PrincipalName(req->context, &pool,
+ &san->u.otherName.value);
+ if ((*out = rk_strpoolcollect(pool)) == NULL)
+ return hx509_enomem(req->context);
+ return ret;
+ }
+ default:
+ *type = HX509_SAN_TYPE_UNSUPPORTED;
+ return 0;
+ }
+ if (*out == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+/**
+ * Display a CSR.
+ *
+ * @param context An hx509 context.
+ * @param req The hx509_request object.
+ * @param f A FILE * to print the CSR to.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_request
+ */
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_request_print(hx509_context context, hx509_request req, FILE *f)
+{
+ uint64_t ku_num;
+ size_t i;
+ char *s = NULL;
+ int ret = 0;
+
+ /*
+ * It's really unformatunate that we can't reuse more of the
+ * lib/hx509/print.c infrastructure here, as it's too focused on
+ * Certificates.
+ *
+ * For that matter, it's really annoying that CSRs don't more resemble
+ * Certificates. Indeed, an ideal CSR would look like this:
+ *
+ * CSRInfo ::= {
+ * desiredTbsCertificate TBSCertificate,
+ * attributes [1] SEQUENCE OF Attribute OPTIONAL,
+ * }
+ * CSR :: = {
+ * csrInfo CSRInfo,
+ * sigAlg AlgorithmIdentifier,
+ * signature BIT STRING
+ * }
+ *
+ * with everything related to the desired certificate in
+ * desiredTbsCertificate and anything not related to the CSR's contents in
+ * the 'attributes' field.
+ *
+ * That wouldn't allow one to have optional desired TBSCertificate
+ * features, but hey. One could express "gimme all or gimme nothing" as an
+ * attribute, or "gimme what you can", then check what one got.
+ */
+ fprintf(f, "PKCS#10 CertificationRequest:\n");
+
+ if (req->name) {
+ char *subject;
+ ret = hx509_name_to_string(req->name, &subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to print name");
+ return ret;
+ }
+ fprintf(f, " name: %s\n", subject);
+ free(subject);
+ }
+ /* XXX Use hx509_request_get_ku() accessor */
+ if ((ku_num = KeyUsage2int(req->ku))) {
+ const struct units *u;
+ const char *first = " ";
+
+ fprintf(f, " key usage:");
+ for (u = asn1_KeyUsage_units(); u->name; ++u) {
+ if ((ku_num & u->mult)) {
+ fprintf(f, "%s%s", first, u->name);
+ first = ", ";
+ ku_num &= ~u->mult;
+ }
+ }
+ if (ku_num)
+ fprintf(f, "%s<unknown-KeyUsage-value(s)>", first);
+ fprintf(f, "\n");
+ }
+ if (req->eku.len) {
+ const char *first = " ";
+
+ fprintf(f, " eku:");
+ for (i = 0; ret == 0; i++) {
+ free(s); s = NULL;
+ ret = hx509_request_get_eku(req, i, &s);
+ if (ret)
+ break;
+ fprintf(f, "%s{%s}", first, s);
+ first = ", ";
+ }
+ fprintf(f, "\n");
+ }
+ free(s); s = NULL;
+ if (ret == HX509_NO_ITEM)
+ ret = 0;
+ for (i = 0; ret == 0; i++) {
+ hx509_san_type san_type;
+
+ free(s); s = NULL;
+ ret = hx509_request_get_san(req, i, &san_type, &s);
+ if (ret)
+ break;
+ switch (san_type) {
+ case HX509_SAN_TYPE_EMAIL:
+ fprintf(f, " san: rfc822Name: %s\n", s);
+ break;
+ case HX509_SAN_TYPE_DNSNAME:
+ fprintf(f, " san: dNSName: %s\n", s);
+ break;
+ case HX509_SAN_TYPE_DN:
+ fprintf(f, " san: dn: %s\n", s);
+ break;
+ case HX509_SAN_TYPE_REGISTERED_ID:
+ fprintf(f, " san: registeredID: %s\n", s);
+ break;
+ case HX509_SAN_TYPE_XMPP:
+ fprintf(f, " san: xmpp: %s\n", s);
+ break;
+ case HX509_SAN_TYPE_PKINIT:
+ fprintf(f, " san: pkinit: %s\n", s);
+ break;
+ case HX509_SAN_TYPE_MS_UPN:
+ fprintf(f, " san: ms-upn: %s\n", s);
+ break;
+ default:
+ fprintf(f, " san: <SAN type not supported>\n");
+ break;
+ }
+ }
+ free(s); s = NULL;
+ if (ret == HX509_NO_ITEM)
+ ret = 0;
+ return ret;
+}
diff --git a/third_party/heimdal/lib/hx509/revoke.c b/third_party/heimdal/lib/hx509/revoke.c
new file mode 100644
index 0000000..18b2f8f
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/revoke.c
@@ -0,0 +1,1650 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * @page page_revoke Revocation methods
+ *
+ * There are two revocation method for PKIX/X.509: CRL and OCSP.
+ * Revocation is needed if the private key is lost and
+ * stolen. Depending on how picky you are, you might want to make
+ * revocation for destroyed private keys too (smartcard broken), but
+ * that should not be a problem.
+ *
+ * CRL is a list of certificates that have expired.
+ *
+ * OCSP is an online checking method where the requestor sends a list
+ * of certificates to the OCSP server to return a signed reply if they
+ * are valid or not. Some services sends a OCSP reply as part of the
+ * hand-shake to make the revoktion decision simpler/faster for the
+ * client.
+ */
+
+#include "hx_locl.h"
+
+struct revoke_crl {
+ char *path;
+ time_t last_modfied;
+ CRLCertificateList crl;
+ int verified;
+ int failed_verify;
+};
+
+struct revoke_ocsp {
+ char *path;
+ time_t last_modfied;
+ OCSPBasicOCSPResponse ocsp;
+ hx509_certs certs;
+ hx509_cert signer;
+};
+
+
+struct hx509_revoke_ctx_data {
+ unsigned int ref;
+ struct {
+ struct revoke_crl *val;
+ size_t len;
+ } crls;
+ struct {
+ struct revoke_ocsp *val;
+ size_t len;
+ } ocsps;
+};
+
+/**
+ * Allocate a revokation context. Free with hx509_revoke_free().
+ *
+ * @param context A hx509 context.
+ * @param ctx returns a newly allocated revokation context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL)
+ return ENOMEM;
+
+ (*ctx)->ref = 1;
+ (*ctx)->crls.len = 0;
+ (*ctx)->crls.val = NULL;
+ (*ctx)->ocsps.len = 0;
+ (*ctx)->ocsps.val = NULL;
+
+ return 0;
+}
+
+HX509_LIB_FUNCTION hx509_revoke_ctx HX509_LIB_CALL
+_hx509_revoke_ref(hx509_revoke_ctx ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+ if (ctx->ref == 0)
+ _hx509_abort("revoke ctx refcount == 0 on ref");
+ ctx->ref++;
+ if (ctx->ref == UINT_MAX)
+ _hx509_abort("revoke ctx refcount == UINT_MAX on ref");
+ return ctx;
+}
+
+static void
+free_ocsp(struct revoke_ocsp *ocsp)
+{
+ free(ocsp->path);
+ free_OCSPBasicOCSPResponse(&ocsp->ocsp);
+ hx509_certs_free(&ocsp->certs);
+ hx509_cert_free(ocsp->signer);
+}
+
+/**
+ * Free a hx509 revokation context.
+ *
+ * @param ctx context to be freed
+ *
+ * @ingroup hx509_revoke
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_revoke_free(hx509_revoke_ctx *ctx)
+{
+ size_t i ;
+
+ if (ctx == NULL || *ctx == NULL)
+ return;
+
+ if ((*ctx)->ref == 0)
+ _hx509_abort("revoke ctx refcount == 0 on free");
+ if (--(*ctx)->ref > 0)
+ return;
+
+ for (i = 0; i < (*ctx)->crls.len; i++) {
+ free((*ctx)->crls.val[i].path);
+ free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
+ }
+
+ for (i = 0; i < (*ctx)->ocsps.len; i++)
+ free_ocsp(&(*ctx)->ocsps.val[i]);
+ free((*ctx)->ocsps.val);
+
+ free((*ctx)->crls.val);
+
+ memset(*ctx, 0, sizeof(**ctx));
+ free(*ctx);
+ *ctx = NULL;
+}
+
+static int
+verify_ocsp(hx509_context context,
+ struct revoke_ocsp *ocsp,
+ time_t time_now,
+ hx509_certs certs,
+ hx509_cert parent)
+{
+ hx509_cert signer = NULL;
+ hx509_query q;
+ int ret;
+
+ _hx509_query_clear(&q);
+
+ /*
+ * Need to match on issuer too in case there are two CA that have
+ * issued the same name to a certificate. One example of this is
+ * the www.openvalidation.org test's ocsp validator.
+ */
+
+ q.match = HX509_QUERY_MATCH_ISSUER_NAME;
+ q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
+
+ switch(ocsp->ocsp.tbsResponseData.responderID.element) {
+ case choice_OCSPResponderID_byName:
+ q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;
+ break;
+ case choice_OCSPResponderID_byKey:
+ q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;
+ q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;
+ break;
+ }
+
+ ret = hx509_certs_find(context, certs, &q, &signer);
+ if (ret && ocsp->certs)
+ ret = hx509_certs_find(context, ocsp->certs, &q, &signer);
+ if (ret == 0 && signer == NULL)
+ ret = HX509_CERT_NOT_FOUND;
+ if (ret)
+ goto out;
+
+ /*
+ * If signer certificate isn't the CA certificate, lets check the
+ * it is the CA that signed the signer certificate and the OCSP EKU
+ * is set.
+ */
+ if (hx509_cert_cmp(signer, parent) != 0) {
+ Certificate *p = _hx509_get_cert(parent);
+ Certificate *s = _hx509_get_cert(signer);
+
+ ret = _hx509_cert_is_parent_cmp(s, p, 0);
+ if (ret != 0) {
+ ret = HX509_PARENT_NOT_CA;
+ hx509_set_error_string(context, 0, ret, "Revoke OCSP signer "
+ "doesn't have CA as signer certificate");
+ goto out;
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ parent,
+ &s->signatureAlgorithm,
+ &s->tbsCertificate._save,
+ &s->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "OCSP signer signature invalid");
+ goto out;
+ }
+
+ ret = hx509_cert_check_eku(context, signer,
+ &asn1_oid_id_pkix_kp_OCSPSigning, 0);
+ if (ret)
+ goto out;
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ signer,
+ &ocsp->ocsp.signatureAlgorithm,
+ &ocsp->ocsp.tbsResponseData._save,
+ &ocsp->ocsp.signature);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "OCSP signature invalid");
+ goto out;
+ }
+
+ ocsp->signer = signer;
+ signer = NULL;
+out:
+ if (signer)
+ hx509_cert_free(signer);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
+{
+ OCSPResponse resp;
+ size_t size;
+ int ret;
+
+ memset(basic, 0, sizeof(*basic));
+
+ ret = decode_OCSPResponse(data, length, &resp, &size);
+ if (ret)
+ return ret;
+ if (length != size) {
+ free_OCSPResponse(&resp);
+ return ASN1_EXTRA_DATA;
+ }
+
+ switch (resp.responseStatus) {
+ case successful:
+ break;
+ default:
+ free_OCSPResponse(&resp);
+ return HX509_REVOKE_WRONG_DATA;
+ }
+
+ if (resp.responseBytes == NULL) {
+ free_OCSPResponse(&resp);
+ return EINVAL;
+ }
+
+ ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
+ &asn1_oid_id_pkix_ocsp_basic);
+ if (ret != 0) {
+ free_OCSPResponse(&resp);
+ return HX509_REVOKE_WRONG_DATA;
+ }
+
+ ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
+ resp.responseBytes->response.length,
+ basic,
+ &size);
+ if (ret) {
+ free_OCSPResponse(&resp);
+ return ret;
+ }
+ if (size != resp.responseBytes->response.length) {
+ free_OCSPResponse(&resp);
+ free_OCSPBasicOCSPResponse(basic);
+ return ASN1_EXTRA_DATA;
+ }
+ free_OCSPResponse(&resp);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
+{
+ OCSPBasicOCSPResponse basic;
+ hx509_certs certs = NULL;
+ size_t length;
+ struct stat sb;
+ void *data;
+ int ret;
+
+ ret = rk_undumpdata(ocsp->path, &data, &length);
+ if (ret)
+ return ret;
+
+ ret = stat(ocsp->path, &sb);
+ if (ret) {
+ rk_xfree(data);
+ return errno;
+ }
+
+ ret = parse_ocsp_basic(data, length, &basic);
+ rk_xfree(data);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to parse OCSP response");
+ return ret;
+ }
+
+ if (basic.certs) {
+ size_t i;
+
+ ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
+ NULL, &certs);
+ if (ret) {
+ free_OCSPBasicOCSPResponse(&basic);
+ return ret;
+ }
+
+ for (i = 0; i < basic.certs->len; i++) {
+ hx509_cert c;
+
+ c = hx509_cert_init(context, &basic.certs->val[i], NULL);
+ if (c == NULL)
+ continue;
+
+ ret = hx509_certs_add(context, certs, c);
+ hx509_cert_free(c);
+ if (ret)
+ continue;
+ }
+ }
+
+ ocsp->last_modfied = sb.st_mtime;
+
+ free_OCSPBasicOCSPResponse(&ocsp->ocsp);
+ hx509_certs_free(&ocsp->certs);
+ hx509_cert_free(ocsp->signer);
+
+ ocsp->ocsp = basic;
+ ocsp->certs = certs;
+ ocsp->signer = NULL;
+
+ return 0;
+}
+
+/**
+ * Add a OCSP file to the revokation context.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param path path to file that is going to be added to the context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_revoke_add_ocsp(hx509_context context,
+ hx509_revoke_ctx ctx,
+ const char *path)
+{
+ void *data;
+ int ret;
+ size_t i;
+
+ if (strncmp(path, "FILE:", 5) != 0) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "unsupport type in %s", path);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+ path += 5;
+
+ for (i = 0; i < ctx->ocsps.len; i++) {
+ if (strcmp(ctx->ocsps.val[0].path, path) == 0)
+ return 0;
+ }
+
+ data = realloc(ctx->ocsps.val,
+ (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
+ if (data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ctx->ocsps.val = data;
+
+ memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
+ sizeof(ctx->ocsps.val[0]));
+
+ ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
+ if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
+ if (ret) {
+ free(ctx->ocsps.val[ctx->ocsps.len].path);
+ return ret;
+ }
+ ctx->ocsps.len++;
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+verify_crl(hx509_context context,
+ hx509_revoke_ctx ctx,
+ CRLCertificateList *crl,
+ time_t time_now,
+ hx509_certs certs,
+ hx509_cert parent)
+{
+ hx509_cert signer;
+ hx509_query q;
+ time_t t;
+ int ret;
+
+ t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
+ if (t > time_now) {
+ hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
+ "CRL used before time");
+ return HX509_CRL_USED_BEFORE_TIME;
+ }
+
+ if (crl->tbsCertList.nextUpdate == NULL) {
+ hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
+ "CRL missing nextUpdate");
+ return HX509_CRL_INVALID_FORMAT;
+ }
+
+ t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
+ if (t < time_now) {
+ hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
+ "CRL used after time");
+ return HX509_CRL_USED_AFTER_TIME;
+ }
+
+ _hx509_query_clear(&q);
+
+ /*
+ * If it's the signer have CRLSIGN bit set, use that as the signer
+ * cert for the certificate, otherwise, search for a certificate.
+ */
+ if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
+ signer = hx509_cert_ref(parent);
+ } else {
+ q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.match |= HX509_QUERY_KU_CRLSIGN;
+ q.subject_name = &crl->tbsCertList.issuer;
+
+ ret = hx509_certs_find(context, certs, &q, &signer);
+ if (ret == 0 && signer == NULL)
+ ret = HX509_CERT_NOT_FOUND;
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to find certificate for CRL");
+ return ret;
+ }
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ signer,
+ &crl->signatureAlgorithm,
+ &crl->tbsCertList._save,
+ &crl->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "CRL signature invalid");
+ goto out;
+ }
+
+ /*
+ * If signer is not CA cert, need to check revoke status of this
+ * CRL signing cert too, this include all parent CRL signer cert
+ * up to the root *sigh*, assume root at least hve CERTSIGN flag
+ * set.
+ */
+ while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
+ hx509_cert crl_parent;
+
+ _hx509_query_clear(&q);
+
+ q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.match |= HX509_QUERY_KU_CRLSIGN;
+ q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
+
+ ret = hx509_certs_find(context, certs, &q, &crl_parent);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to find parent of CRL signer");
+ goto out;
+ }
+
+ ret = hx509_revoke_verify(context,
+ ctx,
+ certs,
+ time_now,
+ signer,
+ crl_parent);
+ hx509_cert_free(signer);
+ signer = crl_parent;
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify revocation "
+ "status of CRL signer");
+ goto out;
+ }
+ }
+
+out:
+ hx509_cert_free(signer);
+
+ return ret;
+}
+
+static int
+crl_parser(hx509_context context, const char *type,
+ const hx509_pem_header *header,
+ const void *data, size_t len, void *ctx)
+{
+ CRLCertificateList *crl = (CRLCertificateList *)ctx;
+ size_t size;
+ int ret;
+
+ if (strcasecmp("X509 CRL", type) != 0)
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+
+ ret = decode_CRLCertificateList(data, len, crl, &size);
+ if (ret)
+ return ret;
+
+ /* check signature is aligned */
+ if (crl->signatureValue.length & 7) {
+ free_CRLCertificateList(crl);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ return 0;
+}
+
+static int
+load_crl(hx509_context context, const char *path, time_t *t, CRLCertificateList *crl)
+{
+ struct stat sb;
+ size_t length;
+ void *data;
+ FILE *f;
+ int ret;
+
+ memset(crl, 0, sizeof(*crl));
+
+ ret = stat(path, &sb);
+ if (ret)
+ return errno;
+
+ *t = sb.st_mtime;
+
+ if ((f = fopen(path, "r")) == NULL)
+ return errno;
+
+ rk_cloexec_file(f);
+
+ ret = hx509_pem_read(context, f, crl_parser, crl);
+ fclose(f);
+
+ if (ret == HX509_PARSING_KEY_FAILED) {
+
+ ret = rk_undumpdata(path, &data, &length);
+ if (ret)
+ return ret;
+
+ ret = crl_parser(context, "X509 CRL", NULL, data, length, crl);
+ rk_xfree(data);
+ }
+ return ret;
+}
+
+/**
+ * Add a CRL file to the revokation context.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param path path to file that is going to be added to the context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_revoke_add_crl(hx509_context context,
+ hx509_revoke_ctx ctx,
+ const char *path)
+{
+ void *data;
+ size_t i;
+ int ret;
+
+ if (strncmp(path, "FILE:", 5) != 0) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "unsupported type in %s", path);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+
+ path += 5;
+
+ for (i = 0; i < ctx->crls.len; i++) {
+ if (strcmp(ctx->crls.val[i].path, path) == 0)
+ return 0;
+ }
+
+ data = realloc(ctx->crls.val,
+ (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
+ if (data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ ctx->crls.val = data;
+
+ memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
+
+ ctx->crls.val[ctx->crls.len].path = strdup(path);
+ if (ctx->crls.val[ctx->crls.len].path == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = load_crl(context,
+ path,
+ &ctx->crls.val[ctx->crls.len].last_modfied,
+ &ctx->crls.val[ctx->crls.len].crl);
+ if (ret) {
+ free(ctx->crls.val[ctx->crls.len].path);
+ return ret;
+ }
+
+ ctx->crls.len++;
+
+ return ret;
+}
+
+/**
+ * Check that a certificate is not expired according to a revokation
+ * context. Also need the parent certificte to the check OCSP
+ * parent identifier.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param certs
+ * @param now
+ * @param cert
+ * @param parent_cert
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_revoke_verify(hx509_context context,
+ hx509_revoke_ctx ctx,
+ hx509_certs certs,
+ time_t now,
+ hx509_cert cert,
+ hx509_cert parent_cert)
+{
+ const Certificate *c = _hx509_get_cert(cert);
+ const Certificate *p = _hx509_get_cert(parent_cert);
+ unsigned long i, j, k;
+ int ret;
+
+ hx509_clear_error_string(context);
+
+ for (i = 0; i < ctx->ocsps.len; i++) {
+ struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
+ struct stat sb;
+
+ /* check this ocsp apply to this cert */
+
+ /* check if there is a newer version of the file */
+ ret = stat(ocsp->path, &sb);
+ if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
+ ret = load_ocsp(context, ocsp);
+ if (ret)
+ continue;
+ }
+
+ /* verify signature in ocsp if not already done */
+ if (ocsp->signer == NULL) {
+ ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
+ if (ret)
+ continue;
+ }
+
+ for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
+ heim_octet_string os;
+
+ ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ /* verify issuer hashes hash */
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
+ if (ret != 0)
+ continue;
+
+ os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
+ &os,
+ &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
+ if (ret != 0)
+ continue;
+
+ switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ break;
+ case choice_OCSPCertStatus_revoked:
+ hx509_set_error_string(context, 0,
+ HX509_CERT_REVOKED,
+ "Certificate revoked by issuer in OCSP");
+ return HX509_CERT_REVOKED;
+ case choice_OCSPCertStatus_unknown:
+ continue;
+ }
+
+ /* don't allow the update to be in the future */
+ if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
+ now + context->ocsp_time_diff)
+ continue;
+
+ /* don't allow the next update to be in the past */
+ if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
+ if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
+ continue;
+ } /* else should force a refetch, but can we ? */
+
+ return 0;
+ }
+ }
+
+ for (i = 0; i < ctx->crls.len; i++) {
+ struct revoke_crl *crl = &ctx->crls.val[i];
+ struct stat sb;
+ int diff;
+
+ /* check if cert.issuer == crls.val[i].crl.issuer */
+ ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
+ &crl->crl.tbsCertList.issuer, &diff);
+ if (ret || diff)
+ continue;
+
+ ret = stat(crl->path, &sb);
+ if (ret == 0 && crl->last_modfied != sb.st_mtime) {
+ CRLCertificateList cl;
+
+ ret = load_crl(context, crl->path, &crl->last_modfied, &cl);
+ if (ret == 0) {
+ free_CRLCertificateList(&crl->crl);
+ crl->crl = cl;
+ crl->verified = 0;
+ crl->failed_verify = 0;
+ }
+ }
+ if (crl->failed_verify)
+ continue;
+
+ /* verify signature in crl if not already done */
+ if (crl->verified == 0) {
+ ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
+ if (ret) {
+ crl->failed_verify = 1;
+ continue;
+ }
+ crl->verified = 1;
+ }
+
+ if (crl->crl.tbsCertList.crlExtensions) {
+ for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
+ if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
+ hx509_set_error_string(context, 0,
+ HX509_CRL_UNKNOWN_EXTENSION,
+ "Unknown CRL extension");
+ return HX509_CRL_UNKNOWN_EXTENSION;
+ }
+ }
+ }
+
+ if (crl->crl.tbsCertList.revokedCertificates == NULL)
+ return 0;
+
+ /* check if cert is in crl */
+ for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
+ time_t t;
+
+ ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
+ if (t > now)
+ continue;
+
+ if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
+ for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
+ if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
+ return HX509_CRL_UNKNOWN_EXTENSION;
+
+ hx509_set_error_string(context, 0,
+ HX509_CERT_REVOKED,
+ "Certificate revoked by issuer in CRL");
+ return HX509_CERT_REVOKED;
+ }
+
+ return 0;
+ }
+
+
+ if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
+ return 0;
+ hx509_set_error_string(context, HX509_ERROR_APPEND,
+ HX509_REVOKE_STATUS_MISSING,
+ "No revocation status found for certificates");
+ return HX509_REVOKE_STATUS_MISSING;
+}
+
+struct ocsp_add_ctx {
+ OCSPTBSRequest *req;
+ hx509_certs certs;
+ const AlgorithmIdentifier *digest;
+ hx509_cert parent;
+};
+
+static int HX509_LIB_CALL
+add_to_req(hx509_context context, void *ptr, hx509_cert cert)
+{
+ struct ocsp_add_ctx *ctx = ptr;
+ OCSPInnerRequest *one;
+ hx509_cert parent = NULL;
+ Certificate *p, *c = _hx509_get_cert(cert);
+ heim_octet_string os;
+ int ret;
+ hx509_query q;
+ void *d;
+
+ d = realloc(ctx->req->requestList.val,
+ sizeof(ctx->req->requestList.val[0]) *
+ (ctx->req->requestList.len + 1));
+ if (d == NULL)
+ return ENOMEM;
+ ctx->req->requestList.val = d;
+
+ one = &ctx->req->requestList.val[ctx->req->requestList.len];
+ memset(one, 0, sizeof(*one));
+
+ _hx509_query_clear(&q);
+
+ q.match |= HX509_QUERY_FIND_ISSUER_CERT;
+ q.subject = c;
+
+ ret = hx509_certs_find(context, ctx->certs, &q, &parent);
+ if (ret)
+ goto out;
+
+ if (ctx->parent) {
+ if (hx509_cert_cmp(ctx->parent, parent) != 0) {
+ ret = HX509_REVOKE_NOT_SAME_PARENT;
+ hx509_set_error_string(context, 0, ret,
+ "Not same parent certifate as "
+ "last certificate in request");
+ goto out;
+ }
+ } else
+ ctx->parent = hx509_cert_ref(parent);
+
+ p = _hx509_get_cert(parent);
+
+ ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
+ if (ret)
+ goto out;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &one->reqCert.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ NULL,
+ &one->reqCert.issuerNameHash);
+ if (ret)
+ goto out;
+
+ os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length =
+ p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &one->reqCert.hashAlgorithm,
+ &os,
+ NULL,
+ &one->reqCert.issuerKeyHash);
+ if (ret)
+ goto out;
+
+ ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
+ &one->reqCert.serialNumber);
+ if (ret)
+ goto out;
+
+ ctx->req->requestList.len++;
+out:
+ hx509_cert_free(parent);
+ if (ret) {
+ free_OCSPInnerRequest(one);
+ memset(one, 0, sizeof(*one));
+ }
+
+ return ret;
+}
+
+/**
+ * Create an OCSP request for a set of certificates.
+ *
+ * @param context a hx509 context
+ * @param reqcerts list of certificates to request ocsp data for
+ * @param pool certificate pool to use when signing
+ * @param signer certificate to use to sign the request
+ * @param digest the signing algorithm in the request, if NULL use the
+ * default signature algorithm,
+ * @param request the encoded request, free with free_heim_octet_string().
+ * @param nonce nonce in the request, free with free_heim_octet_string().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ocsp_request(hx509_context context,
+ hx509_certs reqcerts,
+ hx509_certs pool,
+ hx509_cert signer,
+ const AlgorithmIdentifier *digest,
+ heim_octet_string *request,
+ heim_octet_string *nonce)
+{
+ OCSPRequest req;
+ size_t size;
+ int ret;
+ struct ocsp_add_ctx ctx;
+ Extensions *es;
+
+ memset(&req, 0, sizeof(req));
+
+ if (digest == NULL)
+ digest = _hx509_crypto_default_digest_alg;
+
+ ctx.req = &req.tbsRequest;
+ ctx.certs = pool;
+ ctx.digest = digest;
+ ctx.parent = NULL;
+
+ ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);
+ hx509_cert_free(ctx.parent);
+ if (ret)
+ goto out;
+
+ if (nonce) {
+ req.tbsRequest.requestExtensions =
+ calloc(1, sizeof(*req.tbsRequest.requestExtensions));
+ if (req.tbsRequest.requestExtensions == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ es = req.tbsRequest.requestExtensions;
+
+ es->val = calloc(es->len, sizeof(es->val[0]));
+ if (es->val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ es->len = 1;
+ ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID);
+ if (ret) {
+ free_OCSPRequest(&req);
+ return ret;
+ }
+
+ es->val[0].extnValue.data = malloc(10);
+ if (es->val[0].extnValue.data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ es->val[0].extnValue.length = 10;
+
+ ret = RAND_bytes(es->val[0].extnValue.data,
+ es->val[0].extnValue.length);
+ if (ret != 1) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+ ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
+ if (ret) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
+ &req, &size, ret);
+ free_OCSPRequest(&req);
+ if (ret)
+ goto out;
+ if (size != request->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return 0;
+
+out:
+ free_OCSPRequest(&req);
+ return ret;
+}
+
+static char *
+printable_time(time_t t)
+{
+ static char s[128];
+ char *p;
+ if ((p = ctime(&t)) == NULL)
+ strlcpy(s, "?", sizeof(s));
+ else {
+ strlcpy(s, p + 4, sizeof(s));
+ s[20] = 0;
+ }
+ return s;
+}
+
+/*
+ *
+ */
+
+static int
+print_ocsp(hx509_context context, struct revoke_ocsp *ocsp, FILE *out)
+{
+ int ret = 0;
+ size_t i;
+
+ fprintf(out, "signer: ");
+
+ switch(ocsp->ocsp.tbsResponseData.responderID.element) {
+ case choice_OCSPResponderID_byName: {
+ hx509_name n;
+ char *s;
+ _hx509_name_from_Name(&ocsp->ocsp.tbsResponseData.responderID.u.byName, &n);
+ hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ fprintf(out, " byName: %s\n", s);
+ free(s);
+ break;
+ }
+ case choice_OCSPResponderID_byKey: {
+ char *s;
+ hex_encode(ocsp->ocsp.tbsResponseData.responderID.u.byKey.data,
+ ocsp->ocsp.tbsResponseData.responderID.u.byKey.length,
+ &s);
+ fprintf(out, " byKey: %s\n", s);
+ free(s);
+ break;
+ }
+ default:
+ _hx509_abort("choice_OCSPResponderID unknown");
+ break;
+ }
+
+ fprintf(out, "producedAt: %s\n",
+ printable_time(ocsp->ocsp.tbsResponseData.producedAt));
+
+ fprintf(out, "replies: %d\n", ocsp->ocsp.tbsResponseData.responses.len);
+
+ for (i = 0; i < ocsp->ocsp.tbsResponseData.responses.len; i++) {
+ const char *status;
+ switch (ocsp->ocsp.tbsResponseData.responses.val[i].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ status = "good";
+ break;
+ case choice_OCSPCertStatus_revoked:
+ status = "revoked";
+ break;
+ case choice_OCSPCertStatus_unknown:
+ status = "unknown";
+ break;
+ default:
+ status = "element unknown";
+ }
+
+ fprintf(out, "\t%llu. status: %s\n", (unsigned long long)i, status);
+
+ fprintf(out, "\tthisUpdate: %s\n",
+ printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
+ if (ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate)
+ fprintf(out, "\tproducedAt: %s\n",
+ printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
+
+ }
+
+ fprintf(out, "appended certs:\n");
+ if (ocsp->certs)
+ ret = hx509_certs_iter_f(context, ocsp->certs, hx509_ci_print_names, out);
+
+ return ret;
+}
+
+static int
+print_crl(hx509_context context, struct revoke_crl *crl, FILE *out)
+{
+ {
+ hx509_name n;
+ char *s;
+ _hx509_name_from_Name(&crl->crl.tbsCertList.issuer, &n);
+ hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ fprintf(out, " issuer: %s\n", s);
+ free(s);
+ }
+
+ fprintf(out, " thisUpdate: %s\n",
+ printable_time(_hx509_Time2time_t(&crl->crl.tbsCertList.thisUpdate)));
+
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_revoke_print(hx509_context context,
+ hx509_revoke_ctx ctx,
+ FILE *out)
+{
+ int saved_ret = 0, ret;
+ size_t n;
+
+ for (n = 0; n < ctx->ocsps.len; n++) {
+ struct revoke_ocsp *ocsp = &ctx->ocsps.val[n];
+
+ fprintf(out, "OCSP %s\n", ocsp->path);
+
+ ret = print_ocsp(context, ocsp, out);
+ if (ret) {
+ fprintf(out, "failure printing OCSP: %d\n", ret);
+ saved_ret = ret;
+ }
+ }
+
+ for (n = 0; n < ctx->crls.len; n++) {
+ struct revoke_crl *crl = &ctx->crls.val[n];
+
+ fprintf(out, "CRL %s\n", crl->path);
+
+ ret = print_crl(context, crl, out);
+ if (ret) {
+ fprintf(out, "failure printing CRL: %d\n", ret);
+ saved_ret = ret;
+ }
+ }
+ return saved_ret;
+
+}
+
+/**
+ * Print the OCSP reply stored in a file.
+ *
+ * @param context a hx509 context
+ * @param path path to a file with a OCSP reply
+ * @param out the out FILE descriptor to print the reply on
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
+{
+ struct revoke_ocsp ocsp;
+ int ret;
+
+ if (out == NULL)
+ out = stdout;
+
+ memset(&ocsp, 0, sizeof(ocsp));
+
+ ocsp.path = strdup(path);
+ if (ocsp.path == NULL)
+ return ENOMEM;
+
+ ret = load_ocsp(context, &ocsp);
+ if (ret) {
+ free_ocsp(&ocsp);
+ return ret;
+ }
+
+ ret = print_ocsp(context, &ocsp, out);
+
+ free_ocsp(&ocsp);
+ return ret;
+}
+
+/**
+ * Verify that the certificate is part of the OCSP reply and it's not
+ * expired. Doesn't verify signature the OCSP reply or it's done by a
+ * authorized sender, that is assumed to be already done.
+ *
+ * @param context a hx509 context
+ * @param now the time right now, if 0, use the current time.
+ * @param cert the certificate to verify
+ * @param flags flags control the behavior
+ * @param data pointer to the encode ocsp reply
+ * @param length the length of the encode ocsp reply
+ * @param expiration return the time the OCSP will expire and need to
+ * be rechecked.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_ocsp_verify(hx509_context context,
+ time_t now,
+ hx509_cert cert,
+ int flags,
+ const void *data, size_t length,
+ time_t *expiration)
+{
+ const Certificate *c = _hx509_get_cert(cert);
+ OCSPBasicOCSPResponse basic;
+ int ret;
+ size_t i;
+
+ if (now == 0)
+ now = time(NULL);
+
+ *expiration = 0;
+
+ ret = parse_ocsp_basic(data, length, &basic);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to parse OCSP response");
+ return ret;
+ }
+
+ for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
+
+ ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ /* verify issuer hashes hash */
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
+ if (ret != 0)
+ continue;
+
+ switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ break;
+ case choice_OCSPCertStatus_revoked:
+ case choice_OCSPCertStatus_unknown:
+ continue;
+ }
+
+ /* don't allow the update to be in the future */
+ if (basic.tbsResponseData.responses.val[i].thisUpdate >
+ now + context->ocsp_time_diff)
+ continue;
+
+ /* don't allow the next update to be in the past */
+ if (basic.tbsResponseData.responses.val[i].nextUpdate) {
+ if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
+ continue;
+ *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
+ } else
+ *expiration = now;
+
+ free_OCSPBasicOCSPResponse(&basic);
+ return 0;
+ }
+
+ free_OCSPBasicOCSPResponse(&basic);
+
+ {
+ hx509_name name;
+ char *subject;
+
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ ret = hx509_name_to_string(name, &subject);
+ hx509_name_free(&name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
+ "Certificate %s not in OCSP response "
+ "or not good",
+ subject);
+ free(subject);
+ }
+out:
+ return HX509_CERT_NOT_IN_OCSP;
+}
+
+struct hx509_crl {
+ hx509_certs revoked;
+ time_t expire;
+};
+
+/**
+ * Create a CRL context. Use hx509_crl_free() to free the CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl return pointer to a newly allocated CRL context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crl_alloc(hx509_context context, hx509_crl *crl)
+{
+ int ret;
+
+ *crl = calloc(1, sizeof(**crl));
+ if (*crl == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
+ if (ret) {
+ free(*crl);
+ *crl = NULL;
+ return ret;
+ }
+ (*crl)->expire = 0;
+ return ret;
+}
+
+/**
+ * Add revoked certificate to an CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl the CRL to add the revoked certificate to.
+ * @param certs keyset of certificate to revoke.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crl_add_revoked_certs(hx509_context context,
+ hx509_crl crl,
+ hx509_certs certs)
+{
+ return hx509_certs_merge(context, crl->revoked, certs);
+}
+
+/**
+ * Set the lifetime of a CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl a CRL context
+ * @param delta delta time the certificate is valid, library adds the
+ * current time to this.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
+{
+ crl->expire = time(NULL) + delta;
+ return 0;
+}
+
+/**
+ * Free a CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl a CRL context to free.
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+hx509_crl_free(hx509_context context, hx509_crl *crl)
+{
+ if (*crl == NULL)
+ return;
+ hx509_certs_free(&(*crl)->revoked);
+ memset(*crl, 0, sizeof(**crl));
+ free(*crl);
+ *crl = NULL;
+}
+
+static int HX509_LIB_CALL
+add_revoked(hx509_context context, void *ctx, hx509_cert cert)
+{
+ TBSCRLCertList *c = ctx;
+ unsigned int num;
+ void *ptr;
+ int ret;
+
+ num = c->revokedCertificates->len;
+ ptr = realloc(c->revokedCertificates->val,
+ (num + 1) * sizeof(c->revokedCertificates->val[0]));
+ if (ptr == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ c->revokedCertificates->val = ptr;
+
+ ret = hx509_cert_get_serialnumber(cert,
+ &c->revokedCertificates->val[num].userCertificate);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ c->revokedCertificates->val[num].revocationDate.element =
+ choice_Time_generalTime;
+ c->revokedCertificates->val[num].revocationDate.u.generalTime =
+ time(NULL) - 3600 * 24;
+ c->revokedCertificates->val[num].crlEntryExtensions = NULL;
+
+ c->revokedCertificates->len++;
+
+ return 0;
+}
+
+/**
+ * Sign a CRL and return an encode certificate.
+ *
+ * @param context a hx509 context.
+ * @param signer certificate to sign the CRL with
+ * @param crl the CRL to sign
+ * @param os return the signed and encoded CRL, free with
+ * free_heim_octet_string()
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+hx509_crl_sign(hx509_context context,
+ hx509_cert signer,
+ hx509_crl crl,
+ heim_octet_string *os)
+{
+ const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
+ CRLCertificateList c;
+ size_t size;
+ int ret;
+ hx509_private_key signerkey;
+
+ memset(&c, 0, sizeof(c));
+
+ signerkey = _hx509_cert_private_key(signer);
+ if (signerkey == NULL) {
+ ret = HX509_PRIVATE_KEY_MISSING;
+ hx509_set_error_string(context, 0, ret,
+ "Private key missing for CRL signing");
+ return ret;
+ }
+
+ c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
+ if (c.tbsCertList.version == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ *c.tbsCertList.version = 1;
+
+ ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
+ &c.tbsCertList.issuer);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
+ c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
+
+ c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
+ if (c.tbsCertList.nextUpdate == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ {
+ time_t next = crl->expire;
+ if (next == 0)
+ next = time(NULL) + 24 * 3600 * 365;
+
+ c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
+ c.tbsCertList.nextUpdate->u.generalTime = next;
+ }
+
+ c.tbsCertList.revokedCertificates =
+ calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
+ if (c.tbsCertList.revokedCertificates == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ c.tbsCertList.crlExtensions = NULL;
+
+ ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList);
+ if (ret)
+ goto out;
+
+ /* if not revoked certs, remove OPTIONAL entry */
+ if (c.tbsCertList.revokedCertificates->len == 0) {
+ free(c.tbsCertList.revokedCertificates);
+ c.tbsCertList.revokedCertificates = NULL;
+ }
+
+ ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
+ &c.tbsCertList, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
+ goto out;
+ }
+ if (size != os->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+
+ ret = _hx509_create_signature_bitstring(context,
+ signerkey,
+ sigalg,
+ os,
+ &c.signatureAlgorithm,
+ &c.signatureValue);
+ free(os->data);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to sign CRL");
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
+ &c, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "failed to encode CRL");
+ goto out;
+ }
+ if (size != os->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ free_CRLCertificateList(&c);
+
+ return 0;
+
+out:
+ free_CRLCertificateList(&c);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/hx509/sel-gram.y b/third_party/heimdal/lib/hx509/sel-gram.y
new file mode 100644
index 0000000..09f641d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/sel-gram.y
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2017 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+%{
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <hx_locl.h>
+
+#if !defined(yylex)
+#define yylex _hx509_sel_yylex
+#define yywrap _hx509_sel_yywrap
+#endif
+#if !defined(yyparse)
+#define yyparse _hx509_sel_yyparse
+#define yyerror _hx509_sel_yyerror
+#define yylval _hx509_sel_yylval
+#define yychar _hx509_sel_yychar
+#define yydebug _hx509_sel_yydebug
+#define yynerrs _hx509_sel_yynerrs
+#endif
+
+%}
+
+%union {
+ char *string;
+ struct hx_expr *expr;
+}
+
+%token kw_TRUE
+%token kw_FALSE
+%token kw_AND
+%token kw_OR
+%token kw_IN
+%token kw_TAILMATCH
+
+%type <expr> expr
+%type <expr> comp
+%type <expr> word words
+%type <expr> number
+%type <expr> string
+%type <expr> function
+%type <expr> variable variables
+
+%token <string> NUMBER
+%token <string> STRING
+%token <string> IDENTIFIER
+
+%left '!'
+%left kw_AND
+%left kw_OR
+
+%start start
+
+%%
+
+start: expr { _hx509_expr_input.expr = $1; }
+
+expr : kw_TRUE { $$ = _hx509_make_expr(op_TRUE, NULL, NULL); }
+ | kw_FALSE { $$ = _hx509_make_expr(op_FALSE, NULL, NULL); }
+ | '!' expr { $$ = _hx509_make_expr(op_NOT, $2, NULL); }
+ | expr kw_AND expr { $$ = _hx509_make_expr(op_AND, $1, $3); }
+ | expr kw_OR expr { $$ = _hx509_make_expr(op_OR, $1, $3); }
+ | '(' expr ')' { $$ = $2; }
+ | comp { $$ = _hx509_make_expr(op_COMP, $1, NULL); }
+ ;
+
+words : word { $$ = _hx509_make_expr(expr_WORDS, $1, NULL); }
+ | word ',' words { $$ = _hx509_make_expr(expr_WORDS, $1, $3); }
+ ;
+
+comp : word '=' '=' word { $$ = _hx509_make_expr(comp_EQ, $1, $4); }
+ | word '!' '=' word { $$ = _hx509_make_expr(comp_NE, $1, $4); }
+ | word kw_TAILMATCH word { $$ = _hx509_make_expr(comp_TAILEQ, $1, $3); }
+ | word kw_IN '(' words ')' { $$ = _hx509_make_expr(comp_IN, $1, $4); }
+ | word kw_IN variable { $$ = _hx509_make_expr(comp_IN, $1, $3); }
+ ;
+
+word : number { $$ = $1; }
+ | string { $$ = $1; }
+ | function { $$ = $1; }
+ | variable { $$ = $1; }
+ ;
+
+number : NUMBER { $$ = _hx509_make_expr(expr_NUMBER, $1, NULL); };
+string : STRING { $$ = _hx509_make_expr(expr_STRING, $1, NULL); };
+
+function: IDENTIFIER '(' words ')' {
+ $$ = _hx509_make_expr(expr_FUNCTION, $1, $3); }
+ ;
+variable: '%' '{' variables '}' { $$ = $3; }
+ ;
+
+variables: IDENTIFIER '.' variables {
+ $$ = _hx509_make_expr(expr_VAR, $1, $3); }
+ | IDENTIFIER {
+ $$ = _hx509_make_expr(expr_VAR, $1, NULL); }
+ ;
diff --git a/third_party/heimdal/lib/hx509/sel-lex.l b/third_party/heimdal/lib/hx509/sel-lex.l
new file mode 100644
index 0000000..f401e40
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/sel-lex.l
@@ -0,0 +1,148 @@
+%{
+/*
+ * Copyright (c) 2004 - 2017 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#undef ECHO
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "sel.h"
+#include "sel-gram.h"
+unsigned lineno = 1;
+
+static char * handle_string(void);
+static int lex_input(char *, int);
+
+struct hx_expr_input _hx509_expr_input;
+
+#ifndef YY_NULL
+#define YY_NULL 0
+#endif
+
+#define YY_NO_UNPUT 1
+
+#undef YY_INPUT
+#define YY_INPUT(buf,res,maxsize) (res = lex_input(buf, maxsize))
+
+#undef ECHO
+
+%}
+%%
+
+TRUE { return kw_TRUE; }
+FALSE { return kw_FALSE; }
+AND { return kw_AND; }
+OR { return kw_OR; }
+IN { return kw_IN; }
+TAILMATCH { return kw_TAILMATCH; }
+
+[A-Za-z][-A-Za-z0-9_]* {
+ yylval.string = strdup ((const char *)yytext);
+ return IDENTIFIER;
+ }
+"\"" { yylval.string = handle_string(); return STRING; }
+\n { ++lineno; }
+[,.!={}()%] { return *yytext; }
+[ \t] ;
+%%
+
+static char *
+handle_string(void)
+{
+ char x[1024];
+ int i = 0;
+ int c;
+ int quote = 0;
+ while((c = input()) != EOF){
+ if(quote) {
+ x[i++] = '\\';
+ x[i++] = c;
+ quote = 0;
+ continue;
+ }
+ if(c == '\n'){
+ _hx509_sel_yyerror("unterminated string");
+ lineno++;
+ break;
+ }
+ if(c == '\\'){
+ quote++;
+ continue;
+ }
+ if(c == '\"')
+ break;
+ x[i++] = c;
+ }
+ x[i] = '\0';
+ return strdup(x);
+}
+
+#if !defined(yywrap)
+#define yywrap _hx509_sel_yywrap
+#endif
+
+int
+yywrap ()
+{
+ return 1;
+}
+
+static int
+lex_input(char *buf, int max_size)
+{
+ int n;
+
+ n = _hx509_expr_input.length - _hx509_expr_input.offset;
+ if (max_size < n)
+ n = max_size;
+ if (n <= 0)
+ return YY_NULL;
+
+ memcpy(buf, _hx509_expr_input.buf + _hx509_expr_input.offset, n);
+ _hx509_expr_input.offset += n;
+
+ return n;
+}
diff --git a/third_party/heimdal/lib/hx509/sel.c b/third_party/heimdal/lib/hx509/sel.c
new file mode 100644
index 0000000..bfd55e9
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/sel.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+HX509_LIB_FUNCTION struct hx_expr * HX509_LIB_CALL
+_hx509_make_expr(enum hx_expr_op op, void *arg1, void *arg2)
+{
+ struct hx_expr *expr;
+
+ expr = malloc(sizeof(*expr));
+ if (expr == NULL)
+ return NULL;
+ expr->op = op;
+ expr->arg1 = arg1;
+ expr->arg2 = arg2;
+
+ return expr;
+}
+
+static const char *
+eval_word(hx509_context context, hx509_env env, struct hx_expr *word)
+{
+ switch (word->op) {
+ case expr_STRING:
+ return word->arg1;
+ case expr_VAR:
+ if (word->arg2 == NULL)
+ return hx509_env_find(context, env, word->arg1);
+
+ env = hx509_env_find_binding(context, env, word->arg1);
+ if (env == NULL)
+ return NULL;
+
+ return eval_word(context, env, word->arg2);
+ default:
+ return NULL;
+ }
+}
+
+static hx509_env
+find_variable(hx509_context context, hx509_env env, struct hx_expr *word)
+{
+ assert(word->op == expr_VAR);
+
+ if (word->arg2 == NULL)
+ return hx509_env_find_binding(context, env, word->arg1);
+
+ env = hx509_env_find_binding(context, env, word->arg1);
+ if (env == NULL)
+ return NULL;
+ return find_variable(context, env, word->arg2);
+}
+
+static int
+eval_comp(hx509_context context, hx509_env env, struct hx_expr *expr)
+{
+ switch (expr->op) {
+ case comp_NE:
+ case comp_EQ:
+ case comp_TAILEQ: {
+ const char *s1, *s2;
+ int ret;
+
+ s1 = eval_word(context, env, expr->arg1);
+ s2 = eval_word(context, env, expr->arg2);
+
+ if (s1 == NULL || s2 == NULL)
+ return FALSE;
+
+ if (expr->op == comp_TAILEQ) {
+ size_t len1 = strlen(s1);
+ size_t len2 = strlen(s2);
+
+ if (len1 < len2)
+ return 0;
+ ret = strcmp(s1 + (len1 - len2), s2) == 0;
+ } else {
+ ret = strcmp(s1, s2) == 0;
+ if (expr->op == comp_NE)
+ ret = !ret;
+ }
+ return ret;
+ }
+ case comp_IN: {
+ struct hx_expr *subexpr;
+ const char *w, *s1;
+
+ w = eval_word(context, env, expr->arg1);
+
+ subexpr = expr->arg2;
+
+ if (subexpr->op == expr_WORDS) {
+ while (subexpr) {
+ s1 = eval_word(context, env, subexpr->arg1);
+ if (strcmp(w, s1) == 0)
+ return TRUE;
+ subexpr = subexpr->arg2;
+ }
+ } else if (subexpr->op == expr_VAR) {
+ hx509_env subenv;
+
+ subenv = find_variable(context, env, subexpr);
+ if (subenv == NULL)
+ return FALSE;
+
+ while (subenv) {
+ if (subenv->type != env_string)
+ continue;
+ if (strcmp(w, subenv->name) == 0)
+ return TRUE;
+ if (strcmp(w, subenv->u.string) == 0)
+ return TRUE;
+ subenv = subenv->next;
+ }
+
+ } else
+ _hx509_abort("hx509 eval IN unknown op: %d", (int)subexpr->op);
+
+ return FALSE;
+ }
+ default:
+ _hx509_abort("hx509 eval expr with unknown op: %d", (int)expr->op);
+ }
+ return FALSE;
+}
+
+HX509_LIB_FUNCTION int HX509_LIB_CALL
+_hx509_expr_eval(hx509_context context, hx509_env env, struct hx_expr *expr)
+{
+ switch (expr->op) {
+ case op_TRUE:
+ return 1;
+ case op_FALSE:
+ return 0;
+ case op_NOT:
+ return ! _hx509_expr_eval(context, env, expr->arg1);
+ case op_AND:
+ return _hx509_expr_eval(context, env, expr->arg1) &&
+ _hx509_expr_eval(context, env, expr->arg2);
+ case op_OR:
+ return _hx509_expr_eval(context, env, expr->arg1) ||
+ _hx509_expr_eval(context, env, expr->arg2);
+ case op_COMP:
+ return eval_comp(context, env, expr->arg1);
+ default:
+ _hx509_abort("hx509 eval expr with unknown op: %d", (int)expr->op);
+ UNREACHABLE(return 0);
+ }
+}
+
+HX509_LIB_FUNCTION void HX509_LIB_CALL
+_hx509_expr_free(struct hx_expr *expr)
+{
+ switch (expr->op) {
+ case expr_STRING:
+ case expr_NUMBER:
+ free(expr->arg1);
+ break;
+ case expr_WORDS:
+ case expr_FUNCTION:
+ case expr_VAR:
+ free(expr->arg1);
+ if (expr->arg2)
+ _hx509_expr_free(expr->arg2);
+ break;
+ default:
+ if (expr->arg1)
+ _hx509_expr_free(expr->arg1);
+ if (expr->arg2)
+ _hx509_expr_free(expr->arg2);
+ break;
+ }
+ free(expr);
+}
+
+/* XXX Horrible, no good cause not thread-safe */
+HX509_LIB_FUNCTION struct hx_expr * HX509_LIB_CALL
+_hx509_expr_parse(const char *buf)
+{
+ _hx509_expr_input.buf = buf;
+ _hx509_expr_input.length = strlen(buf);
+ _hx509_expr_input.offset = 0;
+ _hx509_expr_input.expr = NULL;
+
+ if (_hx509_expr_input.error) {
+ free(_hx509_expr_input.error);
+ _hx509_expr_input.error = NULL;
+ }
+
+ yyparse();
+
+ return _hx509_expr_input.expr;
+}
+
+const char *
+_hx509_expr_parse_error(void)
+{
+ return _hx509_expr_input.error;
+}
+
+void
+_hx509_sel_yyerror (const char *s)
+{
+ if (_hx509_expr_input.error)
+ free(_hx509_expr_input.error);
+
+ _hx509_expr_input.error = strdup(s);
+}
+
diff --git a/third_party/heimdal/lib/hx509/sel.h b/third_party/heimdal/lib/hx509/sel.h
new file mode 100644
index 0000000..daa471e
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/sel.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+enum hx_expr_op {
+ op_TRUE,
+ op_FALSE,
+ op_NOT,
+ op_AND,
+ op_OR,
+ op_COMP,
+
+ comp_EQ,
+ comp_NE,
+ comp_IN,
+ comp_TAILEQ,
+
+ expr_NUMBER,
+ expr_STRING,
+ expr_FUNCTION,
+ expr_VAR,
+ expr_WORDS
+};
+
+struct hx_expr {
+ enum hx_expr_op op;
+ void *arg1;
+ void *arg2;
+};
+
+struct hx_expr_input {
+ const char *buf;
+ size_t length;
+ size_t offset;
+ struct hx_expr *expr;
+ char *error;
+};
+
+extern struct hx_expr_input _hx509_expr_input;
+
+/*
+ * With bison/flex, the more modern way to allow multiple yacc/lex grammars to
+ * be linked into a single executable is to use the
+ *
+ * bison: -p, --name-prefix=,PREFIX/, -Dapi.prefix=PREFIX
+ * flex: -Pprefix, --prefix=STRING
+ *
+ * options, these take care of renaming all the machine-generated global entry
+ * points, some of which are new. When these options are used "yylex",
+ * "yyparse", ... are already defined and our (potentially incomplete) attempt
+ * to do the same conflicts with the "right" new way to handle this. The below
+ * logic gets us out of the way when the job has already been taken care of by
+ * the parser-generator.
+ */
+#if !defined(yylex)
+#define yylex _hx509_sel_yylex
+#define yywrap _hx509_sel_yywrap
+#endif
+#if !defined(yyparse)
+#define yyparse _hx509_sel_yyparse
+#define yyerror _hx509_sel_yyerror
+#define yylval _hx509_sel_yylval
+#define yychar _hx509_sel_yychar
+#define yydebug _hx509_sel_yydebug
+#define yynerrs _hx509_sel_yynerrs
+#endif
+
+int _hx509_sel_yyparse(void);
+int _hx509_sel_yylex(void);
+void _hx509_sel_yyerror(const char *);
+
diff --git a/third_party/heimdal/lib/hx509/softp11.c b/third_party/heimdal/lib/hx509/softp11.c
new file mode 100644
index 0000000..75f6755
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/softp11.c
@@ -0,0 +1,1777 @@
+/*
+ * Copyright (c) 2004 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define CRYPTOKI_EXPORTS 1
+
+#include "hx_locl.h"
+#include "ref/pkcs11.h"
+
+#define OBJECT_ID_MASK 0xfff
+#define HANDLE_OBJECT_ID(h) ((h) & OBJECT_ID_MASK)
+#define OBJECT_ID(obj) HANDLE_OBJECT_ID((obj)->object_handle)
+
+#ifndef HAVE_RANDOM
+#define random() rand()
+#define srandom(s) srand(s)
+#endif
+
+#ifdef _WIN32
+#include <shlobj.h>
+#endif
+
+struct st_attr {
+ CK_ATTRIBUTE attribute;
+ int secret;
+};
+
+struct st_object {
+ CK_OBJECT_HANDLE object_handle;
+ struct st_attr *attrs;
+ int num_attributes;
+ hx509_cert cert;
+};
+
+static struct soft_token {
+ CK_VOID_PTR application;
+ CK_NOTIFY notify;
+ char *config_file;
+ hx509_certs certs;
+ struct {
+ struct st_object **objs;
+ int num_objs;
+ } object;
+ struct {
+ int hardware_slot;
+ int app_error_fatal;
+ int login_done;
+ } flags;
+ int open_sessions;
+ struct session_state {
+ CK_SESSION_HANDLE session_handle;
+
+ struct {
+ CK_ATTRIBUTE *attributes;
+ CK_ULONG num_attributes;
+ int next_object;
+ } find;
+
+ int sign_object;
+ CK_MECHANISM_PTR sign_mechanism;
+ int verify_object;
+ CK_MECHANISM_PTR verify_mechanism;
+ } state[10];
+#define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0]))
+ FILE *logfile;
+} soft_token;
+
+static hx509_context context;
+
+static void
+application_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ if (soft_token.flags.app_error_fatal)
+ abort();
+}
+
+static void
+st_logf(const char *fmt, ...)
+{
+ va_list ap;
+ if (soft_token.logfile == NULL)
+ return;
+ va_start(ap, fmt);
+ vfprintf(soft_token.logfile, fmt, ap);
+ va_end(ap);
+ fflush(soft_token.logfile);
+}
+
+static CK_RV
+init_context(void)
+{
+ if (context == NULL) {
+ int ret = hx509_context_init(&context);
+ if (ret)
+ return CKR_GENERAL_ERROR;
+ }
+ return CKR_OK;
+}
+
+#define INIT_CONTEXT() { CK_RV icret = init_context(); if (icret) return icret; }
+
+static void
+snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...)
+{
+ int len;
+ va_list ap;
+ va_start(ap, fmt);
+ len = vsnprintf(str, size, fmt, ap);
+ va_end(ap);
+ if (len < 0 || (size_t)len > size)
+ return;
+ while ((size_t)len < size)
+ str[len++] = fillchar;
+}
+
+#ifndef TEST_APP
+#define printf error_use_st_logf
+#endif
+
+#define VERIFY_SESSION_HANDLE(s, state) \
+{ \
+ CK_RV xret; \
+ xret = verify_session_handle(s, state); \
+ if (xret != CKR_OK) { \
+ /* return CKR_OK */; \
+ } \
+}
+
+static CK_RV
+verify_session_handle(CK_SESSION_HANDLE hSession,
+ struct session_state **state)
+{
+ size_t i;
+
+ for (i = 0; i < MAX_NUM_SESSION; i++){
+ if (soft_token.state[i].session_handle == hSession)
+ break;
+ }
+ if (i == MAX_NUM_SESSION) {
+ application_error("use of invalid handle: 0x%08lx\n",
+ (unsigned long)hSession);
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ if (state)
+ *state = &soft_token.state[i];
+ return CKR_OK;
+}
+
+static CK_RV
+object_handle_to_object(CK_OBJECT_HANDLE handle,
+ struct st_object **object)
+{
+ int i = HANDLE_OBJECT_ID(handle);
+
+ *object = NULL;
+ if (i >= soft_token.object.num_objs)
+ return CKR_ARGUMENTS_BAD;
+ if (soft_token.object.objs[i] == NULL)
+ return CKR_ARGUMENTS_BAD;
+ if (soft_token.object.objs[i]->object_handle != handle)
+ return CKR_ARGUMENTS_BAD;
+ *object = soft_token.object.objs[i];
+ return CKR_OK;
+}
+
+static int
+attributes_match(const struct st_object *obj,
+ const CK_ATTRIBUTE *attributes,
+ CK_ULONG num_attributes)
+{
+ CK_ULONG i;
+ int j;
+
+ st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj));
+
+ for (i = 0; i < num_attributes; i++) {
+ int match = 0;
+ for (j = 0; j < obj->num_attributes; j++) {
+ if (attributes[i].type == obj->attrs[j].attribute.type &&
+ attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen &&
+ memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue,
+ attributes[i].ulValueLen) == 0) {
+ match = 1;
+ break;
+ }
+ }
+ if (match == 0) {
+ st_logf("type %d attribute have no match\n", attributes[i].type);
+ return 0;
+ }
+ }
+ st_logf("attribute matches\n");
+ return 1;
+}
+
+static void
+print_attributes(const CK_ATTRIBUTE *attributes,
+ CK_ULONG num_attributes)
+{
+ CK_ULONG i;
+
+ st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes);
+
+ for (i = 0; i < num_attributes; i++) {
+ st_logf(" type: ");
+ switch (attributes[i].type) {
+ case CKA_TOKEN: {
+ CK_BBOOL *ck_true;
+ if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) {
+ application_error("token attribute wrong length\n");
+ break;
+ }
+ ck_true = attributes[i].pValue;
+ st_logf("token: %s", *ck_true ? "TRUE" : "FALSE");
+ break;
+ }
+ case CKA_CLASS: {
+ CK_OBJECT_CLASS *class;
+ if (attributes[i].ulValueLen != sizeof(CK_ULONG)) {
+ application_error("class attribute wrong length\n");
+ break;
+ }
+ class = attributes[i].pValue;
+ st_logf("class ");
+ switch (*class) {
+ case CKO_CERTIFICATE:
+ st_logf("certificate");
+ break;
+ case CKO_PUBLIC_KEY:
+ st_logf("public key");
+ break;
+ case CKO_PRIVATE_KEY:
+ st_logf("private key");
+ break;
+ case CKO_SECRET_KEY:
+ st_logf("secret key");
+ break;
+ case CKO_DOMAIN_PARAMETERS:
+ st_logf("domain parameters");
+ break;
+ default:
+ st_logf("[class %lx]", (long unsigned)*class);
+ break;
+ }
+ break;
+ }
+ case CKA_PRIVATE:
+ st_logf("private");
+ break;
+ case CKA_LABEL:
+ st_logf("label");
+ break;
+ case CKA_APPLICATION:
+ st_logf("application");
+ break;
+ case CKA_VALUE:
+ st_logf("value");
+ break;
+ case CKA_ID:
+ st_logf("id");
+ break;
+ default:
+ st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type);
+ break;
+ }
+ st_logf("\n");
+ }
+}
+
+static struct st_object *
+add_st_object(void)
+{
+ struct st_object *o, **objs;
+ int i;
+
+ o = calloc(1, sizeof(*o));
+ if (o == NULL)
+ return NULL;
+
+ for (i = 0; i < soft_token.object.num_objs; i++) {
+ if (soft_token.object.objs[i] == NULL) {
+ soft_token.object.objs[i] = o;
+ break;
+ }
+ }
+ if (i == soft_token.object.num_objs) {
+ objs = realloc(soft_token.object.objs,
+ (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0]));
+ if (objs == NULL) {
+ free(o);
+ return NULL;
+ }
+ soft_token.object.objs = objs;
+ soft_token.object.objs[soft_token.object.num_objs++] = o;
+ }
+ soft_token.object.objs[i]->object_handle =
+ (random() & (~OBJECT_ID_MASK)) | i;
+
+ return o;
+}
+
+static CK_RV
+add_object_attribute(struct st_object *o,
+ int secret,
+ CK_ATTRIBUTE_TYPE type,
+ CK_VOID_PTR pValue,
+ CK_ULONG ulValueLen)
+{
+ struct st_attr *a;
+ int i;
+
+ if (pValue == NULL && ulValueLen)
+ return CKR_ARGUMENTS_BAD;
+
+ i = o->num_attributes;
+ a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0]));
+ if (a == NULL)
+ return CKR_DEVICE_MEMORY;
+ o->attrs = a;
+ o->attrs[i].secret = secret;
+ o->attrs[i].attribute.type = type;
+ o->attrs[i].attribute.pValue = malloc(ulValueLen);
+ if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0)
+ return CKR_DEVICE_MEMORY;
+ if (ulValueLen)
+ memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen);
+ o->attrs[i].attribute.ulValueLen = ulValueLen;
+ o->num_attributes++;
+
+ return CKR_OK;
+}
+
+static CK_RV
+add_pubkey_info(hx509_context hxctx, struct st_object *o,
+ CK_KEY_TYPE key_type, hx509_cert cert)
+{
+ BIGNUM *num;
+ CK_BYTE *modulus = NULL;
+ size_t modulus_len = 0;
+ CK_ULONG modulus_bits = 0;
+ CK_BYTE *exponent = NULL;
+ size_t exponent_len = 0;
+
+ if (key_type != CKK_RSA)
+ return CKR_OK;
+ if (_hx509_cert_private_key(cert) == NULL)
+ return CKR_OK;
+
+ num = _hx509_private_key_get_internal(context,
+ _hx509_cert_private_key(cert),
+ "rsa-modulus");
+ if (num == NULL)
+ return CKR_GENERAL_ERROR;
+ modulus_bits = BN_num_bits(num);
+
+ modulus_len = BN_num_bytes(num);
+ modulus = malloc(modulus_len);
+ BN_bn2bin(num, modulus);
+ BN_free(num);
+
+ add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len);
+ add_object_attribute(o, 0, CKA_MODULUS_BITS,
+ &modulus_bits, sizeof(modulus_bits));
+
+ free(modulus);
+
+ num = _hx509_private_key_get_internal(context,
+ _hx509_cert_private_key(cert),
+ "rsa-exponent");
+ if (num == NULL)
+ return CKR_GENERAL_ERROR;
+
+ exponent_len = BN_num_bytes(num);
+ exponent = malloc(exponent_len);
+ BN_bn2bin(num, exponent);
+ BN_free(num);
+
+ add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT,
+ exponent, exponent_len);
+
+ free(exponent);
+
+ return CKR_OK;
+}
+
+
+struct foo {
+ char *label;
+ char *id;
+};
+
+static int HX509_LIB_CALL
+add_cert(hx509_context hxctx, void *ctx, hx509_cert cert)
+{
+ static char empty[] = "";
+ struct foo *foo = (struct foo *)ctx;
+ struct st_object *o = NULL;
+ CK_OBJECT_CLASS type;
+ CK_BBOOL bool_true = CK_TRUE;
+ CK_BBOOL bool_false = CK_FALSE;
+ CK_CERTIFICATE_TYPE cert_type = CKC_X_509;
+ CK_KEY_TYPE key_type;
+ CK_MECHANISM_TYPE mech_type;
+ CK_RV ret = CKR_GENERAL_ERROR;
+ int hret;
+ heim_octet_string cert_data, subject_data, issuer_data, serial_data;
+
+ st_logf("adding certificate\n");
+
+ serial_data.data = NULL;
+ serial_data.length = 0;
+ cert_data = subject_data = issuer_data = serial_data;
+
+ hret = hx509_cert_binary(hxctx, cert, &cert_data);
+ if (hret)
+ goto out;
+
+ {
+ hx509_name name;
+
+ hret = hx509_cert_get_issuer(cert, &name);
+ if (hret)
+ goto out;
+ hret = hx509_name_binary(name, &issuer_data);
+ hx509_name_free(&name);
+ if (hret)
+ goto out;
+
+ hret = hx509_cert_get_subject(cert, &name);
+ if (hret)
+ goto out;
+ hret = hx509_name_binary(name, &subject_data);
+ hx509_name_free(&name);
+ if (hret)
+ goto out;
+ }
+
+ {
+ AlgorithmIdentifier alg;
+
+ hret = hx509_cert_get_SPKI_AlgorithmIdentifier(context, cert, &alg);
+ if (hret) {
+ ret = CKR_DEVICE_MEMORY;
+ goto out;
+ }
+
+ key_type = CKK_RSA; /* XXX */
+
+ free_AlgorithmIdentifier(&alg);
+ }
+
+
+ type = CKO_CERTIFICATE;
+ o = add_st_object();
+ if (o == NULL) {
+ ret = CKR_DEVICE_MEMORY;
+ goto out;
+ }
+
+ o->cert = hx509_cert_ref(cert);
+
+ add_object_attribute(o, 0, CKA_CLASS, &type, sizeof(type));
+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label));
+
+ add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type));
+ add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id));
+
+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length);
+ add_object_attribute(o, 0, CKA_ISSUER, issuer_data.data, issuer_data.length);
+ add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data.data, serial_data.length);
+ add_object_attribute(o, 0, CKA_VALUE, cert_data.data, cert_data.length);
+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false));
+
+ st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o));
+
+ type = CKO_PUBLIC_KEY;
+ o = add_st_object();
+ if (o == NULL) {
+ ret = CKR_DEVICE_MEMORY;
+ goto out;
+ }
+ o->cert = hx509_cert_ref(cert);
+
+ add_object_attribute(o, 0, CKA_CLASS, &type, sizeof(type));
+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label));
+
+ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type));
+ add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id));
+ add_object_attribute(o, 0, CKA_START_DATE, empty, 1); /* XXX */
+ add_object_attribute(o, 0, CKA_END_DATE, empty, 1); /* XXX */
+ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false));
+ mech_type = CKM_RSA_X_509;
+ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type));
+
+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length);
+ add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true));
+
+ add_pubkey_info(hxctx, o, key_type, cert);
+
+ st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o));
+
+ if (hx509_cert_have_private_key(cert)) {
+ CK_FLAGS flags;
+
+ type = CKO_PRIVATE_KEY;
+
+ /* Note to static analyzers: `o' is still referred to via globals */
+ o = add_st_object();
+ if (o == NULL) {
+ ret = CKR_DEVICE_MEMORY;
+ goto out;
+ }
+ o->cert = hx509_cert_ref(cert);
+
+ add_object_attribute(o, 0, CKA_CLASS, &type, sizeof(type));
+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label));
+
+ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type));
+ add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id));
+ add_object_attribute(o, 0, CKA_START_DATE, empty, 1); /* XXX */
+ add_object_attribute(o, 0, CKA_END_DATE, empty, 1); /* XXX */
+ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false));
+ mech_type = CKM_RSA_X_509;
+ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type));
+
+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length);
+ add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true));
+ flags = 0;
+ add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags));
+
+ add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false));
+ add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true));
+ add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false));
+
+ add_pubkey_info(hxctx, o, key_type, cert);
+ }
+
+ ret = CKR_OK;
+ out:
+ if (ret != CKR_OK) {
+ st_logf("something went wrong when adding cert!\n");
+
+ /* XXX wack o */;
+ }
+ hx509_xfree(cert_data.data);
+ hx509_xfree(serial_data.data);
+ hx509_xfree(issuer_data.data);
+ hx509_xfree(subject_data.data);
+
+ /* Note to static analyzers: `o' is still referred to via globals */
+ return 0;
+}
+
+static CK_RV
+add_certificate(const char *cert_file,
+ const char *pin,
+ char *id,
+ char *label)
+{
+ hx509_certs certs;
+ hx509_lock lock = NULL;
+ int ret, flags = 0;
+
+ struct foo foo;
+ foo.id = id;
+ foo.label = label;
+
+ if (pin == NULL)
+ flags |= HX509_CERTS_UNPROTECT_ALL;
+
+ if (pin) {
+ char *str;
+ ret = asprintf(&str, "PASS:%s", pin);
+ if (ret == -1 || !str) {
+ st_logf("failed to allocate memory\n");
+ return CKR_GENERAL_ERROR;
+ }
+
+ hx509_lock_init(context, &lock);
+ hx509_lock_command_string(lock, str);
+
+ memset(str, 0, strlen(str));
+ free(str);
+ }
+
+ ret = hx509_certs_init(context, cert_file, flags, lock, &certs);
+ if (ret) {
+ st_logf("failed to open file %s\n", cert_file);
+ return CKR_GENERAL_ERROR;
+ }
+
+ ret = hx509_certs_iter_f(context, certs, add_cert, &foo);
+ hx509_certs_free(&certs);
+ if (ret) {
+ st_logf("failed adding certs from file %s\n", cert_file);
+ return CKR_GENERAL_ERROR;
+ }
+
+ return CKR_OK;
+}
+
+static void
+find_object_final(struct session_state *state)
+{
+ if (state->find.attributes) {
+ CK_ULONG i;
+
+ for (i = 0; i < state->find.num_attributes; i++) {
+ if (state->find.attributes[i].pValue)
+ free(state->find.attributes[i].pValue);
+ }
+ free(state->find.attributes);
+ state->find.attributes = NULL;
+ state->find.num_attributes = 0;
+ state->find.next_object = -1;
+ }
+}
+
+static void
+reset_crypto_state(struct session_state *state)
+{
+ state->sign_object = -1;
+ if (state->sign_mechanism)
+ free(state->sign_mechanism);
+ state->sign_mechanism = NULL_PTR;
+ state->verify_object = -1;
+ if (state->verify_mechanism)
+ free(state->verify_mechanism);
+ state->verify_mechanism = NULL_PTR;
+}
+
+static void
+close_session(struct session_state *state)
+{
+ if (state->find.attributes) {
+ application_error("application didn't do C_FindObjectsFinal\n");
+ find_object_final(state);
+ }
+
+ state->session_handle = CK_INVALID_HANDLE;
+ soft_token.application = NULL_PTR;
+ soft_token.notify = NULL_PTR;
+ reset_crypto_state(state);
+}
+
+static const char *
+has_session(void)
+{
+ return soft_token.open_sessions > 0 ? "yes" : "no";
+}
+
+static CK_RV
+read_conf_file(const char *fn, CK_USER_TYPE userType, const char *pin)
+{
+ char buf[1024], *type, *s, *p;
+ FILE *f;
+ CK_RV ret = CKR_OK;
+ CK_RV failed = CKR_OK;
+
+ if (fn == NULL) {
+ st_logf("Can't open configuration file. No file specified\n");
+ return CKR_GENERAL_ERROR;
+ }
+
+ f = fopen(fn, "r");
+ if (f == NULL) {
+ st_logf("can't open configuration file %s\n", fn);
+ return CKR_GENERAL_ERROR;
+ }
+ rk_cloexec_file(f);
+
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ buf[strcspn(buf, "\n")] = '\0';
+
+ st_logf("line: %s\n", buf);
+
+ p = buf;
+ while (isspace((unsigned char)*p))
+ p++;
+ if (*p == '#')
+ continue;
+ while (isspace((unsigned char)*p))
+ p++;
+
+ s = NULL;
+ type = strtok_r(p, "\t", &s);
+ if (type == NULL)
+ continue;
+
+ if (strcasecmp("certificate", type) == 0) {
+ char *cert, *id, *label;
+
+ id = strtok_r(NULL, "\t", &s);
+ if (id == NULL) {
+ st_logf("no id\n");
+ continue;
+ }
+ st_logf("id: %s\n", id);
+ label = strtok_r(NULL, "\t", &s);
+ if (label == NULL) {
+ st_logf("no label\n");
+ continue;
+ }
+ cert = strtok_r(NULL, "\t", &s);
+ if (cert == NULL) {
+ st_logf("no certfiicate store\n");
+ continue;
+ }
+
+ st_logf("adding: %s: %s in file %s\n", id, label, cert);
+
+ ret = add_certificate(cert, pin, id, label);
+ if (ret)
+ failed = ret;
+ } else if (strcasecmp("debug", type) == 0) {
+ char *name;
+
+ name = strtok_r(NULL, "\t", &s);
+ if (name == NULL) {
+ st_logf("no filename\n");
+ continue;
+ }
+
+ if (soft_token.logfile)
+ fclose(soft_token.logfile);
+
+ if (strcasecmp(name, "stdout") == 0)
+ soft_token.logfile = stdout;
+ else {
+ soft_token.logfile = fopen(name, "a");
+ if (soft_token.logfile)
+ rk_cloexec_file(soft_token.logfile);
+ }
+ if (soft_token.logfile == NULL)
+ st_logf("failed to open file: %s\n", name);
+
+ } else if (strcasecmp("app-fatal", type) == 0) {
+ char *name;
+
+ name = strtok_r(NULL, "\t", &s);
+ if (name == NULL) {
+ st_logf("argument to app-fatal\n");
+ continue;
+ }
+
+ if (strcmp(name, "true") == 0 || strcmp(name, "on") == 0)
+ soft_token.flags.app_error_fatal = 1;
+ else if (strcmp(name, "false") == 0 || strcmp(name, "off") == 0)
+ soft_token.flags.app_error_fatal = 0;
+ else
+ st_logf("unknown app-fatal: %s\n", name);
+
+ } else {
+ st_logf("unknown type: %s\n", type);
+ }
+ }
+
+ fclose(f);
+
+ return failed;
+}
+
+static CK_RV
+func_not_supported(void)
+{
+ st_logf("function not supported\n");
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static char *
+get_config_file_for_user(void)
+{
+ char *fn;
+ int ret;
+
+ fn = secure_getenv("SOFTPKCS11RC");
+ if (fn)
+ fn = strdup(fn);
+ if (fn == NULL) {
+ char homebuf[MAX_PATH];
+ const char *home = roken_get_appdatadir(homebuf, sizeof(homebuf));
+
+ if (home) {
+ ret = asprintf(&fn, "%s/.soft-token.rc", home);
+ if (ret == -1)
+ fn = NULL;
+ } else {
+#ifndef WIN32
+ fn = strdup("/etc/soft-token.rc");
+#endif
+ }
+ }
+
+ return fn;
+}
+
+
+CK_RV CK_SPEC
+C_Initialize(CK_VOID_PTR a)
+{
+ CK_C_INITIALIZE_ARGS_PTR args = a;
+ CK_RV ret;
+ size_t i;
+
+ st_logf("Initialize\n");
+
+ INIT_CONTEXT();
+
+ OpenSSL_add_all_algorithms();
+
+ srandom(getpid() ^ (int) time(NULL));
+
+ for (i = 0; i < MAX_NUM_SESSION; i++) {
+ soft_token.state[i].session_handle = CK_INVALID_HANDLE;
+ soft_token.state[i].find.attributes = NULL;
+ soft_token.state[i].find.num_attributes = 0;
+ soft_token.state[i].find.next_object = -1;
+ reset_crypto_state(&soft_token.state[i]);
+ }
+
+ soft_token.flags.hardware_slot = 1;
+ soft_token.flags.app_error_fatal = 0;
+ soft_token.flags.login_done = 0;
+
+ soft_token.object.objs = NULL;
+ soft_token.object.num_objs = 0;
+
+ soft_token.logfile = NULL;
+#if 0
+ soft_token.logfile = stdout;
+#endif
+#if 0
+ soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a");
+#endif
+
+ if (a != NULL_PTR) {
+ st_logf("\tCreateMutex:\t%p\n", args->CreateMutex);
+ st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex);
+ st_logf("\tLockMutext\t%p\n", args->LockMutex);
+ st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex);
+ st_logf("\tFlags\t%04x\n", (unsigned int)args->flags);
+ }
+
+ soft_token.config_file = get_config_file_for_user();
+
+ /*
+ * This operations doesn't return CKR_OK if any of the
+ * certificates failes to be unparsed (ie password protected).
+ */
+ ret = read_conf_file(soft_token.config_file, CKU_USER, NULL);
+ if (ret == CKR_OK)
+ soft_token.flags.login_done = 1;
+
+ return CKR_OK;
+}
+
+CK_RV
+C_Finalize(CK_VOID_PTR args)
+{
+ size_t i;
+
+ INIT_CONTEXT();
+
+ st_logf("Finalize\n");
+
+ for (i = 0; i < MAX_NUM_SESSION; i++) {
+ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) {
+ application_error("application finalized without "
+ "closing session\n");
+ close_session(&soft_token.state[i]);
+ }
+ }
+
+ return CKR_OK;
+}
+
+CK_RV
+C_GetInfo(CK_INFO_PTR args)
+{
+ INIT_CONTEXT();
+
+ st_logf("GetInfo\n");
+
+ memset(args, 17, sizeof(*args));
+ args->cryptokiVersion.major = 2;
+ args->cryptokiVersion.minor = 10;
+ snprintf_fill((char *)args->manufacturerID,
+ sizeof(args->manufacturerID),
+ ' ',
+ "Heimdal hx509 SoftToken");
+ snprintf_fill((char *)args->libraryDescription,
+ sizeof(args->libraryDescription), ' ',
+ "Heimdal hx509 SoftToken");
+ args->libraryVersion.major = 2;
+ args->libraryVersion.minor = 0;
+
+ return CKR_OK;
+}
+
+extern CK_FUNCTION_LIST funcs;
+
+CK_RV
+C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
+{
+ INIT_CONTEXT();
+
+ *ppFunctionList = &funcs;
+ return CKR_OK;
+}
+
+CK_RV
+C_GetSlotList(CK_BBOOL tokenPresent,
+ CK_SLOT_ID_PTR pSlotList,
+ CK_ULONG_PTR pulCount)
+{
+ INIT_CONTEXT();
+ st_logf("GetSlotList: %s\n",
+ tokenPresent ? "tokenPresent" : "token not Present");
+ if (pSlotList)
+ pSlotList[0] = 1;
+ *pulCount = 1;
+ return CKR_OK;
+}
+
+CK_RV
+C_GetSlotInfo(CK_SLOT_ID slotID,
+ CK_SLOT_INFO_PTR pInfo)
+{
+ INIT_CONTEXT();
+ st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session());
+
+ memset(pInfo, 18, sizeof(*pInfo));
+
+ if (slotID != 1)
+ return CKR_ARGUMENTS_BAD;
+
+ snprintf_fill((char *)pInfo->slotDescription,
+ sizeof(pInfo->slotDescription),
+ ' ',
+ "Heimdal hx509 SoftToken (slot)");
+ snprintf_fill((char *)pInfo->manufacturerID,
+ sizeof(pInfo->manufacturerID),
+ ' ',
+ "Heimdal hx509 SoftToken (slot)");
+ pInfo->flags = CKF_TOKEN_PRESENT;
+ if (soft_token.flags.hardware_slot)
+ pInfo->flags |= CKF_HW_SLOT;
+ pInfo->hardwareVersion.major = 1;
+ pInfo->hardwareVersion.minor = 0;
+ pInfo->firmwareVersion.major = 1;
+ pInfo->firmwareVersion.minor = 0;
+
+ return CKR_OK;
+}
+
+CK_RV
+C_GetTokenInfo(CK_SLOT_ID slotID,
+ CK_TOKEN_INFO_PTR pInfo)
+{
+ INIT_CONTEXT();
+ st_logf("GetTokenInfo: %s\n", has_session());
+
+ memset(pInfo, 19, sizeof(*pInfo));
+
+ snprintf_fill((char *)pInfo->label,
+ sizeof(pInfo->label),
+ ' ',
+ "Heimdal hx509 SoftToken (token)");
+ snprintf_fill((char *)pInfo->manufacturerID,
+ sizeof(pInfo->manufacturerID),
+ ' ',
+ "Heimdal hx509 SoftToken (token)");
+ snprintf_fill((char *)pInfo->model,
+ sizeof(pInfo->model),
+ ' ',
+ "Heimdal hx509 SoftToken (token)");
+ snprintf_fill((char *)pInfo->serialNumber,
+ sizeof(pInfo->serialNumber),
+ ' ',
+ "4711");
+ pInfo->flags =
+ CKF_TOKEN_INITIALIZED |
+ CKF_USER_PIN_INITIALIZED;
+
+ if (soft_token.flags.login_done == 0)
+ pInfo->flags |= CKF_LOGIN_REQUIRED;
+
+ /* CFK_RNG |
+ CKF_RESTORE_KEY_NOT_NEEDED |
+ */
+ pInfo->ulMaxSessionCount = MAX_NUM_SESSION;
+ pInfo->ulSessionCount = soft_token.open_sessions;
+ pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION;
+ pInfo->ulRwSessionCount = soft_token.open_sessions;
+ pInfo->ulMaxPinLen = 1024;
+ pInfo->ulMinPinLen = 0;
+ pInfo->ulTotalPublicMemory = 4711;
+ pInfo->ulFreePublicMemory = 4712;
+ pInfo->ulTotalPrivateMemory = 4713;
+ pInfo->ulFreePrivateMemory = 4714;
+ pInfo->hardwareVersion.major = 2;
+ pInfo->hardwareVersion.minor = 0;
+ pInfo->firmwareVersion.major = 2;
+ pInfo->firmwareVersion.minor = 0;
+
+ return CKR_OK;
+}
+
+CK_RV
+C_GetMechanismList(CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE_PTR pMechanismList,
+ CK_ULONG_PTR pulCount)
+{
+ INIT_CONTEXT();
+ st_logf("GetMechanismList\n");
+
+ *pulCount = 1;
+ if (pMechanismList == NULL_PTR)
+ return CKR_OK;
+ pMechanismList[0] = CKM_RSA_PKCS;
+
+ return CKR_OK;
+}
+
+CK_RV
+C_GetMechanismInfo(CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE type,
+ CK_MECHANISM_INFO_PTR pInfo)
+{
+ INIT_CONTEXT();
+ st_logf("GetMechanismInfo: slot %d type: %d\n",
+ (int)slotID, (int)type);
+ memset(pInfo, 0, sizeof(*pInfo));
+
+ return CKR_OK;
+}
+
+CK_RV
+C_InitToken(CK_SLOT_ID slotID,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen,
+ CK_UTF8CHAR_PTR pLabel)
+{
+ INIT_CONTEXT();
+ st_logf("InitToken: slot %d\n", (int)slotID);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_OpenSession(CK_SLOT_ID slotID,
+ CK_FLAGS flags,
+ CK_VOID_PTR pApplication,
+ CK_NOTIFY Notify,
+ CK_SESSION_HANDLE_PTR phSession)
+{
+ size_t i;
+ INIT_CONTEXT();
+ st_logf("OpenSession: slot: %d\n", (int)slotID);
+
+ if (soft_token.open_sessions == MAX_NUM_SESSION)
+ return CKR_SESSION_COUNT;
+
+ soft_token.application = pApplication;
+ soft_token.notify = Notify;
+
+ for (i = 0; i < MAX_NUM_SESSION; i++)
+ if (soft_token.state[i].session_handle == CK_INVALID_HANDLE)
+ break;
+ if (i == MAX_NUM_SESSION)
+ abort();
+
+ soft_token.open_sessions++;
+
+ soft_token.state[i].session_handle =
+ (CK_SESSION_HANDLE)(random() & 0xfffff);
+ *phSession = soft_token.state[i].session_handle;
+
+ return CKR_OK;
+}
+
+CK_RV
+C_CloseSession(CK_SESSION_HANDLE hSession)
+{
+ struct session_state *state;
+ INIT_CONTEXT();
+ st_logf("CloseSession\n");
+
+ if (verify_session_handle(hSession, &state) != CKR_OK)
+ application_error("closed session not open");
+ else
+ close_session(state);
+
+ return CKR_OK;
+}
+
+CK_RV
+C_CloseAllSessions(CK_SLOT_ID slotID)
+{
+ size_t i;
+ INIT_CONTEXT();
+
+ st_logf("CloseAllSessions\n");
+
+ for (i = 0; i < MAX_NUM_SESSION; i++)
+ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE)
+ close_session(&soft_token.state[i]);
+
+ return CKR_OK;
+}
+
+CK_RV
+C_GetSessionInfo(CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo)
+{
+ st_logf("GetSessionInfo\n");
+ INIT_CONTEXT();
+
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+
+ memset(pInfo, 20, sizeof(*pInfo));
+
+ pInfo->slotID = 1;
+ if (soft_token.flags.login_done)
+ pInfo->state = CKS_RO_USER_FUNCTIONS;
+ else
+ pInfo->state = CKS_RO_PUBLIC_SESSION;
+ pInfo->flags = CKF_SERIAL_SESSION;
+ pInfo->ulDeviceError = 0;
+
+ return CKR_OK;
+}
+
+CK_RV
+C_Login(CK_SESSION_HANDLE hSession,
+ CK_USER_TYPE userType,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen)
+{
+ char *pin = NULL;
+ CK_RV ret;
+ INIT_CONTEXT();
+
+ st_logf("Login\n");
+
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+
+ if (pPin != NULL_PTR) {
+ int aret;
+
+ aret = asprintf(&pin, "%.*s", (int)ulPinLen, pPin);
+ if (aret != -1 && pin)
+ st_logf("type: %d password: %s\n", (int)userType, pin);
+ else
+ st_logf("memory error: asprintf failed\n");
+ }
+
+ /*
+ * Login
+ */
+
+ ret = read_conf_file(soft_token.config_file, userType, pin);
+ if (ret == CKR_OK)
+ soft_token.flags.login_done = 1;
+
+ free(pin);
+
+ return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT;
+}
+
+CK_RV
+C_Logout(CK_SESSION_HANDLE hSession)
+{
+ st_logf("Logout\n");
+ INIT_CONTEXT();
+
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_GetObjectSize(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ULONG_PTR pulSize)
+{
+ st_logf("GetObjectSize\n");
+ INIT_CONTEXT();
+
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_GetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{
+ struct session_state *state;
+ struct st_object *obj;
+ CK_ULONG i;
+ CK_RV ret;
+ int j;
+
+ INIT_CONTEXT();
+
+ st_logf("GetAttributeValue: %lx\n",
+ (unsigned long)HANDLE_OBJECT_ID(hObject));
+ VERIFY_SESSION_HANDLE(hSession, &state);
+
+ if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) {
+ st_logf("object not found: %lx\n",
+ (unsigned long)HANDLE_OBJECT_ID(hObject));
+ return ret;
+ }
+
+ for (i = 0; i < ulCount; i++) {
+ st_logf(" getting 0x%08lx\n", (unsigned long)pTemplate[i].type);
+ for (j = 0; j < obj->num_attributes; j++) {
+ if (obj->attrs[j].secret) {
+ pTemplate[i].ulValueLen = (CK_ULONG)-1;
+ break;
+ }
+ if (pTemplate[i].type == obj->attrs[j].attribute.type) {
+ if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) {
+ if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen)
+ memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue,
+ obj->attrs[j].attribute.ulValueLen);
+ }
+ pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen;
+ break;
+ }
+ }
+ if (j == obj->num_attributes) {
+ st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type);
+ pTemplate[i].ulValueLen = (CK_ULONG)-1;
+ }
+
+ }
+ return CKR_OK;
+}
+
+CK_RV
+C_FindObjectsInit(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{
+ struct session_state *state;
+
+ st_logf("FindObjectsInit\n");
+
+ INIT_CONTEXT();
+
+ VERIFY_SESSION_HANDLE(hSession, &state);
+
+ if (state->find.next_object != -1) {
+ application_error("application didn't do C_FindObjectsFinal\n");
+ find_object_final(state);
+ }
+ if (ulCount) {
+ CK_ULONG i;
+
+ print_attributes(pTemplate, ulCount);
+
+ state->find.attributes =
+ calloc(1, ulCount * sizeof(state->find.attributes[0]));
+ if (state->find.attributes == NULL)
+ return CKR_DEVICE_MEMORY;
+ for (i = 0; i < ulCount; i++) {
+ state->find.attributes[i].pValue =
+ malloc(pTemplate[i].ulValueLen);
+ if (state->find.attributes[i].pValue == NULL) {
+ find_object_final(state);
+ return CKR_DEVICE_MEMORY;
+ }
+ memcpy(state->find.attributes[i].pValue,
+ pTemplate[i].pValue, pTemplate[i].ulValueLen);
+ state->find.attributes[i].type = pTemplate[i].type;
+ state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen;
+ }
+ state->find.num_attributes = ulCount;
+ state->find.next_object = 0;
+ } else {
+ st_logf("find all objects\n");
+ state->find.attributes = NULL;
+ state->find.num_attributes = 0;
+ state->find.next_object = 0;
+ }
+
+ return CKR_OK;
+}
+
+CK_RV
+C_FindObjects(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,
+ CK_ULONG ulMaxObjectCount,
+ CK_ULONG_PTR pulObjectCount)
+{
+ struct session_state *state;
+ int i;
+
+ INIT_CONTEXT();
+
+ st_logf("FindObjects\n");
+
+ VERIFY_SESSION_HANDLE(hSession, &state);
+
+ if (state->find.next_object == -1) {
+ application_error("application didn't do C_FindObjectsInit\n");
+ return CKR_ARGUMENTS_BAD;
+ }
+ if (ulMaxObjectCount == 0) {
+ application_error("application asked for 0 objects\n");
+ return CKR_ARGUMENTS_BAD;
+ }
+ *pulObjectCount = 0;
+ for (i = state->find.next_object; i < soft_token.object.num_objs; i++) {
+ st_logf("FindObjects: %d\n", i);
+ state->find.next_object = i + 1;
+ if (attributes_match(soft_token.object.objs[i],
+ state->find.attributes,
+ state->find.num_attributes)) {
+ *phObject++ = soft_token.object.objs[i]->object_handle;
+ ulMaxObjectCount--;
+ (*pulObjectCount)++;
+ if (ulMaxObjectCount == 0)
+ break;
+ }
+ }
+ return CKR_OK;
+}
+
+CK_RV
+C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
+{
+ struct session_state *state;
+
+ INIT_CONTEXT();
+
+ st_logf("FindObjectsFinal\n");
+ VERIFY_SESSION_HANDLE(hSession, &state);
+ find_object_final(state);
+ return CKR_OK;
+}
+
+static CK_RV
+commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len,
+ const CK_MECHANISM_TYPE *mechs, int mechs_len,
+ const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey,
+ struct st_object **o)
+{
+ CK_RV ret;
+ int i;
+
+ *o = NULL;
+ if ((ret = object_handle_to_object(hKey, o)) != CKR_OK)
+ return ret;
+
+ ret = attributes_match(*o, attr_match, attr_match_len);
+ if (!ret) {
+ application_error("called commonInit on key that doesn't "
+ "support required attr");
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ for (i = 0; i < mechs_len; i++)
+ if (mechs[i] == pMechanism->mechanism)
+ break;
+ if (i == mechs_len) {
+ application_error("called mech (%08lx) not supported\n",
+ pMechanism->mechanism);
+ return CKR_ARGUMENTS_BAD;
+ }
+ return CKR_OK;
+}
+
+
+static CK_RV
+dup_mechanism(CK_MECHANISM_PTR *dp, const CK_MECHANISM_PTR pMechanism)
+{
+ CK_MECHANISM_PTR p;
+
+ p = malloc(sizeof(*p));
+ if (p == NULL)
+ return CKR_DEVICE_MEMORY;
+
+ if (*dp)
+ free(*dp);
+ *dp = p;
+ memcpy(p, pMechanism, sizeof(*p));
+
+ return CKR_OK;
+}
+
+CK_RV
+C_DigestInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism)
+{
+ st_logf("DigestInit\n");
+ INIT_CONTEXT();
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_SignInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ struct session_state *state;
+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS };
+ CK_BBOOL bool_true = CK_TRUE;
+ CK_ATTRIBUTE attr[] = {
+ { CKA_SIGN, &bool_true, sizeof(bool_true) }
+ };
+ struct st_object *o;
+ CK_RV ret;
+
+ INIT_CONTEXT();
+ st_logf("SignInit\n");
+ VERIFY_SESSION_HANDLE(hSession, &state);
+
+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]),
+ mechs, sizeof(mechs)/sizeof(mechs[0]),
+ pMechanism, hKey, &o);
+ if (ret)
+ return ret;
+
+ ret = dup_mechanism(&state->sign_mechanism, pMechanism);
+ if (ret == CKR_OK)
+ state->sign_object = OBJECT_ID(o);
+
+ return CKR_OK;
+}
+
+CK_RV
+C_Sign(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ struct session_state *state;
+ struct st_object *o;
+ CK_RV ret;
+ int hret;
+ const AlgorithmIdentifier *alg;
+ heim_octet_string sig, data;
+
+ INIT_CONTEXT();
+ st_logf("Sign\n");
+ VERIFY_SESSION_HANDLE(hSession, &state);
+
+ sig.data = NULL;
+ sig.length = 0;
+
+ if (state->sign_object == -1)
+ return CKR_ARGUMENTS_BAD;
+
+ if (pulSignatureLen == NULL) {
+ st_logf("signature len NULL\n");
+ ret = CKR_ARGUMENTS_BAD;
+ goto out;
+ }
+
+ if (pData == NULL_PTR) {
+ st_logf("data NULL\n");
+ ret = CKR_ARGUMENTS_BAD;
+ goto out;
+ }
+
+ o = soft_token.object.objs[state->sign_object];
+
+ if (hx509_cert_have_private_key(o->cert) == 0) {
+ st_logf("private key NULL\n");
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ switch(state->sign_mechanism->mechanism) {
+ case CKM_RSA_PKCS:
+ alg = hx509_signature_rsa_pkcs1_x509();
+ break;
+ default:
+ ret = CKR_FUNCTION_NOT_SUPPORTED;
+ goto out;
+ }
+
+ data.data = pData;
+ data.length = ulDataLen;
+
+ hret = _hx509_create_signature(context,
+ _hx509_cert_private_key(o->cert),
+ alg,
+ &data,
+ NULL,
+ &sig);
+ if (hret) {
+ ret = CKR_DEVICE_ERROR;
+ goto out;
+ }
+ *pulSignatureLen = sig.length;
+
+ if (pSignature != NULL_PTR)
+ memcpy(pSignature, sig.data, sig.length);
+
+ ret = CKR_OK;
+ out:
+ if (sig.data) {
+ memset(sig.data, 0, sig.length);
+ der_free_octet_string(&sig);
+ }
+ return ret;
+}
+
+CK_RV
+C_SignUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ INIT_CONTEXT();
+ st_logf("SignUpdate\n");
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+
+CK_RV
+C_SignFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ INIT_CONTEXT();
+ st_logf("SignUpdate\n");
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_VerifyInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ struct session_state *state;
+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS };
+ CK_BBOOL bool_true = CK_TRUE;
+ CK_ATTRIBUTE attr[] = {
+ { CKA_VERIFY, &bool_true, sizeof(bool_true) }
+ };
+ struct st_object *o;
+ CK_RV ret;
+
+ INIT_CONTEXT();
+ st_logf("VerifyInit\n");
+ VERIFY_SESSION_HANDLE(hSession, &state);
+
+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]),
+ mechs, sizeof(mechs)/sizeof(mechs[0]),
+ pMechanism, hKey, &o);
+ if (ret)
+ return ret;
+
+ ret = dup_mechanism(&state->verify_mechanism, pMechanism);
+ if (ret == CKR_OK)
+ state->verify_object = OBJECT_ID(o);
+
+ return ret;
+}
+
+CK_RV
+C_Verify(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{
+ struct session_state *state;
+ struct st_object *o;
+ const AlgorithmIdentifier *alg;
+ CK_RV ret;
+ int hret;
+ heim_octet_string data, sig;
+
+ INIT_CONTEXT();
+ st_logf("Verify\n");
+ VERIFY_SESSION_HANDLE(hSession, &state);
+
+ if (state->verify_object == -1)
+ return CKR_ARGUMENTS_BAD;
+
+ o = soft_token.object.objs[state->verify_object];
+
+ switch(state->verify_mechanism->mechanism) {
+ case CKM_RSA_PKCS:
+ alg = hx509_signature_rsa_pkcs1_x509();
+ break;
+ default:
+ ret = CKR_FUNCTION_NOT_SUPPORTED;
+ goto out;
+ }
+
+ sig.data = pData;
+ sig.length = ulDataLen;
+ data.data = pSignature;
+ data.length = ulSignatureLen;
+
+ hret = _hx509_verify_signature(context,
+ o->cert,
+ alg,
+ &data,
+ &sig);
+ if (hret) {
+ ret = CKR_GENERAL_ERROR;
+ goto out;
+ }
+ ret = CKR_OK;
+
+ out:
+ return ret;
+}
+
+
+CK_RV
+C_VerifyUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ INIT_CONTEXT();
+ st_logf("VerifyUpdate\n");
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_VerifyFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{
+ INIT_CONTEXT();
+ st_logf("VerifyFinal\n");
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_GenerateRandom(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR RandomData,
+ CK_ULONG ulRandomLen)
+{
+ INIT_CONTEXT();
+ st_logf("GenerateRandom\n");
+ VERIFY_SESSION_HANDLE(hSession, NULL);
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+
+CK_FUNCTION_LIST funcs = {
+ { 2, 11 },
+ C_Initialize,
+ C_Finalize,
+ C_GetInfo,
+ C_GetFunctionList,
+ C_GetSlotList,
+ C_GetSlotInfo,
+ C_GetTokenInfo,
+ C_GetMechanismList,
+ C_GetMechanismInfo,
+ C_InitToken,
+ (void *)func_not_supported, /* C_InitPIN */
+ (void *)func_not_supported, /* C_SetPIN */
+ C_OpenSession,
+ C_CloseSession,
+ C_CloseAllSessions,
+ C_GetSessionInfo,
+ (void *)func_not_supported, /* C_GetOperationState */
+ (void *)func_not_supported, /* C_SetOperationState */
+ C_Login,
+ C_Logout,
+ (void *)func_not_supported, /* C_CreateObject */
+ (void *)func_not_supported, /* C_CopyObject */
+ (void *)func_not_supported, /* C_DestroyObject */
+ (void *)func_not_supported, /* C_GetObjectSize */
+ C_GetAttributeValue,
+ (void *)func_not_supported, /* C_SetAttributeValue */
+ C_FindObjectsInit,
+ C_FindObjects,
+ C_FindObjectsFinal,
+ (void *)func_not_supported, /* C_EncryptInit, */
+ (void *)func_not_supported, /* C_Encrypt, */
+ (void *)func_not_supported, /* C_EncryptUpdate, */
+ (void *)func_not_supported, /* C_EncryptFinal, */
+ (void *)func_not_supported, /* C_DecryptInit, */
+ (void *)func_not_supported, /* C_Decrypt, */
+ (void *)func_not_supported, /* C_DecryptUpdate, */
+ (void *)func_not_supported, /* C_DecryptFinal, */
+ C_DigestInit,
+ (void *)func_not_supported, /* C_Digest */
+ (void *)func_not_supported, /* C_DigestUpdate */
+ (void *)func_not_supported, /* C_DigestKey */
+ (void *)func_not_supported, /* C_DigestFinal */
+ C_SignInit,
+ C_Sign,
+ C_SignUpdate,
+ C_SignFinal,
+ (void *)func_not_supported, /* C_SignRecoverInit */
+ (void *)func_not_supported, /* C_SignRecover */
+ C_VerifyInit,
+ C_Verify,
+ C_VerifyUpdate,
+ C_VerifyFinal,
+ (void *)func_not_supported, /* C_VerifyRecoverInit */
+ (void *)func_not_supported, /* C_VerifyRecover */
+ (void *)func_not_supported, /* C_DigestEncryptUpdate */
+ (void *)func_not_supported, /* C_DecryptDigestUpdate */
+ (void *)func_not_supported, /* C_SignEncryptUpdate */
+ (void *)func_not_supported, /* C_DecryptVerifyUpdate */
+ (void *)func_not_supported, /* C_GenerateKey */
+ (void *)func_not_supported, /* C_GenerateKeyPair */
+ (void *)func_not_supported, /* C_WrapKey */
+ (void *)func_not_supported, /* C_UnwrapKey */
+ (void *)func_not_supported, /* C_DeriveKey */
+ (void *)func_not_supported, /* C_SeedRandom */
+ C_GenerateRandom,
+ (void *)func_not_supported, /* C_GetFunctionStatus */
+ (void *)func_not_supported, /* C_CancelFunction */
+ (void *)func_not_supported /* C_WaitForSlotEvent */
+};
diff --git a/third_party/heimdal/lib/hx509/test_ca.in b/third_party/heimdal/lib/hx509/test_ca.in
new file mode 100644
index 0000000..cf739a1
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_ca.in
@@ -0,0 +1,480 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+echo "create certificate request"
+${hxtool} request-create \
+ --subject="CN=Love,DC=it,DC=su,DC=se" \
+ --key=FILE:$srcdir/data/key.der \
+ pkcs10-request.der || exit 1
+
+echo "issue certificate"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=foo" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+
+echo "verify certificate"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "issue crl (no cert)"
+${hxtool} crl-sign \
+ --crl-file=crl.crl \
+ --signer=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key || exit 1
+
+echo "verify certificate (with CRL)"
+${hxtool} verify \
+ cert:FILE:cert-ee.pem \
+ crl:FILE:crl.crl \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "issue crl (with cert)"
+${hxtool} crl-sign \
+ --crl-file=crl.crl \
+ --signer=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ FILE:cert-ee.pem || exit 1
+
+echo "verify certificate (included in CRL)"
+${hxtool} verify \
+ cert:FILE:cert-ee.pem \
+ crl:FILE:crl.crl \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+# XXX Check that the certs issued below have the requested content
+
+echo "issue crl (with cert)"
+${hxtool} crl-sign \
+ --crl-file=crl.crl \
+ --lifetime='1 month' \
+ --signer=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ FILE:cert-ee.pem || exit 1
+
+echo "verify certificate (included in CRL, and lifetime 1 month)"
+${hxtool} verify \
+ cert:FILE:cert-ee.pem \
+ crl:FILE:crl.crl \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "issue certificate (10years 1 month)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=foo" \
+ --lifetime="10years 1 month" \
+ --req="PKCS10:pkcs10-request.der" \
+ --permanent-id=1.2.3.4.5.6.6:SomeVendor:A0B1C2D3 \
+ --hardware-module-name=tcg-tpm20:SomeVendor:Z0Y1X2W3 \
+ --policy="1.2.3.4.5.6:data:foo this is a warning" \
+ --policy="id-x509-ce-certificatePolicies-anyPolicy" \
+ --policy-mapping="1.2.3.4.5.6:1.2.3.4.5.6" \
+ --policy-mapping="1.2.3.4.5.6:1.2.3.4.5.7" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "issue certificate (with https ekus)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=foo" \
+ --type="https-server" \
+ --type="https-client" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "issue certificate (pkinit KDC)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=foo" \
+ --type="pkinit-kdc" \
+ --pk-init-principal="krbtgt/TEST.H5L.SE@TEST.H5L.SE" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "issue certificate (pkinit client)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=foo" \
+ --type="pkinit-client" \
+ --pk-init-principal="lha@TEST.H5L.SE" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "issue certificate (hostnames)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=foo" \
+ --type="https-server" \
+ --hostname="www.test.h5l.se" \
+ --hostname="ftp.test.h5l.se" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "verify certificate hostname (ok)"
+${hxtool} verify --missing-revoke \
+ --hostname=www.test.h5l.se \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "verify certificate hostname (fail)"
+${hxtool} verify --missing-revoke \
+ --hostname=www2.test.h5l.se \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "verify certificate hostname (fail)"
+${hxtool} verify --missing-revoke \
+ --hostname=2www.test.h5l.se \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "issue certificate (hostname in CN)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=www.test.h5l.se" \
+ --type="https-server" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "verify certificate hostname (ok)"
+${hxtool} verify --missing-revoke \
+ --hostname=www.test.h5l.se \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "verify certificate hostname (fail)"
+${hxtool} verify --missing-revoke \
+ --hostname=www2.test.h5l.se \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "issue certificate (email)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=foo" \
+ --email="lha@test.h5l.se" \
+ --email="test@test.h5l.se" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "issue certificate (email, null subject DN)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="" \
+ --email="lha@test.h5l.se" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-null.pem" || exit 1
+${hxtool} print --content FILE:cert-null.pem || exit 1
+
+echo "issue certificate (jabber)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --subject="cn=foo" \
+ --jid="lha@test.h5l.se" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "issue self-signed cert"
+${hxtool} issue-certificate \
+ --self-signed \
+ --ca-private-key=FILE:$srcdir/data/key.der \
+ --subject="cn=test" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "issue ca cert"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \
+ --issue-ca \
+ --subject="cn=ca-cert" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ca.der" || exit 1
+${hxtool} print --content FILE:cert-ca.der || exit 1
+
+echo "issue self-signed ca cert"
+${hxtool} issue-certificate \
+ --self-signed \
+ --issue-ca \
+ --ca-private-key=FILE:$srcdir/data/key.der \
+ --subject="cn=ca-root" \
+ --certificate="FILE:cert-ca.der" || exit 1
+${hxtool} print --content FILE:cert-ca.der || exit 1
+
+echo "issue proxy certificate"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --issue-proxy \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-proxy.der" || exit 1
+${hxtool} print --content FILE:cert-proxy.der || exit 1
+
+echo "verify proxy cert"
+${hxtool} verify --missing-revoke \
+ --allow-proxy-certificate \
+ cert:FILE:cert-proxy.der \
+ chain:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "issue ca cert (generate rsa key)"
+${hxtool} issue-certificate \
+ --self-signed \
+ --issue-ca \
+ --serial-number="deadbeaf" \
+ --generate-key=rsa \
+ --path-length=-1 \
+ --subject="cn=ca2-cert" \
+ --certificate="FILE:cert-ca.pem" || exit 1
+${hxtool} print --content FILE:cert-ca.pem || exit 1
+
+echo "issue sub-ca cert (generate rsa key)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --issue-ca \
+ --serial-number="deadbeaf22" \
+ --generate-key=rsa \
+ --subject="cn=sub-ca2-cert" \
+ --certificate="FILE:cert-sub-ca.pem" || exit 1
+${hxtool} print --content FILE:cert-sub-ca.pem || exit 1
+
+echo "issue ee cert (generate rsa key)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --generate-key=rsa \
+ --subject="cn=cert-ee2" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-ee.pem || exit 1
+
+echo "issue sub-ca ee cert (generate rsa key)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-sub-ca.pem \
+ --generate-key=rsa \
+ --subject="cn=cert-sub-ee2" \
+ --certificate="FILE:cert-sub-ee.pem" || exit 1
+${hxtool} print --content FILE:cert-sub-ee.pem || exit 1
+
+echo "verify certificate (ee)"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+echo "verify certificate (sub-ee)"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-sub-ee.pem \
+ chain:FILE:cert-sub-ca.pem \
+ anchor:FILE:cert-ca.pem || exit 1
+
+echo "sign CMS signature (generate key)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:cert-ee.pem \
+ "$srcdir/test_name.c" \
+ sd.data > /dev/null || exit 1
+
+echo "verify CMS signature (generate key)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:cert-ca.pem \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_name.c" sd.data.out || exit 1
+
+echo "extend ca cert"
+${hxtool} issue-certificate \
+ --self-signed \
+ --issue-ca \
+ --lifetime="2years" \
+ --serial-number="deadbeaf" \
+ --ca-private-key=FILE:cert-ca.pem \
+ --subject="cn=ca2-cert" \
+ --certificate="FILE:cert-ca.pem" || exit 1
+${hxtool} print --content FILE:cert-ca.pem || exit 1
+
+echo "verify certificate generated by previous ca"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+echo "extend ca cert (template)"
+${hxtool} issue-certificate \
+ --self-signed \
+ --issue-ca \
+ --lifetime="3years" \
+ --template-certificate="FILE:cert-ca.pem" \
+ --template-fields="serialNumber,notBefore,subject" \
+ --path-length=-1 \
+ --ca-private-key=FILE:cert-ca.pem \
+ --certificate="FILE:cert-ca.pem" || exit 1
+${hxtool} print --content FILE:cert-ca.pem || exit 1
+
+echo "verify certificate generated by previous ca"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+echo "extend sub-ca cert (template)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --issue-ca \
+ --lifetime="2years" \
+ --template-certificate="FILE:cert-sub-ca.pem" \
+ --template-fields="serialNumber,notBefore,subject,SPKI" \
+ --certificate="FILE:cert-sub-ca2.pem" || exit 1
+${hxtool} print --content FILE:cert-sub-ca2.pem || exit 1
+
+echo "verify certificate (sub-ee) with extended chain"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-sub-ee.pem \
+ chain:FILE:cert-sub-ca.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+echo "+++++++++++ test basic constraints"
+
+echo "extend ca cert (too low path-length constraint)"
+${hxtool} issue-certificate \
+ --self-signed \
+ --issue-ca \
+ --lifetime="3years" \
+ --template-certificate="FILE:cert-ca.pem" \
+ --template-fields="serialNumber,notBefore,subject" \
+ --path-length=0 \
+ --ca-private-key=FILE:cert-ca.pem \
+ --certificate="FILE:cert-ca.pem" || exit 1
+
+echo "verify failure of certificate (sub-ee) with path-length constraint"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-sub-ee.pem \
+ chain:FILE:cert-sub-ca.pem \
+ anchor:FILE:cert-ca.pem > /dev/null && exit 1
+
+echo "extend ca cert (exact path-length constraint)"
+${hxtool} issue-certificate \
+ --self-signed \
+ --issue-ca \
+ --lifetime="3years" \
+ --template-certificate="FILE:cert-ca.pem" \
+ --template-fields="serialNumber,notBefore,subject" \
+ --path-length=1 \
+ --ca-private-key=FILE:cert-ca.pem \
+ --certificate="FILE:cert-ca.pem" || exit 1
+
+echo "verify certificate (sub-ee) with exact path-length constraint"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-sub-ee.pem \
+ chain:FILE:cert-sub-ca.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+echo "Check missing basicConstrants.isCa"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --lifetime="2years" \
+ --template-certificate="FILE:cert-sub-ca.pem" \
+ --template-fields="serialNumber,notBefore,subject,SPKI" \
+ --certificate="FILE:cert-sub-ca2.pem" || exit 1
+
+echo "verify failure certificate (sub-ee) with missing isCA"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-sub-ee.pem \
+ chain:FILE:cert-sub-ca2.pem \
+ anchor:FILE:cert-ca.pem > /dev/null && exit 1
+
+echo "issue ee cert (crl uri)"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --req="PKCS10:pkcs10-request.der" \
+ --crl-uri="http://www.test.h5l.se/crl1.crl" \
+ --subject="cn=cert-ee-crl-uri" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+
+echo "issue null subject cert"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --req="PKCS10:pkcs10-request.der" \
+ --subject="" \
+ --email="lha@test.h5l.se" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+
+echo "verify certificate null subject"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+echo "+++++++++++ test sigalg"
+
+echo "issue cert with sha256"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --signature-algorithm=rsa-with-sha256 \
+ --subject="cn=foo" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+
+echo "verify certificate"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+echo "issue cert with sha1"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --signature-algorithm=rsa-with-sha1 \
+ --subject="cn=foo" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+
+echo "verify certificate"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_cert.in b/third_party/heimdal/lib/hx509/test_cert.in
new file mode 100644
index 0000000..5fa14d0
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_cert.in
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id: test_chain.in 20809 2007-06-03 03:19:06Z lha $
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+echo "print DIR"
+${hxtool} print --content DIR:$srcdir/data > /dev/null 2>/dev/null || exit 1
+
+echo "print FILE"
+for a in $srcdir/data/*.crt; do
+ ${hxtool} print --content FILE:"$a" > /dev/null 2>/dev/null
+done
+
+echo "print NULL"
+${hxtool} print --content NULL: > /dev/null || exit 1
+
+echo "copy dance"
+${hxtool} certificate-copy \
+ FILE:${srcdir}/data/test.crt PEM-FILE:cert-pem.tmp || exit 1
+
+${hxtool} certificate-copy PEM-FILE:cert-pem.tmp DER-FILE:cert-der.tmp || exit 1
+${hxtool} certificate-copy DER-FILE:cert-der.tmp PEM-FILE:cert-pem2.tmp || exit 1
+
+cmp cert-pem.tmp cert-pem2.tmp || exit 1
+
+echo "verify n0ll cert (fail)"
+${hxtool} verify --missing-revoke \
+ --hostname=foo.com \
+ cert:FILE:$srcdir/data/n0ll.pem \
+ anchor:FILE:$srcdir/data/n0ll.pem && exit 1
+
+echo "verify n0ll cert (fail)"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/n0ll.pem \
+ anchor:FILE:$srcdir/data/n0ll.pem && exit 1
+
+echo "check that windows cert with utf16 in printable string works"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/win-u16-in-printablestring.der \
+ anchor:FILE:$srcdir/data/win-u16-in-printablestring.der || exit 1
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_chain.in b/third_party/heimdal/lib/hx509/test_chain.in
new file mode 100644
index 0000000..b8c8cf5
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_chain.in
@@ -0,0 +1,256 @@
+#!/bin/sh
+#
+# Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+echo "cert -> root"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/test.crt \
+ chain:FILE:$srcdir/data/test.crt \
+ chain:FILE:$srcdir/data/ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "cert -> root"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/test.crt \
+ chain:FILE:$srcdir/data/ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "cert -> root"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "sub-cert -> root"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/sub-cert.crt \
+ chain:FILE:$srcdir/data/ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "sub-cert -> sub-ca -> root"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/sub-cert.crt \
+ chain:FILE:$srcdir/data/sub-ca.crt \
+ chain:FILE:$srcdir/data/ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "sub-cert -> sub-ca"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/sub-cert.crt \
+ anchor:FILE:$srcdir/data/sub-ca.crt > /dev/null || exit 1
+
+echo "sub-cert -> sub-ca -> root"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/sub-cert.crt \
+ chain:FILE:$srcdir/data/sub-ca.crt \
+ chain:FILE:$srcdir/data/ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "sub-cert -> sub-ca -> root"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/sub-cert.crt \
+ chain:FILE:$srcdir/data/ca.crt \
+ chain:FILE:$srcdir/data/sub-ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "sub-cert -> sub-ca -> root"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/sub-cert.crt \
+ chain:FILE:$srcdir/data/sub-ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "max depth 2 (ok)"
+${hxtool} verify --missing-revoke \
+ --max-depth=2 \
+ cert:FILE:$srcdir/data/sub-cert.crt \
+ chain:FILE:$srcdir/data/sub-ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "max depth 1 (fail)"
+${hxtool} verify --missing-revoke \
+ --max-depth=1 \
+ cert:FILE:$srcdir/data/sub-cert.crt \
+ chain:FILE:$srcdir/data/sub-ca.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "ocsp non-ca responder"
+${hxtool} verify \
+ cert:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt \
+ ocsp:FILE:$srcdir/data/ocsp-resp1-ocsp.der > /dev/null || exit 1
+
+echo "ocsp ca responder"
+${hxtool} verify \
+ cert:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt \
+ ocsp:FILE:$srcdir/data/ocsp-resp1-ca.der > /dev/null || exit 1
+
+echo "ocsp no-ca responder, missing cert"
+${hxtool} verify \
+ cert:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt \
+ ocsp:FILE:$srcdir/data/ocsp-resp1-ocsp-no-cert.der > /dev/null && exit 1
+
+echo "ocsp no-ca responder, missing cert, in pool"
+${hxtool} verify \
+ cert:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt \
+ ocsp:FILE:$srcdir/data/ocsp-resp1-ocsp-no-cert.der \
+ chain:FILE:$srcdir/data/ocsp-responder.crt > /dev/null || exit 1
+
+echo "ocsp no-ca responder, keyHash"
+${hxtool} verify \
+ cert:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt \
+ ocsp:FILE:$srcdir/data/ocsp-resp1-keyhash.der > /dev/null || exit 1
+
+echo "ocsp revoked cert"
+${hxtool} verify \
+ cert:FILE:$srcdir/data/revoke.crt \
+ anchor:FILE:$srcdir/data/ca.crt \
+ ocsp:FILE:$srcdir/data/ocsp-resp2.der > /dev/null && exit 1
+
+for a in resp1-ocsp-no-cert resp1-ca resp1-keyhash resp2 ; do
+ echo "ocsp print reply $a"
+ ${hxtool} ocsp-print \
+ $srcdir/data/ocsp-${a}.der > /dev/null || exit 1
+done
+
+echo "ocsp verify exists"
+${hxtool} ocsp-verify \
+ --ocsp-file=$srcdir/data/ocsp-resp1-ca.der \
+ FILE:$srcdir/data/test.crt > /dev/null || exit 1
+
+echo "ocsp verify not exists"
+${hxtool} ocsp-verify \
+ --ocsp-file=$srcdir/data/ocsp-resp1.der \
+ FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "ocsp verify revoked"
+${hxtool} ocsp-verify \
+ --ocsp-file=$srcdir/data/ocsp-resp2.der \
+ FILE:$srcdir/data/revoke.crt > /dev/null && exit 1
+
+echo "crl non-revoked cert"
+${hxtool} verify \
+ cert:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt \
+ crl:FILE:$srcdir/data/crl1.der > /dev/null || exit 1
+
+echo "crl revoked cert"
+${hxtool} verify \
+ cert:FILE:$srcdir/data/revoke.crt \
+ anchor:FILE:$srcdir/data/ca.crt \
+ crl:FILE:$srcdir/data/crl1.der > /dev/null && exit 1
+
+if ${hxtool} info | grep 'ecdsa: hcrypto null' > /dev/null ; then
+ echo "not testing ECDSA since hcrypto doesnt support ECDSA"
+else
+ echo "eccert -> root"
+ ${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/secp256r2TestServer.cert.pem \
+ anchor:FILE:$srcdir/data/secp256r1TestCA.cert.pem > /dev/null || exit 1
+
+ echo "eccert -> root"
+ ${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/secp256r2TestClient.cert.pem \
+ anchor:FILE:$srcdir/data/secp256r1TestCA.cert.pem > /dev/null || exit 1
+fi
+
+echo "proxy cert"
+${hxtool} verify --missing-revoke \
+ --allow-proxy-certificate \
+ cert:FILE:$srcdir/data/proxy-test.crt \
+ chain:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "proxy cert (negative)"
+${hxtool} verify --missing-revoke \
+ cert:FILE:$srcdir/data/proxy-test.crt \
+ chain:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "proxy cert (level fail)"
+${hxtool} verify --missing-revoke \
+ --allow-proxy-certificate \
+ cert:FILE:$srcdir/data/proxy-level-test.crt \
+ chain:FILE:$srcdir/data/proxy-test.crt \
+ chain:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "not a proxy cert"
+${hxtool} verify --missing-revoke \
+ --allow-proxy-certificate \
+ cert:FILE:$srcdir/data/no-proxy-test.crt \
+ chain:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null && exit 1
+
+echo "proxy cert (max level 10)"
+${hxtool} verify --missing-revoke \
+ --allow-proxy-certificate \
+ cert:FILE:$srcdir/data/proxy10-test.crt \
+ chain:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "proxy cert (second level)"
+${hxtool} verify --missing-revoke \
+ --allow-proxy-certificate \
+ cert:FILE:$srcdir/data/proxy10-child-test.crt \
+ chain:FILE:$srcdir/data/proxy10-test.crt \
+ chain:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+echo "proxy cert (third level)"
+${hxtool} verify --missing-revoke \
+ --allow-proxy-certificate \
+ cert:FILE:$srcdir/data/proxy10-child-child-test.crt \
+ chain:FILE:$srcdir/data/proxy10-child-test.crt \
+ chain:FILE:$srcdir/data/proxy10-test.crt \
+ chain:FILE:$srcdir/data/test.crt \
+ anchor:FILE:$srcdir/data/ca.crt > /dev/null || exit 1
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_cms.in b/third_party/heimdal/lib/hx509/test_cms.in
new file mode 100644
index 0000000..8b3de76
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_cms.in
@@ -0,0 +1,514 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+if ${hxtool} info | grep 'ecdsa: hcrypto null' > /dev/null ; then
+ echo "not testing ECDSA since hcrypto doesnt support ECDSA"
+else
+ echo "create signed data (ec)"
+ ${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/secp256r2TestClient.pem \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+ echo "verify signed data (ec)"
+ ${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/secp256r1TestCA.cert.pem \
+ sd.data sd.data.out > /dev/null || exit 1
+ cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+fi
+
+echo "create signed data"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (no signer)"
+${hxtool} cms-create-sd \
+ --no-signer \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data (no signer)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --no-signer-allowed \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > signer.tmp || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+grep "unsigned" signer.tmp > /dev/null || exit 1
+
+echo "verify signed data (no signer) (test failure)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out 2> signer.tmp && exit 1
+grep "No signers where found" signer.tmp > /dev/null || exit 1
+
+echo "create signed data (id-by-name)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --id-by-name \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "verify signed data (EE cert as anchor)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/test.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (password)"
+${hxtool} cms-create-sd \
+ --pass=PASS:foobar \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test-pw.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (combined)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test.combined.crt \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (content info)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --content-info \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data (content info)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --content-info \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (content type)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --content-type=1.1.1.1 \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data (content type)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (pem)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --pem \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data (pem)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --pem \
+ sd.data sd.data.out > /dev/null
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (pem, detached)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --detached-signature \
+ --pem \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data (pem, detached)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --pem \
+ --signed-content="$srcdir/test_chain.in" \
+ sd.data sd.data.out > /dev/null
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (p12)"
+${hxtool} cms-create-sd \
+ --pass=PASS:foobar \
+ --certificate=PKCS12:$srcdir/data/test.p12 \
+ --signer=friendlyname-test \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --content-info \
+ "$srcdir/data/test-signed-data" sd.data.out > /dev/null || exit 1
+cmp "$srcdir/data/static-file" sd.data.out || exit 1
+
+echo "verify signed data (no attr)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --content-info \
+ "$srcdir/data/test-signed-data-noattr" sd.data.out > /dev/null || exit 1
+cmp "$srcdir/data/static-file" sd.data.out || exit 1
+
+echo "verify failure signed data (no attr, no certs)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --content-info \
+ "$srcdir/data/test-signed-data-noattr-nocerts" \
+ sd.data.out > /dev/null 2>/dev/null && exit 1
+
+echo "verify signed data (no attr, no certs)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --certificate=FILE:$srcdir/data/test.crt \
+ --content-info \
+ "$srcdir/data/test-signed-data-noattr-nocerts" \
+ sd.data.out > /dev/null || exit 1
+cmp "$srcdir/data/static-file" sd.data.out || exit 1
+
+echo "verify signed data - sha1"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --content-info \
+ "$srcdir/data/test-signed-sha-1" sd.data.out > /dev/null || exit 1
+cmp "$srcdir/data/static-file" sd.data.out || exit 1
+
+echo "verify signed data - sha256"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --content-info \
+ "$srcdir/data/test-signed-sha-256" sd.data.out > /dev/null || exit 1
+cmp "$srcdir/data/static-file" sd.data.out || exit 1
+
+#echo "verify signed data - sha512"
+#${hxtool} cms-verify-sd \
+# --missing-revoke \
+# --anchors=FILE:$srcdir/data/ca.crt \
+# --content-info \
+# "$srcdir/data/test-signed-sha-512" sd.data.out > /dev/null || exit 1
+#cmp "$srcdir/data/static-file" sd.data.out || exit 1
+
+
+echo "create signed data (subcert, no certs)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/sub-cert.crt,$srcdir/data/sub-cert.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify failure signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null 2> /dev/null && exit 1
+
+echo "verify success signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --certificate=FILE:$srcdir/data/sub-ca.crt \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (subcert, certs)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/sub-cert.crt,$srcdir/data/sub-cert.key \
+ --pool=FILE:$srcdir/data/sub-ca.crt \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify success signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (subcert, certs, no-root)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/sub-cert.crt,$srcdir/data/sub-cert.key \
+ --pool=FILE:$srcdir/data/sub-ca.crt \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify success signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (subcert, no-subca, no-root)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/sub-cert.crt,$srcdir/data/sub-cert.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify failure signed data"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null 2>/dev/null && exit 1
+
+echo "create signed data (sd cert)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test-ds-only.crt,$srcdir/data/test-ds-only.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "create signed data (ke cert)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test-ke-only.crt,$srcdir/data/test-ke-only.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null 2>/dev/null && exit 1
+
+echo "create signed data (sd + ke certs)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test-ke-only.crt,$srcdir/data/test-ke-only.key \
+ --certificate=FILE:$srcdir/data/test-ds-only.crt,$srcdir/data/test-ds-only.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "create signed data (ke + sd certs)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test-ds-only.crt,$srcdir/data/test-ds-only.key \
+ --certificate=FILE:$srcdir/data/test-ke-only.crt,$srcdir/data/test-ke-only.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "create signed data (detached)"
+${hxtool} cms-create-sd \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --detached-signature \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data (detached)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --signed-content="$srcdir/test_chain.in" \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "verify failure signed data (detached)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null 2>/dev/null && exit 1
+
+echo "create signed data (rsa)"
+${hxtool} cms-create-sd \
+ --peer-alg=1.2.840.113549.1.1.1 \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
+
+echo "verify signed data (rsa)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ sd.data sd.data.out > /dev/null 2>/dev/null || exit 1
+cmp "$srcdir/test_chain.in" sd.data.out || exit 1
+
+echo "create signed data (pem, detached)"
+cp "$srcdir/test_chain.in" sd
+${hxtool} cms-sign \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --detached-signature \
+ --pem \
+ sd > /dev/null || exit 1
+
+echo "verify signed data (pem, detached)"
+${hxtool} cms-verify-sd \
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/ca.crt \
+ --pem \
+ sd.pem > /dev/null
+
+echo "create signed data (no certs, detached sig)"
+cp "$srcdir/test_chain.in" sd
+${hxtool} cms-sign \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --detached-signature \
+ --no-embedded-certs \
+ "$srcdir/data/static-file" \
+ sd > /dev/null || exit 1
+
+echo "create signed data (leif only, detached sig)"
+cp "$srcdir/test_chain.in" sd
+${hxtool} cms-sign \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --detached-signature \
+ --embed-leaf-only \
+ "$srcdir/data/static-file" \
+ sd > /dev/null || exit 1
+
+echo "create signed data (no certs, detached sig, 2 signers)"
+cp "$srcdir/test_chain.in" sd
+${hxtool} cms-sign \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --certificate=FILE:$srcdir/data/sub-cert.crt,$srcdir/data/sub-cert.key \
+ --detached-signature \
+ --no-embedded-certs \
+ "$srcdir/data/static-file" \
+ sd > /dev/null || exit 1
+
+echo "create signed data (no certs, detached sig, 3 signers)"
+cp "$srcdir/test_chain.in" sd
+${hxtool} cms-sign \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --certificate=FILE:$srcdir/data/sub-cert.crt,$srcdir/data/sub-cert.key \
+ --certificate=FILE:$srcdir/data/test-ds-only.crt,$srcdir/data/test-ds-only.key \
+ --detached-signature \
+ --no-embedded-certs \
+ "$srcdir/data/static-file" \
+ sd > /dev/null || exit 1
+
+echo "envelope data (content-type)"
+${hxtool} cms-envelope \
+ --certificate=FILE:$srcdir/data/test.crt \
+ --content-type=1.1.1.1 \
+ "$srcdir/data/static-file" \
+ ev.data > /dev/null || exit 1
+
+echo "unenvelope data (content-type)"
+${hxtool} cms-unenvelope \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ ev.data ev.data.out \
+ FILE:$srcdir/data/test.crt,$srcdir/data/test.key > /dev/null || exit 1
+cmp "$srcdir/data/static-file" ev.data.out || exit 1
+
+echo "envelope data (content-info)"
+${hxtool} cms-envelope \
+ --certificate=FILE:$srcdir/data/test.crt \
+ --content-info \
+ "$srcdir/data/static-file" \
+ ev.data > /dev/null || exit 1
+
+echo "unenvelope data (content-info)"
+${hxtool} cms-unenvelope \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --content-info \
+ ev.data ev.data.out \
+ FILE:$srcdir/data/test.crt,$srcdir/data/test.key > /dev/null || exit 1
+cmp "$srcdir/data/static-file" ev.data.out || exit 1
+
+for a in des-ede3 aes-128 aes-256; do
+
+ rm -f ev.data ev.data.out
+ echo "envelope data ($a)"
+ ${hxtool} cms-envelope \
+ --encryption-type="$a-cbc" \
+ --certificate=FILE:$srcdir/data/test.crt \
+ "$srcdir/data/static-file" \
+ ev.data || exit 1
+
+ echo "unenvelope data ($a)"
+ ${hxtool} cms-unenvelope \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ ev.data ev.data.out > /dev/null || exit 1
+ cmp "$srcdir/data/static-file" ev.data.out || exit 1
+done
+
+for a in rc2-40 rc2-64 rc2-128 des-ede3 aes-128 aes-256; do
+ echo "static unenvelope data ($a)"
+
+ rm -f ev.data.out
+ ${hxtool} cms-unenvelope \
+ --certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \
+ --content-info \
+ --allow-weak \
+ "$srcdir/data/test-enveloped-$a" ev.data.out > /dev/null || exit 1
+ cmp "$srcdir/data/static-file" ev.data.out || exit 1
+done
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_crypto.in b/third_party/heimdal/lib/hx509/test_crypto.in
new file mode 100644
index 0000000..9206031
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_crypto.in
@@ -0,0 +1,192 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+
+echo "Bleichenbacher good cert (from eay)"
+${hxtool} verify --missing-revoke \
+ --time=2006-09-25 \
+ cert:FILE:$srcdir/data/bleichenbacher-good.pem \
+ anchor:FILE:$srcdir/data/bleichenbacher-good.pem > /dev/null || exit 1
+
+echo "Bleichenbacher bad cert (from eay)"
+${hxtool} verify --missing-revoke \
+ --time=2006-09-25 \
+ cert:FILE:$srcdir/data/bleichenbacher-bad.pem \
+ anchor:FILE:$srcdir/data/bleichenbacher-bad.pem > /dev/null && exit 1
+
+echo "Bleichenbacher good cert (from yutaka)"
+${hxtool} verify --missing-revoke \
+ --time=2006-09-25 \
+ cert:FILE:$srcdir/data/yutaka-pad-ok-cert.pem \
+ anchor:FILE:$srcdir/data/yutaka-pad-ok-ca.pem > /dev/null || exit 1
+
+echo "Bleichenbacher bad cert (from yutaka)"
+${hxtool} verify --missing-revoke \
+ --time=2006-09-25 \
+ cert:FILE:$srcdir/data/yutaka-pad-broken-cert.pem \
+ anchor:FILE:$srcdir/data/yutaka-pad-broken-ca.pem > /dev/null && exit 1
+
+# Ralf-Philipp Weinmann <weinmann@cdc.informatik.tu-darmstadt.de>
+# Andrew Pyshkin <pychkine@cdc.informatik.tu-darmstadt.de>
+echo "Bleichenbacher bad cert (sf pad correct)"
+${hxtool} verify --missing-revoke \
+ --time=2006-09-25 \
+ cert:FILE:$srcdir/data/bleichenbacher-sf-pad-correct.pem \
+ anchor:FILE:$srcdir/data/sf-class2-root.pem > /dev/null && exit 1
+
+echo Read 50 kilobyte random data
+${hxtool} random-data 50kilobyte > random-data || exit 1
+
+echo "crypto select1"
+${hxtool} crypto-select > test || { echo "select1"; exit 1; }
+cmp test ${srcdir}/tst-crypto-select1 > /dev/null || \
+ { echo "select1 failure"; exit 1; }
+
+echo "crypto select1"
+${hxtool} crypto-select --type=digest > test || { echo "select1"; exit 1; }
+cmp test ${srcdir}/tst-crypto-select1 > /dev/null || \
+ { echo "select1 failure"; exit 1; }
+
+echo "crypto select2"
+${hxtool} crypto-select --type=public-sig > test || { echo "select2"; exit 1; }
+cmp test ${srcdir}/tst-crypto-select2 > /dev/null || \
+ { echo "select2 failure"; exit 1; }
+
+echo "crypto select3"
+${hxtool} crypto-select \
+ --type=public-sig \
+ --peer-cmstype=1.2.840.113549.1.1.4 \
+ > test || { echo "select3"; exit 1; }
+cmp test ${srcdir}/tst-crypto-select3 > /dev/null || \
+ { echo "select3 failure"; exit 1; }
+
+echo "crypto select4"
+${hxtool} crypto-select \
+ --type=public-sig \
+ --peer-cmstype=1.2.840.113549.1.1.5 \
+ --peer-cmstype=1.2.840.113549.1.1.4 \
+ > test || { echo "select4"; exit 1; }
+cmp test ${srcdir}/tst-crypto-select4 > /dev/null || \
+ { echo "select4 failure"; exit 1; }
+
+echo "crypto select5"
+${hxtool} crypto-select \
+ --type=public-sig \
+ --peer-cmstype=1.2.840.113549.1.1.11 \
+ --peer-cmstype=1.2.840.113549.1.1.5 \
+ > test || { echo "select5"; exit 1; }
+cmp test ${srcdir}/tst-crypto-select5 > /dev/null || \
+ { echo "select5 failure"; exit 1; }
+
+echo "crypto select6"
+${hxtool} crypto-select \
+ --type=public-sig \
+ --peer-cmstype=1.2.840.113549.2.5 \
+ --peer-cmstype=1.2.840.113549.1.1.5 \
+ > test || { echo "select6"; exit 1; }
+cmp test ${srcdir}/tst-crypto-select6 > /dev/null || \
+ { echo "select6 failure"; exit 1; }
+
+echo "crypto select7"
+${hxtool} crypto-select \
+ --type=secret \
+ --peer-cmstype=2.16.840.1.101.3.4.1.42 \
+ --peer-cmstype=1.2.840.113549.3.7 \
+ --peer-cmstype=1.2.840.113549.1.1.5 \
+ > test || { echo "select7"; exit 1; }
+cmp test ${srcdir}/tst-crypto-select7 > /dev/null || \
+ { echo "select7 failure"; exit 1; }
+
+#echo "crypto available1"
+#${hxtool} crypto-available \
+# --type=all \
+# > test || { echo "available1"; exit 1; }
+#cmp test ${srcdir}/tst-crypto-available1 > /dev/null || \
+# { echo "available1 failure"; exit 1; }
+
+echo "crypto available2"
+${hxtool} crypto-available \
+ --type=digest \
+ > test || { echo "available2"; exit 1; }
+cmp test ${srcdir}/tst-crypto-available2 > /dev/null || \
+ { echo "available2 failure"; exit 1; }
+
+#echo "crypto available3"
+#${hxtool} crypto-available \
+# --type=public-sig \
+# > test || { echo "available3"; exit 1; }
+#cmp test ${srcdir}/tst-crypto-available3 > /dev/null || \
+# { echo "available3 failure"; exit 1; }
+
+echo "copy keystore FILE existing -> FILE"
+${hxtool} certificate-copy \
+ FILE:${srcdir}/data/test.crt,${srcdir}/data/test.key \
+ FILE:out.pem || exit 1
+
+echo "copy keystore FILE -> FILE"
+${hxtool} certificate-copy \
+ FILE:out.pem \
+ FILE:out2.pem || exit 1
+
+echo "copy keystore FILE -> PKCS12"
+${hxtool} certificate-copy \
+ FILE:out.pem \
+ PKCS12:out2.pem || exit 1
+
+echo "print certificate with utf8"
+${hxtool} print \
+ FILE:$srcdir/data/j.pem >/dev/null 2>/dev/null || exit 1
+
+echo "Make sure that we can parse EC private keys"
+${hxtool} print --content \
+ FILE:$srcdir/data/pkinit-ec.crt,$srcdir/data/pkinit-ec.key \
+ > /dev/null || exit 1
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_expr.c b/third_party/heimdal/lib/hx509/test_expr.c
new file mode 100644
index 0000000..8c2c0a5
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_expr.c
@@ -0,0 +1,87 @@
+
+#include "hx_locl.h"
+#include <err.h>
+
+struct foo {
+ int val;
+ char *str;
+} foo[] = {
+ { 0, "FALSE" },
+ { 1, "TRUE" },
+ { 0, "!TRUE" },
+ { 0, "! TRUE" },
+ { 0, "!\tTRUE" },
+ { 0, "( FALSE AND FALSE )" },
+ { 0, "( TRUE AND FALSE )" },
+ { 1, "( TRUE AND TRUE )" },
+ { 1, "( TRUE OR TRUE )" },
+ { 1, "( TRUE OR FALSE )" },
+ { 0, "( FALSE OR FALSE )" },
+ { 1, "! ( FALSE OR FALSE )" },
+
+ { 1, "\"foo\" TAILMATCH \"foo\"" },
+ { 1, "\"foobar\" TAILMATCH \"bar\"" },
+ { 0, "\"foobar\" TAILMATCH \"foo\"" },
+
+ { 1, "\"foo\" == \"foo\"" },
+ { 0, "\"foo\" == \"bar\"" },
+ { 0, "\"foo\" != \"foo\"" },
+ { 1, "\"foo\" != \"bar\"" },
+ { 1, "%{variable} == \"foo\"" },
+ { 0, "%{variable} == \"bar\"" },
+ { 1, "%{context.variable} == \"foo\"" },
+ { 0, "%{context.variable} == \"bar\"" },
+ { 1, "\"foo\" IN ( \"bar\", \"foo\")" },
+ { 0, "\"foo\" IN ( \"bar\", \"baz\")" },
+ { 0, "\"bar\" IN %{context}" },
+ { 1, "\"foo\" IN %{context}" },
+ { 1, "\"variable\" IN %{context}" },
+
+ { 1, "\"foo\" IN %{context} AND %{context.variable} == \"foo\"" }
+};
+
+int
+main(int argc, char **argv)
+{
+ struct hx_expr *expr;
+ hx509_context context;
+ hx509_env env = NULL, env2 = NULL;
+ int val, i, ret;
+
+#if 0
+ extern int yydebug;
+ yydebug = 1;
+#endif
+
+ ret = hx509_context_init(&context);
+ if (ret)
+ errx(1, "hx509_context_init failed with %d", ret);
+
+ hx509_env_add(context, &env, "variable", "foo");
+ hx509_env_add(context, &env2, "variable", "foo");
+ hx509_env_add_binding(context, &env, "context", env2);
+
+ for (i = 0; i < sizeof(foo)/sizeof(foo[0]); i++) {
+
+ expr = _hx509_expr_parse(foo[i].str);
+ if (expr == NULL)
+ errx(1, "_hx509_expr_parse failed for %d: %s", i, foo[i].str);
+
+ val = _hx509_expr_eval(context, env, expr);
+ if (foo[i].val) {
+ if (val == 0)
+ errx(1, "_hx509_expr_eval not true when it should: %d: %s",
+ i, foo[i].str);
+ } else {
+ if (val)
+ errx(1, "_hx509_expr_eval true when it should not: %d: %s",
+ i, foo[i].str);
+ }
+
+ _hx509_expr_free(expr);
+ }
+
+ hx509_env_free(&env);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/hx509/test_java_pkcs11.in b/third_party/heimdal/lib/hx509/test_java_pkcs11.in
new file mode 100644
index 0000000..9a843a4
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_java_pkcs11.in
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+exit 0
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+dir=$objdir
+file=
+
+for a in libhx509.so .libs/libhx509.so libhx509.dylib .libs/libhx509.dylib ; do
+ if [ -f $dir/$a ] ; then
+ file=$dir/$a
+ break
+ fi
+done
+
+if [ "X$file" = X ] ; then
+ exit 0
+fi
+
+cat > pkcs11.cfg <<EOF
+name = Heimdal
+library = $file
+EOF
+
+cat > test-rc-file.rc <<EOF
+certificate cert User certificate FILE:$srcdir/data/test.crt,$srcdir/data/test.key
+debug stdout
+EOF
+
+
+env SOFTPKCS11RC="test-rc-file.rc" \
+ keytool \
+ -keystore NONE \
+ -storetype PKCS11 \
+ -providerClass sun.security.pkcs11.SunPKCS11 \
+ -providerArg pkcs11.cfg \
+ -list || exit 1
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_name.c b/third_party/heimdal/lib/hx509/test_name.c
new file mode 100644
index 0000000..ba4cbaa
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_name.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+static int
+test_name(hx509_context context, const char *name)
+{
+ hx509_name n;
+ char *s;
+ int ret;
+
+ ret = hx509_parse_name(context, name, &n);
+ if (ret)
+ return 1;
+
+ ret = hx509_name_to_string(n, &s);
+ if (ret)
+ return 1;
+
+ if (strcmp(s, name) != 0)
+ return 1;
+
+ hx509_name_free(&n);
+ free(s);
+
+ return 0;
+}
+
+static int
+test_name_fail(hx509_context context, const char *name)
+{
+ hx509_name n;
+
+ if (hx509_parse_name(context, name, &n) == HX509_NAME_MALFORMED)
+ return 0;
+ hx509_name_free(&n);
+ return 1;
+}
+
+static int
+test_expand(hx509_context context, const char *name, const char *expected)
+{
+ hx509_env env = NULL;
+ hx509_name n;
+ char *s;
+ int ret;
+
+ hx509_env_add(context, &env, "uid", "lha");
+
+ ret = hx509_parse_name(context, name, &n);
+ if (ret)
+ return 1;
+
+ ret = hx509_name_expand(context, n, env);
+ hx509_env_free(&env);
+ if (ret)
+ return 1;
+
+ ret = hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ if (ret)
+ return 1;
+
+ ret = strcmp(s, expected) != 0;
+ free(s);
+ if (ret)
+ return 1;
+
+ return 0;
+}
+
+char certdata1[] =
+ "\x30\x82\x04\x1d\x30\x82\x03\x05\xa0\x03\x02\x01\x02\x02\x10\x4e"
+ "\x81\x2d\x8a\x82\x65\xe0\x0b\x02\xee\x3e\x35\x02\x46\xe5\x3d\x30"
+ "\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x81"
+ "\x81\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x47\x42\x31\x1b"
+ "\x30\x19\x06\x03\x55\x04\x08\x13\x12\x47\x72\x65\x61\x74\x65\x72"
+ "\x20\x4d\x61\x6e\x63\x68\x65\x73\x74\x65\x72\x31\x10\x30\x0e\x06"
+ "\x03\x55\x04\x07\x13\x07\x53\x61\x6c\x66\x6f\x72\x64\x31\x1a\x30"
+ "\x18\x06\x03\x55\x04\x0a\x13\x11\x43\x4f\x4d\x4f\x44\x4f\x20\x43"
+ "\x41\x20\x4c\x69\x6d\x69\x74\x65\x64\x31\x27\x30\x25\x06\x03\x55"
+ "\x04\x03\x13\x1e\x43\x4f\x4d\x4f\x44\x4f\x20\x43\x65\x72\x74\x69"
+ "\x66\x69\x63\x61\x74\x69\x6f\x6e\x20\x41\x75\x74\x68\x6f\x72\x69"
+ "\x74\x79\x30\x1e\x17\x0d\x30\x36\x31\x32\x30\x31\x30\x30\x30\x30"
+ "\x30\x30\x5a\x17\x0d\x32\x39\x31\x32\x33\x31\x32\x33\x35\x39\x35"
+ "\x39\x5a\x30\x81\x81\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02"
+ "\x47\x42\x31\x1b\x30\x19\x06\x03\x55\x04\x08\x13\x12\x47\x72\x65"
+ "\x61\x74\x65\x72\x20\x4d\x61\x6e\x63\x68\x65\x73\x74\x65\x72\x31"
+ "\x10\x30\x0e\x06\x03\x55\x04\x07\x13\x07\x53\x61\x6c\x66\x6f\x72"
+ "\x64\x31\x1a\x30\x18\x06\x03\x55\x04\x0a\x13\x11\x43\x4f\x4d\x4f"
+ "\x44\x4f\x20\x43\x41\x20\x4c\x69\x6d\x69\x74\x65\x64\x31\x27\x30"
+ "\x25\x06\x03\x55\x04\x03\x13\x1e\x43\x4f\x4d\x4f\x44\x4f\x20\x43"
+ "\x65\x72\x74\x69\x66\x69\x63\x61\x74\x69\x6f\x6e\x20\x41\x75\x74"
+ "\x68\x6f\x72\x69\x74\x79\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86"
+ "\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82"
+ "\x01\x0a\x02\x82\x01\x01\x00\xd0\x40\x8b\x8b\x72\xe3\x91\x1b\xf7"
+ "\x51\xc1\x1b\x54\x04\x98\xd3\xa9\xbf\xc1\xe6\x8a\x5d\x3b\x87\xfb"
+ "\xbb\x88\xce\x0d\xe3\x2f\x3f\x06\x96\xf0\xa2\x29\x50\x99\xae\xdb"
+ "\x3b\xa1\x57\xb0\x74\x51\x71\xcd\xed\x42\x91\x4d\x41\xfe\xa9\xc8"
+ "\xd8\x6a\x86\x77\x44\xbb\x59\x66\x97\x50\x5e\xb4\xd4\x2c\x70\x44"
+ "\xcf\xda\x37\x95\x42\x69\x3c\x30\xc4\x71\xb3\x52\xf0\x21\x4d\xa1"
+ "\xd8\xba\x39\x7c\x1c\x9e\xa3\x24\x9d\xf2\x83\x16\x98\xaa\x16\x7c"
+ "\x43\x9b\x15\x5b\xb7\xae\x34\x91\xfe\xd4\x62\x26\x18\x46\x9a\x3f"
+ "\xeb\xc1\xf9\xf1\x90\x57\xeb\xac\x7a\x0d\x8b\xdb\x72\x30\x6a\x66"
+ "\xd5\xe0\x46\xa3\x70\xdc\x68\xd9\xff\x04\x48\x89\x77\xde\xb5\xe9"
+ "\xfb\x67\x6d\x41\xe9\xbc\x39\xbd\x32\xd9\x62\x02\xf1\xb1\xa8\x3d"
+ "\x6e\x37\x9c\xe2\x2f\xe2\xd3\xa2\x26\x8b\xc6\xb8\x55\x43\x88\xe1"
+ "\x23\x3e\xa5\xd2\x24\x39\x6a\x47\xab\x00\xd4\xa1\xb3\xa9\x25\xfe"
+ "\x0d\x3f\xa7\x1d\xba\xd3\x51\xc1\x0b\xa4\xda\xac\x38\xef\x55\x50"
+ "\x24\x05\x65\x46\x93\x34\x4f\x2d\x8d\xad\xc6\xd4\x21\x19\xd2\x8e"
+ "\xca\x05\x61\x71\x07\x73\x47\xe5\x8a\x19\x12\xbd\x04\x4d\xce\x4e"
+ "\x9c\xa5\x48\xac\xbb\x26\xf7\x02\x03\x01\x00\x01\xa3\x81\x8e\x30"
+ "\x81\x8b\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x0b\x58\xe5"
+ "\x8b\xc6\x4c\x15\x37\xa4\x40\xa9\x30\xa9\x21\xbe\x47\x36\x5a\x56"
+ "\xff\x30\x0e\x06\x03\x55\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01"
+ "\x06\x30\x0f\x06\x03\x55\x1d\x13\x01\x01\xff\x04\x05\x30\x03\x01"
+ "\x01\xff\x30\x49\x06\x03\x55\x1d\x1f\x04\x42\x30\x40\x30\x3e\xa0"
+ "\x3c\xa0\x3a\x86\x38\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x2e"
+ "\x63\x6f\x6d\x6f\x64\x6f\x63\x61\x2e\x63\x6f\x6d\x2f\x43\x4f\x4d"
+ "\x4f\x44\x4f\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x69\x6f\x6e"
+ "\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x2e\x63\x72\x6c\x30\x0d\x06"
+ "\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x82\x01\x01"
+ "\x00\x3e\x98\x9e\x9b\xf6\x1b\xe9\xd7\x39\xb7\x78\xae\x1d\x72\x18"
+ "\x49\xd3\x87\xe4\x43\x82\xeb\x3f\xc9\xaa\xf5\xa8\xb5\xef\x55\x7c"
+ "\x21\x52\x65\xf9\xd5\x0d\xe1\x6c\xf4\x3e\x8c\x93\x73\x91\x2e\x02"
+ "\xc4\x4e\x07\x71\x6f\xc0\x8f\x38\x61\x08\xa8\x1e\x81\x0a\xc0\x2f"
+ "\x20\x2f\x41\x8b\x91\xdc\x48\x45\xbc\xf1\xc6\xde\xba\x76\x6b\x33"
+ "\xc8\x00\x2d\x31\x46\x4c\xed\xe7\x9d\xcf\x88\x94\xff\x33\xc0\x56"
+ "\xe8\x24\x86\x26\xb8\xd8\x38\x38\xdf\x2a\x6b\xdd\x12\xcc\xc7\x3f"
+ "\x47\x17\x4c\xa2\xc2\x06\x96\x09\xd6\xdb\xfe\x3f\x3c\x46\x41\xdf"
+ "\x58\xe2\x56\x0f\x3c\x3b\xc1\x1c\x93\x35\xd9\x38\x52\xac\xee\xc8"
+ "\xec\x2e\x30\x4e\x94\x35\xb4\x24\x1f\x4b\x78\x69\xda\xf2\x02\x38"
+ "\xcc\x95\x52\x93\xf0\x70\x25\x59\x9c\x20\x67\xc4\xee\xf9\x8b\x57"
+ "\x61\xf4\x92\x76\x7d\x3f\x84\x8d\x55\xb7\xe8\xe5\xac\xd5\xf1\xf5"
+ "\x19\x56\xa6\x5a\xfb\x90\x1c\xaf\x93\xeb\xe5\x1c\xd4\x67\x97\x5d"
+ "\x04\x0e\xbe\x0b\x83\xa6\x17\x83\xb9\x30\x12\xa0\xc5\x33\x15\x05"
+ "\xb9\x0d\xfb\xc7\x05\x76\xe3\xd8\x4a\x8d\xfc\x34\x17\xa3\xc6\x21"
+ "\x28\xbe\x30\x45\x31\x1e\xc7\x78\xbe\x58\x61\x38\xac\x3b\xe2\x01"
+ "\x65";
+
+char certdata2[] =
+ "\x30\x82\x03\x02\x30\x82\x02\x6b\x02\x10\x39\xca\x54\x89\xfe\x50"
+ "\x22\x32\xfe\x32\xd9\xdb\xfb\x1b\x84\x19\x30\x0d\x06\x09\x2a\x86"
+ "\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x81\xc1\x31\x0b\x30\x09"
+ "\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x17\x30\x15\x06\x03\x55"
+ "\x04\x0a\x13\x0e\x56\x65\x72\x69\x53\x69\x67\x6e\x2c\x20\x49\x6e"
+ "\x63\x2e\x31\x3c\x30\x3a\x06\x03\x55\x04\x0b\x13\x33\x43\x6c\x61"
+ "\x73\x73\x20\x31\x20\x50\x75\x62\x6c\x69\x63\x20\x50\x72\x69\x6d"
+ "\x61\x72\x79\x20\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x69\x6f"
+ "\x6e\x20\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x20\x2d\x20\x47\x32"
+ "\x31\x3a\x30\x38\x06\x03\x55\x04\x0b\x13\x31\x28\x63\x29\x20\x31"
+ "\x39\x39\x38\x20\x56\x65\x72\x69\x53\x69\x67\x6e\x2c\x20\x49\x6e"
+ "\x63\x2e\x20\x2d\x20\x46\x6f\x72\x20\x61\x75\x74\x68\x6f\x72\x69"
+ "\x7a\x65\x64\x20\x75\x73\x65\x20\x6f\x6e\x6c\x79\x31\x1f\x30\x1d"
+ "\x06\x03\x55\x04\x0b\x13\x16\x56\x65\x72\x69\x53\x69\x67\x6e\x20"
+ "\x54\x72\x75\x73\x74\x20\x4e\x65\x74\x77\x6f\x72\x6b\x30\x1e\x17"
+ "\x0d\x39\x38\x30\x35\x31\x38\x30\x30\x30\x30\x30\x30\x5a\x17\x0d"
+ "\x31\x38\x30\x35\x31\x38\x32\x33\x35\x39\x35\x39\x5a\x30\x81\xc1"
+ "\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x17\x30"
+ "\x15\x06\x03\x55\x04\x0a\x13\x0e\x56\x65\x72\x69\x53\x69\x67\x6e"
+ "\x2c\x20\x49\x6e\x63\x2e\x31\x3c\x30\x3a\x06\x03\x55\x04\x0b\x13"
+ "\x33\x43\x6c\x61\x73\x73\x20\x31\x20\x50\x75\x62\x6c\x69\x63\x20"
+ "\x50\x72\x69\x6d\x61\x72\x79\x20\x43\x65\x72\x74\x69\x66\x69\x63"
+ "\x61\x74\x69\x6f\x6e\x20\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x20"
+ "\x2d\x20\x47\x32\x31\x3a\x30\x38\x06\x03\x55\x04\x0b\x13\x31\x28"
+ "\x63\x29\x20\x31\x39\x39\x38\x20\x56\x65\x72\x69\x53\x69\x67\x6e"
+ "\x2c\x20\x49\x6e\x63\x2e\x20\x2d\x20\x46\x6f\x72\x20\x61\x75\x74"
+ "\x68\x6f\x72\x69\x7a\x65\x64\x20\x75\x73\x65\x20\x6f\x6e\x6c\x79"
+ "\x31\x1f\x30\x1d\x06\x03\x55\x04\x0b\x13\x16\x56\x65\x72\x69\x53"
+ "\x69\x67\x6e\x20\x54\x72\x75\x73\x74\x20\x4e\x65\x74\x77\x6f\x72"
+ "\x6b\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01"
+ "\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xaa\xd0"
+ "\xba\xbe\x16\x2d\xb8\x83\xd4\xca\xd2\x0f\xbc\x76\x31\xca\x94\xd8"
+ "\x1d\x93\x8c\x56\x02\xbc\xd9\x6f\x1a\x6f\x52\x36\x6e\x75\x56\x0a"
+ "\x55\xd3\xdf\x43\x87\x21\x11\x65\x8a\x7e\x8f\xbd\x21\xde\x6b\x32"
+ "\x3f\x1b\x84\x34\x95\x05\x9d\x41\x35\xeb\x92\xeb\x96\xdd\xaa\x59"
+ "\x3f\x01\x53\x6d\x99\x4f\xed\xe5\xe2\x2a\x5a\x90\xc1\xb9\xc4\xa6"
+ "\x15\xcf\xc8\x45\xeb\xa6\x5d\x8e\x9c\x3e\xf0\x64\x24\x76\xa5\xcd"
+ "\xab\x1a\x6f\xb6\xd8\x7b\x51\x61\x6e\xa6\x7f\x87\xc8\xe2\xb7\xe5"
+ "\x34\xdc\x41\x88\xea\x09\x40\xbe\x73\x92\x3d\x6b\xe7\x75\x02\x03"
+ "\x01\x00\x01\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05"
+ "\x05\x00\x03\x81\x81\x00\x8b\xf7\x1a\x10\xce\x76\x5c\x07\xab\x83"
+ "\x99\xdc\x17\x80\x6f\x34\x39\x5d\x98\x3e\x6b\x72\x2c\xe1\xc7\xa2"
+ "\x7b\x40\x29\xb9\x78\x88\xba\x4c\xc5\xa3\x6a\x5e\x9e\x6e\x7b\xe3"
+ "\xf2\x02\x41\x0c\x66\xbe\xad\xfb\xae\xa2\x14\xce\x92\xf3\xa2\x34"
+ "\x8b\xb4\xb2\xb6\x24\xf2\xe5\xd5\xe0\xc8\xe5\x62\x6d\x84\x7b\xcb"
+ "\xbe\xbb\x03\x8b\x7c\x57\xca\xf0\x37\xa9\x90\xaf\x8a\xee\x03\xbe"
+ "\x1d\x28\x9c\xd9\x26\x76\xa0\xcd\xc4\x9d\x4e\xf0\xae\x07\x16\xd5"
+ "\xbe\xaf\x57\x08\x6a\xd0\xa0\x42\x42\x42\x1e\xf4\x20\xcc\xa5\x78"
+ "\x82\x95\x26\x38\x8a\x47";
+
+char certdata3[] =
+ "\x30\x82\x04\x43\x30\x82\x03\x2b\xa0\x03\x02\x01\x02\x02\x01\x01"
+ "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30"
+ "\x7f\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x47\x42\x31\x1b"
+ "\x30\x19\x06\x03\x55\x04\x08\x0c\x12\x47\x72\x65\x61\x74\x65\x72"
+ "\x20\x4d\x61\x6e\x63\x68\x65\x73\x74\x65\x72\x31\x10\x30\x0e\x06"
+ "\x03\x55\x04\x07\x0c\x07\x53\x61\x6c\x66\x6f\x72\x64\x31\x1a\x30"
+ "\x18\x06\x03\x55\x04\x0a\x0c\x11\x43\x6f\x6d\x6f\x64\x6f\x20\x43"
+ "\x41\x20\x4c\x69\x6d\x69\x74\x65\x64\x31\x25\x30\x23\x06\x03\x55"
+ "\x04\x03\x0c\x1c\x54\x72\x75\x73\x74\x65\x64\x20\x43\x65\x72\x74"
+ "\x69\x66\x69\x63\x61\x74\x65\x20\x53\x65\x72\x76\x69\x63\x65\x73"
+ "\x30\x1e\x17\x0d\x30\x34\x30\x31\x30\x31\x30\x30\x30\x30\x30\x30"
+ "\x5a\x17\x0d\x32\x38\x31\x32\x33\x31\x32\x33\x35\x39\x35\x39\x5a"
+ "\x30\x7f\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x47\x42\x31"
+ "\x1b\x30\x19\x06\x03\x55\x04\x08\x0c\x12\x47\x72\x65\x61\x74\x65"
+ "\x72\x20\x4d\x61\x6e\x63\x68\x65\x73\x74\x65\x72\x31\x10\x30\x0e"
+ "\x06\x03\x55\x04\x07\x0c\x07\x53\x61\x6c\x66\x6f\x72\x64\x31\x1a"
+ "\x30\x18\x06\x03\x55\x04\x0a\x0c\x11\x43\x6f\x6d\x6f\x64\x6f\x20"
+ "\x43\x41\x20\x4c\x69\x6d\x69\x74\x65\x64\x31\x25\x30\x23\x06\x03"
+ "\x55\x04\x03\x0c\x1c\x54\x72\x75\x73\x74\x65\x64\x20\x43\x65\x72"
+ "\x74\x69\x66\x69\x63\x61\x74\x65\x20\x53\x65\x72\x76\x69\x63\x65"
+ "\x73\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
+ "\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01"
+ "\x01\x00\xdf\x71\x6f\x36\x58\x53\x5a\xf2\x36\x54\x57\x80\xc4\x74"
+ "\x08\x20\xed\x18\x7f\x2a\x1d\xe6\x35\x9a\x1e\x25\xac\x9c\xe5\x96"
+ "\x7e\x72\x52\xa0\x15\x42\xdb\x59\xdd\x64\x7a\x1a\xd0\xb8\x7b\xdd"
+ "\x39\x15\xbc\x55\x48\xc4\xed\x3a\x00\xea\x31\x11\xba\xf2\x71\x74"
+ "\x1a\x67\xb8\xcf\x33\xcc\xa8\x31\xaf\xa3\xe3\xd7\x7f\xbf\x33\x2d"
+ "\x4c\x6a\x3c\xec\x8b\xc3\x92\xd2\x53\x77\x24\x74\x9c\x07\x6e\x70"
+ "\xfc\xbd\x0b\x5b\x76\xba\x5f\xf2\xff\xd7\x37\x4b\x4a\x60\x78\xf7"
+ "\xf0\xfa\xca\x70\xb4\xea\x59\xaa\xa3\xce\x48\x2f\xa9\xc3\xb2\x0b"
+ "\x7e\x17\x72\x16\x0c\xa6\x07\x0c\x1b\x38\xcf\xc9\x62\xb7\x3f\xa0"
+ "\x93\xa5\x87\x41\xf2\xb7\x70\x40\x77\xd8\xbe\x14\x7c\xe3\xa8\xc0"
+ "\x7a\x8e\xe9\x63\x6a\xd1\x0f\x9a\xc6\xd2\xf4\x8b\x3a\x14\x04\x56"
+ "\xd4\xed\xb8\xcc\x6e\xf5\xfb\xe2\x2c\x58\xbd\x7f\x4f\x6b\x2b\xf7"
+ "\x60\x24\x58\x24\xce\x26\xef\x34\x91\x3a\xd5\xe3\x81\xd0\xb2\xf0"
+ "\x04\x02\xd7\x5b\xb7\x3e\x92\xac\x6b\x12\x8a\xf9\xe4\x05\xb0\x3b"
+ "\x91\x49\x5c\xb2\xeb\x53\xea\xf8\x9f\x47\x86\xee\xbf\x95\xc0\xc0"
+ "\x06\x9f\xd2\x5b\x5e\x11\x1b\xf4\xc7\x04\x35\x29\xd2\x55\x5c\xe4"
+ "\xed\xeb\x02\x03\x01\x00\x01\xa3\x81\xc9\x30\x81\xc6\x30\x1d\x06"
+ "\x03\x55\x1d\x0e\x04\x16\x04\x14\xc5\x7b\x58\xbd\xed\xda\x25\x69"
+ "\xd2\xf7\x59\x16\xa8\xb3\x32\xc0\x7b\x27\x5b\xf4\x30\x0e\x06\x03"
+ "\x55\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x06\x30\x0f\x06\x03"
+ "\x55\x1d\x13\x01\x01\xff\x04\x05\x30\x03\x01\x01\xff\x30\x81\x83"
+ "\x06\x03\x55\x1d\x1f\x04\x7c\x30\x7a\x30\x3c\xa0\x3a\xa0\x38\x86"
+ "\x36\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x2e\x63\x6f\x6d\x6f"
+ "\x64\x6f\x63\x61\x2e\x63\x6f\x6d\x2f\x54\x72\x75\x73\x74\x65\x64"
+ "\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x65\x53\x65\x72\x76\x69"
+ "\x63\x65\x73\x2e\x63\x72\x6c\x30\x3a\xa0\x38\xa0\x36\x86\x34\x68"
+ "\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x2e\x63\x6f\x6d\x6f\x64\x6f"
+ "\x2e\x6e\x65\x74\x2f\x54\x72\x75\x73\x74\x65\x64\x43\x65\x72\x74"
+ "\x69\x66\x69\x63\x61\x74\x65\x53\x65\x72\x76\x69\x63\x65\x73\x2e"
+ "\x63\x72\x6c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05"
+ "\x05\x00\x03\x82\x01\x01\x00\xc8\x93\x81\x3b\x89\xb4\xaf\xb8\x84"
+ "\x12\x4c\x8d\xd2\xf0\xdb\x70\xba\x57\x86\x15\x34\x10\xb9\x2f\x7f"
+ "\x1e\xb0\xa8\x89\x60\xa1\x8a\xc2\x77\x0c\x50\x4a\x9b\x00\x8b\xd8"
+ "\x8b\xf4\x41\xe2\xd0\x83\x8a\x4a\x1c\x14\x06\xb0\xa3\x68\x05\x70"
+ "\x31\x30\xa7\x53\x9b\x0e\xe9\x4a\xa0\x58\x69\x67\x0e\xae\x9d\xf6"
+ "\xa5\x2c\x41\xbf\x3c\x06\x6b\xe4\x59\xcc\x6d\x10\xf1\x96\x6f\x1f"
+ "\xdf\xf4\x04\x02\xa4\x9f\x45\x3e\xc8\xd8\xfa\x36\x46\x44\x50\x3f"
+ "\x82\x97\x91\x1f\x28\xdb\x18\x11\x8c\x2a\xe4\x65\x83\x57\x12\x12"
+ "\x8c\x17\x3f\x94\x36\xfe\x5d\xb0\xc0\x04\x77\x13\xb8\xf4\x15\xd5"
+ "\x3f\x38\xcc\x94\x3a\x55\xd0\xac\x98\xf5\xba\x00\x5f\xe0\x86\x19"
+ "\x81\x78\x2f\x28\xc0\x7e\xd3\xcc\x42\x0a\xf5\xae\x50\xa0\xd1\x3e"
+ "\xc6\xa1\x71\xec\x3f\xa0\x20\x8c\x66\x3a\x89\xb4\x8e\xd4\xd8\xb1"
+ "\x4d\x25\x47\xee\x2f\x88\xc8\xb5\xe1\x05\x45\xc0\xbe\x14\x71\xde"
+ "\x7a\xfd\x8e\x7b\x7d\x4d\x08\x96\xa5\x12\x73\xf0\x2d\xca\x37\x27"
+ "\x74\x12\x27\x4c\xcb\xb6\x97\xe9\xd9\xae\x08\x6d\x5a\x39\x40\xdd"
+ "\x05\x47\x75\x6a\x5a\x21\xb3\xa3\x18\xcf\x4e\xf7\x2e\x57\xb7\x98"
+ "\x70\x5e\xc8\xc4\x78\xb0\x62";
+
+
+static int
+compare_subject(hx509_cert c1, hx509_cert c2, int *l)
+{
+ hx509_name n1, n2;
+ int ret;
+
+ ret = hx509_cert_get_subject(c1, &n1);
+ if (ret) return 1;
+ ret = hx509_cert_get_subject(c2, &n2);
+ if (ret) return 1;
+
+ *l = hx509_name_cmp(n1, n2);
+ hx509_name_free(&n1);
+ hx509_name_free(&n2);
+
+ return 0;
+}
+
+static int
+test_compare(hx509_context context)
+{
+ int ret;
+ hx509_cert c1, c2, c3;
+ int l0, l1, l2, l3;
+
+ /* check transative properties of name compare function */
+
+ c1 = hx509_cert_init_data(context, certdata1, sizeof(certdata1) - 1, NULL);
+ if (c1 == NULL) return 1;
+
+ c2 = hx509_cert_init_data(context, certdata2, sizeof(certdata2) - 1, NULL);
+ if (c2 == NULL) return 1;
+
+ c3 = hx509_cert_init_data(context, certdata3, sizeof(certdata3) - 1, NULL);
+ if (c3 == NULL) return 1;
+
+ ret = compare_subject(c1, c1, &l0);
+ if (ret) return 1;
+ ret = compare_subject(c1, c2, &l1);
+ if (ret) return 1;
+ ret = compare_subject(c1, c3, &l2);
+ if (ret) return 1;
+ ret = compare_subject(c2, c3, &l3);
+ if (ret) return 1;
+
+ if (l0 != 0) return 1;
+ if (l2 < l1) return 1;
+ if (l3 < l2) return 1;
+ if (l3 < l1) return 1;
+
+ hx509_cert_free(c1);
+ hx509_cert_free(c2);
+ hx509_cert_free(c3);
+
+ return 0;
+}
+
+static int
+test_pkinit_san(hx509_context context, const char *p, const char *realm, ...)
+{
+ KRB5PrincipalName kn;
+ GeneralName gn;
+ va_list ap;
+ size_t i, sz;
+ char *round_trip;
+ int ret;
+
+ memset(&kn, 0, sizeof(kn));
+ memset(&gn, 0, sizeof(gn));
+
+ ret = _hx509_make_pkinit_san(context, p, &gn.u.otherName.value);
+ if (ret == 0)
+ ret = decode_KRB5PrincipalName(gn.u.otherName.value.data,
+ gn.u.otherName.value.length, &kn, &sz);
+ if (ret)
+ return 1;
+ if (strcmp(realm, kn.realm) != 0)
+ return 1;
+
+ va_start(ap, realm);
+ for (i = 0; i < kn.principalName.name_string.len; i++) {
+ const char *s = va_arg(ap, const char *);
+
+ if (s == NULL || strcmp(kn.principalName.name_string.val[i], s) != 0)
+ return 1;
+ }
+ if (va_arg(ap, const char *) != NULL)
+ return 1;
+ va_end(ap);
+
+ gn.element = choice_GeneralName_otherName;
+ gn.u.otherName.type_id.length = 0;
+ gn.u.otherName.type_id.components = 0;
+ ret = der_copy_oid(&asn1_oid_id_pkinit_san, &gn.u.otherName.type_id);
+ if (ret == 0)
+ ret = hx509_general_name_unparse(&gn, &round_trip);
+ if (ret)
+ return 1;
+ if (strncmp(round_trip, "otherName: 1.3.6.1.5.2.2 KerberosPrincipalName ",
+ sizeof("otherName: 1.3.6.1.5.2.2 KerberosPrincipalName ") - 1))
+ return 1;
+ if (ret || strcmp(round_trip + sizeof("otherName: 1.3.6.1.5.2.2 KerberosPrincipalName ") - 1, p) != 0)
+ return 1;
+ free_KRB5PrincipalName(&kn);
+ free_GeneralName(&gn);
+ free(round_trip);
+ return 0;
+}
+
+static int
+test_pkinit_san_fail(hx509_context context, const char *p)
+{
+ heim_octet_string os;
+ KRB5PrincipalName kn;
+ int ret;
+
+ memset(&kn, 0, sizeof(kn));
+ ret = _hx509_make_pkinit_san(context, p, &os);
+ if (ret == 0) {
+ free(os.data);
+ return 1;
+ }
+ return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ hx509_context context;
+ int ret = 0;
+
+ ret = hx509_context_init(&context);
+ if (ret)
+ errx(1, "hx509_context_init failed with %d", ret);
+
+ ret += test_name(context, "CN=foo,C=SE");
+ ret += test_name(context, "CN=foo,CN=kaka,CN=FOO,DC=ad1,C=SE");
+ ret += test_name(context, "1.2.3.4=foo,C=SE");
+ ret += test_name_fail(context, "=");
+ ret += test_name_fail(context, "CN=foo,=foo");
+ ret += test_name_fail(context, "CN=foo,really-unknown-type=foo");
+
+ ret += test_expand(context, "UID=${uid},C=SE", "UID=lha,C=SE");
+ ret += test_expand(context, "UID=foo${uid},C=SE", "UID=foolha,C=SE");
+ ret += test_expand(context, "UID=${uid}bar,C=SE", "UID=lhabar,C=SE");
+ ret += test_expand(context, "UID=f${uid}b,C=SE", "UID=flhab,C=SE");
+ ret += test_expand(context, "UID=${uid}${uid},C=SE", "UID=lhalha,C=SE");
+ ret += test_expand(context, "UID=${uid}{uid},C=SE", "UID=lha{uid},C=SE");
+
+ ret += test_compare(context);
+
+ ret += test_pkinit_san(context, "foo@BAR.H5L.SE",
+ "BAR.H5L.SE", "foo", NULL);
+ ret += test_pkinit_san(context, "foo\\ bar@BAR.H5L.SE",
+ "BAR.H5L.SE", "foo bar", NULL);
+ ret += test_pkinit_san(context, "foo\\/bar@BAR.H5L.SE",
+ "BAR.H5L.SE", "foo/bar", NULL);
+ ret += test_pkinit_san(context, "foo/bar@BAR.H5L.SE",
+ "BAR.H5L.SE", "foo", "bar", NULL);
+ ret += test_pkinit_san(context, "foo\\tbar@BAR.H5L.SE",
+ "BAR.H5L.SE", "foo\tbar", NULL);
+ ret += test_pkinit_san(context, "foo\\nbar@BAR.H5L.SE",
+ "BAR.H5L.SE", "foo\nbar", NULL);
+ ret += test_pkinit_san(context, "foo@\\ BAR.H5L.SE",
+ " BAR.H5L.SE", "foo", NULL);
+ ret += test_pkinit_san(context, "foo@\\nBAR.H5L.SE",
+ "\nBAR.H5L.SE", "foo", NULL);
+ ret += test_pkinit_san_fail(context, "foo\\0bar@BAR.H5L.SE");
+
+ hx509_context_free(&context);
+
+ return !!ret;
+}
diff --git a/third_party/heimdal/lib/hx509/test_nist.in b/third_party/heimdal/lib/hx509/test_nist.in
new file mode 100644
index 0000000..09034fe
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_nist.in
@@ -0,0 +1,117 @@
+#!/bin/sh
+#
+# Copyright (c) 2004 - 2005 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+nistdir=${objdir}/PKITS_data
+nistzip=${srcdir}/data/PKITS_data.zip
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+# nistzip is not distributed part of the distribution
+test -f "$nistzip" || exit 77
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+echo "nist tests"
+
+if [ ! -d "$nistdir" ] ; then
+ ( mkdir "$nistdir" && unzip -d "${nistdir}" "${nistzip}" ) >/dev/null || \
+ { rm -rf "$nistdir" ; exit 1; }
+fi
+
+ec=0
+while read id verify cert arg1 arg2 arg3 arg4 arg5 ; do
+ expr "$id" : "#" > /dev/null && continue
+
+ test "$id" = "end" && break
+
+ args=""
+ case "$arg1" in
+ *.crt) args="$args chain:FILE:$nistdir/certs/$arg1" ;;
+ *.crl) args="$args crl:FILE:$nistdir/crls/$arg1" ;;
+ *) args="$args $arg1" ;;
+ esac
+ case "$arg2" in
+ *.crt) args="$args chain:FILE:$nistdir/certs/$arg2" ;;
+ *.crl) args="$args crl:FILE:$nistdir/crls/$arg2" ;;
+ *) args="$args $arg2" ;;
+ esac
+ case "$arg3" in
+ *.crt) args="$args chain:FILE:$nistdir/certs/$arg3" ;;
+ *.crl) args="$args crl:FILE:$nistdir/crls/$arg3" ;;
+ *) args="$args $arg3" ;;
+ esac
+ case "$arg4" in
+ *.crt) args="$args chain:FILE:$nistdir/certs/$arg4" ;;
+ *.crl) args="$args crl:FILE:$nistdir/crls/$arg4" ;;
+ *) args="$args $arg4" ;;
+ esac
+ case "$arg5" in
+ *.crt) args="$args chain:FILE:$nistdir/certs/$arg5" ;;
+ *.crl) args="$args crl:FILE:$nistdir/crls/$arg5" ;;
+ *) args="$args $arg5" ;;
+ esac
+
+ args="$args anchor:FILE:$nistdir/certs/TrustAnchorRootCertificate.crt"
+ args="$args crl:FILE:$nistdir/crls/TrustAnchorRootCRL.crl"
+ args="$args cert:FILE:$nistdir/certs/$cert"
+
+ if ${hxtool} verify --time=2008-05-20 $args > /dev/null; then
+ if test "$verify" = "f"; then
+ echo ${hxtool} verify --time=2008-05-20 $args
+ echo "verify passed on fail: $id $cert"
+ ec=1
+ fi
+ elif test "$verify" = "p"; then
+ echo ${hxtool} verify --time=2008-05-20 $args
+ echo "verify failed on pass: $id $cert"
+ ec=1
+ fi
+
+done < $srcdir/data/nist-data
+
+
+echo "done!"
+
+exit $ec
diff --git a/third_party/heimdal/lib/hx509/test_nist2.in b/third_party/heimdal/lib/hx509/test_nist2.in
new file mode 100644
index 0000000..0c4276b
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_nist2.in
@@ -0,0 +1,136 @@
+#!/bin/sh
+#
+# Copyright (c) 2004 - 2008 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id: test_nist.in 21787 2007-08-02 08:50:24Z lha $
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+nistdir=${objdir}/PKITS_data
+nistzip=${srcdir}/data/PKITS_data.zip
+egrep="@egrep@"
+
+limit="${1:-nolimit}"
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+# nistzip is not distributed part of the distribution
+test -f "$nistzip" || exit 77
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+#--------- Try to find unzip
+
+oldifs=$IFS
+IFS=:
+set -- $PATH
+IFS=$oldifs
+found=
+
+for p in "$@" ; do
+ test -x "$p/unzip" && { found=1 ; break; }
+done
+test "X$found" = "X" && exit 77
+
+#---------
+
+
+echo "nist tests, version 2"
+
+if [ ! -d "$nistdir" ] ; then
+ ( mkdir "$nistdir" && unzip -d "${nistdir}" "${nistzip}" ) >/dev/null || \
+ { rm -rf "$nistdir" ; exit 1; }
+fi
+
+ec=
+name=
+description=
+while read result cert other ; do
+ if expr "$result" : "#" > /dev/null; then
+ name=${cert}
+ description="${other}"
+ continue
+ fi
+
+ test nolimit != "${limit}" && ! expr "$name" : "$limit" > /dev/null && continue
+
+ test "$result" = "end" && break
+
+ args=
+ args="$args cert:FILE:$nistdir/certs/$cert"
+ args="$args chain:DIR:$nistdir/certs"
+ args="$args anchor:FILE:$nistdir/certs/TrustAnchorRootCertificate.crt"
+
+ for a in $nistdir/crls/*.crl; do
+ args="$args crl:FILE:$a"
+ done
+
+ cmd="${hxtool} verify --time=2008-05-20 $args"
+ eval ${cmd} > /dev/null
+ res=$?
+
+ case "${result},${res}" in
+ 0,0) r="PASSs";;
+ 0,*) r="FAILs";;
+ [123],0) r="FAILf";;
+ [123],*) r="PASSf";;
+ *) echo="unknown result ${result},${res}" ; exit 1 ;;
+ esac
+ if ${egrep} "^${name} FAIL" $srcdir/data/nist-result2 > /dev/null; then
+ if expr "$r" : "PASS" >/dev/null; then
+ echo "${name} passed when expected not to"
+ echo "# ${description}" > nist2-passed-${name}.tmp
+ ec=1
+ fi
+ elif ${egrep} "^${name} EITHER" $srcdir/data/nist-result2 > /dev/null; then
+ :
+ elif expr "$r" : "FAIL.*" >/dev/null ; then
+ echo "$r ${name} ${description}"
+ echo "# ${description}" > nist2-failed-${name}.tmp
+ echo "$cmd" >> nist2-failed-${name}.tmp
+ ec=1
+ fi
+
+done < $srcdir/data/nist-data2
+
+
+echo "done!"
+
+exit $ec
diff --git a/third_party/heimdal/lib/hx509/test_nist_cert.in b/third_party/heimdal/lib/hx509/test_nist_cert.in
new file mode 100644
index 0000000..8c683d6
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_nist_cert.in
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+nistdir=${objdir}/PKITS_data
+nistzip=${srcdir}/data/PKITS_data.zip
+
+# nistzip is not distributed part of the distribution
+test -f "$nistzip" || exit 77
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+if [ ! -d "$nistdir" ] ; then
+ ( mkdir "$nistdir" && cd "$nistdir" && unzip "$nistzip" ) >/dev/null || \
+ { rm -rf "$nistdir" ; exit 1; }
+fi
+
+if ${hxtool} validate DIR:$nistdir/certs > /dev/null; then
+ :
+else
+ echo "validate failed"
+ exit 1
+fi
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_nist_pkcs12.in b/third_party/heimdal/lib/hx509/test_nist_pkcs12.in
new file mode 100644
index 0000000..7898eee
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_nist_pkcs12.in
@@ -0,0 +1,77 @@
+#!/bin/sh
+#
+# Copyright (c) 2004 - 2005 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+pass="--pass=PASS:password"
+nistdir=${objdir}/PKITS_data
+nistzip=${srcdir}/data/PKITS_data.zip
+
+# nistzip is not distributed part of the distribution
+test -f "$nistzip" || exit 77
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+if [ ! -d "$nistdir" ] ; then
+ ( mkdir "$nistdir" && cd "$nistdir" && unzip "$nistzip" ) >/dev/null || \
+ { rm -rf "$nistdir" ; exit 1; }
+fi
+
+echo "nist pkcs12 tests"
+
+for a in $nistdir/pkcs12/*.p12 ; do
+
+ if ${hxtool} validate $pass PKCS12:$a > /dev/null; then
+ :
+ else
+ echo "$a failed"
+ exit 1
+ fi
+
+done
+
+echo "done!"
+
+exit 0 \ No newline at end of file
diff --git a/third_party/heimdal/lib/hx509/test_pkcs11.in b/third_party/heimdal/lib/hx509/test_pkcs11.in
new file mode 100644
index 0000000..278296a
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_pkcs11.in
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+SOFTPKCS11RC="test-rc-file.rc" \
+export SOFTPKCS11RC
+
+echo "password less"
+
+cat > test-rc-file.rc <<EOF
+certificate cert User certificate FILE:$srcdir/data/test.crt,$srcdir/data/test.key
+debug p11dbg.log
+app-fatal true
+EOF
+
+./test_soft_pkcs11 || exit 1
+
+echo "password"
+
+cat > test-rc-file.rc <<EOF
+certificate cert User certificate FILE:$srcdir/data/test.crt,$srcdir/data/test-pw.key
+debug p11dbg.log
+app-fatal true
+EOF
+
+./test_soft_pkcs11 || exit 1
+
+echo "done"
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_query.in b/third_party/heimdal/lib/hx509/test_query.in
new file mode 100644
index 0000000..d29d78a
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_query.in
@@ -0,0 +1,203 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 - 2008 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+echo "try printing"
+${hxtool} print \
+ --pass=PASS:foobar \
+ --info --content \
+ PKCS12:$srcdir/data/test.p12 >/dev/null 2>/dev/null || exit 1
+
+echo "try printing"
+${hxtool} print \
+ --pass=PASS:foobar \
+ --info --content \
+ FILE:$srcdir/data/kdc.crt >/dev/null 2>/dev/null || exit 1
+
+${hxtool} print \
+ --pass=PASS:foobar \
+ --info \
+ PKCS12:$srcdir/data/test.p12 >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is found (friendlyname)"
+${hxtool} query \
+ --pass=PASS:foobar \
+ --friendlyname=friendlyname-test \
+ PKCS12:$srcdir/data/test.p12 >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is not found (friendlyname)"
+${hxtool} query \
+ --pass=PASS:foobar \
+ --friendlyname=friendlyname-test-not \
+ PKCS12:$srcdir/data/test.p12 >/dev/null 2>/dev/null && exit 1
+
+echo "make sure entry is found (eku)"
+${hxtool} query \
+ --eku=1.3.6.1.5.2.3.5 \
+ FILE:$srcdir/data/kdc.crt >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is not found (eku)"
+${hxtool} query \
+ --eku=1.3.6.1.5.2.3.6 \
+ FILE:$srcdir/data/kdc.crt >/dev/null 2>/dev/null && exit 1
+
+echo "make sure entry is found (friendlyname, no-pw)"
+${hxtool} query \
+ --friendlyname=friendlyname-cert \
+ PKCS12:$srcdir/data/test-nopw.p12 >/dev/null 2>/dev/null || exit 1
+
+echo "check for ca cert (friendlyname)"
+${hxtool} query \
+ --pass=PASS:foobar \
+ --friendlyname=ca \
+ PKCS12:$srcdir/data/test.p12 >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is not found (friendlyname)"
+${hxtool} query \
+ --pass=PASS:foobar \
+ --friendlyname=friendlyname-test \
+ PKCS12:$srcdir/data/sub-cert.p12 >/dev/null 2>/dev/null && exit 1
+
+echo "make sure entry is found (friendlyname|private key)"
+${hxtool} query \
+ --pass=PASS:foobar \
+ --friendlyname=friendlyname-test \
+ --private-key \
+ PKCS12:$srcdir/data/test.p12 > /dev/null || exit 1
+
+echo "make sure entry is not found (friendlyname|private key)"
+${hxtool} query \
+ --pass=PASS:foobar \
+ --friendlyname=ca \
+ --private-key \
+ PKCS12:$srcdir/data/test.p12 >/dev/null 2>/dev/null && exit 1
+
+echo "make sure entry is found (cert ds)"
+${hxtool} query \
+ --digitalSignature \
+ FILE:$srcdir/data/test.crt >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is found (cert ke)"
+${hxtool} query \
+ --keyEncipherment \
+ FILE:$srcdir/data/test.crt >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is found (cert ke + ds)"
+${hxtool} query \
+ --digitalSignature \
+ --keyEncipherment \
+ FILE:$srcdir/data/test.crt >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is found (cert-ds ds)"
+${hxtool} query \
+ --digitalSignature \
+ FILE:$srcdir/data/test-ds-only.crt >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is not found (cert-ds ke)"
+${hxtool} query \
+ --keyEncipherment \
+ FILE:$srcdir/data/test-ds-only.crt >/dev/null 2>/dev/null && exit 1
+
+echo "make sure entry is not found (cert-ds ke + ds)"
+${hxtool} query \
+ --digitalSignature \
+ --keyEncipherment \
+ FILE:$srcdir/data/test-ds-only.crt >/dev/null 2>/dev/null && exit 1
+
+echo "make sure entry is not found (cert-ke ds)"
+${hxtool} query \
+ --digitalSignature \
+ FILE:$srcdir/data/test-ke-only.crt >/dev/null 2>/dev/null && exit 1
+
+echo "make sure entry is found (cert-ke ke)"
+${hxtool} query \
+ --keyEncipherment \
+ FILE:$srcdir/data/test-ke-only.crt >/dev/null 2>/dev/null || exit 1
+
+echo "make sure entry is not found (cert-ke ke + ds)"
+${hxtool} query \
+ --digitalSignature \
+ --keyEncipherment \
+ FILE:$srcdir/data/test-ke-only.crt >/dev/null 2>/dev/null && exit 1
+
+echo "make sure entry is found (eku) in query language"
+${hxtool} query \
+ --expr='"1.3.6.1.5.2.3.5" IN %{certificate.eku}' \
+ FILE:$srcdir/data/kdc.crt > /dev/null || exit 1
+
+echo "make sure entry is not found (eku) in query language"
+${hxtool} query \
+ --expr='"1.3.6.1.5.2.3.6" IN %{certificate.eku}' \
+ FILE:$srcdir/data/kdc.crt > /dev/null && exit 1
+
+echo "make sure entry is found (subject) in query language"
+${hxtool} query \
+ --expr='%{certificate.subject} == "CN=kdc,C=SE"' \
+ FILE:$srcdir/data/kdc.crt > /dev/null || exit 1
+
+echo "make sure entry is found using TAILMATCH (subject) in query language"
+${hxtool} query \
+ --expr='%{certificate.subject} TAILMATCH "C=SE"' \
+ FILE:$srcdir/data/kdc.crt > /dev/null || exit 1
+
+echo "make sure entry is not found using TAILMATCH (subject) in query language"
+${hxtool} query \
+ --expr='%{certificate.subject} TAILMATCH "C=FI"' \
+ FILE:$srcdir/data/kdc.crt > /dev/null && exit 1
+
+echo "make sure entry is found (issuer) in query language"
+${hxtool} query \
+ --expr='%{certificate.issuer} == "C=SE,CN=hx509 Test Root CA"' \
+ FILE:$srcdir/data/kdc.crt > /dev/null || exit 1
+
+echo "make sure entry match with EKU and TAILMATCH in query language"
+${hxtool} query \
+ --expr='"1.3.6.1.5.2.3.5" IN %{certificate.eku} AND %{certificate.subject} TAILMATCH "C=SE"' \
+ FILE:$srcdir/data/kdc.crt > /dev/null || exit 1
+
+echo "make sure entry match with hash.sha1"
+${hxtool} query \
+ --expr='"%{certificate.hash.sha1}EQ "412120212A2CBFD777DE5499ECB4724345F33F16"' \
+ FILE:$srcdir/data/kdc.crt > /dev/null || exit 1
+
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/test_req.in b/third_party/heimdal/lib/hx509/test_req.in
new file mode 100644
index 0000000..a070b1d
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_req.in
@@ -0,0 +1,135 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+${hxtool} request-create \
+ --subject="CN=Love,DC=it,DC=su,DC=se" \
+ --key="FILE:$srcdir/data/key.der" \
+ "${objdir}/request.out" || exit 1
+
+${hxtool} request-print \
+ PKCS10:request.out > /dev/null || exit 1
+
+${hxtool} request-create \
+ --subject="CN=Love,DC=it,DC=su,DC=se" \
+ --eku=1.2.3.4.5.6.7 --eku=1.2.3.4.5.6.8 \
+ --registered=1.2.3.4.5.6.9 --eku=1.2.3.4.5.6.10 \
+ --dnsname=nutcracker.test.h5l.se \
+ --dnsname=foo.nutcracker.test.h5l.se \
+ --kerberos=HTTP/foo.nutcracker.it.su.se@TEST.H5L.SE \
+ --kerberos=host/foo.nutcracker.it.su.se@TEST.H5L.SE \
+ --email=foo@test.h5l.se \
+ --key="FILE:$srcdir/data/key.der" \
+ "${objdir}/request.out" || exit 1
+
+cat > "$objdir/expected" <<EOF
+request print
+PKCS#10 CertificationRequest:
+ name: CN=Love,DC=it,DC=su,DC=se
+ eku: {1.2.3.4.5.6.7}, {1.2.3.4.5.6.8}, {1.2.3.4.5.6.10}
+ san: rfc822Name: foo@test.h5l.se
+ san: dNSName: nutcracker.test.h5l.se
+ san: dNSName: foo.nutcracker.test.h5l.se
+ san: pkinit: HTTP/foo.nutcracker.it.su.se@TEST.H5L.SE
+ san: pkinit: host/foo.nutcracker.it.su.se@TEST.H5L.SE
+ san: registeredID: 1.2.3.4.5.6.9
+EOF
+
+# Check that we got what we wanted:
+${hxtool} request-print \
+ PKCS10:request.out > "${objdir}/actual" || exit 1
+
+diff "$objdir/expected" "${objdir}/actual" || exit 1
+
+# Check that OpenSSL can parse our request:
+if openssl version > /dev/null; then
+ openssl req -inform DER -in "${objdir}/request.out" -text | head -25 > "${objdir}/actual"
+
+ # Various versions of openssl differ slightly in their text output for our
+ # CSR. Figure out what to expect:
+ if grep "Version: 0" "${objdir}/actual" > /dev/null; then
+ v=0
+ else
+ v=1
+ fi
+ if grep "RSA Public-Key:" "${objdir}/actual" > /dev/null; then
+ k="RSA "
+ else
+ k=""
+ fi
+ # Note interpolation of $v and $k in the here doc below:
+ cat > "$objdir/expected" <<EOF
+Certificate Request:
+ Data:
+ Version: $v (0x0)
+ Subject: DC = se, DC = su, DC = it, CN = Love
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ ${k}Public-Key: (1024 bit)
+ Modulus:
+ 00:c2:aa:a2:42:b7:5b:99:a3:fd:ba:f0:9b:75:db:
+ ef:3c:9b:8c:cf:63:5f:46:d8:95:be:09:4a:a7:76:
+ 79:77:61:30:ef:0b:98:d2:47:ea:9c:09:b9:b9:b7:
+ 15:ac:4b:9c:2d:3f:f0:d9:99:9d:4d:5a:68:67:24:
+ 58:5e:65:60:13:9f:4d:dc:2f:03:1d:cd:e9:b6:33:
+ c2:5c:c6:de:c9:93:6c:ec:8d:9a:67:0e:dd:31:20:
+ ac:91:39:7a:c1:8f:39:65:ff:b3:1f:cf:7a:aa:79:
+ 8b:ed:eb:ad:a0:be:01:10:4c:5a:a7:47:1d:c6:ee:
+ 79:39:5c:c7:11:6c:b9:e7:2b
+ Exponent: 65537 (0x10001)
+ Attributes:
+ Requested Extensions:
+ X509v3 Extended Key Usage: critical
+ 1.2.3.4.5.6.7, 1.2.3.4.5.6.8, 1.2.3.4.5.6.10
+ X509v3 Subject Alternative Name:
+ email:foo@test.h5l.se, DNS:nutcracker.test.h5l.se, DNS:foo.nutcracker.test.h5l.se, othername:<unsupported>, othername:<unsupported>, Registered ID:1.2.3.4.5.6.9
+ Signature Algorithm: sha256WithRSAEncryption
+EOF
+ diff -w "${objdir}/expected" "${objdir}/actual" || exit 1
+fi
diff --git a/third_party/heimdal/lib/hx509/test_soft_pkcs11.c b/third_party/heimdal/lib/hx509/test_soft_pkcs11.c
new file mode 100644
index 0000000..cdffcf8
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_soft_pkcs11.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include "ref/pkcs11.h"
+#include <err.h>
+
+static CK_FUNCTION_LIST_PTR func;
+
+
+static CK_RV
+find_object(CK_SESSION_HANDLE session,
+ char *id,
+ CK_OBJECT_CLASS key_class,
+ CK_OBJECT_HANDLE_PTR object)
+{
+ CK_ULONG object_count;
+ CK_RV ret;
+ CK_ATTRIBUTE search_data[] = {
+ {CKA_ID, id, 0 },
+ {CKA_CLASS, &key_class, sizeof(key_class)}
+ };
+ CK_ULONG num_search_data = sizeof(search_data)/sizeof(search_data[0]);
+
+ search_data[0].ulValueLen = strlen(id);
+
+ ret = (*func->C_FindObjectsInit)(session, search_data, num_search_data);
+ if (ret != CKR_OK)
+ return ret;
+
+ ret = (*func->C_FindObjects)(session, object, 1, &object_count);
+ if (ret != CKR_OK)
+ return ret;
+ if (object_count == 0) {
+ printf("found no object\n");
+ return 1;
+ }
+
+ ret = (*func->C_FindObjectsFinal)(session);
+ if (ret != CKR_OK)
+ return ret;
+
+ return CKR_OK;
+}
+
+static char *sighash = "hej";
+static char signature[1024];
+
+
+int
+main(int argc, char **argv)
+{
+ CK_SLOT_ID_PTR slot_ids;
+ CK_SLOT_ID slot;
+ CK_ULONG num_slots;
+ CK_RV ret;
+ CK_SLOT_INFO slot_info;
+ CK_TOKEN_INFO token_info;
+ CK_SESSION_HANDLE session;
+ CK_OBJECT_HANDLE public, private;
+
+ ret = C_GetFunctionList(&func);
+ if (ret != CKR_OK)
+ errx(1, "C_GetFunctionList failed: %d", (int)ret);
+
+ (*func->C_Initialize)(NULL_PTR);
+
+ ret = (*func->C_GetSlotList)(FALSE, NULL, &num_slots);
+ if (ret != CKR_OK)
+ errx(1, "C_GetSlotList1 failed: %d", (int)ret);
+
+ if (num_slots == 0)
+ errx(1, "no slots");
+
+ if ((slot_ids = calloc(1, num_slots * sizeof(*slot_ids))) == NULL)
+ err(1, "alloc slots failed");
+
+ ret = (*func->C_GetSlotList)(FALSE, slot_ids, &num_slots);
+ if (ret != CKR_OK)
+ errx(1, "C_GetSlotList2 failed: %d", (int)ret);
+
+ slot = slot_ids[0];
+ free(slot_ids);
+
+ ret = (*func->C_GetSlotInfo)(slot, &slot_info);
+ if (ret)
+ errx(1, "C_GetSlotInfo failed: %d", (int)ret);
+
+ if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0)
+ errx(1, "no token present");
+
+ ret = (*func->C_OpenSession)(slot, CKF_SERIAL_SESSION,
+ NULL, NULL, &session);
+ if (ret != CKR_OK)
+ errx(1, "C_OpenSession failed: %d", (int)ret);
+
+ ret = (*func->C_GetTokenInfo)(slot, &token_info);
+ if (ret)
+ errx(1, "C_GetTokenInfo1 failed: %d", (int)ret);
+
+ if (token_info.flags & CKF_LOGIN_REQUIRED) {
+ ret = (*func->C_Login)(session, CKU_USER,
+ (unsigned char*)"foobar", 6);
+ if (ret != CKR_OK)
+ errx(1, "C_Login failed: %d", (int)ret);
+ }
+
+ ret = (*func->C_GetTokenInfo)(slot, &token_info);
+ if (ret)
+ errx(1, "C_GetTokenInfo2 failed: %d", (int)ret);
+
+ if (token_info.flags & CKF_LOGIN_REQUIRED)
+ errx(1, "login required, even after C_Login");
+
+ ret = find_object(session, "cert", CKO_PUBLIC_KEY, &public);
+ if (ret != CKR_OK)
+ errx(1, "find cert failed: %d", (int)ret);
+ ret = find_object(session, "cert", CKO_PRIVATE_KEY, &private);
+ if (ret != CKR_OK)
+ errx(1, "find private key failed: %d", (int)ret);
+
+ {
+ CK_ULONG ck_sigsize;
+ CK_MECHANISM mechanism;
+
+ memset(&mechanism, 0, sizeof(mechanism));
+ mechanism.mechanism = CKM_RSA_PKCS;
+
+ ret = (*func->C_SignInit)(session, &mechanism, private);
+ if (ret != CKR_OK)
+ return 1;
+
+ ck_sigsize = sizeof(signature);
+ ret = (*func->C_Sign)(session, (CK_BYTE *)sighash, strlen(sighash),
+ (CK_BYTE *)signature, &ck_sigsize);
+ if (ret != CKR_OK) {
+ printf("C_Sign failed with: %d\n", (int)ret);
+ return 1;
+ }
+
+ ret = (*func->C_VerifyInit)(session, &mechanism, public);
+ if (ret != CKR_OK)
+ return 1;
+
+ ret = (*func->C_Verify)(session, (CK_BYTE *)signature, ck_sigsize,
+ (CK_BYTE *)sighash, strlen(sighash));
+ if (ret != CKR_OK) {
+ printf("message: %d\n", (int)ret);
+ return 1;
+ }
+ }
+
+#if 0
+ {
+ CK_ULONG ck_sigsize, outsize;
+ CK_MECHANISM mechanism;
+ char outdata[1024];
+
+ memset(&mechanism, 0, sizeof(mechanism));
+ mechanism.mechanism = CKM_RSA_PKCS;
+
+ ret = (*func->C_EncryptInit)(session, &mechanism, public);
+ if (ret != CKR_OK)
+ return 1;
+
+ ck_sigsize = sizeof(signature);
+ ret = (*func->C_Encrypt)(session, (CK_BYTE *)sighash, strlen(sighash),
+ (CK_BYTE *)signature, &ck_sigsize);
+ if (ret != CKR_OK) {
+ printf("message: %d\n", (int)ret);
+ return 1;
+ }
+
+ ret = (*func->C_DecryptInit)(session, &mechanism, private);
+ if (ret != CKR_OK)
+ return 1;
+
+ outsize = sizeof(outdata);
+ ret = (*func->C_Decrypt)(session, (CK_BYTE *)signature, ck_sigsize,
+ (CK_BYTE *)outdata, &outsize);
+ if (ret != CKR_OK) {
+ printf("message: %d\n", (int)ret);
+ return 1;
+ }
+
+ if (ct_memcmp(sighash, outdata, strlen(sighash)) != 0)
+ return 1;
+ }
+#endif
+
+ ret = (*func->C_CloseSession)(session);
+ if (ret != CKR_OK)
+ return 1;
+
+ (*func->C_Finalize)(NULL_PTR);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/hx509/test_windows.in b/third_party/heimdal/lib/hx509/test_windows.in
new file mode 100644
index 0000000..c617f81
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/test_windows.in
@@ -0,0 +1,89 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+#
+
+srcdir="@srcdir@"
+objdir="@objdir@"
+
+stat="--statistic-file=${objdir}/statfile"
+
+hxtool="${TESTS_ENVIRONMENT} ./hxtool ${stat}"
+
+if ${hxtool} info | grep 'rsa: hcrypto null RSA' > /dev/null ; then
+ exit 77
+fi
+if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
+ exit 77
+fi
+
+echo "Create trust anchor"
+${hxtool} issue-certificate \
+ --self-signed \
+ --issue-ca \
+ --generate-key=rsa \
+ --subject="CN=Windows-CA,DC=heimdal,DC=pki" \
+ --lifetime=10years \
+ --certificate="FILE:wca.pem" || exit 1
+
+echo "Create domain controller cert"
+${hxtool} issue-certificate \
+ --type="pkinit-kdc" \
+ --pk-init-principal="krbtgt/HEIMDAL.PKI@HEIMDAL.PKI" \
+ --hostname=kdc.heimdal.pki \
+ --generate-key=rsa \
+ --subject="CN=kdc.heimdal.pki,dc=heimdal,dc=pki" \
+ --certificate="FILE:wdc.pem" \
+ --domain-controller \
+ --crl-uri="http://www.test.h5l.se/test-hemdal-pki-crl1.crl" \
+ --ca-certificate=FILE:wca.pem || exit 1
+
+
+echo "Create user cert"
+${hxtool} issue-certificate \
+ --type="pkinit-client" \
+ --pk-init-principal="user@HEIMDAL.PKI" \
+ --generate-key=rsa \
+ --subject="CN=User,DC=heimdal,DC=pki" \
+ --ms-upn="user@heimdal.pki" \
+ --crl-uri="http://www.test.h5l.se/test-hemdal-pki-crl1.crl" \
+ --certificate="FILE:wuser.pem" \
+ --ca-certificate=FILE:wca.pem || exit 1
+
+echo "Create crl"
+${hxtool} crl-sign \
+ --crl-file=wcrl.crl \
+ --signer=FILE:wca.pem || exit 1
+
+exit 0
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-available1 b/third_party/heimdal/lib/hx509/tst-crypto-available1
new file mode 100644
index 0000000..71fa741
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-available1
@@ -0,0 +1,13 @@
+1.2.840.113549.1.1.11
+1.2.840.113549.1.1.5
+1.2.840.113549.1.1.5
+1.2.840.113549.1.1.4
+1.2.840.113549.1.1.2
+1.2.752.43.16.1
+2.16.840.1.101.3.4.2.1
+1.3.14.3.2.26
+1.2.840.113549.2.5
+1.2.840.113549.2.2
+1.2.840.113549.3.7
+2.16.840.1.101.3.4.1.2
+2.16.840.1.101.3.4.1.42
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-available2 b/third_party/heimdal/lib/hx509/tst-crypto-available2
new file mode 100644
index 0000000..22c0920
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-available2
@@ -0,0 +1,5 @@
+2.16.840.1.101.3.4.2.3
+2.16.840.1.101.3.4.2.2
+2.16.840.1.101.3.4.2.1
+1.3.14.3.2.26
+1.2.840.113549.2.5
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-available3 b/third_party/heimdal/lib/hx509/tst-crypto-available3
new file mode 100644
index 0000000..0b1a855
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-available3
@@ -0,0 +1,6 @@
+1.2.840.113549.1.1.11
+1.2.840.113549.1.1.5
+1.2.840.113549.1.1.5
+1.2.840.113549.1.1.4
+1.2.840.113549.1.1.2
+1.2.752.43.16.1
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-select b/third_party/heimdal/lib/hx509/tst-crypto-select
new file mode 100644
index 0000000..399c883
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-select
@@ -0,0 +1 @@
+1.2.840.113549.1.1.11
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-select1 b/third_party/heimdal/lib/hx509/tst-crypto-select1
new file mode 100644
index 0000000..c343b57
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-select1
@@ -0,0 +1 @@
+2.16.840.1.101.3.4.2.1
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-select2 b/third_party/heimdal/lib/hx509/tst-crypto-select2
new file mode 100644
index 0000000..399c883
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-select2
@@ -0,0 +1 @@
+1.2.840.113549.1.1.11
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-select3 b/third_party/heimdal/lib/hx509/tst-crypto-select3
new file mode 100644
index 0000000..ba9f29f
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-select3
@@ -0,0 +1 @@
+1.2.840.113549.1.1.4
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-select4 b/third_party/heimdal/lib/hx509/tst-crypto-select4
new file mode 100644
index 0000000..749a549
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-select4
@@ -0,0 +1 @@
+1.2.840.113549.1.1.5
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-select5 b/third_party/heimdal/lib/hx509/tst-crypto-select5
new file mode 100644
index 0000000..399c883
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-select5
@@ -0,0 +1 @@
+1.2.840.113549.1.1.11
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-select6 b/third_party/heimdal/lib/hx509/tst-crypto-select6
new file mode 100644
index 0000000..749a549
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-select6
@@ -0,0 +1 @@
+1.2.840.113549.1.1.5
diff --git a/third_party/heimdal/lib/hx509/tst-crypto-select7 b/third_party/heimdal/lib/hx509/tst-crypto-select7
new file mode 100644
index 0000000..9b0ac64
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/tst-crypto-select7
@@ -0,0 +1 @@
+2.16.840.1.101.3.4.1.42
diff --git a/third_party/heimdal/lib/hx509/version-script.map b/third_party/heimdal/lib/hx509/version-script.map
new file mode 100644
index 0000000..a6c81da
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/version-script.map
@@ -0,0 +1,302 @@
+# $Id$
+
+HEIMDAL_X509_1.2 {
+ global:
+ _hx509_cert_assign_key;
+ _hx509_cert_get_keyusage;
+ _hx509_cert_get_version;
+ _hx509_cert_private_key;
+ _hx509_certs_keys_free;
+ _hx509_certs_keys_get;
+ _hx509_expr_eval;
+ _hx509_expr_free;
+ _hx509_expr_parse;
+ _hx509_generate_private_key;
+ _hx509_generate_private_key_bits;
+ _hx509_generate_private_key_free;
+ _hx509_generate_private_key_init;
+ _hx509_generate_private_key_is_ca;
+ _hx509_get_cert;
+ _hx509_ks_type;
+ _hx509_make_pkinit_san;
+ _hx509_map_file_os;
+ _hx509_name_from_Name;
+ _hx509_private_key_export;
+ _hx509_private_key_exportable;
+ _hx509_private_key_get_internal;
+ _hx509_private_key_oid;
+ _hx509_private_key_ref;
+ hx509_request_add_GeneralName;
+ hx509_request_add_dns_name;
+ hx509_request_add_dns_srv;
+ hx509_request_add_eku;
+ hx509_request_add_email;
+ hx509_request_add_ms_upn_name;
+ hx509_request_add_pkinit;
+ hx509_request_add_registered;
+ hx509_request_add_xmpp_name;
+ hx509_request_authorize_ku;
+ hx509_request_authorize_eku;
+ hx509_request_authorize_san;
+ hx509_request_count_unsupported;
+ hx509_request_count_unauthorized;
+ hx509_request_print;
+ hx509_request_to_pkcs10;
+ _hx509_unmap_file_os;
+ _hx509_write_file;
+ hx509_bitstring_print;
+ _hx509_ca_issue_certificate;
+ hx509_ca_sign;
+ hx509_ca_sign_self;
+ hx509_ca_tbs_add_crl_dp_uri;
+ hx509_ca_tbs_add_eku;
+ hx509_ca_tbs_add_ku;
+ hx509_ca_tbs_add_pol;
+ hx509_ca_tbs_add_pol_mapping;
+ hx509_ca_tbs_add_san;
+ hx509_ca_tbs_add_san_dnssrv;
+ hx509_ca_tbs_add_san_hardwareModuleName;
+ hx509_ca_tbs_add_san_hardwareModuleName_string;
+ hx509_ca_tbs_add_san_hostname;
+ hx509_ca_tbs_add_san_jid;
+ hx509_ca_tbs_add_san_ms_upn;
+ hx509_ca_tbs_add_san_otherName;
+ hx509_ca_tbs_add_san_permanentIdentifier;
+ hx509_ca_tbs_add_san_permanentIdentifier_string;
+ hx509_ca_tbs_add_san_pkinit;
+ hx509_ca_tbs_add_san_rfc822name;
+ hx509_ca_tbs_free;
+ hx509_ca_tbs_get_name;
+ hx509_ca_tbs_init;
+ hx509_ca_tbs_set_ca;
+ hx509_ca_tbs_set_domaincontroller;
+ hx509_ca_tbs_set_from_csr;
+ hx509_ca_tbs_set_notAfter;
+ hx509_ca_tbs_set_notAfter_lifetime;
+ hx509_ca_tbs_set_notBefore;
+ hx509_ca_tbs_set_pkinit_max_life;
+ hx509_ca_tbs_set_proxy;
+ hx509_ca_tbs_set_serialnumber;
+ hx509_ca_tbs_set_spki;
+ hx509_ca_tbs_set_subject;
+ hx509_ca_tbs_set_template;
+ hx509_ca_tbs_set_unique;
+ hx509_ca_tbs_subject_expand;
+ hx509_ca_tbs_template_units;
+ hx509_cert;
+ hx509_cert_attribute;
+ hx509_cert_binary;
+ hx509_cert_check_eku;
+ hx509_cert_cmp;
+ hx509_cert_find_subjectAltName_otherName;
+ hx509_cert_free;
+ hx509_cert_get_SPKI;
+ hx509_cert_get_SPKI_AlgorithmIdentifier;
+ hx509_cert_get_attribute;
+ hx509_cert_get_base_subject;
+ hx509_cert_get_friendly_name;
+ hx509_cert_get_issuer;
+ hx509_cert_get_notAfter;
+ hx509_cert_get_notBefore;
+ hx509_cert_get_pkinit_max_life;
+ hx509_cert_get_serialnumber;
+ hx509_cert_get_subject;
+ hx509_cert_get_issuer_unique_id;
+ hx509_cert_get_subject_unique_id;
+ hx509_cert_have_private_key;
+ hx509_cert_have_private_key_only;
+ hx509_cert_init;
+ hx509_cert_init_data;
+ hx509_cert_init_private_key;
+ hx509_cert_keyusage_print;
+ hx509_cert_public_encrypt;
+ hx509_cert_ref;
+ hx509_cert_set_friendly_name;
+ hx509_certs_add;
+ hx509_certs_append;
+ hx509_certs_end_seq;
+ hx509_certs_destroy;
+ hx509_certs_ref;
+ hx509_certs_filter;
+ hx509_certs_find;
+ hx509_certs_free;
+ hx509_certs_info;
+ hx509_certs_init;
+ hx509_certs_iter;
+ hx509_certs_iter_f;
+ hx509_certs_merge;
+ hx509_certs_next_cert;
+ hx509_certs_start_seq;
+ hx509_certs_store;
+ hx509_ci_print_names;
+ hx509_clear_error_string;
+ hx509_cms_create_signed;
+ hx509_cms_create_signed_1;
+ hx509_cms_decrypt_encrypted;
+ hx509_cms_envelope_1;
+ hx509_cms_unenvelope;
+ hx509_cms_unwrap_ContentInfo;
+ hx509_cms_verify_signed;
+ hx509_cms_verify_signed_ext;
+ hx509_cms_wrap_ContentInfo;
+ hx509_context_free;
+ hx509_context_init;
+ hx509_context_set_missing_revoke;
+ hx509_crl_add_revoked_certs;
+ hx509_crl_alloc;
+ hx509_crl_free;
+ hx509_crl_lifetime;
+ hx509_crl_sign;
+ hx509_crypto_aes128_cbc;
+ hx509_crypto_aes256_cbc;
+ hx509_crypto_allow_weak;
+ hx509_crypto_available;
+ hx509_crypto_decrypt;
+ hx509_crypto_des_rsdi_ede3_cbc;
+ hx509_crypto_destroy;
+ hx509_crypto_encrypt;
+ hx509_crypto_enctype_by_name;
+ hx509_crypto_free_algs;
+ hx509_crypto_get_params;
+ hx509_crypto_init;
+ hx509_crypto_provider;
+ hx509_crypto_select;
+ hx509_crypto_set_key_data;
+ hx509_crypto_set_key_name;
+ hx509_crypto_set_padding;
+ hx509_crypto_set_params;
+ hx509_crypto_set_random_key;
+ hx509_empty_name;
+ hx509_env_add;
+ hx509_env_add_binding;
+ hx509_env_find;
+ hx509_env_find_binding;
+ hx509_env_free;
+ hx509_env_init;
+ hx509_env_lfind;
+ hx509_err;
+ hx509_free_error_string;
+ hx509_free_octet_string_list;
+ hx509_find_private_alg;
+ hx509_general_name_unparse;
+ hx509_get_error_string;
+ hx509_get_instance;
+ hx509_get_one_cert;
+ hx509_lock_add_cert;
+ hx509_lock_add_certs;
+ hx509_lock_add_password;
+ hx509_lock_command_string;
+ hx509_lock_free;
+ hx509_lock_init;
+ hx509_lock_prompt;
+ hx509_lock_reset_certs;
+ hx509_lock_reset_passwords;
+ hx509_lock_reset_promper;
+ hx509_lock_set_prompter;
+ hx509_name_binary;
+ hx509_name_cmp;
+ hx509_name_copy;
+ hx509_name_expand;
+ hx509_name_free;
+ hx509_name_is_null_p;
+ hx509_name_normalize;
+ hx509_name_to_Name;
+ hx509_name_to_string;
+ hx509_ocsp_request;
+ hx509_ocsp_verify;
+ hx509_oid_print;
+ hx509_oid_sprint;
+ hx509_parse_name;
+ hx509_parse_private_key;
+ hx509_peer_info_add_cms_alg;
+ hx509_peer_info_alloc;
+ hx509_peer_info_free;
+ hx509_peer_info_set_cert;
+ hx509_peer_info_set_cms_algs;
+ hx509_pem_add_header;
+ hx509_pem_find_header;
+ hx509_pem_free_header;
+ hx509_pem_read;
+ hx509_pem_write;
+ hx509_print_stdout;
+ hx509_print_cert;
+ hx509_private_key_assign_rsa;
+ hx509_private_key_free;
+ hx509_private_key_private_decrypt;
+ hx509_private_key_init;
+ hx509_private_key2SPKI;
+ hx509_prompt_hidden;
+ hx509_query_alloc;
+ hx509_query_free;
+ hx509_query_match_cmp_func;
+ hx509_query_match_eku;
+ hx509_query_match_expr;
+ hx509_query_match_friendly_name;
+ hx509_query_match_issuer_serial;
+ hx509_query_match_option;
+ hx509_query_statistic_file;
+ hx509_query_unparse_stats;
+ hx509_request_get_eku;
+ hx509_request_get_exts;
+ hx509_request_get_ku;
+ hx509_request_get_name;
+ hx509_request_get_san;
+ hx509_request_get_SubjectPublicKeyInfo;
+ hx509_request_free;
+ hx509_request_init;
+ hx509_request_parse;
+ hx509_request_parse_der;
+ hx509_request_set_ku;
+ hx509_request_set_name;
+ hx509_request_set_SubjectPublicKeyInfo;
+ hx509_revoke_add_crl;
+ hx509_revoke_add_ocsp;
+ hx509_revoke_free;
+ hx509_revoke_init;
+ hx509_revoke_ocsp_print;
+ hx509_revoke_verify;
+ hx509_revoke_print;
+ hx509_set_error_string;
+ hx509_set_error_stringv;
+ hx509_signature_md5;
+ hx509_signature_rsa;
+ hx509_signature_rsa_with_md5;
+ hx509_signature_rsa_with_sha1;
+ hx509_signature_rsa_with_sha256;
+ hx509_signature_rsa_with_sha384;
+ hx509_signature_rsa_with_sha512;
+ hx509_signature_sha1;
+ hx509_signature_sha256;
+ hx509_signature_sha384;
+ hx509_signature_sha512;
+ hx509_unparse_der_name;
+ hx509_validate_cert;
+ hx509_validate_ctx_add_flags;
+ hx509_validate_ctx_free;
+ hx509_validate_ctx_init;
+ hx509_validate_ctx_set_print;
+ hx509_verify_attach_anchors;
+ hx509_verify_attach_revoke;
+ hx509_verify_ctx_f_allow_default_trustanchors;
+ hx509_verify_destroy_ctx;
+ hx509_verify_hostname;
+ hx509_verify_init_ctx;
+ hx509_verify_path;
+ hx509_verify_set_max_depth;
+ hx509_verify_set_proxy_certificate;
+ hx509_verify_set_strict_rfc3280_verification;
+ hx509_verify_set_time;
+ hx509_verify_signature;
+ hx509_xfree;
+ initialize_hx_error_table_r;
+ # pkcs11 symbols
+ C_GetFunctionList;
+ local:
+ *;
+};
+
+HEIMDAL_X509_1.3 {
+ global:
+ hx509_ca_tbs_set_signature_algorithm;
+};
+