Initial commit of BoringSSL for Android.
diff --git a/src/crypto/evp/CMakeLists.txt b/src/crypto/evp/CMakeLists.txt
new file mode 100644
index 0000000..9854a18
--- /dev/null
+++ b/src/crypto/evp/CMakeLists.txt
@@ -0,0 +1,38 @@
+include_directories(. .. ../../include)
+
+add_library(
+  evp
+
+  OBJECT
+
+  algorithm.c
+  asn1.c
+  digestsign.c
+  evp.c
+  evp_ctx.c
+  evp_error.c
+  p_ec.c
+  p_ec_asn1.c
+  p_hmac.c
+  p_hmac_asn1.c
+  p_rsa.c
+  p_rsa_asn1.c
+  pbkdf.c
+  sign.c
+)
+
+
+add_executable(
+  evp_test
+
+  evp_test.c
+)
+
+add_executable(
+  pbkdf_test
+
+  pbkdf_test.c
+)
+
+target_link_libraries(evp_test crypto)
+target_link_libraries(pbkdf_test crypto)
diff --git a/src/crypto/evp/algorithm.c b/src/crypto/evp/algorithm.c
new file mode 100644
index 0000000..ea28dfa
--- /dev/null
+++ b/src/crypto/evp/algorithm.c
@@ -0,0 +1,159 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <assert.h>
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+#include "internal.h"
+
+
+int EVP_DigestSignAlgorithm(EVP_MD_CTX *ctx, X509_ALGOR *algor) {
+  const EVP_MD *digest;
+  EVP_PKEY *pkey;
+  int sign_nid, paramtype;
+
+  digest = EVP_MD_CTX_md(ctx);
+  pkey = EVP_PKEY_CTX_get0_pkey(ctx->pctx);
+  if (!digest || !pkey) {
+    OPENSSL_PUT_ERROR(EVP, EVP_DigestSignAlgorithm,
+                      EVP_R_CONTEXT_NOT_INITIALISED);
+    return 0;
+  }
+
+  if (pkey->ameth->digest_sign_algorithm) {
+    switch (pkey->ameth->digest_sign_algorithm(ctx, algor)) {
+      case EVP_DIGEST_SIGN_ALGORITHM_ERROR:
+        return 0;
+      case EVP_DIGEST_SIGN_ALGORITHM_SUCCESS:
+        return 1;
+      case EVP_DIGEST_SIGN_ALGORITHM_DEFAULT:
+        /* Use default behavior. */
+        break;
+      default:
+        assert(0);
+    }
+  }
+
+  /* Default behavior: look up the OID for the algorithm/hash pair and encode
+   * that. */
+  if (!OBJ_find_sigid_by_algs(&sign_nid, EVP_MD_type(digest),
+                              pkey->ameth->pkey_id)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_DigestSignAlgorithm,
+                      EVP_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED);
+    return 0;
+  }
+
+  if (pkey->ameth->pkey_flags & ASN1_PKEY_SIGPARAM_NULL) {
+    paramtype = V_ASN1_NULL;
+  } else {
+    paramtype = V_ASN1_UNDEF;
+  }
+
+  X509_ALGOR_set0(algor, OBJ_nid2obj(sign_nid), paramtype, NULL);
+  return 1;
+}
+
+int EVP_DigestVerifyInitFromAlgorithm(EVP_MD_CTX *ctx,
+                                      X509_ALGOR *algor,
+                                      EVP_PKEY *pkey) {
+  int digest_nid, pkey_nid;
+  const EVP_PKEY_ASN1_METHOD *ameth;
+  const EVP_MD *digest;
+
+  /* Convert signature OID into digest and public key OIDs */
+  if (!OBJ_find_sigid_algs(OBJ_obj2nid(algor->algorithm), &digest_nid,
+                           &pkey_nid)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_DigestVerifyInitFromAlgorithm,
+                      EVP_R_UNKNOWN_SIGNATURE_ALGORITHM);
+    return 0;
+  }
+
+  /* Check public key OID matches public key type */
+  ameth = EVP_PKEY_asn1_find(NULL, pkey_nid);
+  if (ameth == NULL || ameth->pkey_id != pkey->ameth->pkey_id) {
+    OPENSSL_PUT_ERROR(EVP, EVP_DigestVerifyInitFromAlgorithm,
+                      EVP_R_WRONG_PUBLIC_KEY_TYPE);
+    return 0;
+  }
+
+  /* NID_undef signals that there are custom parameters to set. */
+  if (digest_nid == NID_undef) {
+    if (!pkey->ameth || !pkey->ameth->digest_verify_init_from_algorithm) {
+      OPENSSL_PUT_ERROR(EVP, EVP_DigestVerifyInitFromAlgorithm,
+                        EVP_R_UNKNOWN_SIGNATURE_ALGORITHM);
+      return 0;
+    }
+
+    return pkey->ameth->digest_verify_init_from_algorithm(ctx, algor, pkey);
+  }
+
+  /* Otherwise, initialize with the digest from the OID. */
+  digest = EVP_get_digestbynid(digest_nid);
+  if (digest == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_DigestVerifyInitFromAlgorithm,
+                      EVP_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM);
+    return 0;
+  }
+
+  return EVP_DigestVerifyInit(ctx, NULL, digest, NULL, pkey);
+}
+
diff --git a/src/crypto/evp/asn1.c b/src/crypto/evp/asn1.c
new file mode 100644
index 0000000..27ae017
--- /dev/null
+++ b/src/crypto/evp/asn1.c
@@ -0,0 +1,167 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+#include "internal.h"
+
+
+EVP_PKEY *d2i_PrivateKey(int type, EVP_PKEY **out, const uint8_t **inp,
+                         long len) {
+  EVP_PKEY *ret;
+
+  if (out == NULL || *out == NULL) {
+    ret = EVP_PKEY_new();
+    if (ret == NULL) {
+      OPENSSL_PUT_ERROR(EVP, d2i_PrivateKey, ERR_R_EVP_LIB);
+      return NULL;
+    }
+  } else {
+    ret = *out;
+  }
+
+  if (!EVP_PKEY_set_type(ret, type)) {
+    OPENSSL_PUT_ERROR(EVP, d2i_PrivateKey, EVP_R_UNKNOWN_PUBLIC_KEY_TYPE);
+    goto err;
+  }
+
+  if (!ret->ameth->old_priv_decode ||
+      !ret->ameth->old_priv_decode(ret, inp, len)) {
+    if (ret->ameth->priv_decode) {
+      PKCS8_PRIV_KEY_INFO *p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, inp, len);
+      if (!p8) {
+        goto err;
+      }
+      EVP_PKEY_free(ret);
+      ret = EVP_PKCS82PKEY(p8);
+      PKCS8_PRIV_KEY_INFO_free(p8);
+    } else {
+      OPENSSL_PUT_ERROR(EVP, d2i_PrivateKey, ERR_R_ASN1_LIB);
+      goto err;
+    }
+  }
+
+  if (out != NULL) {
+    *out = ret;
+  }
+  return ret;
+
+err:
+  if (ret != NULL && (out == NULL || *out != ret)) {
+    EVP_PKEY_free(ret);
+  }
+  return NULL;
+}
+
+EVP_PKEY *d2i_AutoPrivateKey(EVP_PKEY **out, const uint8_t **inp, long len) {
+  STACK_OF(ASN1_TYPE) *inkey;
+  const uint8_t *p;
+  int keytype;
+  p = *inp;
+
+  /* Dirty trick: read in the ASN1 data into out STACK_OF(ASN1_TYPE):
+   * by analyzing it we can determine the passed structure: this
+   * assumes the input is surrounded by an ASN1 SEQUENCE. */
+  inkey = d2i_ASN1_SEQUENCE_ANY(NULL, &p, len);
+  /* Since we only need to discern "traditional format" RSA and DSA
+   * keys we can just count the elements. */
+  if (sk_ASN1_TYPE_num(inkey) == 6) {
+    keytype = EVP_PKEY_DSA;
+  } else if (sk_ASN1_TYPE_num(inkey) == 4) {
+    keytype = EVP_PKEY_EC;
+  } else if (sk_ASN1_TYPE_num(inkey) == 3) {
+    /* This seems to be PKCS8, not traditional format */
+    PKCS8_PRIV_KEY_INFO *p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, inp, len);
+    EVP_PKEY *ret;
+
+    sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
+    if (!p8) {
+      OPENSSL_PUT_ERROR(EVP, d2i_AutoPrivateKey,
+                        EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+      return NULL;
+    }
+    ret = EVP_PKCS82PKEY(p8);
+    PKCS8_PRIV_KEY_INFO_free(p8);
+    if (out) {
+      *out = ret;
+    }
+    return ret;
+  } else {
+    keytype = EVP_PKEY_RSA;
+  }
+
+  sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
+  return d2i_PrivateKey(keytype, out, inp, len);
+}
+
+int i2d_PublicKey(EVP_PKEY *key, uint8_t **outp) {
+  switch (key->type) {
+    case EVP_PKEY_RSA:
+      return i2d_RSAPublicKey(key->pkey.rsa, outp);
+    case EVP_PKEY_DSA:
+      return i2d_DSAPublicKey(key->pkey.dsa, outp);
+    case EVP_PKEY_EC:
+      return i2o_ECPublicKey(key->pkey.ec, outp);
+    default:
+      OPENSSL_PUT_ERROR(EVP, i2d_PublicKey, EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+      return -1;
+  }
+}
diff --git a/src/crypto/evp/digestsign.c b/src/crypto/evp/digestsign.c
new file mode 100644
index 0000000..c86b805
--- /dev/null
+++ b/src/crypto/evp/digestsign.c
@@ -0,0 +1,205 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006,2007 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/digest.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+#include "../digest/internal.h"
+
+
+/* md_begin_digset is a callback from the |EVP_MD_CTX| code that is called when
+ * a new digest is begun. */
+static int md_begin_digest(EVP_MD_CTX *ctx) {
+  int r = EVP_PKEY_CTX_ctrl(ctx->pctx, -1, EVP_PKEY_OP_TYPE_SIG,
+                            EVP_PKEY_CTRL_DIGESTINIT, 0, ctx);
+  return r > 0 || r == -2;
+}
+
+static const struct evp_md_pctx_ops md_pctx_ops = {
+  EVP_PKEY_CTX_free,
+  EVP_PKEY_CTX_dup,
+  md_begin_digest,
+};
+
+static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+                          const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey,
+                          int is_verify) {
+  if (ctx->pctx == NULL) {
+    ctx->pctx = EVP_PKEY_CTX_new(pkey, e);
+  }
+  if (ctx->pctx == NULL) {
+    return 0;
+  }
+  ctx->pctx_ops = &md_pctx_ops;
+
+  if (type == NULL) {
+    type = EVP_sha1();
+  }
+
+  if (type == NULL) {
+    OPENSSL_PUT_ERROR(EVP, do_sigver_init, EVP_R_NO_DEFAULT_DIGEST);
+    return 0;
+  }
+
+  if (is_verify) {
+    if (ctx->pctx->pmeth->verifyctx_init) {
+      if (ctx->pctx->pmeth->verifyctx_init(ctx->pctx, ctx) <= 0) {
+        return 0;
+      }
+      ctx->pctx->operation = EVP_PKEY_OP_VERIFYCTX;
+    } else if (EVP_PKEY_verify_init(ctx->pctx) <= 0) {
+      return 0;
+    }
+  } else {
+    if (ctx->pctx->pmeth->signctx_init) {
+      if (ctx->pctx->pmeth->signctx_init(ctx->pctx, ctx) <= 0) {
+        return 0;
+      }
+      ctx->pctx->operation = EVP_PKEY_OP_SIGNCTX;
+    } else if (EVP_PKEY_sign_init(ctx->pctx) <= 0) {
+      return 0;
+    }
+  }
+  if (EVP_PKEY_CTX_set_signature_md(ctx->pctx, type) <= 0) {
+    return 0;
+  }
+  if (pctx) {
+    *pctx = ctx->pctx;
+  }
+  if (!EVP_DigestInit_ex(ctx, type, e)) {
+    return 0;
+  }
+  return 1;
+}
+
+int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type,
+                       ENGINE *e, EVP_PKEY *pkey) {
+  return do_sigver_init(ctx, pctx, type, e, pkey, 0);
+}
+
+int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+                         const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey) {
+  return do_sigver_init(ctx, pctx, type, e, pkey, 1);
+}
+
+int EVP_DigestSignUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return EVP_DigestUpdate(ctx, data, len);
+}
+
+int EVP_DigestVerifyUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return EVP_DigestUpdate(ctx, data, len);
+}
+
+int EVP_DigestSignFinal(EVP_MD_CTX *ctx, uint8_t *out_sig,
+                        size_t *out_sig_len) {
+  int r = 0;
+  const int has_signctx = ctx->pctx->pmeth->signctx != NULL;
+
+  if (out_sig) {
+    EVP_MD_CTX tmp_ctx;
+    uint8_t md[EVP_MAX_MD_SIZE];
+    unsigned int mdlen;
+
+    EVP_MD_CTX_init(&tmp_ctx);
+    if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx)) {
+      return 0;
+    }
+    if (has_signctx) {
+      r = tmp_ctx.pctx->pmeth->signctx(tmp_ctx.pctx, out_sig, out_sig_len, &tmp_ctx);
+    } else {
+      r = EVP_DigestFinal_ex(&tmp_ctx, md, &mdlen);
+    }
+    EVP_MD_CTX_cleanup(&tmp_ctx);
+    if (has_signctx || !r) {
+      return r;
+    }
+    return EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, md, mdlen);
+  } else {
+    if (has_signctx) {
+      return ctx->pctx->pmeth->signctx(ctx->pctx, out_sig, out_sig_len, ctx);
+    } else {
+      size_t s = EVP_MD_size(ctx->digest);
+      return EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, NULL, s);
+    }
+  }
+}
+
+int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig,
+                          size_t sig_len) {
+  EVP_MD_CTX tmp_ctx;
+  uint8_t md[EVP_MAX_MD_SIZE];
+  int r;
+  unsigned int mdlen;
+  const int has_verifyctx = ctx->pctx->pmeth->verifyctx != NULL;
+
+  EVP_MD_CTX_init(&tmp_ctx);
+  if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx)) {
+    return 0;
+  }
+  if (has_verifyctx) {
+    r = tmp_ctx.pctx->pmeth->verifyctx(tmp_ctx.pctx, sig, sig_len, &tmp_ctx);
+  } else {
+    r = EVP_DigestFinal_ex(&tmp_ctx, md, &mdlen);
+  }
+
+  EVP_MD_CTX_cleanup(&tmp_ctx);
+  if (has_verifyctx || !r) {
+    return r;
+  }
+  return EVP_PKEY_verify(ctx->pctx, sig, sig_len, md, mdlen);
+}
diff --git a/src/crypto/evp/evp.c b/src/crypto/evp/evp.c
new file mode 100644
index 0000000..8a1d513
--- /dev/null
+++ b/src/crypto/evp/evp.c
@@ -0,0 +1,440 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/bio.h>
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/rsa.h>
+
+#include "internal.h"
+
+
+extern const EVP_PKEY_ASN1_METHOD ec_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD hmac_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD rsa_asn1_meth;
+
+EVP_PKEY *EVP_PKEY_new(void) {
+  EVP_PKEY *ret;
+
+  ret = OPENSSL_malloc(sizeof(EVP_PKEY));
+  if (ret == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  memset(ret, 0, sizeof(EVP_PKEY));
+  ret->type = EVP_PKEY_NONE;
+  ret->references = 1;
+
+  return ret;
+}
+
+static void free_it(EVP_PKEY *pkey) {
+  if (pkey->ameth && pkey->ameth->pkey_free) {
+    pkey->ameth->pkey_free(pkey);
+    pkey->pkey.ptr = NULL;
+    pkey->type = EVP_PKEY_NONE;
+  }
+}
+
+void EVP_PKEY_free(EVP_PKEY *pkey) {
+  if (pkey == NULL) {
+    return;
+  }
+
+  if (CRYPTO_add(&pkey->references, -1, CRYPTO_LOCK_EVP_PKEY)) {
+    return;
+  }
+
+  free_it(pkey);
+  if (pkey->attributes) {
+    /* TODO(fork): layering: X509_ATTRIBUTE_free is an X.509 function. In
+     * practice this path isn't called but should be removed in the future. */
+    /*sk_X509_ATTRIBUTE_pop_free(pkey->attributes, X509_ATTRIBUTE_free);*/
+    assert(0);
+  }
+  OPENSSL_free(pkey);
+}
+
+int EVP_PKEY_is_opaque(const EVP_PKEY *pkey) {
+  if (pkey->ameth && pkey->ameth->pkey_opaque) {
+    return pkey->ameth->pkey_opaque(pkey);
+  }
+  return 0;
+}
+
+int EVP_PKEY_supports_digest(const EVP_PKEY *pkey, const EVP_MD *md) {
+  if (pkey->ameth && pkey->ameth->pkey_supports_digest) {
+    return pkey->ameth->pkey_supports_digest(pkey, md);
+  }
+  return 1;
+}
+
+int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b) {
+  if (a->type != b->type) {
+    return -1;
+  }
+
+  if (a->ameth) {
+    int ret;
+    /* Compare parameters if the algorithm has them */
+    if (a->ameth->param_cmp) {
+      ret = a->ameth->param_cmp(a, b);
+      if (ret <= 0)
+        return ret;
+    }
+
+    if (a->ameth->pub_cmp) {
+      return a->ameth->pub_cmp(a, b);
+    }
+  }
+
+  return -2;
+}
+
+EVP_PKEY *EVP_PKEY_dup(EVP_PKEY *pkey) {
+  CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+  return pkey;
+}
+
+int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) {
+  if (to->type != from->type) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_copy_parameters, EVP_R_DIFFERENT_KEY_TYPES);
+    goto err;
+  }
+
+  if (EVP_PKEY_missing_parameters(from)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_copy_parameters, EVP_R_MISSING_PARAMETERS);
+    goto err;
+  }
+
+  if (from->ameth && from->ameth->param_copy) {
+    return from->ameth->param_copy(to, from);
+  }
+
+err:
+  return 0;
+}
+
+int EVP_PKEY_missing_parameters(const EVP_PKEY *pkey) {
+  if (pkey->ameth && pkey->ameth->param_missing) {
+    return pkey->ameth->param_missing(pkey);
+  }
+  return 0;
+}
+
+int EVP_PKEY_size(const EVP_PKEY *pkey) {
+  if (pkey && pkey->ameth && pkey->ameth->pkey_size) {
+    return pkey->ameth->pkey_size(pkey);
+  }
+  return 0;
+}
+
+int EVP_PKEY_bits(EVP_PKEY *pkey) {
+  if (pkey && pkey->ameth && pkey->ameth->pkey_bits) {
+    return pkey->ameth->pkey_bits(pkey);
+  }
+  return 0;
+}
+
+int EVP_PKEY_id(const EVP_PKEY *pkey) {
+  return pkey->type;
+}
+
+/* TODO(fork): remove the first argument. */
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find(ENGINE **pengine, int nid) {
+  switch (nid) {
+    case EVP_PKEY_RSA:
+    case EVP_PKEY_RSA2:
+      return &rsa_asn1_meth;
+    case EVP_PKEY_HMAC:
+      return &hmac_asn1_meth;
+    case EVP_PKEY_EC:
+      return &ec_asn1_meth;
+    default:
+      return NULL;
+  }
+}
+
+int EVP_PKEY_type(int nid) {
+  const EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_find(NULL, nid);
+  if (meth == NULL) {
+    return NID_undef;
+  }
+  return meth->pkey_id;
+}
+
+EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, const uint8_t *mac_key,
+                               size_t mac_key_len) {
+  EVP_PKEY_CTX *mac_ctx = NULL;
+  EVP_PKEY *ret = NULL;
+
+  mac_ctx = EVP_PKEY_CTX_new_id(type, e);
+  if (!mac_ctx) {
+    return NULL;
+  }
+
+  if (EVP_PKEY_keygen_init(mac_ctx) <= 0 ||
+      EVP_PKEY_CTX_ctrl(mac_ctx, -1, EVP_PKEY_OP_KEYGEN,
+                        EVP_PKEY_CTRL_SET_MAC_KEY, mac_key_len,
+                        (uint8_t *)mac_key) <= 0 ||
+      EVP_PKEY_keygen(mac_ctx, &ret) <= 0) {
+    ret = NULL;
+    goto merr;
+  }
+
+merr:
+  if (mac_ctx)
+    EVP_PKEY_CTX_free(mac_ctx);
+  return ret;
+}
+
+int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key) {
+  if (EVP_PKEY_assign_RSA(pkey, key)) {
+    RSA_up_ref(key);
+    return 1;
+  }
+  return 0;
+}
+
+int EVP_PKEY_assign_RSA(EVP_PKEY *pkey, RSA *key) {
+  return EVP_PKEY_assign(pkey, EVP_PKEY_RSA, key);
+}
+
+RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey) {
+  if (pkey->type != EVP_PKEY_RSA) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_get1_RSA, EVP_R_EXPECTING_AN_RSA_KEY);
+    return NULL;
+  }
+  RSA_up_ref(pkey->pkey.rsa);
+  return pkey->pkey.rsa;
+}
+
+int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key) {
+  if (EVP_PKEY_assign_DSA(pkey, key)) {
+    DSA_up_ref(key);
+    return 1;
+  }
+  return 0;
+}
+
+int EVP_PKEY_assign_DSA(EVP_PKEY *pkey, DSA *key) {
+  return EVP_PKEY_assign(pkey, EVP_PKEY_DSA, key);
+}
+
+DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey) {
+  if (pkey->type != EVP_PKEY_DSA) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_get1_DSA, EVP_R_EXPECTING_A_DSA_KEY);
+    return NULL;
+  }
+  DSA_up_ref(pkey->pkey.dsa);
+  return pkey->pkey.dsa;
+}
+
+int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, EC_KEY *key) {
+  if (EVP_PKEY_assign_EC_KEY(pkey, key)) {
+    EC_KEY_up_ref(key);
+    return 1;
+  }
+  return 0;
+}
+
+int EVP_PKEY_assign_EC_KEY(EVP_PKEY *pkey, EC_KEY *key) {
+  return EVP_PKEY_assign(pkey, EVP_PKEY_EC, key);
+}
+
+EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey) {
+  if (pkey->type != EVP_PKEY_EC) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_get1_EC_KEY, EVP_R_EXPECTING_AN_EC_KEY_KEY);
+    return NULL;
+  }
+  EC_KEY_up_ref(pkey->pkey.ec);
+  return pkey->pkey.ec;
+}
+
+int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key) {
+  if (EVP_PKEY_assign_DH(pkey, key)) {
+    DH_up_ref(key);
+    return 1;
+  }
+  return 0;
+}
+
+int EVP_PKEY_assign_DH(EVP_PKEY *pkey, DH *key) {
+  return EVP_PKEY_assign(pkey, EVP_PKEY_EC, key);
+}
+
+DH *EVP_PKEY_get1_DH(EVP_PKEY *pkey) {
+  if (pkey->type != EVP_PKEY_DH) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_get1_DH, EVP_R_EXPECTING_A_DH_KEY);
+    return NULL;
+  }
+  DH_up_ref(pkey->pkey.dh);
+  return pkey->pkey.dh;
+}
+
+int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key) {
+  if (!EVP_PKEY_set_type(pkey, type)) {
+    return 0;
+  }
+  pkey->pkey.ptr = key;
+  return key != NULL;
+}
+
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find_str(ENGINE **pengine,
+                                                   const char *name,
+                                                   size_t len) {
+  if (len == 3 && memcmp(name, "RSA", 3) == 0) {
+    return &rsa_asn1_meth;
+  } else if (len == 4 && memcmp(name, "HMAC", 4) == 0) {
+    return &hmac_asn1_meth;
+  } if (len == 2 && memcmp(name, "EC", 2) == 0) {
+    return &ec_asn1_meth;
+  }
+  return NULL;
+}
+
+int EVP_PKEY_set_type(EVP_PKEY *pkey, int type) {
+  const EVP_PKEY_ASN1_METHOD *ameth;
+
+  if (pkey && pkey->pkey.ptr) {
+    free_it(pkey);
+  }
+
+  ameth = EVP_PKEY_asn1_find(NULL, type);
+  if (ameth == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_set_type, EVP_R_UNSUPPORTED_ALGORITHM);
+    ERR_add_error_dataf("algorithm %d (%s)", type, OBJ_nid2sn(type));
+    return 0;
+  }
+
+  if (pkey) {
+    pkey->ameth = ameth;
+    pkey->type = pkey->ameth->pkey_id;
+  }
+
+  return 1;
+}
+
+
+
+int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) {
+  if (a->type != b->type) {
+    return -1;
+  }
+  if (a->ameth && a->ameth->param_cmp) {
+    return a->ameth->param_cmp(a, b);
+  }
+  return -2;
+}
+
+static int print_unsupported(BIO *out, const EVP_PKEY *pkey, int indent,
+                             const char *kstr) {
+  BIO_indent(out, indent, 128);
+  BIO_printf(out, "%s algorithm \"%s\" unsupported\n", kstr,
+             OBJ_nid2ln(pkey->type));
+  return 1;
+}
+
+int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *pctx) {
+  if (pkey->ameth && pkey->ameth->pub_print) {
+    return pkey->ameth->pub_print(out, pkey, indent, pctx);
+  }
+
+  return print_unsupported(out, pkey, indent, "Public Key");
+}
+
+int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *pctx) {
+  if (pkey->ameth && pkey->ameth->priv_print) {
+    return pkey->ameth->priv_print(out, pkey, indent, pctx);
+  }
+
+  return print_unsupported(out, pkey, indent, "Private Key");
+}
+
+int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *pctx) {
+  if (pkey->ameth && pkey->ameth->param_print) {
+    return pkey->ameth->param_print(out, pkey, indent, pctx);
+  }
+
+  return print_unsupported(out, pkey, indent, "Parameters");
+}
+
+int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
+  return EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_TYPE_SIG, EVP_PKEY_CTRL_MD, 0,
+                           (void *)md);
+}
+
+int EVP_PKEY_CTX_get_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md) {
+  return EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_TYPE_SIG, EVP_PKEY_CTRL_GET_MD,
+                           0, (void *)out_md);
+}
+
+void OpenSSL_add_all_algorithms(void) {}
+
+void EVP_cleanup(void) {}
diff --git a/src/crypto/evp/evp_ctx.c b/src/crypto/evp/evp_ctx.c
new file mode 100644
index 0000000..a383725
--- /dev/null
+++ b/src/crypto/evp/evp_ctx.c
@@ -0,0 +1,512 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+extern const EVP_PKEY_METHOD rsa_pkey_meth;
+extern const EVP_PKEY_METHOD hmac_pkey_meth;
+extern const EVP_PKEY_METHOD ec_pkey_meth;
+
+static const EVP_PKEY_METHOD *const evp_methods[] = {
+  &rsa_pkey_meth,
+  &hmac_pkey_meth,
+  &ec_pkey_meth,
+};
+
+static const EVP_PKEY_METHOD *evp_pkey_meth_find(int type) {
+  unsigned i;
+
+  for (i = 0; i < sizeof(evp_methods)/sizeof(EVP_PKEY_METHOD*); i++) {
+    if (evp_methods[i]->pkey_id == type) {
+      return evp_methods[i];
+    }
+  }
+
+  return NULL;
+}
+
+static EVP_PKEY_CTX *evp_pkey_ctx_new(EVP_PKEY *pkey, ENGINE *e, int id) {
+  EVP_PKEY_CTX *ret;
+  const EVP_PKEY_METHOD *pmeth;
+
+  if (id == -1) {
+    if (!pkey || !pkey->ameth) {
+      return NULL;
+    }
+    id = pkey->ameth->pkey_id;
+  }
+
+  pmeth = evp_pkey_meth_find(id);
+
+  if (pmeth == NULL) {
+    OPENSSL_PUT_ERROR(EVP, evp_pkey_ctx_new, EVP_R_UNSUPPORTED_ALGORITHM);
+    const char *name = OBJ_nid2sn(id);
+    ERR_add_error_dataf("algorithm %d (%s)", id, name);
+    return NULL;
+  }
+
+  ret = OPENSSL_malloc(sizeof(EVP_PKEY_CTX));
+  if (!ret) {
+    OPENSSL_PUT_ERROR(EVP, evp_pkey_ctx_new, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  memset(ret, 0, sizeof(EVP_PKEY_CTX));
+
+  ret->engine = e;
+  ret->pmeth = pmeth;
+  ret->operation = EVP_PKEY_OP_UNDEFINED;
+
+  if (pkey) {
+    ret->pkey = EVP_PKEY_dup(pkey);
+  }
+
+  if (pmeth->init) {
+    if (pmeth->init(ret) <= 0) {
+      if (pkey) {
+        EVP_PKEY_free(ret->pkey);
+      }
+      OPENSSL_free(ret);
+      return NULL;
+    }
+  }
+
+  return ret;
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e) {
+  return evp_pkey_ctx_new(pkey, e, -1);
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int id, ENGINE *e) {
+  return evp_pkey_ctx_new(NULL, e, id);
+}
+
+void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx) {
+  if (ctx == NULL) {
+    return;
+  }
+  if (ctx->pmeth && ctx->pmeth->cleanup) {
+    ctx->pmeth->cleanup(ctx);
+  }
+  if (ctx->pkey) {
+    EVP_PKEY_free(ctx->pkey);
+  }
+  if (ctx->peerkey) {
+    EVP_PKEY_free(ctx->peerkey);
+  }
+  OPENSSL_free(ctx);
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_dup(EVP_PKEY_CTX *pctx) {
+  EVP_PKEY_CTX *rctx;
+
+  if (!pctx->pmeth || !pctx->pmeth->copy) {
+    return NULL;
+  }
+
+  rctx = OPENSSL_malloc(sizeof(EVP_PKEY_CTX));
+  if (!rctx) {
+    return NULL;
+  }
+
+  memset(rctx, 0, sizeof(EVP_PKEY_CTX));
+
+  rctx->pmeth = pctx->pmeth;
+  rctx->engine = pctx->engine;
+  rctx->operation = pctx->operation;
+
+  if (pctx->pkey) {
+    rctx->pkey = EVP_PKEY_dup(pctx->pkey);
+    if (rctx->pkey == NULL) {
+      goto err;
+    }
+  }
+
+  if (pctx->peerkey) {
+    rctx->peerkey = EVP_PKEY_dup(pctx->peerkey);
+    if (rctx->peerkey == NULL) {
+      goto err;
+    }
+  }
+
+  if (pctx->pmeth->copy(rctx, pctx) > 0) {
+    return rctx;
+  }
+
+err:
+  EVP_PKEY_CTX_free(rctx);
+  OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_dup, ERR_LIB_EVP);
+  return NULL;
+}
+
+EVP_PKEY *EVP_PKEY_CTX_get0_pkey(EVP_PKEY_CTX *ctx) { return ctx->pkey; }
+
+void EVP_PKEY_CTX_set_app_data(EVP_PKEY_CTX *ctx, void *data) {
+  ctx->app_data = data;
+}
+
+void *EVP_PKEY_CTX_get_app_data(EVP_PKEY_CTX *ctx) { return ctx->app_data; }
+
+int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype, int cmd,
+                      int p1, void *p2) {
+  int ret;
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->ctrl) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_ctrl, EVP_R_COMMAND_NOT_SUPPORTED);
+    return -2;
+  }
+  if (keytype != -1 && ctx->pmeth->pkey_id != keytype) {
+    return -1;
+  }
+
+  if (ctx->operation == EVP_PKEY_OP_UNDEFINED) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_ctrl, EVP_R_NO_OPERATION_SET);
+    return -1;
+  }
+
+  if (optype != -1 && !(ctx->operation & optype)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_ctrl, EVP_R_INVALID_OPERATION);
+    return -1;
+  }
+
+  ret = ctx->pmeth->ctrl(ctx, cmd, p1, p2);
+
+  if (ret == -2) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_CTX_ctrl, EVP_R_COMMAND_NOT_SUPPORTED);
+  }
+
+  return ret;
+}
+
+int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->sign) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_sign_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+
+  ctx->operation = EVP_PKEY_OP_SIGN;
+  if (!ctx->pmeth->sign_init) {
+    return 1;
+  }
+
+  if (!ctx->pmeth->sign_init(ctx)) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return 0;
+  }
+
+  return 1;
+}
+
+int EVP_PKEY_sign(EVP_PKEY_CTX *ctx, uint8_t *sig, size_t *sig_len,
+                  const uint8_t *data, size_t data_len) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->sign) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_sign,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  if (ctx->operation != EVP_PKEY_OP_SIGN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_sign, EVP_R_OPERATON_NOT_INITIALIZED);
+    return 0;
+  }
+  return ctx->pmeth->sign(ctx, sig, sig_len, data, data_len);
+}
+
+int EVP_PKEY_verify_init(EVP_PKEY_CTX *ctx) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->verify) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_verify_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  ctx->operation = EVP_PKEY_OP_VERIFY;
+  if (!ctx->pmeth->verify_init) {
+    return 1;
+  }
+  if (!ctx->pmeth->verify_init(ctx)) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return 0;
+  }
+
+  return 1;
+}
+
+int EVP_PKEY_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig, size_t sig_len,
+                    const uint8_t *data, size_t data_len) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->verify) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_verify,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  if (ctx->operation != EVP_PKEY_OP_VERIFY) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_verify, EVP_R_OPERATON_NOT_INITIALIZED);
+    return 0;
+  }
+  return ctx->pmeth->verify(ctx, sig, sig_len, data, data_len);
+}
+
+int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->encrypt) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_encrypt_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  ctx->operation = EVP_PKEY_OP_ENCRYPT;
+  if (!ctx->pmeth->encrypt_init) {
+    return 1;
+  }
+  if (!ctx->pmeth->encrypt_init(ctx)) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return 0;
+  }
+  return 1;
+}
+
+int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *outlen,
+                     const uint8_t *in, size_t inlen) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->encrypt) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_encrypt,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  if (ctx->operation != EVP_PKEY_OP_ENCRYPT) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_encrypt, EVP_R_OPERATON_NOT_INITIALIZED);
+    return 0;
+  }
+  return ctx->pmeth->encrypt(ctx, out, outlen, in, inlen);
+}
+
+int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->decrypt) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_decrypt_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  ctx->operation = EVP_PKEY_OP_DECRYPT;
+  if (!ctx->pmeth->decrypt_init) {
+    return 1;
+  }
+  if (!ctx->pmeth->decrypt_init(ctx)) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return 0;
+  }
+  return 1;
+}
+
+int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *outlen,
+                     const uint8_t *in, size_t inlen) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->decrypt) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_decrypt,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  if (ctx->operation != EVP_PKEY_OP_DECRYPT) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_decrypt, EVP_R_OPERATON_NOT_INITIALIZED);
+    return 0;
+  }
+  return ctx->pmeth->decrypt(ctx, out, outlen, in, inlen);
+}
+
+int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->derive) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  ctx->operation = EVP_PKEY_OP_DERIVE;
+  if (!ctx->pmeth->derive_init) {
+    return 1;
+  }
+  if (!ctx->pmeth->derive_init(ctx)) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return 0;
+  }
+  return 1;
+}
+
+int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer) {
+  int ret;
+  if (!ctx || !ctx->pmeth ||
+      !(ctx->pmeth->derive || ctx->pmeth->encrypt || ctx->pmeth->decrypt) ||
+      !ctx->pmeth->ctrl) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  if (ctx->operation != EVP_PKEY_OP_DERIVE &&
+      ctx->operation != EVP_PKEY_OP_ENCRYPT &&
+      ctx->operation != EVP_PKEY_OP_DECRYPT) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer,
+                      EVP_R_OPERATON_NOT_INITIALIZED);
+    return 0;
+  }
+
+  ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 0, peer);
+
+  if (ret <= 0) {
+    return 0;
+  }
+
+  if (ret == 2) {
+    return 1;
+  }
+
+  if (!ctx->pkey) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer, EVP_R_NO_KEY_SET);
+    return 0;
+  }
+
+  if (ctx->pkey->type != peer->type) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer, EVP_R_DIFFERENT_KEY_TYPES);
+    return 0;
+  }
+
+  /* ran@cryptocom.ru: For clarity.  The error is if parameters in peer are
+   * present (!missing) but don't match.  EVP_PKEY_cmp_parameters may return
+   * 1 (match), 0 (don't match) and -2 (comparison is not defined).  -1
+   * (different key types) is impossible here because it is checked earlier.
+   * -2 is OK for us here, as well as 1, so we can check for 0 only. */
+  if (!EVP_PKEY_missing_parameters(peer) &&
+      !EVP_PKEY_cmp_parameters(ctx->pkey, peer)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive_set_peer,
+                      EVP_R_DIFFERENT_PARAMETERS);
+    return 0;
+  }
+
+  if (ctx->peerkey) {
+    EVP_PKEY_free(ctx->peerkey);
+  }
+  ctx->peerkey = peer;
+
+  ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 1, peer);
+
+  if (ret <= 0) {
+    ctx->peerkey = NULL;
+    return 0;
+  }
+
+  EVP_PKEY_dup(peer);
+  return 1;
+}
+
+int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, uint8_t *key, size_t *out_key_len) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->derive) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  if (ctx->operation != EVP_PKEY_OP_DERIVE) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_derive, EVP_R_OPERATON_NOT_INITIALIZED);
+    return 0;
+  }
+  return ctx->pmeth->derive(ctx, key, out_key_len);
+}
+
+int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->keygen) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_keygen_init,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  ctx->operation = EVP_PKEY_OP_KEYGEN;
+  if (!ctx->pmeth->keygen_init) {
+    return 1;
+  }
+  if (!ctx->pmeth->keygen_init(ctx)) {
+    ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return 0;
+  }
+  return 1;
+}
+
+int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey) {
+  if (!ctx || !ctx->pmeth || !ctx->pmeth->keygen) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_keygen,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+  if (ctx->operation != EVP_PKEY_OP_KEYGEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_PKEY_keygen, EVP_R_OPERATON_NOT_INITIALIZED);
+    return 0;
+  }
+
+  if (!ppkey) {
+    return 0;
+  }
+
+  if (!*ppkey) {
+    *ppkey = EVP_PKEY_new();
+    if (!*ppkey) {
+      OPENSSL_PUT_ERROR(EVP, EVP_PKEY_keygen, ERR_LIB_EVP);
+      return 0;
+    }
+  }
+
+  if (!ctx->pmeth->keygen(ctx, *ppkey)) {
+    EVP_PKEY_free(*ppkey);
+    *ppkey = NULL;
+    return 0;
+  }
+  return 1;
+}
diff --git a/src/crypto/evp/evp_error.c b/src/crypto/evp/evp_error.c
new file mode 100644
index 0000000..b0d311e
--- /dev/null
+++ b/src/crypto/evp/evp_error.c
@@ -0,0 +1,131 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/err.h>
+
+#include <openssl/evp.h>
+
+const ERR_STRING_DATA EVP_error_string_data[] = {
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_DigestSignAlgorithm, 0), "EVP_DigestSignAlgorithm"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_DigestVerifyInitFromAlgorithm, 0), "EVP_DigestVerifyInitFromAlgorithm"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_CTX_ctrl, 0), "EVP_PKEY_CTX_ctrl"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_CTX_dup, 0), "EVP_PKEY_CTX_dup"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_copy_parameters, 0), "EVP_PKEY_copy_parameters"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_decrypt, 0), "EVP_PKEY_decrypt"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_decrypt_init, 0), "EVP_PKEY_decrypt_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_derive, 0), "EVP_PKEY_derive"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_derive_init, 0), "EVP_PKEY_derive_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_derive_set_peer, 0), "EVP_PKEY_derive_set_peer"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_encrypt, 0), "EVP_PKEY_encrypt"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_encrypt_init, 0), "EVP_PKEY_encrypt_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_get1_DH, 0), "EVP_PKEY_get1_DH"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_get1_DSA, 0), "EVP_PKEY_get1_DSA"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_get1_EC_KEY, 0), "EVP_PKEY_get1_EC_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_get1_RSA, 0), "EVP_PKEY_get1_RSA"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_keygen, 0), "EVP_PKEY_keygen"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_keygen_init, 0), "EVP_PKEY_keygen_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_new, 0), "EVP_PKEY_new"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_set_type, 0), "EVP_PKEY_set_type"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_sign, 0), "EVP_PKEY_sign"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_sign_init, 0), "EVP_PKEY_sign_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_verify, 0), "EVP_PKEY_verify"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_verify_init, 0), "EVP_PKEY_verify_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_check_padding_md, 0), "check_padding_md"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_d2i_AutoPrivateKey, 0), "d2i_AutoPrivateKey"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_d2i_PrivateKey, 0), "d2i_PrivateKey"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_do_EC_KEY_print, 0), "do_EC_KEY_print"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_do_rsa_print, 0), "do_rsa_print"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_do_sigver_init, 0), "do_sigver_init"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_param2type, 0), "eckey_param2type"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_param_decode, 0), "eckey_param_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_priv_decode, 0), "eckey_priv_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_priv_encode, 0), "eckey_priv_encode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_pub_decode, 0), "eckey_pub_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_pub_encode, 0), "eckey_pub_encode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_eckey_type2param, 0), "eckey_type2param"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_evp_pkey_ctx_new, 0), "evp_pkey_ctx_new"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_hmac_signctx, 0), "hmac_signctx"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_i2d_PublicKey, 0), "i2d_PublicKey"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_old_ec_priv_decode, 0), "old_ec_priv_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_old_rsa_priv_decode, 0), "old_rsa_priv_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_ctrl, 0), "pkey_ec_ctrl"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_derive, 0), "pkey_ec_derive"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_keygen, 0), "pkey_ec_keygen"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_paramgen, 0), "pkey_ec_paramgen"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_ec_sign, 0), "pkey_ec_sign"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_rsa_ctrl, 0), "pkey_rsa_ctrl"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_rsa_decrypt, 0), "pkey_rsa_decrypt"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_rsa_encrypt, 0), "pkey_rsa_encrypt"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_pkey_rsa_sign, 0), "pkey_rsa_sign"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_algor_to_md, 0), "rsa_algor_to_md"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_digest_verify_init_from_algorithm, 0), "rsa_digest_verify_init_from_algorithm"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_item_verify, 0), "rsa_item_verify"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_mgf1_to_md, 0), "rsa_mgf1_to_md"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_priv_decode, 0), "rsa_priv_decode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_priv_encode, 0), "rsa_priv_encode"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_pss_to_ctx, 0), "rsa_pss_to_ctx"},
+  {ERR_PACK(ERR_LIB_EVP, EVP_F_rsa_pub_decode, 0), "rsa_pub_decode"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_BUFFER_TOO_SMALL), "BUFFER_TOO_SMALL"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_COMMAND_NOT_SUPPORTED), "COMMAND_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_CONTEXT_NOT_INITIALISED), "CONTEXT_NOT_INITIALISED"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DECODE_ERROR), "DECODE_ERROR"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DIFFERENT_KEY_TYPES), "DIFFERENT_KEY_TYPES"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DIFFERENT_PARAMETERS), "DIFFERENT_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED), "DIGEST_AND_KEY_TYPE_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_DIGEST_DOES_NOT_MATCH), "DIGEST_DOES_NOT_MATCH"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_AN_DSA_KEY), "EXPECTING_AN_DSA_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_AN_EC_KEY_KEY), "EXPECTING_AN_EC_KEY_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_AN_RSA_KEY), "EXPECTING_AN_RSA_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_A_DH_KEY), "EXPECTING_A_DH_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPECTING_A_DSA_KEY), "EXPECTING_A_DSA_KEY"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_EXPLICIT_EC_PARAMETERS_NOT_SUPPORTED), "EXPLICIT_EC_PARAMETERS_NOT_SUPPORTED"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE), "ILLEGAL_OR_UNSUPPORTED_PADDING_MODE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_CURVE), "INVALID_CURVE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_DIGEST_LENGTH), "INVALID_DIGEST_LENGTH"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_DIGEST_TYPE), "INVALID_DIGEST_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_KEYBITS), "INVALID_KEYBITS"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_MGF1_MD), "INVALID_MGF1_MD"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_OPERATION), "INVALID_OPERATION"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_PADDING_MODE), "INVALID_PADDING_MODE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_PSS_PARAMETERS), "INVALID_PSS_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_PSS_SALTLEN), "INVALID_PSS_SALTLEN"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_SALT_LENGTH), "INVALID_SALT_LENGTH"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_TRAILER), "INVALID_TRAILER"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KDF_PARAMETER_ERROR), "KDF_PARAMETER_ERROR"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEYS_NOT_SET), "KEYS_NOT_SET"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_MISSING_PARAMETERS), "MISSING_PARAMETERS"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DEFAULT_DIGEST), "NO_DEFAULT_DIGEST"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEY_SET), "NO_KEY_SET"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_MDC2_SUPPORT), "NO_MDC2_SUPPORT"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_NID_FOR_CURVE), "NO_NID_FOR_CURVE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_OPERATION_SET), "NO_OPERATION_SET"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_PARAMETERS_SET), "NO_PARAMETERS_SET"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE), "OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_OPERATON_NOT_INITIALIZED), "OPERATON_NOT_INITIALIZED"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_PEER_KEY_ERROR), "PEER_KEY_ERROR"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_SHARED_INFO_ERROR), "SHARED_INFO_ERROR"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_DIGEST), "UNKNOWN_DIGEST"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_MASK_DIGEST), "UNKNOWN_MASK_DIGEST"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM), "UNKNOWN_MESSAGE_DIGEST_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_PUBLIC_KEY_TYPE), "UNKNOWN_PUBLIC_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_SIGNATURE_ALGORITHM), "UNKNOWN_SIGNATURE_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_ALGORITHM), "UNSUPPORTED_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_MASK_ALGORITHM), "UNSUPPORTED_MASK_ALGORITHM"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_MASK_PARAMETER), "UNSUPPORTED_MASK_PARAMETER"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE), "UNSUPPORTED_PUBLIC_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNSUPPORTED_SIGNATURE_TYPE), "UNSUPPORTED_SIGNATURE_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_WRONG_PUBLIC_KEY_TYPE), "WRONG_PUBLIC_KEY_TYPE"},
+  {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_X931_UNSUPPORTED), "X931_UNSUPPORTED"},
+  {0, NULL},
+};
diff --git a/src/crypto/evp/evp_test.c b/src/crypto/evp/evp_test.c
new file mode 100644
index 0000000..3e74cd5
--- /dev/null
+++ b/src/crypto/evp/evp_test.c
@@ -0,0 +1,639 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <openssl/bio.h>
+#include <openssl/bytestring.h>
+#include <openssl/crypto.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+
+/* kExampleRSAKeyDER is an RSA private key in ASN.1, DER format. Of course, you
+ * should never use this key anywhere but in an example. */
+static const uint8_t kExampleRSAKeyDER[] = {
+    0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xf8,
+    0xb8, 0x6c, 0x83, 0xb4, 0xbc, 0xd9, 0xa8, 0x57, 0xc0, 0xa5, 0xb4, 0x59,
+    0x76, 0x8c, 0x54, 0x1d, 0x79, 0xeb, 0x22, 0x52, 0x04, 0x7e, 0xd3, 0x37,
+    0xeb, 0x41, 0xfd, 0x83, 0xf9, 0xf0, 0xa6, 0x85, 0x15, 0x34, 0x75, 0x71,
+    0x5a, 0x84, 0xa8, 0x3c, 0xd2, 0xef, 0x5a, 0x4e, 0xd3, 0xde, 0x97, 0x8a,
+    0xdd, 0xff, 0xbb, 0xcf, 0x0a, 0xaa, 0x86, 0x92, 0xbe, 0xb8, 0x50, 0xe4,
+    0xcd, 0x6f, 0x80, 0x33, 0x30, 0x76, 0x13, 0x8f, 0xca, 0x7b, 0xdc, 0xec,
+    0x5a, 0xca, 0x63, 0xc7, 0x03, 0x25, 0xef, 0xa8, 0x8a, 0x83, 0x58, 0x76,
+    0x20, 0xfa, 0x16, 0x77, 0xd7, 0x79, 0x92, 0x63, 0x01, 0x48, 0x1a, 0xd8,
+    0x7b, 0x67, 0xf1, 0x52, 0x55, 0x49, 0x4e, 0xd6, 0x6e, 0x4a, 0x5c, 0xd7,
+    0x7a, 0x37, 0x36, 0x0c, 0xde, 0xdd, 0x8f, 0x44, 0xe8, 0xc2, 0xa7, 0x2c,
+    0x2b, 0xb5, 0xaf, 0x64, 0x4b, 0x61, 0x07, 0x02, 0x03, 0x01, 0x00, 0x01,
+    0x02, 0x81, 0x80, 0x74, 0x88, 0x64, 0x3f, 0x69, 0x45, 0x3a, 0x6d, 0xc7,
+    0x7f, 0xb9, 0xa3, 0xc0, 0x6e, 0xec, 0xdc, 0xd4, 0x5a, 0xb5, 0x32, 0x85,
+    0x5f, 0x19, 0xd4, 0xf8, 0xd4, 0x3f, 0x3c, 0xfa, 0xc2, 0xf6, 0x5f, 0xee,
+    0xe6, 0xba, 0x87, 0x74, 0x2e, 0xc7, 0x0c, 0xd4, 0x42, 0xb8, 0x66, 0x85,
+    0x9c, 0x7b, 0x24, 0x61, 0xaa, 0x16, 0x11, 0xf6, 0xb5, 0xb6, 0xa4, 0x0a,
+    0xc9, 0x55, 0x2e, 0x81, 0xa5, 0x47, 0x61, 0xcb, 0x25, 0x8f, 0xc2, 0x15,
+    0x7b, 0x0e, 0x7c, 0x36, 0x9f, 0x3a, 0xda, 0x58, 0x86, 0x1c, 0x5b, 0x83,
+    0x79, 0xe6, 0x2b, 0xcc, 0xe6, 0xfa, 0x2c, 0x61, 0xf2, 0x78, 0x80, 0x1b,
+    0xe2, 0xf3, 0x9d, 0x39, 0x2b, 0x65, 0x57, 0x91, 0x3d, 0x71, 0x99, 0x73,
+    0xa5, 0xc2, 0x79, 0x20, 0x8c, 0x07, 0x4f, 0xe5, 0xb4, 0x60, 0x1f, 0x99,
+    0xa2, 0xb1, 0x4f, 0x0c, 0xef, 0xbc, 0x59, 0x53, 0x00, 0x7d, 0xb1, 0x02,
+    0x41, 0x00, 0xfc, 0x7e, 0x23, 0x65, 0x70, 0xf8, 0xce, 0xd3, 0x40, 0x41,
+    0x80, 0x6a, 0x1d, 0x01, 0xd6, 0x01, 0xff, 0xb6, 0x1b, 0x3d, 0x3d, 0x59,
+    0x09, 0x33, 0x79, 0xc0, 0x4f, 0xde, 0x96, 0x27, 0x4b, 0x18, 0xc6, 0xd9,
+    0x78, 0xf1, 0xf4, 0x35, 0x46, 0xe9, 0x7c, 0x42, 0x7a, 0x5d, 0x9f, 0xef,
+    0x54, 0xb8, 0xf7, 0x9f, 0xc4, 0x33, 0x6c, 0xf3, 0x8c, 0x32, 0x46, 0x87,
+    0x67, 0x30, 0x7b, 0xa7, 0xac, 0xe3, 0x02, 0x41, 0x00, 0xfc, 0x2c, 0xdf,
+    0x0c, 0x0d, 0x88, 0xf5, 0xb1, 0x92, 0xa8, 0x93, 0x47, 0x63, 0x55, 0xf5,
+    0xca, 0x58, 0x43, 0xba, 0x1c, 0xe5, 0x9e, 0xb6, 0x95, 0x05, 0xcd, 0xb5,
+    0x82, 0xdf, 0xeb, 0x04, 0x53, 0x9d, 0xbd, 0xc2, 0x38, 0x16, 0xb3, 0x62,
+    0xdd, 0xa1, 0x46, 0xdb, 0x6d, 0x97, 0x93, 0x9f, 0x8a, 0xc3, 0x9b, 0x64,
+    0x7e, 0x42, 0xe3, 0x32, 0x57, 0x19, 0x1b, 0xd5, 0x6e, 0x85, 0xfa, 0xb8,
+    0x8d, 0x02, 0x41, 0x00, 0xbc, 0x3d, 0xde, 0x6d, 0xd6, 0x97, 0xe8, 0xba,
+    0x9e, 0x81, 0x37, 0x17, 0xe5, 0xa0, 0x64, 0xc9, 0x00, 0xb7, 0xe7, 0xfe,
+    0xf4, 0x29, 0xd9, 0x2e, 0x43, 0x6b, 0x19, 0x20, 0xbd, 0x99, 0x75, 0xe7,
+    0x76, 0xf8, 0xd3, 0xae, 0xaf, 0x7e, 0xb8, 0xeb, 0x81, 0xf4, 0x9d, 0xfe,
+    0x07, 0x2b, 0x0b, 0x63, 0x0b, 0x5a, 0x55, 0x90, 0x71, 0x7d, 0xf1, 0xdb,
+    0xd9, 0xb1, 0x41, 0x41, 0x68, 0x2f, 0x4e, 0x39, 0x02, 0x40, 0x5a, 0x34,
+    0x66, 0xd8, 0xf5, 0xe2, 0x7f, 0x18, 0xb5, 0x00, 0x6e, 0x26, 0x84, 0x27,
+    0x14, 0x93, 0xfb, 0xfc, 0xc6, 0x0f, 0x5e, 0x27, 0xe6, 0xe1, 0xe9, 0xc0,
+    0x8a, 0xe4, 0x34, 0xda, 0xe9, 0xa2, 0x4b, 0x73, 0xbc, 0x8c, 0xb9, 0xba,
+    0x13, 0x6c, 0x7a, 0x2b, 0x51, 0x84, 0xa3, 0x4a, 0xe0, 0x30, 0x10, 0x06,
+    0x7e, 0xed, 0x17, 0x5a, 0x14, 0x00, 0xc9, 0xef, 0x85, 0xea, 0x52, 0x2c,
+    0xbc, 0x65, 0x02, 0x40, 0x51, 0xe3, 0xf2, 0x83, 0x19, 0x9b, 0xc4, 0x1e,
+    0x2f, 0x50, 0x3d, 0xdf, 0x5a, 0xa2, 0x18, 0xca, 0x5f, 0x2e, 0x49, 0xaf,
+    0x6f, 0xcc, 0xfa, 0x65, 0x77, 0x94, 0xb5, 0xa1, 0x0a, 0xa9, 0xd1, 0x8a,
+    0x39, 0x37, 0xf4, 0x0b, 0xa0, 0xd7, 0x82, 0x27, 0x5e, 0xae, 0x17, 0x17,
+    0xa1, 0x1e, 0x54, 0x34, 0xbf, 0x6e, 0xc4, 0x8e, 0x99, 0x5d, 0x08, 0xf1,
+    0x2d, 0x86, 0x9d, 0xa5, 0x20, 0x1b, 0xe5, 0xdf,
+};
+
+static const uint8_t kMsg[] = {1, 2, 3, 4};
+
+static const uint8_t kSignature[] = {
+    0xa5, 0xf0, 0x8a, 0x47, 0x5d, 0x3c, 0xb3, 0xcc, 0xa9, 0x79, 0xaf, 0x4d,
+    0x8c, 0xae, 0x4c, 0x14, 0xef, 0xc2, 0x0b, 0x34, 0x36, 0xde, 0xf4, 0x3e,
+    0x3d, 0xbb, 0x4a, 0x60, 0x5c, 0xc8, 0x91, 0x28, 0xda, 0xfb, 0x7e, 0x04,
+    0x96, 0x7e, 0x63, 0x13, 0x90, 0xce, 0xb9, 0xb4, 0x62, 0x7a, 0xfd, 0x09,
+    0x3d, 0xc7, 0x67, 0x78, 0x54, 0x04, 0xeb, 0x52, 0x62, 0x6e, 0x24, 0x67,
+    0xb4, 0x40, 0xfc, 0x57, 0x62, 0xc6, 0xf1, 0x67, 0xc1, 0x97, 0x8f, 0x6a,
+    0xa8, 0xae, 0x44, 0x46, 0x5e, 0xab, 0x67, 0x17, 0x53, 0x19, 0x3a, 0xda,
+    0x5a, 0xc8, 0x16, 0x3e, 0x86, 0xd5, 0xc5, 0x71, 0x2f, 0xfc, 0x23, 0x48,
+    0xd9, 0x0b, 0x13, 0xdd, 0x7b, 0x5a, 0x25, 0x79, 0xef, 0xa5, 0x7b, 0x04,
+    0xed, 0x44, 0xf6, 0x18, 0x55, 0xe4, 0x0a, 0xe9, 0x57, 0x79, 0x5d, 0xd7,
+    0x55, 0xa7, 0xab, 0x45, 0x02, 0x97, 0x60, 0x42,
+};
+
+/* kExamplePSSCert is an example self-signed certificate, signed with
+ * kExampleRSAKeyDER using RSA-PSS with default hash functions. */
+static const uint8_t kExamplePSSCert[] = {
+    0x30, 0x82, 0x02, 0x62, 0x30, 0x82, 0x01, 0xc6, 0xa0, 0x03, 0x02, 0x01,
+    0x02, 0x02, 0x09, 0x00, 0x8d, 0xea, 0x53, 0x24, 0xfa, 0x48, 0x87, 0xf3,
+    0x30, 0x12, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+    0x0a, 0x30, 0x05, 0xa2, 0x03, 0x02, 0x01, 0x6a, 0x30, 0x45, 0x31, 0x0b,
+    0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31,
+    0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f,
+    0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f,
+    0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
+    0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20,
+    0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+    0x34, 0x31, 0x30, 0x30, 0x39, 0x31, 0x39, 0x30, 0x39, 0x35, 0x35, 0x5a,
+    0x17, 0x0d, 0x31, 0x35, 0x31, 0x30, 0x30, 0x39, 0x31, 0x39, 0x30, 0x39,
+    0x35, 0x35, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+    0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+    0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
+    0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+    0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
+    0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+    0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+    0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00,
+    0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xf8, 0xb8, 0x6c, 0x83, 0xb4,
+    0xbc, 0xd9, 0xa8, 0x57, 0xc0, 0xa5, 0xb4, 0x59, 0x76, 0x8c, 0x54, 0x1d,
+    0x79, 0xeb, 0x22, 0x52, 0x04, 0x7e, 0xd3, 0x37, 0xeb, 0x41, 0xfd, 0x83,
+    0xf9, 0xf0, 0xa6, 0x85, 0x15, 0x34, 0x75, 0x71, 0x5a, 0x84, 0xa8, 0x3c,
+    0xd2, 0xef, 0x5a, 0x4e, 0xd3, 0xde, 0x97, 0x8a, 0xdd, 0xff, 0xbb, 0xcf,
+    0x0a, 0xaa, 0x86, 0x92, 0xbe, 0xb8, 0x50, 0xe4, 0xcd, 0x6f, 0x80, 0x33,
+    0x30, 0x76, 0x13, 0x8f, 0xca, 0x7b, 0xdc, 0xec, 0x5a, 0xca, 0x63, 0xc7,
+    0x03, 0x25, 0xef, 0xa8, 0x8a, 0x83, 0x58, 0x76, 0x20, 0xfa, 0x16, 0x77,
+    0xd7, 0x79, 0x92, 0x63, 0x01, 0x48, 0x1a, 0xd8, 0x7b, 0x67, 0xf1, 0x52,
+    0x55, 0x49, 0x4e, 0xd6, 0x6e, 0x4a, 0x5c, 0xd7, 0x7a, 0x37, 0x36, 0x0c,
+    0xde, 0xdd, 0x8f, 0x44, 0xe8, 0xc2, 0xa7, 0x2c, 0x2b, 0xb5, 0xaf, 0x64,
+    0x4b, 0x61, 0x07, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e,
+    0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xd0,
+    0x41, 0xfb, 0x89, 0x41, 0x1e, 0xa7, 0xad, 0x5a, 0xec, 0x34, 0x5d, 0x49,
+    0x11, 0xf9, 0x55, 0x81, 0x78, 0x1f, 0x13, 0x30, 0x1f, 0x06, 0x03, 0x55,
+    0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd0, 0x41, 0xfb, 0x89,
+    0x41, 0x1e, 0xa7, 0xad, 0x5a, 0xec, 0x34, 0x5d, 0x49, 0x11, 0xf9, 0x55,
+    0x81, 0x78, 0x1f, 0x13, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04,
+    0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x12, 0x06, 0x09, 0x2a, 0x86,
+    0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x05, 0xa2, 0x03, 0x02,
+    0x01, 0x6a, 0x03, 0x81, 0x81, 0x00, 0x49, 0x4c, 0xb6, 0x45, 0x97, 0x20,
+    0x35, 0xb3, 0x50, 0x64, 0x0d, 0x3f, 0xec, 0x5f, 0x95, 0xd5, 0x84, 0xcb,
+    0x11, 0x7c, 0x03, 0xd7, 0xa6, 0xe6, 0xfa, 0x24, 0x95, 0x9f, 0x31, 0xb0,
+    0xb5, 0xec, 0x66, 0x41, 0x51, 0x18, 0x21, 0x91, 0xbb, 0xe0, 0xaf, 0xf0,
+    0xc5, 0xb7, 0x59, 0x41, 0xd4, 0xdb, 0xa4, 0xd2, 0x64, 0xa7, 0x54, 0x0f,
+    0x8c, 0xf7, 0xe1, 0xd3, 0x3b, 0x1a, 0xb7, 0x0e, 0x9d, 0x9a, 0xde, 0x50,
+    0xa1, 0x9f, 0x0a, 0xf0, 0xda, 0x34, 0x0e, 0x34, 0x7d, 0x76, 0x07, 0xfe,
+    0x5a, 0xfb, 0xf9, 0x58, 0x9b, 0xc9, 0x50, 0x84, 0x01, 0xa0, 0x05, 0x4d,
+    0x67, 0x42, 0x0b, 0xf8, 0xe4, 0x05, 0xcf, 0xaf, 0x8b, 0x71, 0x31, 0xf1,
+    0x0f, 0x6e, 0xc9, 0x24, 0x27, 0x9b, 0xac, 0x04, 0xd7, 0x64, 0x0d, 0x30,
+    0x4e, 0x11, 0x93, 0x40, 0x39, 0xbb, 0x72, 0xb2, 0xfe, 0x6b, 0xe4, 0xae,
+    0x8c, 0x16,
+};
+
+/* kExampleRSAKeyPKCS8 is kExampleRSAKeyDER encoded in a PKCS #8
+ * PrivateKeyInfo. */
+static const uint8_t kExampleRSAKeyPKCS8[] = {
+    0x30, 0x82, 0x02, 0x76, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
+    0x02, 0x60, 0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81,
+    0x00, 0xf8, 0xb8, 0x6c, 0x83, 0xb4, 0xbc, 0xd9, 0xa8, 0x57, 0xc0, 0xa5,
+    0xb4, 0x59, 0x76, 0x8c, 0x54, 0x1d, 0x79, 0xeb, 0x22, 0x52, 0x04, 0x7e,
+    0xd3, 0x37, 0xeb, 0x41, 0xfd, 0x83, 0xf9, 0xf0, 0xa6, 0x85, 0x15, 0x34,
+    0x75, 0x71, 0x5a, 0x84, 0xa8, 0x3c, 0xd2, 0xef, 0x5a, 0x4e, 0xd3, 0xde,
+    0x97, 0x8a, 0xdd, 0xff, 0xbb, 0xcf, 0x0a, 0xaa, 0x86, 0x92, 0xbe, 0xb8,
+    0x50, 0xe4, 0xcd, 0x6f, 0x80, 0x33, 0x30, 0x76, 0x13, 0x8f, 0xca, 0x7b,
+    0xdc, 0xec, 0x5a, 0xca, 0x63, 0xc7, 0x03, 0x25, 0xef, 0xa8, 0x8a, 0x83,
+    0x58, 0x76, 0x20, 0xfa, 0x16, 0x77, 0xd7, 0x79, 0x92, 0x63, 0x01, 0x48,
+    0x1a, 0xd8, 0x7b, 0x67, 0xf1, 0x52, 0x55, 0x49, 0x4e, 0xd6, 0x6e, 0x4a,
+    0x5c, 0xd7, 0x7a, 0x37, 0x36, 0x0c, 0xde, 0xdd, 0x8f, 0x44, 0xe8, 0xc2,
+    0xa7, 0x2c, 0x2b, 0xb5, 0xaf, 0x64, 0x4b, 0x61, 0x07, 0x02, 0x03, 0x01,
+    0x00, 0x01, 0x02, 0x81, 0x80, 0x74, 0x88, 0x64, 0x3f, 0x69, 0x45, 0x3a,
+    0x6d, 0xc7, 0x7f, 0xb9, 0xa3, 0xc0, 0x6e, 0xec, 0xdc, 0xd4, 0x5a, 0xb5,
+    0x32, 0x85, 0x5f, 0x19, 0xd4, 0xf8, 0xd4, 0x3f, 0x3c, 0xfa, 0xc2, 0xf6,
+    0x5f, 0xee, 0xe6, 0xba, 0x87, 0x74, 0x2e, 0xc7, 0x0c, 0xd4, 0x42, 0xb8,
+    0x66, 0x85, 0x9c, 0x7b, 0x24, 0x61, 0xaa, 0x16, 0x11, 0xf6, 0xb5, 0xb6,
+    0xa4, 0x0a, 0xc9, 0x55, 0x2e, 0x81, 0xa5, 0x47, 0x61, 0xcb, 0x25, 0x8f,
+    0xc2, 0x15, 0x7b, 0x0e, 0x7c, 0x36, 0x9f, 0x3a, 0xda, 0x58, 0x86, 0x1c,
+    0x5b, 0x83, 0x79, 0xe6, 0x2b, 0xcc, 0xe6, 0xfa, 0x2c, 0x61, 0xf2, 0x78,
+    0x80, 0x1b, 0xe2, 0xf3, 0x9d, 0x39, 0x2b, 0x65, 0x57, 0x91, 0x3d, 0x71,
+    0x99, 0x73, 0xa5, 0xc2, 0x79, 0x20, 0x8c, 0x07, 0x4f, 0xe5, 0xb4, 0x60,
+    0x1f, 0x99, 0xa2, 0xb1, 0x4f, 0x0c, 0xef, 0xbc, 0x59, 0x53, 0x00, 0x7d,
+    0xb1, 0x02, 0x41, 0x00, 0xfc, 0x7e, 0x23, 0x65, 0x70, 0xf8, 0xce, 0xd3,
+    0x40, 0x41, 0x80, 0x6a, 0x1d, 0x01, 0xd6, 0x01, 0xff, 0xb6, 0x1b, 0x3d,
+    0x3d, 0x59, 0x09, 0x33, 0x79, 0xc0, 0x4f, 0xde, 0x96, 0x27, 0x4b, 0x18,
+    0xc6, 0xd9, 0x78, 0xf1, 0xf4, 0x35, 0x46, 0xe9, 0x7c, 0x42, 0x7a, 0x5d,
+    0x9f, 0xef, 0x54, 0xb8, 0xf7, 0x9f, 0xc4, 0x33, 0x6c, 0xf3, 0x8c, 0x32,
+    0x46, 0x87, 0x67, 0x30, 0x7b, 0xa7, 0xac, 0xe3, 0x02, 0x41, 0x00, 0xfc,
+    0x2c, 0xdf, 0x0c, 0x0d, 0x88, 0xf5, 0xb1, 0x92, 0xa8, 0x93, 0x47, 0x63,
+    0x55, 0xf5, 0xca, 0x58, 0x43, 0xba, 0x1c, 0xe5, 0x9e, 0xb6, 0x95, 0x05,
+    0xcd, 0xb5, 0x82, 0xdf, 0xeb, 0x04, 0x53, 0x9d, 0xbd, 0xc2, 0x38, 0x16,
+    0xb3, 0x62, 0xdd, 0xa1, 0x46, 0xdb, 0x6d, 0x97, 0x93, 0x9f, 0x8a, 0xc3,
+    0x9b, 0x64, 0x7e, 0x42, 0xe3, 0x32, 0x57, 0x19, 0x1b, 0xd5, 0x6e, 0x85,
+    0xfa, 0xb8, 0x8d, 0x02, 0x41, 0x00, 0xbc, 0x3d, 0xde, 0x6d, 0xd6, 0x97,
+    0xe8, 0xba, 0x9e, 0x81, 0x37, 0x17, 0xe5, 0xa0, 0x64, 0xc9, 0x00, 0xb7,
+    0xe7, 0xfe, 0xf4, 0x29, 0xd9, 0x2e, 0x43, 0x6b, 0x19, 0x20, 0xbd, 0x99,
+    0x75, 0xe7, 0x76, 0xf8, 0xd3, 0xae, 0xaf, 0x7e, 0xb8, 0xeb, 0x81, 0xf4,
+    0x9d, 0xfe, 0x07, 0x2b, 0x0b, 0x63, 0x0b, 0x5a, 0x55, 0x90, 0x71, 0x7d,
+    0xf1, 0xdb, 0xd9, 0xb1, 0x41, 0x41, 0x68, 0x2f, 0x4e, 0x39, 0x02, 0x40,
+    0x5a, 0x34, 0x66, 0xd8, 0xf5, 0xe2, 0x7f, 0x18, 0xb5, 0x00, 0x6e, 0x26,
+    0x84, 0x27, 0x14, 0x93, 0xfb, 0xfc, 0xc6, 0x0f, 0x5e, 0x27, 0xe6, 0xe1,
+    0xe9, 0xc0, 0x8a, 0xe4, 0x34, 0xda, 0xe9, 0xa2, 0x4b, 0x73, 0xbc, 0x8c,
+    0xb9, 0xba, 0x13, 0x6c, 0x7a, 0x2b, 0x51, 0x84, 0xa3, 0x4a, 0xe0, 0x30,
+    0x10, 0x06, 0x7e, 0xed, 0x17, 0x5a, 0x14, 0x00, 0xc9, 0xef, 0x85, 0xea,
+    0x52, 0x2c, 0xbc, 0x65, 0x02, 0x40, 0x51, 0xe3, 0xf2, 0x83, 0x19, 0x9b,
+    0xc4, 0x1e, 0x2f, 0x50, 0x3d, 0xdf, 0x5a, 0xa2, 0x18, 0xca, 0x5f, 0x2e,
+    0x49, 0xaf, 0x6f, 0xcc, 0xfa, 0x65, 0x77, 0x94, 0xb5, 0xa1, 0x0a, 0xa9,
+    0xd1, 0x8a, 0x39, 0x37, 0xf4, 0x0b, 0xa0, 0xd7, 0x82, 0x27, 0x5e, 0xae,
+    0x17, 0x17, 0xa1, 0x1e, 0x54, 0x34, 0xbf, 0x6e, 0xc4, 0x8e, 0x99, 0x5d,
+    0x08, 0xf1, 0x2d, 0x86, 0x9d, 0xa5, 0x20, 0x1b, 0xe5, 0xdf,
+};
+
+/* kExampleECKeyDER is a sample EC private key encoded as an ECPrivateKey
+ * structure. */
+static const uint8_t kExampleECKeyDER[] = {
+    0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x07, 0x0f, 0x08, 0x72, 0x7a,
+    0xd4, 0xa0, 0x4a, 0x9c, 0xdd, 0x59, 0xc9, 0x4d, 0x89, 0x68, 0x77, 0x08,
+    0xb5, 0x6f, 0xc9, 0x5d, 0x30, 0x77, 0x0e, 0xe8, 0xd1, 0xc9, 0xce, 0x0a,
+    0x8b, 0xb4, 0x6a, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xe6, 0x2b, 0x69,
+    0xe2, 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e, 0x0d, 0x94, 0x8a, 0x4c,
+    0xd5, 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d, 0x46, 0xfb, 0xdd, 0xa9, 0xa9,
+    0x1e, 0x9d, 0xdc, 0xba, 0x5a, 0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18,
+    0xf9, 0xc3, 0xc4, 0xa3, 0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16,
+    0x1a, 0x1c, 0xf5, 0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22,
+    0xc1,
+};
+
+/* kExampleBadECKeyDER is a sample EC private key encoded as an ECPrivateKey
+ * structure. The private key is equal to the order and will fail to import */
+static const uint8_t kExampleBadECKeyDER[] = {
+    0x30, 0x66, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48,
+    0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03,
+    0x01, 0x07, 0x04, 0x4C, 0x30, 0x4A, 0x02, 0x01, 0x01, 0x04, 0x20, 0xFF,
+    0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3,
+    0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0xA1, 0x23, 0x03, 0x21, 0x00,
+    0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+    0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51
+};
+
+static EVP_PKEY *load_example_rsa_key(void) {
+  EVP_PKEY *ret = NULL;
+  const uint8_t *derp = kExampleRSAKeyDER;
+  EVP_PKEY *pkey = NULL;
+  RSA *rsa = NULL;
+
+  if (!d2i_RSAPrivateKey(&rsa, &derp, sizeof(kExampleRSAKeyDER))) {
+    return NULL;
+  }
+
+  pkey = EVP_PKEY_new();
+  if (pkey == NULL || !EVP_PKEY_set1_RSA(pkey, rsa)) {
+    goto out;
+  }
+
+  ret = pkey;
+  pkey = NULL;
+
+out:
+  if (pkey) {
+    EVP_PKEY_free(pkey);
+  }
+  if (rsa) {
+    RSA_free(rsa);
+  }
+
+  return ret;
+}
+
+static int test_EVP_DigestSignInit(void) {
+  int ret = 0;
+  EVP_PKEY *pkey = NULL;
+  uint8_t *sig = NULL;
+  size_t sig_len = 0;
+  EVP_MD_CTX md_ctx, md_ctx_verify;
+
+  EVP_MD_CTX_init(&md_ctx);
+  EVP_MD_CTX_init(&md_ctx_verify);
+
+  pkey = load_example_rsa_key();
+  if (pkey == NULL ||
+      !EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, pkey) ||
+      !EVP_DigestSignUpdate(&md_ctx, kMsg, sizeof(kMsg))) {
+    goto out;
+  }
+  /* Determine the size of the signature. */
+  if (!EVP_DigestSignFinal(&md_ctx, NULL, &sig_len)) {
+    goto out;
+  }
+  /* Sanity check for testing. */
+  if (sig_len != EVP_PKEY_size(pkey)) {
+    fprintf(stderr, "sig_len mismatch\n");
+    goto out;
+  }
+
+  sig = malloc(sig_len);
+  if (sig == NULL || !EVP_DigestSignFinal(&md_ctx, sig, &sig_len)) {
+    goto out;
+  }
+
+  /* Ensure that the signature round-trips. */
+  if (!EVP_DigestVerifyInit(&md_ctx_verify, NULL, EVP_sha256(), NULL, pkey) ||
+      !EVP_DigestVerifyUpdate(&md_ctx_verify, kMsg, sizeof(kMsg)) ||
+      !EVP_DigestVerifyFinal(&md_ctx_verify, sig, sig_len)) {
+    goto out;
+  }
+
+  ret = 1;
+
+out:
+  if (!ret) {
+    BIO_print_errors_fp(stderr);
+  }
+
+  EVP_MD_CTX_cleanup(&md_ctx);
+  EVP_MD_CTX_cleanup(&md_ctx_verify);
+  if (pkey) {
+    EVP_PKEY_free(pkey);
+  }
+  if (sig) {
+    free(sig);
+  }
+
+  return ret;
+}
+
+static int test_EVP_DigestVerifyInit(void) {
+  int ret = 0;
+  EVP_PKEY *pkey = NULL;
+  EVP_MD_CTX md_ctx;
+
+  EVP_MD_CTX_init(&md_ctx);
+
+  pkey = load_example_rsa_key();
+  if (pkey == NULL ||
+      !EVP_DigestVerifyInit(&md_ctx, NULL, EVP_sha256(), NULL, pkey) ||
+      !EVP_DigestVerifyUpdate(&md_ctx, kMsg, sizeof(kMsg)) ||
+      !EVP_DigestVerifyFinal(&md_ctx, kSignature, sizeof(kSignature))) {
+    goto out;
+  }
+  ret = 1;
+
+out:
+  if (!ret) {
+    BIO_print_errors_fp(stderr);
+  }
+
+  EVP_MD_CTX_cleanup(&md_ctx);
+  if (pkey) {
+    EVP_PKEY_free(pkey);
+  }
+
+  return ret;
+}
+
+/* test_algorithm_roundtrip signs a message using an already-initialized
+ * |md_ctx|, sampling the AlgorithmIdentifier. It then uses |pkey| and the
+ * AlgorithmIdentifier to verify the signature. */
+static int test_algorithm_roundtrip(EVP_MD_CTX *md_ctx, EVP_PKEY *pkey) {
+  int ret = 0;
+  uint8_t *sig = NULL;
+  size_t sig_len = 0;
+  EVP_MD_CTX md_ctx_verify;
+  X509_ALGOR *algor = NULL;
+
+  EVP_MD_CTX_init(&md_ctx_verify);
+
+  if (!EVP_DigestSignUpdate(md_ctx, kMsg, sizeof(kMsg))) {
+    goto out;
+  }
+
+  /* Save the algorithm. */
+  algor = X509_ALGOR_new();
+  if (algor == NULL || !EVP_DigestSignAlgorithm(md_ctx, algor)) {
+    goto out;
+  }
+
+  /* Determine the size of the signature. */
+  if (!EVP_DigestSignFinal(md_ctx, NULL, &sig_len)) {
+    goto out;
+  }
+  /* Sanity check for testing. */
+  if (sig_len != EVP_PKEY_size(pkey)) {
+    fprintf(stderr, "sig_len mismatch\n");
+    goto out;
+  }
+
+  sig = malloc(sig_len);
+  if (sig == NULL || !EVP_DigestSignFinal(md_ctx, sig, &sig_len)) {
+    goto out;
+  }
+
+  /* Ensure that the signature round-trips. */
+  if (!EVP_DigestVerifyInitFromAlgorithm(&md_ctx_verify, algor, pkey) ||
+      !EVP_DigestVerifyUpdate(&md_ctx_verify, kMsg, sizeof(kMsg)) ||
+      !EVP_DigestVerifyFinal(&md_ctx_verify, sig, sig_len)) {
+    goto out;
+  }
+
+  ret = 1;
+
+out:
+  EVP_MD_CTX_cleanup(&md_ctx_verify);
+  if (sig) {
+    free(sig);
+  }
+  if (algor) {
+    X509_ALGOR_free(algor);
+  }
+
+  return ret;
+}
+
+static int test_EVP_DigestSignAlgorithm(void) {
+  int ret = 0;
+  EVP_PKEY *pkey = NULL;
+  EVP_MD_CTX md_ctx;
+  EVP_PKEY_CTX *pkey_ctx;
+
+  EVP_MD_CTX_init(&md_ctx);
+
+  pkey = load_example_rsa_key();
+  if (pkey == NULL) {
+    goto out;
+  }
+
+  /* Test a simple AlgorithmIdentifier. */
+  if (!EVP_DigestSignInit(&md_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey) ||
+      !test_algorithm_roundtrip(&md_ctx, pkey)) {
+    fprintf(stderr, "RSA with SHA-256 failed\n");
+    goto out;
+  }
+
+  EVP_MD_CTX_cleanup(&md_ctx);
+  EVP_MD_CTX_init(&md_ctx);
+
+  /* Test RSA-PSS with custom parameters. */
+  if (!EVP_DigestSignInit(&md_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey) ||
+      EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1 ||
+      EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha512()) != 1 ||
+      !test_algorithm_roundtrip(&md_ctx, pkey)) {
+    fprintf(stderr, "RSA-PSS failed\n");
+    goto out;
+  }
+
+  ret = 1;
+
+out:
+  if (!ret) {
+    BIO_print_errors_fp(stderr);
+  }
+
+  EVP_MD_CTX_cleanup(&md_ctx);
+  if (pkey) {
+    EVP_PKEY_free(pkey);
+  }
+
+  return ret;
+}
+
+static int test_EVP_DigestVerifyInitFromAlgorithm(void) {
+  int ret = 0;
+  CBS cert, cert_body, tbs_cert, algorithm, signature;
+  uint8_t padding;
+  X509_ALGOR *algor = NULL;
+  const uint8_t *derp;
+  EVP_PKEY *pkey = NULL;
+  EVP_MD_CTX md_ctx;
+
+  EVP_MD_CTX_init(&md_ctx);
+
+  CBS_init(&cert, kExamplePSSCert, sizeof(kExamplePSSCert));
+  if (!CBS_get_asn1(&cert, &cert_body, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&cert) != 0 ||
+      !CBS_get_any_asn1_element(&cert_body, &tbs_cert, NULL, NULL) ||
+      !CBS_get_asn1_element(&cert_body, &algorithm, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1(&cert_body, &signature, CBS_ASN1_BITSTRING) ||
+      CBS_len(&cert_body) != 0) {
+    fprintf(stderr, "Failed to parse certificate\n");
+    goto out;
+  }
+
+  /* Signatures are BIT STRINGs, but they have are multiple of 8 bytes, so the
+     leading phase byte is just a zero. */
+  if (!CBS_get_u8(&signature, &padding) || padding != 0) {
+    fprintf(stderr, "Invalid signature padding\n");
+    goto out;
+  }
+
+  derp = CBS_data(&algorithm);
+  if (!d2i_X509_ALGOR(&algor, &derp, CBS_len(&algorithm)) ||
+      derp != CBS_data(&algorithm) + CBS_len(&algorithm)) {
+    fprintf(stderr, "Failed to parse algorithm\n");
+  }
+
+  pkey = load_example_rsa_key();
+  if (pkey == NULL ||
+      !EVP_DigestVerifyInitFromAlgorithm(&md_ctx, algor, pkey) ||
+      !EVP_DigestVerifyUpdate(&md_ctx, CBS_data(&tbs_cert),
+                              CBS_len(&tbs_cert)) ||
+      !EVP_DigestVerifyFinal(&md_ctx, CBS_data(&signature),
+                             CBS_len(&signature))) {
+    goto out;
+  }
+  ret = 1;
+
+out:
+  if (!ret) {
+    BIO_print_errors_fp(stderr);
+  }
+
+  EVP_MD_CTX_cleanup(&md_ctx);
+  if (pkey) {
+    EVP_PKEY_free(pkey);
+  }
+
+  return ret;
+}
+
+static int test_d2i_AutoPrivateKey(const uint8_t *input, size_t input_len,
+                                   int expected_id) {
+  int ret = 0;
+  const uint8_t *p;
+  EVP_PKEY *pkey = NULL;
+
+  p = input;
+  pkey = d2i_AutoPrivateKey(NULL, &p, input_len);
+  if (pkey == NULL || p != input + input_len) {
+    fprintf(stderr, "d2i_AutoPrivateKey failed\n");
+    goto done;
+  }
+
+  if (EVP_PKEY_id(pkey) != expected_id) {
+    fprintf(stderr, "Did not decode expected type\n");
+    goto done;
+  }
+
+  ret = 1;
+
+done:
+  if (!ret) {
+    BIO_print_errors_fp(stderr);
+  }
+
+  if (pkey != NULL) {
+    EVP_PKEY_free(pkey);
+  }
+  return ret;
+}
+
+/* Tests loading a bad key in PKCS8 format */
+static int test_EVP_PKCS82PKEY(void) {
+  int ret = 0;
+  const uint8_t *derp = kExampleBadECKeyDER;
+  PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+  EVP_PKEY *pkey = NULL;
+
+  p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &derp, sizeof(kExampleBadECKeyDER));
+
+  if (!p8inf || derp != kExampleBadECKeyDER + sizeof(kExampleBadECKeyDER)) {
+    fprintf(stderr, "Failed to parse key\n");
+    goto done;
+  }
+
+  pkey = EVP_PKCS82PKEY(p8inf);
+  if (pkey) {
+    fprintf(stderr, "Imported invalid EC key\n");
+    goto done;
+  }
+
+  ret = 1;
+
+done:
+  if (p8inf != NULL) {
+    PKCS8_PRIV_KEY_INFO_free(p8inf);
+  }
+
+  if (pkey != NULL) {
+    EVP_PKEY_free(pkey);
+  }
+
+  return ret;
+}
+
+int main(void) {
+  CRYPTO_library_init();
+  ERR_load_crypto_strings();
+
+  if (!test_EVP_DigestSignInit()) {
+    fprintf(stderr, "EVP_DigestSignInit failed\n");
+    return 1;
+  }
+
+  if (!test_EVP_DigestVerifyInit()) {
+    fprintf(stderr, "EVP_DigestVerifyInit failed\n");
+    return 1;
+  }
+
+  if (!test_EVP_DigestSignAlgorithm()) {
+    fprintf(stderr, "EVP_DigestSignInit failed\n");
+    return 1;
+  }
+
+  if (!test_EVP_DigestVerifyInitFromAlgorithm()) {
+    fprintf(stderr, "EVP_DigestVerifyInitFromAlgorithm failed\n");
+    return 1;
+  }
+
+  if (!test_d2i_AutoPrivateKey(kExampleRSAKeyDER, sizeof(kExampleRSAKeyDER),
+                               EVP_PKEY_RSA)) {
+    fprintf(stderr, "d2i_AutoPrivateKey(kExampleRSAKeyDER) failed\n");
+    return 1;
+  }
+
+  if (!test_d2i_AutoPrivateKey(kExampleRSAKeyPKCS8, sizeof(kExampleRSAKeyPKCS8),
+                               EVP_PKEY_RSA)) {
+    fprintf(stderr, "d2i_AutoPrivateKey(kExampleRSAKeyPKCS8) failed\n");
+    return 1;
+  }
+
+  if (!test_d2i_AutoPrivateKey(kExampleECKeyDER, sizeof(kExampleECKeyDER),
+                               EVP_PKEY_EC)) {
+    fprintf(stderr, "d2i_AutoPrivateKey(kExampleECKeyDER) failed\n");
+    return 1;
+  }
+
+  if (!test_EVP_PKCS82PKEY()) {
+    fprintf(stderr, "test_EVP_PKCS82PKEY failed\n");
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/src/crypto/evp/internal.h b/src/crypto/evp/internal.h
new file mode 100644
index 0000000..2b0f608
--- /dev/null
+++ b/src/crypto/evp/internal.h
@@ -0,0 +1,255 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_EVP_INTERNAL_H
+#define OPENSSL_HEADER_EVP_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* These values are flags for EVP_PKEY_ASN1_METHOD.flags. */
+
+/* ASN1_PKEY_SIGPARAM_NULL controls whether the default behavior of
+ * EVP_DigestSignAlgorithm writes an explicit NULL parameter in the
+ * AlgorithmIdentifier. */
+#define ASN1_PKEY_SIGPARAM_NULL 0x1
+
+/* evp_digest_sign_algorithm_result_t is the return value of the
+ * digest_sign_algorithm function in EVP_PKEY_ASN1_METHOD. */
+typedef enum {
+  /* EVP_DIGEST_SIGN_ALGORITHM_ERROR signals an error. */
+  EVP_DIGEST_SIGN_ALGORITHM_ERROR = 0,
+  /* EVP_DIGEST_SIGN_ALGORITHM_SUCCESS signals that the parameters were
+   * serialized in the AlgorithmIdentifier. */
+  EVP_DIGEST_SIGN_ALGORITHM_SUCCESS = 1,
+  /* EVP_DIGEST_SIGN_ALGORITHM_DEFAULT signals that the parameters are
+   * serialized using the default behavior. */
+  EVP_DIGEST_SIGN_ALGORITHM_DEFAULT = 2,
+} evp_digest_sign_algorithm_result_t;
+
+struct evp_pkey_asn1_method_st {
+  int pkey_id;
+  int pkey_base_id;
+  unsigned long pkey_flags;
+
+  char *pem_str;
+  char *info;
+
+  int (*pub_decode)(EVP_PKEY *pk, X509_PUBKEY *pub);
+  int (*pub_encode)(X509_PUBKEY *pub, const EVP_PKEY *pk);
+  int (*pub_cmp)(const EVP_PKEY *a, const EVP_PKEY *b);
+  int (*pub_print)(BIO *out, const EVP_PKEY *pkey, int indent, ASN1_PCTX *pctx);
+
+  int (*priv_decode)(EVP_PKEY *pk, PKCS8_PRIV_KEY_INFO *p8inf);
+  int (*priv_encode)(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pk);
+  int (*priv_print)(BIO *out, const EVP_PKEY *pkey, int indent,
+                    ASN1_PCTX *pctx);
+
+  /* pkey_opaque returns 1 if the |pk| is opaque. Opaque keys are backed by
+   * custom implementations which do not expose key material and parameters.*/
+  int (*pkey_opaque)(const EVP_PKEY *pk);
+
+  /* pkey_supports_digest returns one if |pkey| supports digests of
+   * type |md|. This is intended for use with EVP_PKEYs backing custom
+   * implementations which can't sign all digests. If null, it is
+   * assumed that all digests are supported. */
+  int (*pkey_supports_digest)(const EVP_PKEY *pkey, const EVP_MD *md);
+
+  int (*pkey_size)(const EVP_PKEY *pk);
+  int (*pkey_bits)(const EVP_PKEY *pk);
+
+  int (*param_decode)(EVP_PKEY *pkey, const unsigned char **pder, int derlen);
+  int (*param_encode)(const EVP_PKEY *pkey, unsigned char **pder);
+  int (*param_missing)(const EVP_PKEY *pk);
+  int (*param_copy)(EVP_PKEY *to, const EVP_PKEY *from);
+  int (*param_cmp)(const EVP_PKEY *a, const EVP_PKEY *b);
+  int (*param_print)(BIO *out, const EVP_PKEY *pkey, int indent,
+                     ASN1_PCTX *pctx);
+  int (*sig_print)(BIO *out, const X509_ALGOR *sigalg, const ASN1_STRING *sig,
+                   int indent, ASN1_PCTX *pctx);
+
+
+  void (*pkey_free)(EVP_PKEY *pkey);
+
+  /* Legacy functions for old PEM */
+
+  int (*old_priv_decode)(EVP_PKEY *pkey, const unsigned char **pder,
+                         int derlen);
+  int (*old_priv_encode)(const EVP_PKEY *pkey, unsigned char **pder);
+
+  /* Converting parameters to/from AlgorithmIdentifier (X509_ALGOR). */
+  int (*digest_verify_init_from_algorithm)(EVP_MD_CTX *ctx,
+                                           X509_ALGOR *algor,
+                                           EVP_PKEY *pkey);
+  evp_digest_sign_algorithm_result_t (*digest_sign_algorithm)(
+      EVP_MD_CTX *ctx,
+      X509_ALGOR *algor);
+
+} /* EVP_PKEY_ASN1_METHOD */;
+
+
+typedef int EVP_PKEY_gen_cb(EVP_PKEY_CTX *ctx);
+
+#define EVP_PKEY_OP_UNDEFINED 0
+#define EVP_PKEY_OP_PARAMGEN (1 << 1)
+#define EVP_PKEY_OP_KEYGEN (1 << 2)
+#define EVP_PKEY_OP_SIGN (1 << 3)
+#define EVP_PKEY_OP_VERIFY (1 << 4)
+#define EVP_PKEY_OP_VERIFYRECOVER (1 << 5)
+#define EVP_PKEY_OP_SIGNCTX (1 << 6)
+#define EVP_PKEY_OP_VERIFYCTX (1 << 7)
+#define EVP_PKEY_OP_ENCRYPT (1 << 8)
+#define EVP_PKEY_OP_DECRYPT (1 << 9)
+#define EVP_PKEY_OP_DERIVE (1 << 10)
+
+#define EVP_PKEY_OP_TYPE_SIG                                           \
+  (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY | EVP_PKEY_OP_VERIFYRECOVER | \
+   EVP_PKEY_OP_SIGNCTX | EVP_PKEY_OP_VERIFYCTX)
+
+#define EVP_PKEY_OP_TYPE_CRYPT (EVP_PKEY_OP_ENCRYPT | EVP_PKEY_OP_DECRYPT)
+
+#define EVP_PKEY_OP_TYPE_NOGEN \
+  (EVP_PKEY_OP_SIG | EVP_PKEY_OP_CRYPT | EVP_PKEY_OP_DERIVE)
+
+#define EVP_PKEY_OP_TYPE_GEN (EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN)
+
+#define EVP_PKEY_CTRL_MD 1
+#define EVP_PKEY_CTRL_GET_MD 2
+#define EVP_PKEY_CTRL_RSA_PADDING (EVP_PKEY_ALG_CTRL + 1)
+#define EVP_PKEY_CTRL_GET_RSA_PADDING (EVP_PKEY_ALG_CTRL + 2)
+#define EVP_PKEY_CTRL_RSA_PSS_SALTLEN (EVP_PKEY_ALG_CTRL + 3)
+#define EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN (EVP_PKEY_ALG_CTRL + 4)
+#define EVP_PKEY_CTRL_RSA_KEYGEN_BITS (EVP_PKEY_ALG_CTRL + 5)
+#define EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP	(EVP_PKEY_ALG_CTRL + 6)
+#define EVP_PKEY_CTRL_RSA_OAEP_MD (EVP_PKEY_ALG_CTRL + 7)
+#define EVP_PKEY_CTRL_GET_RSA_OAEP_MD (EVP_PKEY_ALG_CTRL + 8)
+#define EVP_PKEY_CTRL_RSA_MGF1_MD (EVP_PKEY_ALG_CTRL + 9)
+#define EVP_PKEY_CTRL_GET_RSA_MGF1_MD (EVP_PKEY_ALG_CTRL + 10)
+#define EVP_PKEY_CTRL_RSA_OAEP_LABEL (EVP_PKEY_ALG_CTRL + 11)
+#define EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL (EVP_PKEY_ALG_CTRL + 12)
+
+struct evp_pkey_ctx_st {
+  /* Method associated with this operation */
+  const EVP_PKEY_METHOD *pmeth;
+  /* Engine that implements this method or NULL if builtin */
+  ENGINE *engine;
+  /* Key: may be NULL */
+  EVP_PKEY *pkey;
+  /* Peer key for key agreement, may be NULL */
+  EVP_PKEY *peerkey;
+  /* operation contains one of the |EVP_PKEY_OP_*| values. */
+  int operation;
+  /* Algorithm specific data */
+  void *data;
+  /* Application specific data */
+  void *app_data;
+} /* EVP_PKEY_CTX */;
+
+struct evp_pkey_method_st {
+  int pkey_id;
+  int flags;
+
+  int (*init)(EVP_PKEY_CTX *ctx);
+  int (*copy)(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src);
+  void (*cleanup)(EVP_PKEY_CTX *ctx);
+
+  int (*paramgen_init)(EVP_PKEY_CTX *ctx);
+  int (*paramgen)(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
+
+  int (*keygen_init)(EVP_PKEY_CTX *ctx);
+  int (*keygen)(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
+
+  int (*sign_init)(EVP_PKEY_CTX *ctx);
+  int (*sign)(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+              const unsigned char *tbs, size_t tbslen);
+
+  int (*verify_init)(EVP_PKEY_CTX *ctx);
+  int (*verify)(EVP_PKEY_CTX *ctx, const unsigned char *sig, size_t siglen,
+                const unsigned char *tbs, size_t tbslen);
+
+  int (*signctx_init)(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
+  int (*signctx)(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                 EVP_MD_CTX *mctx);
+
+  int (*verifyctx_init)(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
+  int (*verifyctx)(EVP_PKEY_CTX *ctx, const unsigned char *sig, int siglen,
+                   EVP_MD_CTX *mctx);
+
+  int (*encrypt_init)(EVP_PKEY_CTX *ctx);
+  int (*encrypt)(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+                 const unsigned char *in, size_t inlen);
+
+  int (*decrypt_init)(EVP_PKEY_CTX *ctx);
+  int (*decrypt)(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+                 const unsigned char *in, size_t inlen);
+
+  int (*derive_init)(EVP_PKEY_CTX *ctx);
+  int (*derive)(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
+
+  int (*ctrl)(EVP_PKEY_CTX *ctx, int type, int p1, void *p2);
+  int (*ctrl_str)(EVP_PKEY_CTX *ctx, const char *type, const char *value);
+} /* EVP_PKEY_METHOD */;
+
+
+#if defined(__cplusplus)
+}  /* extern C */
+#endif
+
+#endif  /* OPENSSL_HEADER_EVP_INTERNAL_H */
diff --git a/src/crypto/evp/p_ec.c b/src/crypto/evp/p_ec.c
new file mode 100644
index 0000000..c274131
--- /dev/null
+++ b/src/crypto/evp/p_ec.c
@@ -0,0 +1,303 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <string.h>
+
+#include <openssl/asn1.h>
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/digest.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+#include "../ec/internal.h"
+
+
+typedef struct {
+  /* Key and paramgen group */
+  EC_GROUP *gen_group;
+  /* message digest */
+  const EVP_MD *md;
+} EC_PKEY_CTX;
+
+
+static int pkey_ec_init(EVP_PKEY_CTX *ctx) {
+  EC_PKEY_CTX *dctx;
+  dctx = OPENSSL_malloc(sizeof(EC_PKEY_CTX));
+  if (!dctx) {
+    return 0;
+  }
+  memset(dctx, 0, sizeof(EC_PKEY_CTX));
+
+  ctx->data = dctx;
+
+  return 1;
+}
+
+static int pkey_ec_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
+  EC_PKEY_CTX *dctx, *sctx;
+  if (!pkey_ec_init(dst)) {
+    return 0;
+  }
+  sctx = src->data;
+  dctx = dst->data;
+
+  if (sctx->gen_group) {
+    dctx->gen_group = EC_GROUP_dup(sctx->gen_group);
+    if (!dctx->gen_group) {
+      return 0;
+    }
+  }
+  dctx->md = sctx->md;
+
+  return 1;
+}
+
+static void pkey_ec_cleanup(EVP_PKEY_CTX *ctx) {
+  EC_PKEY_CTX *dctx = ctx->data;
+  if (!dctx) {
+    return;
+  }
+
+  if (dctx->gen_group) {
+    EC_GROUP_free(dctx->gen_group);
+  }
+  OPENSSL_free(dctx);
+}
+
+static int pkey_ec_sign(EVP_PKEY_CTX *ctx, uint8_t *sig, size_t *siglen,
+                        const uint8_t *tbs, size_t tbslen) {
+  int type;
+  unsigned int sltmp;
+  EC_PKEY_CTX *dctx = ctx->data;
+  EC_KEY *ec = ctx->pkey->pkey.ec;
+
+  if (!sig) {
+    *siglen = ECDSA_size(ec);
+    return 1;
+  } else if (*siglen < (size_t)ECDSA_size(ec)) {
+    OPENSSL_PUT_ERROR(EVP, pkey_ec_sign, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  type = NID_sha1;
+  if (dctx->md) {
+    type = EVP_MD_type(dctx->md);
+  }
+
+  if (!ECDSA_sign(type, tbs, tbslen, sig, &sltmp, ec)) {
+    return 0;
+  }
+  *siglen = (size_t)sltmp;
+  return 1;
+}
+
+static int pkey_ec_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig, size_t siglen,
+                          const uint8_t *tbs, size_t tbslen) {
+  int type;
+  EC_PKEY_CTX *dctx = ctx->data;
+  EC_KEY *ec = ctx->pkey->pkey.ec;
+
+  type = NID_sha1;
+  if (dctx->md) {
+    type = EVP_MD_type(dctx->md);
+  }
+
+  return ECDSA_verify(type, tbs, tbslen, sig, siglen, ec);
+}
+
+static int pkey_ec_derive(EVP_PKEY_CTX *ctx, uint8_t *key,
+                          size_t *keylen) {
+  int ret;
+  size_t outlen;
+  const EC_POINT *pubkey = NULL;
+  EC_KEY *eckey;
+
+  if (!ctx->pkey || !ctx->peerkey) {
+    OPENSSL_PUT_ERROR(EVP, pkey_ec_derive, EVP_R_KEYS_NOT_SET);
+    return 0;
+  }
+
+  eckey = ctx->pkey->pkey.ec;
+
+  if (!key) {
+    const EC_GROUP *group;
+    group = EC_KEY_get0_group(eckey);
+    *keylen = (EC_GROUP_get_degree(group) + 7) / 8;
+    return 1;
+  }
+  pubkey = EC_KEY_get0_public_key(ctx->peerkey->pkey.ec);
+
+  /* NB: unlike PKCS#3 DH, if *outlen is less than maximum size this is
+   * not an error, the result is truncated. */
+
+  outlen = *keylen;
+
+  ret = ECDH_compute_key(key, outlen, pubkey, eckey, 0);
+  if (ret < 0) {
+    return 0;
+  }
+  *keylen = ret;
+  return 1;
+}
+
+static int pkey_ec_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
+  EC_PKEY_CTX *dctx = ctx->data;
+  EC_GROUP *group;
+
+  switch (type) {
+    case EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID:
+      group = EC_GROUP_new_by_curve_name(p1);
+      if (group == NULL) {
+        OPENSSL_PUT_ERROR(EVP, pkey_ec_ctrl, EVP_R_INVALID_CURVE);
+        return 0;
+      }
+      if (dctx->gen_group)
+        EC_GROUP_free(dctx->gen_group);
+      dctx->gen_group = group;
+      return 1;
+
+    case EVP_PKEY_CTRL_MD:
+      if (EVP_MD_type((const EVP_MD *)p2) != NID_sha1 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_ecdsa_with_SHA1 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_sha224 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_sha256 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_sha384 &&
+          EVP_MD_type((const EVP_MD *)p2) != NID_sha512) {
+        OPENSSL_PUT_ERROR(EVP, pkey_ec_ctrl, EVP_R_INVALID_DIGEST_TYPE);
+        return 0;
+      }
+      dctx->md = p2;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_MD:
+      *(const EVP_MD **)p2 = dctx->md;
+      return 1;
+
+    case EVP_PKEY_CTRL_PEER_KEY:
+    /* Default behaviour is OK */
+    case EVP_PKEY_CTRL_DIGESTINIT:
+      return 1;
+
+    default:
+      return -2;
+  }
+}
+
+static int pkey_ec_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  EC_KEY *ec = NULL;
+  EC_PKEY_CTX *dctx = ctx->data;
+  int ret = 0;
+
+  if (dctx->gen_group == NULL) {
+    OPENSSL_PUT_ERROR(EVP, pkey_ec_paramgen, EVP_R_NO_PARAMETERS_SET);
+    return 0;
+  }
+  ec = EC_KEY_new();
+  if (!ec) {
+    return 0;
+  }
+  ret = EC_KEY_set_group(ec, dctx->gen_group);
+  if (ret) {
+    EVP_PKEY_assign_EC_KEY(pkey, ec);
+  } else {
+    EC_KEY_free(ec);
+  }
+  return ret;
+}
+
+static int pkey_ec_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  EC_KEY *ec = NULL;
+  EC_PKEY_CTX *dctx = ctx->data;
+  if (ctx->pkey == NULL && dctx->gen_group == NULL) {
+    OPENSSL_PUT_ERROR(EVP, pkey_ec_keygen, EVP_R_NO_PARAMETERS_SET);
+    return 0;
+  }
+  ec = EC_KEY_new();
+  if (!ec) {
+    return 0;
+  }
+  EVP_PKEY_assign_EC_KEY(pkey, ec);
+  if (ctx->pkey) {
+    /* Note: if error return, pkey is freed by parent routine */
+    if (!EVP_PKEY_copy_parameters(pkey, ctx->pkey)) {
+      return 0;
+    }
+  } else {
+    if (!EC_KEY_set_group(ec, dctx->gen_group)) {
+      return 0;
+    }
+  }
+  return EC_KEY_generate_key(pkey->pkey.ec);
+}
+
+const EVP_PKEY_METHOD ec_pkey_meth = {
+    EVP_PKEY_EC,            0 /* flags */,        pkey_ec_init,
+    pkey_ec_copy,           pkey_ec_cleanup,      0 /* paramgen_init */,
+    pkey_ec_paramgen,       0 /* keygen_init */,  pkey_ec_keygen,
+    0 /* sign_init */,      pkey_ec_sign,         0 /* verify_init */,
+    pkey_ec_verify,         0 /* signctx_init */, 0 /* signctx */,
+    0 /* verifyctx_init */, 0 /* verifyctx */,    0 /* encrypt_init */,
+    0 /* encrypt */,        0 /* decrypt_init */, 0 /* decrypt */,
+    0 /* derive_init */,    pkey_ec_derive,       pkey_ec_ctrl,
+};
diff --git a/src/crypto/evp/p_ec_asn1.c b/src/crypto/evp/p_ec_asn1.c
new file mode 100644
index 0000000..48a175b
--- /dev/null
+++ b/src/crypto/evp/p_ec_asn1.c
@@ -0,0 +1,574 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/x509.h>
+
+#include "internal.h"
+
+
+static int eckey_param2type(int *pptype, void **ppval, EC_KEY *ec_key) {
+  const EC_GROUP *group;
+  int nid;
+
+  if (ec_key == NULL || (group = EC_KEY_get0_group(ec_key)) == NULL) {
+    OPENSSL_PUT_ERROR(EVP, eckey_param2type, EVP_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
+  nid = EC_GROUP_get_curve_name(group);
+  if (nid == NID_undef) {
+    OPENSSL_PUT_ERROR(EVP, eckey_param2type, EVP_R_NO_NID_FOR_CURVE);
+    return 0;
+  }
+
+  *ppval = (void*) OBJ_nid2obj(nid);
+  *pptype = V_ASN1_OBJECT;
+  return 1;
+}
+
+static int eckey_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) {
+  EC_KEY *ec_key = pkey->pkey.ec;
+  void *pval = NULL;
+  int ptype;
+  uint8_t *penc = NULL, *p;
+  int penclen;
+
+  if (!eckey_param2type(&ptype, &pval, ec_key)) {
+    OPENSSL_PUT_ERROR(EVP, eckey_pub_encode, ERR_R_EC_LIB);
+    return 0;
+  }
+  penclen = i2o_ECPublicKey(ec_key, NULL);
+  if (penclen <= 0) {
+    goto err;
+  }
+  penc = OPENSSL_malloc(penclen);
+  if (!penc) {
+    goto err;
+  }
+  p = penc;
+  penclen = i2o_ECPublicKey(ec_key, &p);
+  if (penclen <= 0) {
+    goto err;
+  }
+  if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_EC), ptype, pval, penc,
+                             penclen)) {
+    return 1;
+  }
+
+err:
+  if (ptype == V_ASN1_OBJECT) {
+    ASN1_OBJECT_free(pval);
+  } else {
+    ASN1_STRING_free(pval);
+  }
+  if (penc) {
+    OPENSSL_free(penc);
+  }
+  return 0;
+}
+
+static EC_KEY *eckey_type2param(int ptype, void *pval) {
+  EC_KEY *eckey = NULL;
+
+  if (ptype == V_ASN1_SEQUENCE) {
+    ASN1_STRING *pstr = pval;
+    const uint8_t *pm = pstr->data;
+    int pmlen = pstr->length;
+
+    eckey = d2i_ECParameters(NULL, &pm, pmlen);
+    if (eckey == NULL) {
+      OPENSSL_PUT_ERROR(EVP, eckey_type2param, EVP_R_DECODE_ERROR);
+      goto err;
+    }
+  } else if (ptype == V_ASN1_OBJECT) {
+    ASN1_OBJECT *poid = pval;
+    EC_GROUP *group;
+
+    /* type == V_ASN1_OBJECT => the parameters are given
+     * by an asn1 OID */
+    eckey = EC_KEY_new();
+    if (eckey == NULL) {
+      OPENSSL_PUT_ERROR(EVP, eckey_type2param, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    group = EC_GROUP_new_by_curve_name(OBJ_obj2nid(poid));
+    if (group == NULL) {
+      goto err;
+    }
+    if (EC_KEY_set_group(eckey, group) == 0) {
+      goto err;
+    }
+    EC_GROUP_free(group);
+  } else {
+    OPENSSL_PUT_ERROR(EVP, eckey_type2param, EVP_R_DECODE_ERROR);
+    goto err;
+  }
+
+  return eckey;
+
+err:
+  if (eckey) {
+    EC_KEY_free(eckey);
+  }
+  return NULL;
+}
+
+static int eckey_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) {
+  const uint8_t *p = NULL;
+  void *pval;
+  int ptype, pklen;
+  EC_KEY *eckey = NULL;
+  X509_ALGOR *palg;
+
+  if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey)) {
+    return 0;
+  }
+  X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+  eckey = eckey_type2param(ptype, pval);
+  if (!eckey) {
+    OPENSSL_PUT_ERROR(EVP, eckey_pub_decode, ERR_R_EC_LIB);
+    return 0;
+  }
+
+  /* We have parameters now set public key */
+  if (!o2i_ECPublicKey(&eckey, &p, pklen)) {
+    OPENSSL_PUT_ERROR(EVP, eckey_pub_decode, EVP_R_DECODE_ERROR);
+    goto err;
+  }
+
+  EVP_PKEY_assign_EC_KEY(pkey, eckey);
+  return 1;
+
+err:
+  if (eckey)
+    EC_KEY_free(eckey);
+  return 0;
+}
+
+static int eckey_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) {
+  int r;
+  const EC_GROUP *group = EC_KEY_get0_group(b->pkey.ec);
+  const EC_POINT *pa = EC_KEY_get0_public_key(a->pkey.ec),
+                 *pb = EC_KEY_get0_public_key(b->pkey.ec);
+  r = EC_POINT_cmp(group, pa, pb, NULL);
+  if (r == 0) {
+    return 1;
+  } else if (r == 1) {
+    return 0;
+  } else {
+    return -2;
+  }
+}
+
+static int eckey_priv_decode(EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8) {
+  const uint8_t *p = NULL;
+  void *pval;
+  int ptype, pklen;
+  EC_KEY *eckey = NULL;
+  X509_ALGOR *palg;
+
+  if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8)) {
+    return 0;
+  }
+  X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+  eckey = eckey_type2param(ptype, pval);
+
+  if (!eckey)
+    goto ecliberr;
+
+  /* We have parameters now set private key */
+  if (!d2i_ECPrivateKey(&eckey, &p, pklen)) {
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, EVP_R_DECODE_ERROR);
+    goto ecerr;
+  }
+
+  /* calculate public key (if necessary) */
+  if (EC_KEY_get0_public_key(eckey) == NULL) {
+    const BIGNUM *priv_key;
+    const EC_GROUP *group;
+    EC_POINT *pub_key;
+    /* the public key was not included in the SEC1 private
+     * key => calculate the public key */
+    group = EC_KEY_get0_group(eckey);
+    pub_key = EC_POINT_new(group);
+    if (pub_key == NULL) {
+      OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+      goto ecliberr;
+    }
+    if (!EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group))) {
+      EC_POINT_free(pub_key);
+      OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+      goto ecliberr;
+    }
+    priv_key = EC_KEY_get0_private_key(eckey);
+    if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL)) {
+      EC_POINT_free(pub_key);
+      OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+      goto ecliberr;
+    }
+    if (EC_KEY_set_public_key(eckey, pub_key) == 0) {
+      EC_POINT_free(pub_key);
+      OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+      goto ecliberr;
+    }
+    EC_POINT_free(pub_key);
+  }
+
+  EVP_PKEY_assign_EC_KEY(pkey, eckey);
+  return 1;
+
+ecliberr:
+  OPENSSL_PUT_ERROR(EVP, eckey_priv_decode, ERR_R_EC_LIB);
+ecerr:
+  if (eckey)
+    EC_KEY_free(eckey);
+  return 0;
+}
+
+static int eckey_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey) {
+  EC_KEY *ec_key;
+  uint8_t *ep, *p;
+  int eplen, ptype;
+  void *pval;
+  unsigned int tmp_flags, old_flags;
+
+  ec_key = pkey->pkey.ec;
+
+  if (!eckey_param2type(&ptype, &pval, ec_key)) {
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_encode, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+
+  /* set the private key */
+
+  /* do not include the parameters in the SEC1 private key
+   * see PKCS#11 12.11 */
+  old_flags = EC_KEY_get_enc_flags(ec_key);
+  tmp_flags = old_flags | EC_PKEY_NO_PARAMETERS;
+  EC_KEY_set_enc_flags(ec_key, tmp_flags);
+  eplen = i2d_ECPrivateKey(ec_key, NULL);
+  if (!eplen) {
+    EC_KEY_set_enc_flags(ec_key, old_flags);
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_encode, ERR_R_EC_LIB);
+    return 0;
+  }
+  ep = (uint8_t *)OPENSSL_malloc(eplen);
+  if (!ep) {
+    EC_KEY_set_enc_flags(ec_key, old_flags);
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_encode, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  p = ep;
+  if (!i2d_ECPrivateKey(ec_key, &p)) {
+    EC_KEY_set_enc_flags(ec_key, old_flags);
+    OPENSSL_free(ep);
+    OPENSSL_PUT_ERROR(EVP, eckey_priv_encode, ERR_R_EC_LIB);
+    return 0;
+  }
+  /* restore old encoding flags */
+  EC_KEY_set_enc_flags(ec_key, old_flags);
+
+  if (!PKCS8_pkey_set0(p8, (ASN1_OBJECT *)OBJ_nid2obj(NID_X9_62_id_ecPublicKey),
+                       0, ptype, pval, ep, eplen)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int int_ec_size(const EVP_PKEY *pkey) {
+  return ECDSA_size(pkey->pkey.ec);
+}
+
+static int ec_bits(const EVP_PKEY *pkey) {
+  BIGNUM *order = BN_new();
+  const EC_GROUP *group;
+  int ret;
+
+  if (!order) {
+    ERR_clear_error();
+    return 0;
+  }
+  group = EC_KEY_get0_group(pkey->pkey.ec);
+  if (!EC_GROUP_get_order(group, order, NULL)) {
+    ERR_clear_error();
+    return 0;
+  }
+
+  ret = BN_num_bits(order);
+  BN_free(order);
+  return ret;
+}
+
+static int ec_missing_parameters(const EVP_PKEY *pkey) {
+  return EC_KEY_get0_group(pkey->pkey.ec) == NULL;
+}
+
+static int ec_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) {
+  EC_GROUP *group = EC_GROUP_dup(EC_KEY_get0_group(from->pkey.ec));
+  if (group == NULL ||
+      EC_KEY_set_group(to->pkey.ec, group) == 0) {
+    return 0;
+  }
+  EC_GROUP_free(group);
+  return 1;
+}
+
+static int ec_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) {
+  const EC_GROUP *group_a = EC_KEY_get0_group(a->pkey.ec),
+                 *group_b = EC_KEY_get0_group(b->pkey.ec);
+  return EC_GROUP_cmp(group_a, group_b);
+}
+
+static void int_ec_free(EVP_PKEY *pkey) { EC_KEY_free(pkey->pkey.ec); }
+
+static int do_EC_KEY_print(BIO *bp, const EC_KEY *x, int off, int ktype) {
+  uint8_t *buffer = NULL;
+  const char *ecstr;
+  size_t buf_len = 0, i;
+  int ret = 0, reason = ERR_R_BIO_LIB;
+  BIGNUM *order = NULL;
+  BN_CTX *ctx = NULL;
+  const EC_GROUP *group;
+  const EC_POINT *public_key;
+  const BIGNUM *priv_key;
+  uint8_t *pub_key_bytes = NULL;
+  size_t pub_key_bytes_len = 0;
+
+  if (x == NULL || (group = EC_KEY_get0_group(x)) == NULL) {
+    reason = ERR_R_PASSED_NULL_PARAMETER;
+    goto err;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    reason = ERR_R_MALLOC_FAILURE;
+    goto err;
+  }
+
+  if (ktype > 0) {
+    public_key = EC_KEY_get0_public_key(x);
+    if (public_key != NULL) {
+      pub_key_bytes_len = EC_POINT_point2oct(
+          group, public_key, EC_KEY_get_conv_form(x), NULL, 0, ctx);
+      if (pub_key_bytes_len == 0) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+      }
+      pub_key_bytes = OPENSSL_malloc(pub_key_bytes_len);
+      if (pub_key_bytes == NULL) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+      }
+      pub_key_bytes_len =
+          EC_POINT_point2oct(group, public_key, EC_KEY_get_conv_form(x),
+                             pub_key_bytes, pub_key_bytes_len, ctx);
+      if (pub_key_bytes_len == 0) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+      }
+      buf_len = pub_key_bytes_len;
+    }
+  }
+
+  if (ktype == 2) {
+    priv_key = EC_KEY_get0_private_key(x);
+    if (priv_key && (i = (size_t)BN_num_bytes(priv_key)) > buf_len)
+      buf_len = i;
+  } else
+    priv_key = NULL;
+
+  if (ktype > 0) {
+    buf_len += 10;
+    if ((buffer = OPENSSL_malloc(buf_len)) == NULL) {
+      reason = ERR_R_MALLOC_FAILURE;
+      goto err;
+    }
+  }
+  if (ktype == 2)
+    ecstr = "Private-Key";
+  else if (ktype == 1)
+    ecstr = "Public-Key";
+  else
+    ecstr = "ECDSA-Parameters";
+
+  if (!BIO_indent(bp, off, 128))
+    goto err;
+  if ((order = BN_new()) == NULL)
+    goto err;
+  if (!EC_GROUP_get_order(group, order, NULL))
+    goto err;
+  if (BIO_printf(bp, "%s: (%d bit)\n", ecstr, BN_num_bits(order)) <= 0)
+    goto err;
+
+  if ((priv_key != NULL) && !ASN1_bn_print(bp, "priv:", priv_key, buffer, off))
+    goto err;
+  if (pub_key_bytes != NULL) {
+    BIO_hexdump(bp, pub_key_bytes, pub_key_bytes_len, off);
+  }
+  /* TODO(fork): implement */
+  /*
+  if (!ECPKParameters_print(bp, group, off))
+    goto err; */
+  ret = 1;
+
+err:
+  if (!ret)
+    OPENSSL_PUT_ERROR(EVP, do_EC_KEY_print, reason);
+  if (pub_key_bytes)
+    OPENSSL_free(pub_key_bytes);
+  if (order)
+    BN_free(order);
+  if (ctx)
+    BN_CTX_free(ctx);
+  if (buffer != NULL)
+    OPENSSL_free(buffer);
+  return ret;
+}
+
+static int eckey_param_decode(EVP_PKEY *pkey, const uint8_t **pder,
+                              int derlen) {
+  EC_KEY *eckey;
+  if (!(eckey = d2i_ECParameters(NULL, pder, derlen))) {
+    OPENSSL_PUT_ERROR(EVP, eckey_param_decode, ERR_R_EC_LIB);
+    return 0;
+  }
+  EVP_PKEY_assign_EC_KEY(pkey, eckey);
+  return 1;
+}
+
+static int eckey_param_encode(const EVP_PKEY *pkey, uint8_t **pder) {
+  return i2d_ECParameters(pkey->pkey.ec, pder);
+}
+
+static int eckey_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                             ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 0);
+}
+
+static int eckey_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 1);
+}
+
+
+static int eckey_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                            ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 2);
+}
+
+static int eckey_opaque(const EVP_PKEY *pkey) {
+  return EC_KEY_is_opaque(pkey->pkey.ec);
+}
+
+static int old_ec_priv_decode(EVP_PKEY *pkey, const uint8_t **pder,
+                              int derlen) {
+  EC_KEY *ec;
+  if (!(ec = d2i_ECPrivateKey(NULL, pder, derlen))) {
+    OPENSSL_PUT_ERROR(EVP, old_ec_priv_decode, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+  EVP_PKEY_assign_EC_KEY(pkey, ec);
+  return 1;
+}
+
+static int old_ec_priv_encode(const EVP_PKEY *pkey, uint8_t **pder) {
+  return i2d_ECPrivateKey(pkey->pkey.ec, pder);
+}
+
+const EVP_PKEY_ASN1_METHOD ec_asn1_meth = {
+  EVP_PKEY_EC,
+  EVP_PKEY_EC,
+  0,
+  "EC",
+  "OpenSSL EC algorithm",
+
+  eckey_pub_decode,
+  eckey_pub_encode,
+  eckey_pub_cmp,
+  eckey_pub_print,
+
+  eckey_priv_decode,
+  eckey_priv_encode,
+  eckey_priv_print,
+
+  eckey_opaque,
+  0 /* pkey_supports_digest */,
+
+  int_ec_size,
+  ec_bits,
+
+  eckey_param_decode,
+  eckey_param_encode,
+  ec_missing_parameters,
+  ec_copy_parameters,
+  ec_cmp_parameters,
+  eckey_param_print,
+  0,
+
+  int_ec_free,
+  old_ec_priv_decode,
+  old_ec_priv_encode
+};
diff --git a/src/crypto/evp/p_hmac.c b/src/crypto/evp/p_hmac.c
new file mode 100644
index 0000000..6d9a909
--- /dev/null
+++ b/src/crypto/evp/p_hmac.c
@@ -0,0 +1,222 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2007.
+ */
+/* ====================================================================
+ * Copyright (c) 2007 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <string.h>
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+typedef struct {
+  const EVP_MD *md;       /* MD for HMAC use */
+  ASN1_OCTET_STRING ktmp; /* Temp storage for key */
+  HMAC_CTX ctx;
+} HMAC_PKEY_CTX;
+
+static int pkey_hmac_init(EVP_PKEY_CTX *ctx) {
+  HMAC_PKEY_CTX *hctx;
+  hctx = OPENSSL_malloc(sizeof(HMAC_PKEY_CTX));
+  if (!hctx) {
+    return 0;
+  }
+  memset(hctx, 0, sizeof(HMAC_PKEY_CTX));
+  hctx->ktmp.type = V_ASN1_OCTET_STRING;
+  HMAC_CTX_init(&hctx->ctx);
+
+  ctx->data = hctx;
+
+  return 1;
+}
+
+static int pkey_hmac_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
+  HMAC_PKEY_CTX *sctx, *dctx;
+  if (!pkey_hmac_init(dst)) {
+    return 0;
+  }
+  sctx = src->data;
+  dctx = dst->data;
+  dctx->md = sctx->md;
+  HMAC_CTX_init(&dctx->ctx);
+  if (!HMAC_CTX_copy_ex(&dctx->ctx, &sctx->ctx)) {
+    return 0;
+  }
+  if (sctx->ktmp.data) {
+    if (!ASN1_OCTET_STRING_set(&dctx->ktmp, sctx->ktmp.data,
+                               sctx->ktmp.length)) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+static void pkey_hmac_cleanup(EVP_PKEY_CTX *ctx) {
+  HMAC_PKEY_CTX *hctx = ctx->data;
+
+  if (hctx == NULL) {
+    return;
+  }
+
+  HMAC_CTX_cleanup(&hctx->ctx);
+  if (hctx->ktmp.data) {
+    if (hctx->ktmp.length) {
+      OPENSSL_cleanse(hctx->ktmp.data, hctx->ktmp.length);
+    }
+    OPENSSL_free(hctx->ktmp.data);
+    hctx->ktmp.data = NULL;
+  }
+  OPENSSL_free(hctx);
+}
+
+static int pkey_hmac_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  ASN1_OCTET_STRING *hkey = NULL;
+  HMAC_PKEY_CTX *hctx = ctx->data;
+
+  if (!hctx->ktmp.data) {
+    return 0;
+  }
+  hkey = ASN1_OCTET_STRING_dup(&hctx->ktmp);
+  if (!hkey) {
+    return 0;
+  }
+  EVP_PKEY_assign(pkey, EVP_PKEY_HMAC, hkey);
+
+  return 1;
+}
+
+static int int_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  HMAC_PKEY_CTX *hctx = ctx->pctx->data;
+  return HMAC_Update(&hctx->ctx, data, count);
+}
+
+static int hmac_signctx_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx) {
+  HMAC_PKEY_CTX *hctx = ctx->data;
+
+  HMAC_CTX_set_flags(&hctx->ctx, mctx->flags & ~EVP_MD_CTX_FLAG_NO_INIT);
+  EVP_MD_CTX_set_flags(mctx, EVP_MD_CTX_FLAG_NO_INIT);
+  mctx->update = int_update;
+  return 1;
+}
+
+static int hmac_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                        EVP_MD_CTX *mctx) {
+  unsigned int hlen;
+  HMAC_PKEY_CTX *hctx = ctx->data;
+  size_t md_size = EVP_MD_CTX_size(mctx);
+
+  if (!sig) {
+    *siglen = md_size;
+    return 1;
+  } else if (*siglen < md_size) {
+    OPENSSL_PUT_ERROR(EVP, hmac_signctx, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  if (!HMAC_Final(&hctx->ctx, sig, &hlen)) {
+    return 0;
+  }
+  *siglen = (size_t)hlen;
+  return 1;
+}
+
+static int pkey_hmac_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
+  HMAC_PKEY_CTX *hctx = ctx->data;
+  ASN1_OCTET_STRING *key;
+
+  switch (type) {
+    case EVP_PKEY_CTRL_SET_MAC_KEY:
+      if ((!p2 && p1 > 0) || (p1 < -1)) {
+        return 0;
+      }
+      if (!ASN1_OCTET_STRING_set(&hctx->ktmp, p2, p1)) {
+        return 0;
+      }
+      break;
+
+    case EVP_PKEY_CTRL_MD:
+      hctx->md = p2;
+      break;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+      key = (ASN1_OCTET_STRING *)ctx->pkey->pkey.ptr;
+      if (!HMAC_Init_ex(&hctx->ctx, key->data, key->length, hctx->md,
+                        ctx->engine)) {
+        return 0;
+      }
+      break;
+
+    default:
+      return -2;
+  }
+  return 1;
+}
+
+const EVP_PKEY_METHOD hmac_pkey_meth = {
+    EVP_PKEY_HMAC,          0 /* flags */,        pkey_hmac_init,
+    pkey_hmac_copy,         pkey_hmac_cleanup,    0 /* paramgen_init */,
+    0 /* paramgen */,       0 /* keygen_init */,  pkey_hmac_keygen,
+    0 /* sign_init */,      0 /* sign */,         0 /* verify_init */,
+    0 /* verify */,         hmac_signctx_init,    hmac_signctx,
+    0 /* verifyctx_init */, 0 /* verifyctx */,    0 /* encrypt_init */,
+    0 /* encrypt */,        0 /* decrypt_init */, 0 /* decrypt */,
+    0 /* derive_init */,    0 /* derive */,       pkey_hmac_ctrl,
+    0,
+};
diff --git a/src/crypto/evp/p_hmac_asn1.c b/src/crypto/evp/p_hmac_asn1.c
new file mode 100644
index 0000000..8aa6676
--- /dev/null
+++ b/src/crypto/evp/p_hmac_asn1.c
@@ -0,0 +1,89 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2007.
+ */
+/* ====================================================================
+ * Copyright (c) 2007 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/digest.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+static int hmac_size(const EVP_PKEY *pkey) { return EVP_MAX_MD_SIZE; }
+
+static void hmac_key_free(EVP_PKEY *pkey) {
+  ASN1_OCTET_STRING *os = (ASN1_OCTET_STRING *)pkey->pkey.ptr;
+  if (os) {
+    if (os->data) {
+      OPENSSL_cleanse(os->data, os->length);
+    }
+    ASN1_OCTET_STRING_free(os);
+  }
+}
+
+const EVP_PKEY_ASN1_METHOD hmac_asn1_meth = {
+    EVP_PKEY_HMAC,       EVP_PKEY_HMAC,         0 /* flags */,
+    "HMAC",              "OpenSSL HMAC method", 0 /* pub_decode */,
+    0 /* pub_encode */,  0 /* pub_cmp */,       0 /* pub_print */,
+    0 /*priv_decode */,  0 /* priv_encode */,   0 /* priv_print */,
+    0 /* pkey_opaque */, 0 /* pkey_supports_digest */,
+    hmac_size,           0 /* pkey_bits */,     0 /* param_decode */,
+    0 /* param_encode*/, 0 /* param_missing*/,  0 /* param_copy*/,
+    0 /* param_cmp*/,    0 /* param_print*/,    0 /* sig_print*/,
+    hmac_key_free,       0 /* old_priv_decode */,
+    0 /* old_priv_encode */
+};
diff --git a/src/crypto/evp/p_rsa.c b/src/crypto/evp/p_rsa.c
new file mode 100644
index 0000000..31f5aaa
--- /dev/null
+++ b/src/crypto/evp/p_rsa.c
@@ -0,0 +1,598 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/buf.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/rsa.h>
+
+#include "../rsa/internal.h"
+#include "internal.h"
+
+
+typedef struct {
+  /* Key gen parameters */
+  int nbits;
+  BIGNUM *pub_exp;
+  /* RSA padding mode */
+  int pad_mode;
+  /* message digest */
+  const EVP_MD *md;
+  /* message digest for MGF1 */
+  const EVP_MD *mgf1md;
+  /* PSS salt length */
+  int saltlen;
+  /* tbuf is a buffer which is either NULL, or is the size of the RSA modulus.
+   * It's used to store the output of RSA operations. */
+  uint8_t *tbuf;
+  /* OAEP label */
+  uint8_t *oaep_label;
+  size_t oaep_labellen;
+} RSA_PKEY_CTX;
+
+static int pkey_rsa_init(EVP_PKEY_CTX *ctx) {
+  RSA_PKEY_CTX *rctx;
+  rctx = OPENSSL_malloc(sizeof(RSA_PKEY_CTX));
+  if (!rctx) {
+    return 0;
+  }
+  memset(rctx, 0, sizeof(RSA_PKEY_CTX));
+
+  rctx->nbits = 2048;
+  rctx->pad_mode = RSA_PKCS1_PADDING;
+  rctx->saltlen = -2;
+
+  ctx->data = rctx;
+
+  return 1;
+}
+
+static int pkey_rsa_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
+  RSA_PKEY_CTX *dctx, *sctx;
+  if (!pkey_rsa_init(dst)) {
+    return 0;
+  }
+  sctx = src->data;
+  dctx = dst->data;
+  dctx->nbits = sctx->nbits;
+  if (sctx->pub_exp) {
+    dctx->pub_exp = BN_dup(sctx->pub_exp);
+    if (!dctx->pub_exp) {
+      return 0;
+    }
+  }
+
+  dctx->pad_mode = sctx->pad_mode;
+  dctx->md = sctx->md;
+  dctx->mgf1md = sctx->mgf1md;
+  if (sctx->oaep_label) {
+    if (dctx->oaep_label) {
+      OPENSSL_free(dctx->oaep_label);
+    }
+    dctx->oaep_label = BUF_memdup(sctx->oaep_label, sctx->oaep_labellen);
+    if (!dctx->oaep_label) {
+      return 0;
+    }
+    dctx->oaep_labellen = sctx->oaep_labellen;
+  }
+
+  return 1;
+}
+
+static void pkey_rsa_cleanup(EVP_PKEY_CTX *ctx) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+
+  if (rctx == NULL) {
+    return;
+  }
+
+  if (rctx->pub_exp) {
+    BN_free(rctx->pub_exp);
+  }
+  if (rctx->tbuf) {
+    OPENSSL_free(rctx->tbuf);
+  }
+  if (rctx->oaep_label) {
+    OPENSSL_free(rctx->oaep_label);
+  }
+  OPENSSL_free(rctx);
+}
+
+static int setup_tbuf(RSA_PKEY_CTX *ctx, EVP_PKEY_CTX *pk) {
+  if (ctx->tbuf) {
+    return 1;
+  }
+  ctx->tbuf = OPENSSL_malloc(EVP_PKEY_size(pk->pkey));
+  if (!ctx->tbuf) {
+    return 0;
+  }
+  return 1;
+}
+
+static int pkey_rsa_sign(EVP_PKEY_CTX *ctx, uint8_t *sig, size_t *siglen,
+                         const uint8_t *tbs, size_t tbslen) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+  RSA *rsa = ctx->pkey->pkey.rsa;
+  const size_t key_len = EVP_PKEY_size(ctx->pkey);
+
+  if (!sig) {
+    *siglen = key_len;
+    return 1;
+  }
+
+  if (*siglen < key_len) {
+    OPENSSL_PUT_ERROR(EVP, pkey_rsa_sign, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  if (rctx->md) {
+    unsigned int out_len;
+
+    if (tbslen != EVP_MD_size(rctx->md)) {
+      OPENSSL_PUT_ERROR(EVP, pkey_rsa_sign, EVP_R_INVALID_DIGEST_LENGTH);
+      return 0;
+    }
+
+    if (EVP_MD_type(rctx->md) == NID_mdc2) {
+      OPENSSL_PUT_ERROR(EVP, pkey_rsa_sign, EVP_R_NO_MDC2_SUPPORT);
+      return 0;
+    }
+
+    switch (rctx->pad_mode) {
+      case RSA_PKCS1_PADDING:
+        if (!RSA_sign(EVP_MD_type(rctx->md), tbs, tbslen, sig, &out_len, rsa)) {
+          return 0;
+        }
+        *siglen = out_len;
+        return 1;
+
+      case RSA_PKCS1_PSS_PADDING:
+        if (!setup_tbuf(rctx, ctx) ||
+            !RSA_padding_add_PKCS1_PSS_mgf1(rsa, rctx->tbuf, tbs, rctx->md,
+                                            rctx->mgf1md, rctx->saltlen) ||
+            !RSA_sign_raw(rsa, siglen, sig, *siglen, rctx->tbuf, key_len,
+                          RSA_NO_PADDING)) {
+          return 0;
+        }
+        return 1;
+
+      default:
+        return 0;
+    }
+  }
+
+  return RSA_sign_raw(rsa, siglen, sig, *siglen, tbs, tbslen, rctx->pad_mode);
+}
+
+static int pkey_rsa_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig,
+                           size_t siglen, const uint8_t *tbs,
+                           size_t tbslen) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+  RSA *rsa = ctx->pkey->pkey.rsa;
+  size_t rslen;
+  const size_t key_len = EVP_PKEY_size(ctx->pkey);
+
+  if (rctx->md) {
+    switch (rctx->pad_mode) {
+      case RSA_PKCS1_PADDING:
+        return RSA_verify(EVP_MD_type(rctx->md), tbs, tbslen, sig, siglen, rsa);
+
+      case RSA_PKCS1_PSS_PADDING:
+        if (!setup_tbuf(rctx, ctx) ||
+            !RSA_verify_raw(rsa, &rslen, rctx->tbuf, key_len, sig, siglen,
+                            RSA_NO_PADDING) ||
+            !RSA_verify_PKCS1_PSS_mgf1(rsa, tbs, rctx->md, rctx->mgf1md,
+                                       rctx->tbuf, rctx->saltlen)) {
+          return 0;
+        }
+        return 1;
+
+      default:
+        return 0;
+    }
+  }
+
+  if (!setup_tbuf(rctx, ctx) ||
+      !RSA_verify_raw(rsa, &rslen, rctx->tbuf, key_len, sig, siglen,
+                      rctx->pad_mode) ||
+      rslen != tbslen ||
+      CRYPTO_memcmp(tbs, rctx->tbuf, rslen) != 0) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int pkey_rsa_encrypt(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *outlen,
+                            const uint8_t *in, size_t inlen) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+  RSA *rsa = ctx->pkey->pkey.rsa;
+  const size_t key_len = EVP_PKEY_size(ctx->pkey);
+
+  if (!out) {
+    *outlen = key_len;
+    return 1;
+  }
+
+  if (*outlen < key_len) {
+    OPENSSL_PUT_ERROR(EVP, pkey_rsa_encrypt, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  if (rctx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
+    if (!setup_tbuf(rctx, ctx) ||
+        !RSA_padding_add_PKCS1_OAEP_mgf1(rctx->tbuf, key_len, in, inlen,
+                                         rctx->oaep_label, rctx->oaep_labellen,
+                                         rctx->md, rctx->mgf1md) ||
+        !RSA_encrypt(rsa, outlen, out, *outlen, rctx->tbuf, key_len,
+                     RSA_NO_PADDING)) {
+      return 0;
+    }
+    return 1;
+  }
+
+  return RSA_encrypt(rsa, outlen, out, *outlen, in, inlen, rctx->pad_mode);
+}
+
+static int pkey_rsa_decrypt(EVP_PKEY_CTX *ctx, uint8_t *out,
+                            size_t *outlen, const uint8_t *in,
+                            size_t inlen) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+  RSA *rsa = ctx->pkey->pkey.rsa;
+  const size_t key_len = EVP_PKEY_size(ctx->pkey);
+
+  if (!out) {
+    *outlen = key_len;
+    return 1;
+  }
+
+  if (*outlen < key_len) {
+    OPENSSL_PUT_ERROR(EVP, pkey_rsa_decrypt, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  if (rctx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
+    size_t plaintext_len;
+    int message_len;
+
+    if (!setup_tbuf(rctx, ctx) ||
+        !RSA_decrypt(rsa, &plaintext_len, rctx->tbuf, key_len, in, inlen,
+                     RSA_NO_PADDING)) {
+      return 0;
+    }
+
+    message_len = RSA_padding_check_PKCS1_OAEP_mgf1(
+        out, key_len, rctx->tbuf, plaintext_len, rctx->oaep_label,
+        rctx->oaep_labellen, rctx->md, rctx->mgf1md);
+    if (message_len < 0) {
+      return 0;
+    }
+    *outlen = message_len;
+    return 1;
+  }
+
+  return RSA_decrypt(rsa, outlen, out, key_len, in, inlen, rctx->pad_mode);
+}
+
+static int check_padding_md(const EVP_MD *md, int padding) {
+  if (!md) {
+    return 1;
+  }
+
+  if (padding == RSA_NO_PADDING) {
+    OPENSSL_PUT_ERROR(EVP, check_padding_md, EVP_R_INVALID_PADDING_MODE);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int is_known_padding(int padding_mode) {
+  switch (padding_mode) {
+    case RSA_PKCS1_PADDING:
+    case RSA_NO_PADDING:
+    case RSA_PKCS1_OAEP_PADDING:
+    case RSA_PKCS1_PSS_PADDING:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+static int pkey_rsa_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
+  RSA_PKEY_CTX *rctx = ctx->data;
+  switch (type) {
+    case EVP_PKEY_CTRL_RSA_PADDING:
+      if (!is_known_padding(p1) || !check_padding_md(rctx->md, p1) ||
+          (p1 == RSA_PKCS1_PSS_PADDING &&
+           0 == (ctx->operation & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY))) ||
+          (p1 == RSA_PKCS1_OAEP_PADDING &&
+           0 == (ctx->operation & EVP_PKEY_OP_TYPE_CRYPT))) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl,
+                          EVP_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE);
+        return -2;
+      }
+      if ((p1 == RSA_PKCS1_PSS_PADDING || p1 == RSA_PKCS1_OAEP_PADDING) &&
+          rctx->md == NULL) {
+        rctx->md = EVP_sha1();
+      }
+      rctx->pad_mode = p1;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_RSA_PADDING:
+      *(int *)p2 = rctx->pad_mode;
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_PSS_SALTLEN:
+    case EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN:
+      if (rctx->pad_mode != RSA_PKCS1_PSS_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_PSS_SALTLEN);
+        return -2;
+      }
+      if (type == EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN) {
+        *(int *)p2 = rctx->saltlen;
+      } else {
+        if (p1 < -2) {
+          return -2;
+        }
+        rctx->saltlen = p1;
+      }
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_KEYGEN_BITS:
+      if (p1 < 256) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_KEYBITS);
+        return -2;
+      }
+      rctx->nbits = p1;
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP:
+      if (!p2) {
+        return -2;
+      }
+      BN_free(rctx->pub_exp);
+      rctx->pub_exp = p2;
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_OAEP_MD:
+    case EVP_PKEY_CTRL_GET_RSA_OAEP_MD:
+      if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_PADDING_MODE);
+        return -2;
+      }
+      if (type == EVP_PKEY_CTRL_GET_RSA_OAEP_MD) {
+        *(const EVP_MD **)p2 = rctx->md;
+      } else {
+        rctx->md = p2;
+      }
+      return 1;
+
+    case EVP_PKEY_CTRL_MD:
+      if (!check_padding_md(p2, rctx->pad_mode)) {
+        return 0;
+      }
+      rctx->md = p2;
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_MD:
+      *(const EVP_MD **)p2 = rctx->md;
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_MGF1_MD:
+    case EVP_PKEY_CTRL_GET_RSA_MGF1_MD:
+      if (rctx->pad_mode != RSA_PKCS1_PSS_PADDING &&
+          rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_MGF1_MD);
+        return -2;
+      }
+      if (type == EVP_PKEY_CTRL_GET_RSA_MGF1_MD) {
+        if (rctx->mgf1md) {
+          *(const EVP_MD **)p2 = rctx->mgf1md;
+        } else {
+          *(const EVP_MD **)p2 = rctx->md;
+        }
+      } else {
+        rctx->mgf1md = p2;
+      }
+      return 1;
+
+    case EVP_PKEY_CTRL_RSA_OAEP_LABEL:
+      if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_PADDING_MODE);
+        return -2;
+      }
+      if (rctx->oaep_label) {
+        OPENSSL_free(rctx->oaep_label);
+      }
+      if (p2 && p1 > 0) {
+        /* TODO(fork): this seems wrong. Shouldn't it take a copy of the
+         * buffer? */
+        rctx->oaep_label = p2;
+        rctx->oaep_labellen = p1;
+      } else {
+        rctx->oaep_label = NULL;
+        rctx->oaep_labellen = 0;
+      }
+      return 1;
+
+    case EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL:
+      if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+        OPENSSL_PUT_ERROR(EVP, pkey_rsa_ctrl, EVP_R_INVALID_PADDING_MODE);
+        return -2;
+      }
+      *(uint8_t **)p2 = rctx->oaep_label;
+      return rctx->oaep_labellen;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+      return 1;
+
+    default:
+      return -2;
+  }
+}
+
+static int pkey_rsa_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  RSA *rsa = NULL;
+  RSA_PKEY_CTX *rctx = ctx->data;
+
+  if (!rctx->pub_exp) {
+    rctx->pub_exp = BN_new();
+    if (!rctx->pub_exp || !BN_set_word(rctx->pub_exp, RSA_F4))
+      return 0;
+  }
+  rsa = RSA_new();
+  if (!rsa) {
+    return 0;
+  }
+
+  if (!RSA_generate_key_ex(rsa, rctx->nbits, rctx->pub_exp, NULL)) {
+    RSA_free(rsa);
+    return 0;
+  }
+
+  EVP_PKEY_assign_RSA(pkey, rsa);
+  return 1;
+}
+
+const EVP_PKEY_METHOD rsa_pkey_meth = {
+    EVP_PKEY_RSA,           0 /* flags */,        pkey_rsa_init,
+    pkey_rsa_copy,          pkey_rsa_cleanup,     0 /* paramgen_init */,
+    0 /* paramgen */,       0 /* keygen_init */,  pkey_rsa_keygen,
+    0 /* sign_init */,      pkey_rsa_sign,        0 /* verify_init */,
+    pkey_rsa_verify,        0 /* signctx_init */, 0 /* signctx */,
+    0 /* verifyctx_init */, 0 /* verifyctx */,    0 /* encrypt_init */,
+    pkey_rsa_encrypt,       0 /* decrypt_init */, pkey_rsa_decrypt,
+    0 /* derive_init */,    0 /* derive */,       pkey_rsa_ctrl,
+};
+
+int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int padding) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_RSA_PADDING,
+                           padding, NULL);
+}
+
+int EVP_PKEY_CTX_get_rsa_padding(EVP_PKEY_CTX *ctx, int *out_padding) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_GET_RSA_PADDING,
+                           0, out_padding);
+}
+
+int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int salt_len) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                           (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY),
+                           EVP_PKEY_CTRL_RSA_PSS_SALTLEN, salt_len, NULL);
+}
+
+int EVP_PKEY_CTX_get_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int *out_salt_len) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                           (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY),
+                           EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN, 0, out_salt_len);
+}
+
+int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int bits) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_KEYGEN,
+                           EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL);
+}
+
+int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *e) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_KEYGEN,
+                           EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP, 0, e);
+}
+
+int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_RSA_OAEP_MD, 0, (void *)md);
+}
+
+int EVP_PKEY_CTX_get_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_GET_RSA_OAEP_MD, 0, (void*) out_md);
+}
+
+int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                           EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_RSA_MGF1_MD, 0, (void*) md);
+}
+
+int EVP_PKEY_CTX_get_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
+                           EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_GET_RSA_MGF1_MD, 0, (void*) out_md);
+}
+
+int EVP_PKEY_CTX_set0_rsa_oaep_label(EVP_PKEY_CTX *ctx, const uint8_t *label,
+                                     size_t label_len) {
+  int label_len_int = label_len;
+  if (((size_t) label_len_int) != label_len) {
+    return -2;
+  }
+
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_RSA_OAEP_LABEL, label_len,
+                           (void *)label);
+}
+
+int EVP_PKEY_CTX_get0_rsa_oaep_label(EVP_PKEY_CTX *ctx,
+                                     const uint8_t **out_label) {
+  return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                           EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL, 0, (void *) out_label);
+}
diff --git a/src/crypto/evp/p_rsa_asn1.c b/src/crypto/evp/p_rsa_asn1.c
new file mode 100644
index 0000000..f478d50
--- /dev/null
+++ b/src/crypto/evp/p_rsa_asn1.c
@@ -0,0 +1,705 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 2006.
+ */
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#include "../rsa/internal.h"
+#include "internal.h"
+
+
+static int rsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) {
+  uint8_t *encoded = NULL;
+  int len;
+  len = i2d_RSAPublicKey(pkey->pkey.rsa, &encoded);
+
+  if (len <= 0) {
+    return 0;
+  }
+
+  if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_RSA), V_ASN1_NULL, NULL,
+                              encoded, len)) {
+    OPENSSL_free(encoded);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int rsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) {
+  const uint8_t *p;
+  int pklen;
+  RSA *rsa;
+
+  if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, NULL, pubkey)) {
+    return 0;
+  }
+  rsa = d2i_RSAPublicKey(NULL, &p, pklen);
+  if (rsa == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_pub_decode, ERR_R_RSA_LIB);
+    return 0;
+  }
+  EVP_PKEY_assign_RSA(pkey, rsa);
+  return 1;
+}
+
+static int rsa_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) {
+  return BN_cmp(b->pkey.rsa->n, a->pkey.rsa->n) == 0 &&
+         BN_cmp(b->pkey.rsa->e, a->pkey.rsa->e) == 0;
+}
+
+static int rsa_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey) {
+  uint8_t *rk = NULL;
+  int rklen;
+
+  rklen = i2d_RSAPrivateKey(pkey->pkey.rsa, &rk);
+
+  if (rklen <= 0) {
+    OPENSSL_PUT_ERROR(EVP, rsa_priv_encode, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  /* TODO(fork): const correctness in next line. */
+  if (!PKCS8_pkey_set0(p8, (ASN1_OBJECT *)OBJ_nid2obj(NID_rsaEncryption), 0,
+                       V_ASN1_NULL, NULL, rk, rklen)) {
+    OPENSSL_PUT_ERROR(EVP, rsa_priv_encode, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int rsa_priv_decode(EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8) {
+  const uint8_t *p;
+  int pklen;
+  RSA *rsa;
+
+  if (!PKCS8_pkey_get0(NULL, &p, &pklen, NULL, p8)) {
+    OPENSSL_PUT_ERROR(EVP, rsa_priv_decode, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  rsa = d2i_RSAPrivateKey(NULL, &p, pklen);
+  if (rsa == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_priv_decode, ERR_R_RSA_LIB);
+    return 0;
+  }
+
+  EVP_PKEY_assign_RSA(pkey, rsa);
+  return 1;
+}
+
+static int rsa_opaque(const EVP_PKEY *pkey) {
+  return RSA_is_opaque(pkey->pkey.rsa);
+}
+
+static int rsa_supports_digest(const EVP_PKEY *pkey, const EVP_MD *md) {
+  return RSA_supports_digest(pkey->pkey.rsa, md);
+}
+
+static int int_rsa_size(const EVP_PKEY *pkey) {
+  return RSA_size(pkey->pkey.rsa);
+}
+
+static int rsa_bits(const EVP_PKEY *pkey) {
+  return BN_num_bits(pkey->pkey.rsa->n);
+}
+
+static void int_rsa_free(EVP_PKEY *pkey) { RSA_free(pkey->pkey.rsa); }
+
+static void update_buflen(const BIGNUM *b, size_t *pbuflen) {
+  size_t i;
+
+  if (!b) {
+    return;
+  }
+
+  i = BN_num_bytes(b);
+  if (*pbuflen < i) {
+    *pbuflen = i;
+  }
+}
+
+static int do_rsa_print(BIO *out, const RSA *rsa, int off,
+                        int include_private) {
+  char *str;
+  const char *s;
+  uint8_t *m = NULL;
+  int ret = 0, mod_len = 0;
+  size_t buf_len = 0;
+
+  update_buflen(rsa->n, &buf_len);
+  update_buflen(rsa->e, &buf_len);
+
+  if (include_private) {
+    update_buflen(rsa->d, &buf_len);
+    update_buflen(rsa->p, &buf_len);
+    update_buflen(rsa->q, &buf_len);
+    update_buflen(rsa->dmp1, &buf_len);
+    update_buflen(rsa->dmq1, &buf_len);
+    update_buflen(rsa->iqmp, &buf_len);
+  }
+
+  m = (uint8_t *)OPENSSL_malloc(buf_len + 10);
+  if (m == NULL) {
+    OPENSSL_PUT_ERROR(EVP, do_rsa_print, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (rsa->n != NULL) {
+    mod_len = BN_num_bits(rsa->n);
+  }
+
+  if (!BIO_indent(out, off, 128)) {
+    goto err;
+  }
+
+  if (include_private && rsa->d) {
+    if (BIO_printf(out, "Private-Key: (%d bit)\n", mod_len) <= 0) {
+      goto err;
+    }
+    str = "modulus:";
+    s = "publicExponent:";
+  } else {
+    if (BIO_printf(out, "Public-Key: (%d bit)\n", mod_len) <= 0) {
+      goto err;
+    }
+    str = "Modulus:";
+    s = "Exponent:";
+  }
+  if (!ASN1_bn_print(out, str, rsa->n, m, off) ||
+      !ASN1_bn_print(out, s, rsa->e, m, off)) {
+    goto err;
+  }
+
+  if (include_private) {
+    if (!ASN1_bn_print(out, "privateExponent:", rsa->d, m, off) ||
+        !ASN1_bn_print(out, "prime1:", rsa->p, m, off) ||
+        !ASN1_bn_print(out, "prime2:", rsa->q, m, off) ||
+        !ASN1_bn_print(out, "exponent1:", rsa->dmp1, m, off) ||
+        !ASN1_bn_print(out, "exponent2:", rsa->dmq1, m, off) ||
+        !ASN1_bn_print(out, "coefficient:", rsa->iqmp, m, off)) {
+      goto err;
+    }
+  }
+  ret = 1;
+
+err:
+  if (m != NULL) {
+    OPENSSL_free(m);
+  }
+  return ret;
+}
+
+static int rsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *ctx) {
+  return do_rsa_print(bp, pkey->pkey.rsa, indent, 0);
+}
+
+
+static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *ctx) {
+  return do_rsa_print(bp, pkey->pkey.rsa, indent, 1);
+}
+
+/* Given an MGF1 Algorithm ID decode to an Algorithm Identifier */
+static X509_ALGOR *rsa_mgf1_decode(X509_ALGOR *alg) {
+  const uint8_t *p;
+  int plen;
+
+  if (alg == NULL ||
+      OBJ_obj2nid(alg->algorithm) != NID_mgf1 ||
+      alg->parameter->type != V_ASN1_SEQUENCE) {
+    return NULL;
+  }
+
+  p = alg->parameter->value.sequence->data;
+  plen = alg->parameter->value.sequence->length;
+  return d2i_X509_ALGOR(NULL, &p, plen);
+}
+
+static RSA_PSS_PARAMS *rsa_pss_decode(const X509_ALGOR *alg,
+                                      X509_ALGOR **pmaskHash) {
+  const uint8_t *p;
+  int plen;
+  RSA_PSS_PARAMS *pss;
+
+  *pmaskHash = NULL;
+
+  if (!alg->parameter || alg->parameter->type != V_ASN1_SEQUENCE) {
+    return NULL;
+  }
+  p = alg->parameter->value.sequence->data;
+  plen = alg->parameter->value.sequence->length;
+  pss = d2i_RSA_PSS_PARAMS(NULL, &p, plen);
+
+  if (!pss) {
+    return NULL;
+  }
+
+  *pmaskHash = rsa_mgf1_decode(pss->maskGenAlgorithm);
+
+  return pss;
+}
+
+static int rsa_pss_param_print(BIO *bp, RSA_PSS_PARAMS *pss,
+                               X509_ALGOR *maskHash, int indent) {
+  int rv = 0;
+
+  if (!pss) {
+    if (BIO_puts(bp, " (INVALID PSS PARAMETERS)\n") <= 0) {
+      return 0;
+    }
+    return 1;
+  }
+
+  if (BIO_puts(bp, "\n") <= 0 ||
+      !BIO_indent(bp, indent, 128) ||
+      BIO_puts(bp, "Hash Algorithm: ") <= 0) {
+    goto err;
+  }
+
+  if (pss->hashAlgorithm) {
+    if (i2a_ASN1_OBJECT(bp, pss->hashAlgorithm->algorithm) <= 0) {
+      goto err;
+    }
+  } else if (BIO_puts(bp, "sha1 (default)") <= 0) {
+    goto err;
+  }
+
+  if (BIO_puts(bp, "\n") <= 0 ||
+      !BIO_indent(bp, indent, 128) ||
+      BIO_puts(bp, "Mask Algorithm: ") <= 0) {
+    goto err;
+  }
+
+  if (pss->maskGenAlgorithm) {
+    if (i2a_ASN1_OBJECT(bp, pss->maskGenAlgorithm->algorithm) <= 0 ||
+        BIO_puts(bp, " with ") <= 0) {
+      goto err;
+    }
+
+    if (maskHash) {
+      if (i2a_ASN1_OBJECT(bp, maskHash->algorithm) <= 0) {
+        goto err;
+      }
+    } else if (BIO_puts(bp, "INVALID") <= 0) {
+      goto err;
+    }
+  } else if (BIO_puts(bp, "mgf1 with sha1 (default)") <= 0) {
+    goto err;
+  }
+  BIO_puts(bp, "\n");
+
+  if (!BIO_indent(bp, indent, 128) ||
+      BIO_puts(bp, "Salt Length: 0x") <= 0) {
+    goto err;
+  }
+
+  if (pss->saltLength) {
+    if (i2a_ASN1_INTEGER(bp, pss->saltLength) <= 0) {
+      goto err;
+    }
+  } else if (BIO_puts(bp, "14 (default)") <= 0) {
+    goto err;
+  }
+  BIO_puts(bp, "\n");
+
+  if (!BIO_indent(bp, indent, 128) ||
+      BIO_puts(bp, "Trailer Field: 0x") <= 0) {
+    goto err;
+  }
+
+  if (pss->trailerField) {
+    if (i2a_ASN1_INTEGER(bp, pss->trailerField) <= 0) {
+      goto err;
+    }
+  } else if (BIO_puts(bp, "BC (default)") <= 0) {
+    goto err;
+  }
+  BIO_puts(bp, "\n");
+
+  rv = 1;
+
+err:
+  return rv;
+}
+
+static int rsa_sig_print(BIO *bp, const X509_ALGOR *sigalg,
+                         const ASN1_STRING *sig, int indent, ASN1_PCTX *pctx) {
+  if (OBJ_obj2nid(sigalg->algorithm) == NID_rsassaPss) {
+    int rv;
+    RSA_PSS_PARAMS *pss;
+    X509_ALGOR *maskHash;
+
+    pss = rsa_pss_decode(sigalg, &maskHash);
+    rv = rsa_pss_param_print(bp, pss, maskHash, indent);
+    if (pss) {
+      RSA_PSS_PARAMS_free(pss);
+    }
+    if (maskHash) {
+      X509_ALGOR_free(maskHash);
+    }
+    if (!rv) {
+      return 0;
+    }
+  } else if (!sig && BIO_puts(bp, "\n") <= 0) {
+    return 0;
+  }
+
+  if (sig) {
+    return X509_signature_dump(bp, sig, indent);
+  }
+  return 1;
+}
+
+static int old_rsa_priv_decode(EVP_PKEY *pkey, const unsigned char **pder,
+                               int derlen) {
+  RSA *rsa = d2i_RSAPrivateKey(NULL, pder, derlen);
+  if (rsa == NULL) {
+    OPENSSL_PUT_ERROR(EVP, old_rsa_priv_decode, ERR_R_RSA_LIB);
+    return 0;
+  }
+  EVP_PKEY_assign_RSA(pkey, rsa);
+  return 1;
+}
+
+static int old_rsa_priv_encode(const EVP_PKEY *pkey, unsigned char **pder) {
+  return i2d_RSAPrivateKey(pkey->pkey.rsa, pder);
+}
+
+/* allocate and set algorithm ID from EVP_MD, default SHA1 */
+static int rsa_md_to_algor(X509_ALGOR **palg, const EVP_MD *md) {
+  if (EVP_MD_type(md) == NID_sha1) {
+    return 1;
+  }
+  *palg = X509_ALGOR_new();
+  if (!*palg) {
+    return 0;
+  }
+  X509_ALGOR_set_md(*palg, md);
+  return 1;
+}
+
+/* Allocate and set MGF1 algorithm ID from EVP_MD */
+static int rsa_md_to_mgf1(X509_ALGOR **palg, const EVP_MD *mgf1md) {
+  X509_ALGOR *algtmp = NULL;
+  ASN1_STRING *stmp = NULL;
+  *palg = NULL;
+
+  if (EVP_MD_type(mgf1md) == NID_sha1) {
+    return 1;
+  }
+  /* need to embed algorithm ID inside another */
+  if (!rsa_md_to_algor(&algtmp, mgf1md) ||
+      !ASN1_item_pack(algtmp, ASN1_ITEM_rptr(X509_ALGOR), &stmp)) {
+    goto err;
+  }
+  *palg = X509_ALGOR_new();
+  if (!*palg) {
+    goto err;
+  }
+  X509_ALGOR_set0(*palg, OBJ_nid2obj(NID_mgf1), V_ASN1_SEQUENCE, stmp);
+  stmp = NULL;
+
+err:
+  if (stmp)
+    ASN1_STRING_free(stmp);
+  if (algtmp)
+    X509_ALGOR_free(algtmp);
+  if (*palg)
+    return 1;
+
+  return 0;
+}
+
+/* convert algorithm ID to EVP_MD, default SHA1 */
+static const EVP_MD *rsa_algor_to_md(X509_ALGOR *alg) {
+  const EVP_MD *md;
+  if (!alg) {
+    return EVP_sha1();
+  }
+  md = EVP_get_digestbyobj(alg->algorithm);
+  if (md == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_algor_to_md, EVP_R_UNKNOWN_DIGEST);
+  }
+  return md;
+}
+
+/* convert MGF1 algorithm ID to EVP_MD, default SHA1 */
+static const EVP_MD *rsa_mgf1_to_md(X509_ALGOR *alg, X509_ALGOR *maskHash) {
+  const EVP_MD *md;
+  if (!alg) {
+    return EVP_sha1();
+  }
+  /* Check mask and lookup mask hash algorithm */
+  if (OBJ_obj2nid(alg->algorithm) != NID_mgf1) {
+    OPENSSL_PUT_ERROR(EVP, rsa_mgf1_to_md, EVP_R_UNSUPPORTED_MASK_ALGORITHM);
+    return NULL;
+  }
+  if (!maskHash) {
+    OPENSSL_PUT_ERROR(EVP, rsa_mgf1_to_md, EVP_R_UNSUPPORTED_MASK_PARAMETER);
+    return NULL;
+  }
+  md = EVP_get_digestbyobj(maskHash->algorithm);
+  if (md == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_mgf1_to_md, EVP_R_UNKNOWN_MASK_DIGEST);
+    return NULL;
+  }
+  return md;
+}
+
+/* rsa_ctx_to_pss converts EVP_PKEY_CTX in PSS mode into corresponding
+ * algorithm parameter, suitable for setting as an AlgorithmIdentifier. */
+static ASN1_STRING *rsa_ctx_to_pss(EVP_PKEY_CTX *pkctx) {
+  const EVP_MD *sigmd, *mgf1md;
+  RSA_PSS_PARAMS *pss = NULL;
+  ASN1_STRING *os = NULL;
+  EVP_PKEY *pk = EVP_PKEY_CTX_get0_pkey(pkctx);
+  int saltlen, rv = 0;
+
+  if (EVP_PKEY_CTX_get_signature_md(pkctx, &sigmd) <= 0 ||
+      EVP_PKEY_CTX_get_rsa_mgf1_md(pkctx, &mgf1md) <= 0 ||
+      !EVP_PKEY_CTX_get_rsa_pss_saltlen(pkctx, &saltlen)) {
+    goto err;
+  }
+
+  if (saltlen == -1) {
+    saltlen = EVP_MD_size(sigmd);
+  } else if (saltlen == -2) {
+    saltlen = EVP_PKEY_size(pk) - EVP_MD_size(sigmd) - 2;
+    if (((EVP_PKEY_bits(pk) - 1) & 0x7) == 0) {
+      saltlen--;
+    }
+  } else {
+    goto err;
+  }
+
+  pss = RSA_PSS_PARAMS_new();
+  if (!pss) {
+    goto err;
+  }
+
+  if (saltlen != 20) {
+    pss->saltLength = ASN1_INTEGER_new();
+    if (!pss->saltLength ||
+        !ASN1_INTEGER_set(pss->saltLength, saltlen)) {
+      goto err;
+    }
+  }
+
+  if (!rsa_md_to_algor(&pss->hashAlgorithm, sigmd) ||
+      !rsa_md_to_mgf1(&pss->maskGenAlgorithm, mgf1md)) {
+    goto err;
+  }
+
+  /* Finally create string with pss parameter encoding. */
+  if (!ASN1_item_pack(pss, ASN1_ITEM_rptr(RSA_PSS_PARAMS), &os)) {
+    goto err;
+  }
+  rv = 1;
+
+err:
+  if (pss)
+    RSA_PSS_PARAMS_free(pss);
+  if (rv)
+    return os;
+  if (os)
+    ASN1_STRING_free(os);
+  return NULL;
+}
+
+/* From PSS AlgorithmIdentifier set public key parameters. */
+static int rsa_pss_to_ctx(EVP_MD_CTX *ctx, X509_ALGOR *sigalg, EVP_PKEY *pkey) {
+  int ret = 0;
+  int saltlen;
+  const EVP_MD *mgf1md = NULL, *md = NULL;
+  RSA_PSS_PARAMS *pss;
+  X509_ALGOR *maskHash;
+  EVP_PKEY_CTX *pkctx;
+
+  /* Sanity check: make sure it is PSS */
+  if (OBJ_obj2nid(sigalg->algorithm) != NID_rsassaPss) {
+    OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_UNSUPPORTED_SIGNATURE_TYPE);
+    return 0;
+  }
+  /* Decode PSS parameters */
+  pss = rsa_pss_decode(sigalg, &maskHash);
+  if (pss == NULL) {
+    OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_INVALID_PSS_PARAMETERS);
+    goto err;
+  }
+
+  mgf1md = rsa_mgf1_to_md(pss->maskGenAlgorithm, maskHash);
+  if (!mgf1md) {
+    goto err;
+  }
+  md = rsa_algor_to_md(pss->hashAlgorithm);
+  if (!md) {
+    goto err;
+  }
+
+  saltlen = 20;
+  if (pss->saltLength) {
+    saltlen = ASN1_INTEGER_get(pss->saltLength);
+
+    /* Could perform more salt length sanity checks but the main
+     * RSA routines will trap other invalid values anyway. */
+    if (saltlen < 0) {
+      OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_INVALID_SALT_LENGTH);
+      goto err;
+    }
+  }
+
+  /* low-level routines support only trailer field 0xbc (value 1)
+   * and PKCS#1 says we should reject any other value anyway. */
+  if (pss->trailerField && ASN1_INTEGER_get(pss->trailerField) != 1) {
+    OPENSSL_PUT_ERROR(EVP, rsa_pss_to_ctx, EVP_R_INVALID_TRAILER);
+    goto err;
+  }
+
+  if (!EVP_DigestVerifyInit(ctx, &pkctx, md, NULL, pkey) ||
+      EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING) <= 0 ||
+      EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, saltlen) <= 0 ||
+      EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, mgf1md) <= 0) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  RSA_PSS_PARAMS_free(pss);
+  if (maskHash) {
+    X509_ALGOR_free(maskHash);
+  }
+  return ret;
+}
+
+/* Customised RSA AlgorithmIdentifier handling. This is called when a signature
+ * is encountered requiring special handling. We currently only handle PSS. */
+static int rsa_digest_verify_init_from_algorithm(EVP_MD_CTX *ctx,
+                                                 X509_ALGOR *sigalg,
+                                                 EVP_PKEY *pkey) {
+  /* Sanity check: make sure it is PSS */
+  if (OBJ_obj2nid(sigalg->algorithm) != NID_rsassaPss) {
+    OPENSSL_PUT_ERROR(EVP, rsa_digest_verify_init_from_algorithm,
+                      EVP_R_UNSUPPORTED_SIGNATURE_TYPE);
+    return 0;
+  }
+  return rsa_pss_to_ctx(ctx, sigalg, pkey);
+}
+
+static evp_digest_sign_algorithm_result_t rsa_digest_sign_algorithm(
+    EVP_MD_CTX *ctx, X509_ALGOR *sigalg) {
+  int pad_mode;
+  EVP_PKEY_CTX *pkctx = ctx->pctx;
+  if (EVP_PKEY_CTX_get_rsa_padding(pkctx, &pad_mode) <= 0) {
+    return EVP_DIGEST_SIGN_ALGORITHM_ERROR;
+  }
+  if (pad_mode == RSA_PKCS1_PSS_PADDING) {
+    ASN1_STRING *os1 = rsa_ctx_to_pss(pkctx);
+    if (!os1) {
+      return EVP_DIGEST_SIGN_ALGORITHM_ERROR;
+    }
+    X509_ALGOR_set0(sigalg, OBJ_nid2obj(NID_rsassaPss), V_ASN1_SEQUENCE, os1);
+    return EVP_DIGEST_SIGN_ALGORITHM_SUCCESS;
+  }
+
+  /* Other padding schemes use the default behavior. */
+  return EVP_DIGEST_SIGN_ALGORITHM_DEFAULT;
+}
+
+const EVP_PKEY_ASN1_METHOD rsa_asn1_meth = {
+  EVP_PKEY_RSA,
+  EVP_PKEY_RSA,
+  ASN1_PKEY_SIGPARAM_NULL,
+
+  "RSA",
+  "OpenSSL RSA method",
+
+  rsa_pub_decode,
+  rsa_pub_encode,
+  rsa_pub_cmp,
+  rsa_pub_print,
+
+  rsa_priv_decode,
+  rsa_priv_encode,
+  rsa_priv_print,
+
+  rsa_opaque,
+  rsa_supports_digest,
+
+  int_rsa_size,
+  rsa_bits,
+
+  0,0,0,0,0,0,
+
+  rsa_sig_print,
+  int_rsa_free,
+
+  old_rsa_priv_decode,
+  old_rsa_priv_encode,
+
+  rsa_digest_verify_init_from_algorithm,
+  rsa_digest_sign_algorithm,
+};
diff --git a/src/crypto/evp/pbkdf.c b/src/crypto/evp/pbkdf.c
new file mode 100644
index 0000000..be6ed86
--- /dev/null
+++ b/src/crypto/evp/pbkdf.c
@@ -0,0 +1,135 @@
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project 1999.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <string.h>
+
+#include <openssl/hmac.h>
+
+
+int PKCS5_PBKDF2_HMAC(const char *password, size_t password_len,
+                      const uint8_t *salt, size_t salt_len, unsigned iterations,
+                      const EVP_MD *digest, size_t key_len, uint8_t *out_key) {
+  uint8_t digest_tmp[EVP_MAX_MD_SIZE], *p, itmp[4];
+  size_t cplen, mdlen, tkeylen, k;
+  unsigned j;
+  uint32_t i = 1;
+  HMAC_CTX hctx_tpl, hctx;
+
+  mdlen = EVP_MD_size(digest);
+  HMAC_CTX_init(&hctx_tpl);
+  p = out_key;
+  tkeylen = key_len;
+  if (!HMAC_Init_ex(&hctx_tpl, password, password_len, digest, NULL)) {
+    HMAC_CTX_cleanup(&hctx_tpl);
+    return 0;
+  }
+  while (tkeylen) {
+    if (tkeylen > mdlen) {
+      cplen = mdlen;
+    } else {
+      cplen = tkeylen;
+    }
+    /* We are unlikely to ever use more than 256 blocks (5120 bits!)
+     * but just in case... */
+    itmp[0] = (uint8_t)((i >> 24) & 0xff);
+    itmp[1] = (uint8_t)((i >> 16) & 0xff);
+    itmp[2] = (uint8_t)((i >> 8) & 0xff);
+    itmp[3] = (uint8_t)(i & 0xff);
+    if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) {
+      HMAC_CTX_cleanup(&hctx_tpl);
+      return 0;
+    }
+    if (!HMAC_Update(&hctx, salt, salt_len) ||
+        !HMAC_Update(&hctx, itmp, 4) ||
+        !HMAC_Final(&hctx, digest_tmp, NULL)) {
+      HMAC_CTX_cleanup(&hctx_tpl);
+      HMAC_CTX_cleanup(&hctx);
+      return 0;
+    }
+    HMAC_CTX_cleanup(&hctx);
+    memcpy(p, digest_tmp, cplen);
+    for (j = 1; j < iterations; j++) {
+      if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) {
+        HMAC_CTX_cleanup(&hctx_tpl);
+        return 0;
+      }
+      if (!HMAC_Update(&hctx, digest_tmp, mdlen) ||
+          !HMAC_Final(&hctx, digest_tmp, NULL)) {
+        HMAC_CTX_cleanup(&hctx_tpl);
+        HMAC_CTX_cleanup(&hctx);
+        return 0;
+      }
+      HMAC_CTX_cleanup(&hctx);
+      for (k = 0; k < cplen; k++) {
+        p[k] ^= digest_tmp[k];
+      }
+    }
+    tkeylen -= cplen;
+    i++;
+    p += cplen;
+  }
+  HMAC_CTX_cleanup(&hctx_tpl);
+  return 1;
+}
+
+int PKCS5_PBKDF2_HMAC_SHA1(const char *password, size_t password_len,
+                           const uint8_t *salt, size_t salt_len,
+                           unsigned iterations, size_t key_len,
+                           uint8_t *out_key) {
+  return PKCS5_PBKDF2_HMAC(password, password_len, salt, salt_len, iterations,
+                           EVP_sha1(), key_len, out_key);
+}
diff --git a/src/crypto/evp/sign.c b/src/crypto/evp/sign.c
new file mode 100644
index 0000000..1faf7c6
--- /dev/null
+++ b/src/crypto/evp/sign.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/evp.h>
+
+#include <openssl/digest.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+
+
+int EVP_SignInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) {
+  return EVP_DigestInit_ex(ctx, type, impl);
+}
+
+int EVP_SignInit(EVP_MD_CTX *ctx, const EVP_MD *type) {
+  return EVP_DigestInit(ctx, type);
+}
+
+int EVP_SignUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return EVP_DigestUpdate(ctx, data, len);
+}
+
+int EVP_SignFinal(const EVP_MD_CTX *ctx, uint8_t *sig,
+                  unsigned int *out_sig_len, EVP_PKEY *pkey) {
+  uint8_t m[EVP_MAX_MD_SIZE];
+  unsigned int m_len;
+  int ret = 0;
+  EVP_MD_CTX tmp_ctx;
+  EVP_PKEY_CTX *pkctx = NULL;
+  size_t sig_len = EVP_PKEY_size(pkey);
+
+  *out_sig_len = 0;
+  EVP_MD_CTX_init(&tmp_ctx);
+  if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx) ||
+      !EVP_DigestFinal_ex(&tmp_ctx, m, &m_len)) {
+    goto out;
+  }
+  EVP_MD_CTX_cleanup(&tmp_ctx);
+
+  pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+  if (!pkctx || EVP_PKEY_sign_init(pkctx) <= 0 ||
+      EVP_PKEY_CTX_set_signature_md(pkctx, ctx->digest) <= 0 ||
+      EVP_PKEY_sign(pkctx, sig, &sig_len, m, m_len) <= 0) {
+    goto out;
+  }
+  *out_sig_len = sig_len;
+  ret = 1;
+
+out:
+  if (pkctx) {
+    EVP_PKEY_CTX_free(pkctx);
+  }
+
+  return ret;
+}
+
+int EVP_VerifyInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) {
+  return EVP_DigestInit_ex(ctx, type, impl);
+}
+
+int EVP_VerifyInit(EVP_MD_CTX *ctx, const EVP_MD *type) {
+  return EVP_DigestInit(ctx, type);
+}
+
+int EVP_VerifyUpdate(EVP_MD_CTX *ctx, const void *data, size_t len) {
+  return EVP_DigestUpdate(ctx, data, len);
+}
+
+int EVP_VerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig, size_t sig_len,
+                    EVP_PKEY *pkey) {
+  uint8_t m[EVP_MAX_MD_SIZE];
+  unsigned int m_len;
+  int ret = 0;
+  EVP_MD_CTX tmp_ctx;
+  EVP_PKEY_CTX *pkctx = NULL;
+
+  EVP_MD_CTX_init(&tmp_ctx);
+  if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx) ||
+      !EVP_DigestFinal_ex(&tmp_ctx, m, &m_len)) {
+    EVP_MD_CTX_cleanup(&tmp_ctx);
+    goto out;
+  }
+  EVP_MD_CTX_cleanup(&tmp_ctx);
+
+  pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+  if (!pkctx ||
+      EVP_PKEY_verify_init(pkctx) <= 0 ||
+      EVP_PKEY_CTX_set_signature_md(pkctx, ctx->digest) <= 0) {
+    goto out;
+  }
+  ret = EVP_PKEY_verify(pkctx, sig, sig_len, m, m_len);
+
+out:
+  EVP_PKEY_CTX_free(pkctx);
+  return ret;
+}
+