]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
PROV: Add type specific MSBLOB and PVK decoding for the MS->key decoders
authorRichard Levitte <levitte@openssl.org>
Mon, 15 Mar 2021 14:05:59 +0000 (15:05 +0100)
committerRichard Levitte <levitte@openssl.org>
Fri, 19 Mar 2021 15:46:39 +0000 (16:46 +0100)
To make this cleaner, decoder_ms2key.c is split into decoder_msblob2key.c
and decoder_pvk2key.c.

This required a great deal of refactoring of crypto/pem/pvkfmt.c, to
make cleaner internal functions that our decoder implementations can
use.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14314)

crypto/err/openssl.txt
crypto/pem/pem_err.c
crypto/pem/pvkfmt.c
include/crypto/pem.h
include/crypto/pemerr.h
include/openssl/pemerr.h
providers/implementations/encode_decode/build.info
providers/implementations/encode_decode/decode_ms2key.c [deleted file]
providers/implementations/encode_decode/decode_msblob2key.c [new file with mode: 0644]
providers/implementations/encode_decode/decode_pvk2key.c [new file with mode: 0644]

index 68c2ea8aa36b18ec44a757d4ae303682cb0699f2..0a407ba2dcc2303c965573ee8e29983cb706ecc3 100644 (file)
@@ -849,8 +849,10 @@ PEM_R_BAD_VERSION_NUMBER:117:bad version number
 PEM_R_BIO_WRITE_FAILURE:118:bio write failure
 PEM_R_CIPHER_IS_NULL:127:cipher is null
 PEM_R_ERROR_CONVERTING_PRIVATE_KEY:115:error converting private key
+PEM_R_EXPECTING_DSS_KEY_BLOB:131:expecting dss key blob
 PEM_R_EXPECTING_PRIVATE_KEY_BLOB:119:expecting private key blob
 PEM_R_EXPECTING_PUBLIC_KEY_BLOB:120:expecting public key blob
+PEM_R_EXPECTING_RSA_KEY_BLOB:132:expecting rsa key blob
 PEM_R_HEADER_TOO_LONG:128:header too long
 PEM_R_INCONSISTENT_HEADER:121:inconsistent header
 PEM_R_KEYBLOB_HEADER_PARSE_ERROR:122:keyblob header parse error
index 52a1bc611f6a7ffc998bc8ed65733de8262dcadf..50f9c2b925139c7f5fe749da196459323c3a15f1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -26,10 +26,14 @@ static const ERR_STRING_DATA PEM_str_reasons[] = {
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_CIPHER_IS_NULL), "cipher is null"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_ERROR_CONVERTING_PRIVATE_KEY),
     "error converting private key"},
+    {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_DSS_KEY_BLOB),
+    "expecting dss key blob"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_PRIVATE_KEY_BLOB),
     "expecting private key blob"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_PUBLIC_KEY_BLOB),
     "expecting public key blob"},
+    {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_RSA_KEY_BLOB),
+    "expecting rsa key blob"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_HEADER_TOO_LONG), "header too long"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_INCONSISTENT_HEADER),
     "inconsistent header"},
index 0d8aa509b312fb2f2dfea557a1ddf316e216423e..bc6f6fab71218298c0402ff0e69a34d49ba06417 100644 (file)
@@ -59,6 +59,65 @@ static int read_lebn(const unsigned char **in, unsigned int nbyte, BIGNUM **r)
     return 1;
 }
 
+/*
+ * Create an EVP_PKEY from a type specific key.
+ * This takes ownership of |key|, as long as the |evp_type| is acceptable
+ * (EVP_PKEY_RSA or EVP_PKEY_DSA), even if the resulting EVP_PKEY wasn't
+ * created.
+ */
+#define isdss_to_evp_type(isdss)                                \
+    (isdss == 0 ? EVP_PKEY_RSA : isdss == 1 ? EVP_PKEY_DSA : EVP_PKEY_NONE)
+static EVP_PKEY *evp_pkey_new0_key(void *key, int evp_type)
+{
+    EVP_PKEY *pkey = NULL;
+
+    /*
+     * It's assumed that if |key| is NULL, something went wrong elsewhere
+     * and suitable errors are already reported.
+     */
+    if (key == NULL)
+        return NULL;
+
+    if (!ossl_assert(evp_type == EVP_PKEY_RSA || evp_type == EVP_PKEY_DSA)) {
+        ERR_raise(ERR_LIB_PEM, ERR_R_INTERNAL_ERROR);
+        return NULL;
+    }
+
+    if ((pkey = EVP_PKEY_new()) != NULL) {
+        switch (evp_type) {
+        case EVP_PKEY_RSA:
+            if (EVP_PKEY_set1_RSA(pkey, key))
+                break;
+            EVP_PKEY_free(pkey);
+            pkey = NULL;
+            break;
+#ifndef OPENSSL_NO_DSA
+        case EVP_PKEY_DSA:
+            if (EVP_PKEY_set1_DSA(pkey, key))
+                break;
+            EVP_PKEY_free(pkey);
+            pkey = NULL;
+            break;
+#endif
+        }
+    }
+
+    switch (evp_type) {
+    case EVP_PKEY_RSA:
+        RSA_free(key);
+        break;
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        DSA_free(key);
+        break;
+#endif
+    }
+
+    if (pkey == NULL)
+        ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
+    return pkey;
+}
+
 /* Convert private key blob to EVP_PKEY: RSA and DSA keys supported */
 
 # define MS_PUBLICKEYBLOB        0x6
@@ -74,9 +133,6 @@ static int read_lebn(const unsigned char **in, unsigned int nbyte, BIGNUM **r)
 # define MS_KEYTYPE_KEYX         0x1
 # define MS_KEYTYPE_SIGN         0x2
 
-/* Maximum length of a blob after header */
-# define BLOB_MAX_LENGTH          102400
-
 /* The PVK file magic number: seems to spell out "bobsfile", who is Bob? */
 # define MS_PVKMAGIC             0xb0b5f11eL
 /* Salt length for PVK files */
@@ -86,13 +142,20 @@ static int read_lebn(const unsigned char **in, unsigned int nbyte, BIGNUM **r)
 /* Maximum salt length */
 # define PVK_MAX_SALTLEN         10240
 
-static EVP_PKEY *b2i_rsa(const unsigned char **in,
-                         unsigned int bitlen, int ispub);
-#ifndef OPENSSL_NO_DSA
-static EVP_PKEY *b2i_dss(const unsigned char **in,
-                         unsigned int bitlen, int ispub);
-#endif
-
+/*
+ * Read the MSBLOB header and get relevant data from it.
+ *
+ * |pisdss| and |pispub| have a double role, as they can be used for
+ * discovery as well as to check the the blob meets expectations.
+ * |*pisdss| is the indicator for whether the key is a DSA key or not.
+ * |*pispub| is the indicator for whether the key is public or not.
+ * In both cases, the following input values apply:
+ *
+ * 0    Expected to not be what the variable indicates.
+ * 1    Expected to be what the variable indicates.
+ * -1   No expectations, this function will assign 0 or 1 depending on
+ *      header data.
+ */
 int ossl_do_blob_header(const unsigned char **in, unsigned int length,
                         unsigned int *pmagic, unsigned int *pbitlen,
                         int *pisdss, int *pispub)
@@ -102,19 +165,24 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
     if (length < 16)
         return 0;
     /* bType */
-    if (*p == MS_PUBLICKEYBLOB) {
+    switch (*p) {
+    case MS_PUBLICKEYBLOB:
         if (*pispub == 0) {
             ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_PRIVATE_KEY_BLOB);
             return 0;
         }
         *pispub = 1;
-    } else if (*p == MS_PRIVATEKEYBLOB) {
+        break;
+
+    case MS_PRIVATEKEYBLOB:
         if (*pispub == 1) {
             ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_PUBLIC_KEY_BLOB);
             return 0;
         }
         *pispub = 0;
-    } else {
+        break;
+
+    default:
         return 0;
     }
     p++;
@@ -127,12 +195,10 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
     p += 6;
     *pmagic = read_ledword(&p);
     *pbitlen = read_ledword(&p);
-    *pisdss = 0;
-    switch (*pmagic) {
 
+    /* Consistency check for private vs public */
+    switch (*pmagic) {
     case MS_DSS1MAGIC:
-        *pisdss = 1;
-        /* fall thru */
     case MS_RSA1MAGIC:
         if (*pispub == 0) {
             ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_PRIVATE_KEY_BLOB);
@@ -141,8 +207,6 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
         break;
 
     case MS_DSS2MAGIC:
-        *pisdss = 1;
-        /* fall thru */
     case MS_RSA2MAGIC:
         if (*pispub == 1) {
             ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_PUBLIC_KEY_BLOB);
@@ -150,6 +214,30 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
         }
         break;
 
+    default:
+        ERR_raise(ERR_LIB_PEM, PEM_R_BAD_MAGIC_NUMBER);
+        return -1;
+    }
+
+    /* Check that we got the expected type */
+    switch (*pmagic) {
+    case MS_DSS1MAGIC:
+    case MS_DSS2MAGIC:
+        if (*pisdss == 0) {
+            ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_DSS_KEY_BLOB);
+            return 0;
+        }
+        *pisdss = 1;
+        break;
+    case MS_RSA1MAGIC:
+    case MS_RSA2MAGIC:
+        if (*pisdss == 1) {
+            ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_RSA_KEY_BLOB);
+            return 0;
+        }
+        *pisdss = 0;
+        break;
+
     default:
         ERR_raise(ERR_LIB_PEM, PEM_R_BAD_MAGIC_NUMBER);
         return -1;
@@ -158,7 +246,7 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
     return 1;
 }
 
-static unsigned int blob_length(unsigned bitlen, int isdss, int ispub)
+unsigned int ossl_blob_length(unsigned bitlen, int isdss, int ispub)
 {
     unsigned int nbyte = (bitlen + 7) >> 3;
     unsigned int hnbyte = (bitlen + 15) >> 4;
@@ -191,30 +279,43 @@ static unsigned int blob_length(unsigned bitlen, int isdss, int ispub)
 
 }
 
-EVP_PKEY *ossl_b2i(const unsigned char **in, unsigned int length, int *ispub)
+static void *do_b2i_key(const unsigned char **in, unsigned int length,
+                        int *isdss, int *ispub)
 {
     const unsigned char *p = *in;
     unsigned int bitlen, magic;
-    int isdss;
+    void *key = NULL;
 
-    if (ossl_do_blob_header(&p, length, &magic, &bitlen, &isdss, ispub) <= 0) {
+    if (ossl_do_blob_header(&p, length, &magic, &bitlen, isdss, ispub) <= 0) {
         ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_HEADER_PARSE_ERROR);
         return NULL;
     }
     length -= 16;
-    if (length < blob_length(bitlen, isdss, *ispub)) {
+    if (length < ossl_blob_length(bitlen, *isdss, *ispub)) {
         ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
         return NULL;
     }
-    if (!isdss)
-        return b2i_rsa(&p, bitlen, *ispub);
+    if (!*isdss)
+        key = ossl_b2i_RSA_after_header(&p, bitlen, *ispub);
 #ifndef OPENSSL_NO_DSA
     else
-        return b2i_dss(&p, bitlen, *ispub);
+        key = ossl_b2i_DSA_after_header(&p, bitlen, *ispub);
 #endif
 
-    ERR_raise(ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
-    return NULL;
+    if (key == NULL) {
+        ERR_raise(ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+        return NULL;
+    }
+
+    return key;
+}
+
+EVP_PKEY *ossl_b2i(const unsigned char **in, unsigned int length, int *ispub)
+{
+    int isdss = -1;
+    void *key = do_b2i_key(in, length, &isdss, ispub);
+
+    return evp_pkey_new0_key(key, isdss_to_evp_type(isdss));
 }
 
 EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub)
@@ -223,7 +324,8 @@ EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub)
     unsigned char hdr_buf[16], *buf = NULL;
     unsigned int bitlen, magic, length;
     int isdss;
-    EVP_PKEY *ret = NULL;
+    void *key = NULL;
+    EVP_PKEY *pkey = NULL;
 
     if (BIO_read(in, hdr_buf, 16) != 16) {
         ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
@@ -233,7 +335,7 @@ EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub)
     if (ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, ispub) <= 0)
         return NULL;
 
-    length = blob_length(bitlen, isdss, *ispub);
+    length = ossl_blob_length(bitlen, isdss, *ispub);
     if (length > BLOB_MAX_LENGTH) {
         ERR_raise(ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG);
         return NULL;
@@ -250,26 +352,28 @@ EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub)
     }
 
     if (!isdss)
-        ret = b2i_rsa(&p, bitlen, *ispub);
+        key = ossl_b2i_RSA_after_header(&p, bitlen, *ispub);
 #ifndef OPENSSL_NO_DSA
     else
-        ret = b2i_dss(&p, bitlen, *ispub);
+        key = ossl_b2i_DSA_after_header(&p, bitlen, *ispub);
 #endif
 
-    if (ret == NULL)
+    if (key == NULL) {
         ERR_raise(ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+        goto err;
+    }
 
+    pkey = evp_pkey_new0_key(key, isdss_to_evp_type(isdss));
  err:
     OPENSSL_free(buf);
-    return ret;
+    return pkey;
 }
 
 #ifndef OPENSSL_NO_DSA
-static EVP_PKEY *b2i_dss(const unsigned char **in,
-                         unsigned int bitlen, int ispub)
+DSA *ossl_b2i_DSA_after_header(const unsigned char **in, unsigned int bitlen,
+                               int ispub)
 {
     const unsigned char *p = *in;
-    EVP_PKEY *ret = NULL;
     DSA *dsa = NULL;
     BN_CTX *ctx = NULL;
     BIGNUM *pbn = NULL, *qbn = NULL, *gbn = NULL, *priv_key = NULL;
@@ -277,8 +381,7 @@ static EVP_PKEY *b2i_dss(const unsigned char **in,
     unsigned int nbyte = (bitlen + 7) >> 3;
 
     dsa = DSA_new();
-    ret = EVP_PKEY_new();
-    if (dsa == NULL || ret == NULL)
+    if (dsa == NULL)
         goto memerr;
     if (!read_lebn(&p, nbyte, &pbn))
         goto memerr;
@@ -319,11 +422,8 @@ static EVP_PKEY *b2i_dss(const unsigned char **in,
         goto memerr;
     pub_key = priv_key = NULL;
 
-    if (!EVP_PKEY_set1_DSA(ret, dsa))
-        goto memerr;
-    DSA_free(dsa);
     *in = p;
-    return ret;
+    return dsa;
 
  memerr:
     ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
@@ -333,17 +433,15 @@ static EVP_PKEY *b2i_dss(const unsigned char **in,
     BN_free(gbn);
     BN_free(pub_key);
     BN_free(priv_key);
-    EVP_PKEY_free(ret);
     BN_CTX_free(ctx);
     return NULL;
 }
 #endif
 
-static EVP_PKEY *b2i_rsa(const unsigned char **in,
-                         unsigned int bitlen, int ispub)
+RSA *ossl_b2i_RSA_after_header(const unsigned char **in, unsigned int bitlen,
+                               int ispub)
 {
     const unsigned char *pin = *in;
-    EVP_PKEY *ret = NULL;
     BIGNUM *e = NULL, *n = NULL, *d = NULL;
     BIGNUM *p = NULL, *q = NULL, *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
     RSA *rsa = NULL;
@@ -351,8 +449,7 @@ static EVP_PKEY *b2i_rsa(const unsigned char **in,
     unsigned int hnbyte = (bitlen + 15) >> 4;
 
     rsa = RSA_new();
-    ret = EVP_PKEY_new();
-    if (rsa == NULL || ret == NULL)
+    if (rsa == NULL)
         goto memerr;
     e = BN_new();
     if (e == NULL)
@@ -385,11 +482,8 @@ static EVP_PKEY *b2i_rsa(const unsigned char **in,
         goto memerr;
     n = e = d = NULL;
 
-    if (!EVP_PKEY_set1_RSA(ret, rsa))
-        goto memerr;
-    RSA_free(rsa);
     *in = pin;
-    return ret;
+    return rsa;
  memerr:
     ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
     BN_free(e);
@@ -401,7 +495,6 @@ static EVP_PKEY *b2i_rsa(const unsigned char **in,
     BN_free(iqmp);
     BN_free(d);
     RSA_free(rsa);
-    EVP_PKEY_free(ret);
     return NULL;
 }
 
@@ -463,23 +556,12 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
     unsigned char *p;
     unsigned int bitlen = 0, magic = 0, keyalg = 0;
     int outlen = -1, noinc = 0;
-    int pktype;
-#ifndef OPENSSL_NO_PROVIDER_CODE
-    EVP_PKEY *pkcopy = NULL;
-
-    if (evp_pkey_is_provided(pk)) {
-        if (!evp_pkey_copy_downgraded(&pkcopy, pk))
-            goto end;
-        pk = pkcopy;
-    }
-#endif
 
-    pktype = EVP_PKEY_id(pk);
-    if (pktype == EVP_PKEY_RSA) {
+    if (EVP_PKEY_is_a(pk, "RSA")) {
         bitlen = check_bitlen_rsa(EVP_PKEY_get0_RSA(pk), ispub, &magic);
         keyalg = MS_KEYALG_RSA_KEYX;
 #ifndef OPENSSL_NO_DSA
-    } else if (pktype == EVP_PKEY_DSA) {
+    } else if (EVP_PKEY_is_a(pk, "DSA")) {
         bitlen = check_bitlen_dsa(EVP_PKEY_get0_DSA(pk), ispub, &magic);
         keyalg = MS_KEYALG_DSS_SIGN;
 #endif
@@ -487,8 +569,8 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
     if (bitlen == 0) {
         goto end;
     }
-    outlen = 16 + blob_length(bitlen,
-                              keyalg == MS_KEYALG_DSS_SIGN ? 1 : 0, ispub);
+    outlen = 16
+        + ossl_blob_length(bitlen, keyalg == MS_KEYALG_DSS_SIGN ? 1 : 0, ispub);
     if (out == NULL)
         goto end;
     if (*out)
@@ -521,9 +603,6 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
     if (!noinc)
         *out += outlen;
  end:
-#ifndef OPENSSL_NO_PROVIDER_CODE
-    EVP_PKEY_free(pkcopy);
-#endif
     return outlen;
 }
 
@@ -730,14 +809,15 @@ static int derive_pvk_key(unsigned char *key,
 }
 #endif
 
-static EVP_PKEY *do_PVK_body(const unsigned char **in,
+static void *do_PVK_body_key(const unsigned char **in,
                              unsigned int saltlen, unsigned int keylen,
-                             pem_password_cb *cb, void *u)
+                             pem_password_cb *cb, void *u,
+                             int *isdss, int *ispub)
 {
-    EVP_PKEY *ret = NULL;
     const unsigned char *p = *in;
     unsigned char *enctmp = NULL;
     unsigned char keybuf[20];
+    void *key = NULL;
 
     EVP_CIPHER_CTX *cctx = EVP_CIPHER_CTX_new();
     if (saltlen) {
@@ -802,22 +882,23 @@ static EVP_PKEY *do_PVK_body(const unsigned char **in,
 #endif
     }
 
-    ret = b2i_PrivateKey(&p, keylen);
+    key = do_b2i_key(&p, keylen, isdss, ispub);
  err:
     EVP_CIPHER_CTX_free(cctx);
     if (enctmp != NULL) {
         OPENSSL_cleanse(keybuf, sizeof(keybuf));
         OPENSSL_free(enctmp);
     }
-    return ret;
+    return key;
 }
 
-EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+static void *do_PVK_key_bio(BIO *in, pem_password_cb *cb, void *u,
+                            int *isdss, int *ispub)
 {
     unsigned char pvk_hdr[24], *buf = NULL;
     const unsigned char *p;
     int buflen;
-    EVP_PKEY *ret = NULL;
+    void *key = NULL;
     unsigned int saltlen, keylen;
 
     if (BIO_read(in, pvk_hdr, 24) != 24) {
@@ -839,11 +920,38 @@ EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
         ERR_raise(ERR_LIB_PEM, PEM_R_PVK_DATA_TOO_SHORT);
         goto err;
     }
-    ret = do_PVK_body(&p, saltlen, keylen, cb, u);
+    key = do_PVK_body_key(&p, saltlen, keylen, cb, u, isdss, ispub);
 
  err:
     OPENSSL_clear_free(buf, buflen);
-    return ret;
+    return key;
+}
+
+#ifndef OPENSSL_NO_DSA
+DSA *b2i_DSA_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+{
+    int isdss = 1;
+    int ispub = 0;               /* PVK keys are always private */
+
+    return do_PVK_key_bio(in, cb, u, &isdss, &ispub);
+}
+#endif
+
+RSA *b2i_RSA_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+{
+    int isdss = 0;
+    int ispub = 0;               /* PVK keys are always private */
+
+    return do_PVK_key_bio(in, cb, u, &isdss, &ispub);
+}
+
+EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+{
+    int isdss = -1;
+    int ispub = -1;
+    void *key = do_PVK_key_bio(in, cb, u, &isdss, &ispub);
+
+    return evp_pkey_new0_key(key, isdss_to_evp_type(isdss));
 }
 
 static int i2b_PVK(unsigned char **out, const EVP_PKEY *pk, int enclevel,
index 4b02a00a85097ff443b28e57dd8d461b83946e4c..2a0e6424a5f55960245b4c488c144a4d7cee7429 100644 (file)
 # pragma once
 
 # include <openssl/pem.h>
+# include "crypto/types.h"
 
 /* Found in crypto/pem/pvkfmt.c */
+
+/* Maximum length of a blob after header */
+# define BLOB_MAX_LENGTH          102400
+
 int ossl_do_blob_header(const unsigned char **in, unsigned int length,
                         unsigned int *pmagic, unsigned int *pbitlen,
                         int *pisdss, int *pispub);
+unsigned int ossl_blob_length(unsigned bitlen, int isdss, int ispub);
 int ossl_do_PVK_header(const unsigned char **in, unsigned int length,
                        int skip_magic,
                        unsigned int *psaltlen, unsigned int *pkeylen);
+# ifndef OPENSSL_NO_DEPRECATED_3_0
+#  ifndef OPENSSL_NO_DSA
+DSA *ossl_b2i_DSA_after_header(const unsigned char **in, unsigned int bitlen,
+                               int ispub);
+#  endif
+RSA *ossl_b2i_RSA_after_header(const unsigned char **in, unsigned int bitlen,
+                               int ispub);
+# endif
 EVP_PKEY *ossl_b2i(const unsigned char **in, unsigned int length, int *ispub);
 EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub);
 
+# ifndef OPENSSL_NO_DEPRECATED_3_0
+#  ifndef OPENSSL_NO_DSA
+DSA *b2i_DSA_PVK_bio(BIO *in, pem_password_cb *cb, void *u);
+#  endif
+RSA *b2i_RSA_PVK_bio(BIO *in, pem_password_cb *cb, void *u);
+# endif
+
 #endif
index 08a56f14251c951ba315fe21a2986843515c02ef..24c4d0c585175b8210a25c4007b726ddcf4e2d51 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
index 16ca273a98a74304711c8e87fe2218798392a328..18f6d9ef4cfda759524fbd8d6b68fd998e6174f0 100644 (file)
 # define PEM_R_BIO_WRITE_FAILURE                          118
 # define PEM_R_CIPHER_IS_NULL                             127
 # define PEM_R_ERROR_CONVERTING_PRIVATE_KEY               115
+# define PEM_R_EXPECTING_DSS_KEY_BLOB                     131
 # define PEM_R_EXPECTING_PRIVATE_KEY_BLOB                 119
 # define PEM_R_EXPECTING_PUBLIC_KEY_BLOB                  120
+# define PEM_R_EXPECTING_RSA_KEY_BLOB                     132
 # define PEM_R_HEADER_TOO_LONG                            128
 # define PEM_R_INCONSISTENT_HEADER                        121
 # define PEM_R_KEYBLOB_HEADER_PARSE_ERROR                 122
index 5b8d9f6ef2f2d30614d13159086e4f4b77dc8e36..694e3c94a5cee8c14714626739b745de41e260aa 100644 (file)
@@ -12,7 +12,8 @@ $EC_GOAL=../../libimplementations.a
 
 SOURCE[$ENCODER_GOAL]=endecoder_common.c
 
-SOURCE[$DECODER_GOAL]=decode_der2key.c decode_pem2der.c decode_ms2key.c
+SOURCE[$DECODER_GOAL]=decode_der2key.c decode_pem2der.c \
+                      decode_msblob2key.c decode_pvk2key.c
 
 SOURCE[$ENCODER_GOAL]=encode_key2any.c encode_key2text.c encode_key2ms.c
 # encode_key2blob.c is only being included when EC is enabled, because we
diff --git a/providers/implementations/encode_decode/decode_ms2key.c b/providers/implementations/encode_decode/decode_ms2key.c
deleted file mode 100644 (file)
index f38717c..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License").  You may not use
- * this file except in compliance with the License.  You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-/*
- * low level APIs are deprecated for public use, but still ok for
- * internal use.
- */
-#include "internal/deprecated.h"
-
-#include <string.h>
-
-#include <openssl/core_dispatch.h>
-#include <openssl/core_names.h>
-#include <openssl/core_object.h>
-#include <openssl/crypto.h>
-#include <openssl/params.h>
-#include <openssl/pem.h>         /* For public PVK functions */
-#include <openssl/x509.h>
-#include "internal/passphrase.h"
-#include "crypto/pem.h"          /* For internal PVK and "blob" headers */
-#include "prov/bio.h"
-#include "prov/implementations.h"
-#include "endecoder_local.h"
-
-static EVP_PKEY *read_msblob(PROV_CTX *provctx, OSSL_CORE_BIO *cin, int *ispub)
-{
-    BIO *in = ossl_bio_new_from_core_bio(provctx, cin);
-    EVP_PKEY *pkey = ossl_b2i_bio(in, ispub);
-
-    BIO_free(in);
-    return pkey;
-}
-
-static EVP_PKEY *read_pvk(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
-                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
-{
-    BIO *in = NULL;
-    EVP_PKEY *pkey = NULL;
-    struct ossl_passphrase_data_st pwdata;
-
-    memset(&pwdata, 0, sizeof(pwdata));
-    if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
-        return NULL;
-
-    in = ossl_bio_new_from_core_bio(provctx, cin);
-    pkey = b2i_PVK_bio(in, ossl_pw_pem_password, &pwdata);
-    BIO_free(in);
-
-    return pkey;
-}
-
-static OSSL_FUNC_decoder_freectx_fn ms2key_freectx;
-static OSSL_FUNC_decoder_gettable_params_fn ms2key_gettable_params;
-static OSSL_FUNC_decoder_get_params_fn msblob2key_get_params;
-static OSSL_FUNC_decoder_get_params_fn pvk2key_get_params;
-static OSSL_FUNC_decoder_decode_fn msblob2key_decode;
-static OSSL_FUNC_decoder_decode_fn pvk2key_decode;
-static OSSL_FUNC_decoder_export_object_fn ms2key_export_object;
-
-typedef void *(extract_key_fn)(EVP_PKEY *);
-typedef void (free_key_fn)(void *);
-struct keytype_desc_st {
-    int type;                 /* EVP key type */
-    const char *name;         /* Keytype */
-    const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
-
-    /*
-     * These must be the correct EVP_PKEY_get1_{TYPE}() and {TYPE}_free()
-     * function for the key.
-     */
-    extract_key_fn *extract_key;
-    free_key_fn *free_key;
-};
-
-/*
- * Context used for DER to key decoding.
- */
-struct ms2key_ctx_st {
-    PROV_CTX *provctx;
-    const struct keytype_desc_st *desc;
-};
-
-static struct ms2key_ctx_st *
-ms2key_newctx(void *provctx, const struct keytype_desc_st *desc)
-{
-    struct ms2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
-
-    if (ctx != NULL) {
-        ctx->provctx = provctx;
-        ctx->desc = desc;
-    }
-    return ctx;
-}
-
-static void ms2key_freectx(void *vctx)
-{
-    struct ms2key_ctx_st *ctx = vctx;
-
-    OPENSSL_free(ctx);
-}
-
-static const OSSL_PARAM *ms2key_gettable_params(ossl_unused void *provctx)
-{
-    static const OSSL_PARAM gettables[] = {
-        { OSSL_DECODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
-        OSSL_PARAM_END,
-    };
-
-    return gettables;
-}
-
-static int msblob2key_get_params(OSSL_PARAM params[])
-{
-    OSSL_PARAM *p;
-
-    p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
-    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "MSBLOB"))
-        return 0;
-
-    return 1;
-}
-
-static int pvk2key_get_params(OSSL_PARAM params[])
-{
-    OSSL_PARAM *p;
-
-    p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
-    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "PVK"))
-        return 0;
-
-    return 1;
-}
-
-static int ms2key_post(struct ms2key_ctx_st *ctx, EVP_PKEY *pkey,
-                       OSSL_CALLBACK *data_cb, void *data_cbarg)
-{
-    void *key = NULL;
-    int ok = 0;
-
-    if (pkey != NULL) {
-        /*
-         * Tear out the low-level key pointer from the pkey,
-         * but only if it matches the expected key type.
-         *
-         * The check should be done with EVP_PKEY_is_a(), but
-         * as long as we still have #legacy internal keys, it's safer to
-         * use the type numbers in side the provider.
-         */
-        if (EVP_PKEY_id(pkey) == ctx->desc->type)
-            key = ctx->desc->extract_key(pkey);
-    }
-
-    if (key != NULL) {
-        OSSL_PARAM params[4];
-        int object_type = OSSL_OBJECT_PKEY;
-
-        params[0] =
-            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
-        params[1] =
-            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
-                                             (char *)ctx->desc->name, 0);
-        /* The address of the key becomes the octet string */
-        params[2] =
-            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
-                                              &key, sizeof(key));
-        params[3] = OSSL_PARAM_construct_end();
-
-        ok = data_cb(params, data_cbarg);
-    }
-    ctx->desc->free_key(key);
-
-    return ok;
-}
-
-static int msblob2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
-                             OSSL_CALLBACK *data_cb, void *data_cbarg,
-                             OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
-{
-    struct ms2key_ctx_st *ctx = vctx;
-    int ispub = -1;
-    EVP_PKEY *pkey = read_msblob(ctx->provctx, cin, &ispub);
-    int ok = 0;
-
-    if (selection == 0
-        || (ispub
-            ? (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0
-            : (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0))
-        ok = ms2key_post(ctx, pkey, data_cb, data_cbarg);
-
-    EVP_PKEY_free(pkey);
-    return ok;
-}
-
-static int pvk2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
-                          OSSL_CALLBACK *data_cb, void *data_cbarg,
-                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
-{
-    struct ms2key_ctx_st *ctx = vctx;
-    EVP_PKEY *pkey = read_pvk(ctx->provctx, cin, pw_cb, pw_cbarg);
-    int ok = 0;
-
-    if (selection == 0
-        || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
-        ok = ms2key_post(ctx, pkey, data_cb, data_cbarg);
-
-    EVP_PKEY_free(pkey);
-    return ok;
-}
-
-static int ms2key_export_object(void *vctx,
-                                const void *reference, size_t reference_sz,
-                                OSSL_CALLBACK *export_cb, void *export_cbarg)
-{
-    struct ms2key_ctx_st *ctx = vctx;
-    OSSL_FUNC_keymgmt_export_fn *export =
-        ossl_prov_get_keymgmt_export(ctx->desc->fns);
-    void *keydata;
-
-    if (reference_sz == sizeof(keydata) && export != NULL) {
-        /* The contents of the reference is the address to our object */
-        keydata = *(void **)reference;
-
-        return export(keydata, OSSL_KEYMGMT_SELECT_ALL,
-                      export_cb, export_cbarg);
-    }
-    return 0;
-}
-
-#define IMPLEMENT_TYPE(KEYTYPEstr, KEYTYPE, keytype, extract, free)     \
-    static const struct keytype_desc_st keytype##_desc;                 \
-    static OSSL_FUNC_decoder_newctx_fn ms2##keytype##_newctx;           \
-    static void *ms2##keytype##_newctx(void *provctx)                   \
-    {                                                                   \
-        return ms2key_newctx(provctx, &keytype##_desc);                 \
-    }                                                                   \
-    static const struct keytype_desc_st keytype##_desc =                \
-        { EVP_PKEY_##KEYTYPE, KEYTYPEstr,                               \
-          ossl_##keytype##_keymgmt_functions,                           \
-          (extract_key_fn *)extract,                                    \
-          (free_key_fn *)free }
-
-#define IMPLEMENT_MS(mstype, keytype)                                   \
-    const OSSL_DISPATCH                                                 \
-        ossl_##mstype##_to_##keytype##_decoder_functions[] = {          \
-        { OSSL_FUNC_DECODER_NEWCTX,                                     \
-          (void (*)(void))ms2##keytype##_newctx },                      \
-        { OSSL_FUNC_DECODER_FREECTX,                                    \
-          (void (*)(void))ms2key_freectx },                             \
-        { OSSL_FUNC_DECODER_GETTABLE_PARAMS,                            \
-          (void (*)(void))ms2key_gettable_params },                     \
-        { OSSL_FUNC_DECODER_GET_PARAMS,                                 \
-          (void (*)(void))mstype##2key_get_params },                    \
-        { OSSL_FUNC_DECODER_DECODE,                                     \
-          (void (*)(void))mstype##2key_decode },                        \
-        { OSSL_FUNC_DECODER_EXPORT_OBJECT,                              \
-          (void (*)(void))ms2key_export_object },                       \
-        { 0, NULL }                                                     \
-    }
-
-#ifndef OPENSSL_NO_DSA
-IMPLEMENT_TYPE("DSA", DSA, dsa, EVP_PKEY_get1_DSA, DSA_free);
-IMPLEMENT_MS(msblob, dsa);
-IMPLEMENT_MS(pvk, dsa);
-#endif
-IMPLEMENT_TYPE("RSA", RSA, rsa, EVP_PKEY_get1_RSA, RSA_free);
-IMPLEMENT_MS(msblob, rsa);
-IMPLEMENT_MS(pvk, rsa);
diff --git a/providers/implementations/encode_decode/decode_msblob2key.c b/providers/implementations/encode_decode/decode_msblob2key.c
new file mode 100644 (file)
index 0000000..f47d06f
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/pem.h>         /* For public PVK functions */
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include "internal/passphrase.h"
+#include "crypto/pem.h"          /* For internal PVK and "blob" headers */
+#include "crypto/rsa.h"
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+struct msblob2key_ctx_st;            /* Forward declaration */
+typedef void *b2i_of_void_fn(const unsigned char **in, unsigned int bitlen,
+                             int ispub);
+typedef void adjust_key_fn(void *, struct msblob2key_ctx_st *ctx);
+typedef void free_key_fn(void *);
+struct keytype_desc_st {
+    int type;                 /* EVP key type */
+    const char *name;         /* Keytype */
+    const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
+
+    b2i_of_void_fn *read_private_key;
+    b2i_of_void_fn *read_public_key;
+    adjust_key_fn *adjust_key;
+    free_key_fn *free_key;
+};
+
+static OSSL_FUNC_decoder_freectx_fn msblob2key_freectx;
+static OSSL_FUNC_decoder_gettable_params_fn msblob2key_gettable_params;
+static OSSL_FUNC_decoder_get_params_fn msblob2key_get_params;
+static OSSL_FUNC_decoder_decode_fn msblob2key_decode;
+static OSSL_FUNC_decoder_export_object_fn msblob2key_export_object;
+
+/*
+ * Context used for DER to key decoding.
+ */
+struct msblob2key_ctx_st {
+    PROV_CTX *provctx;
+    const struct keytype_desc_st *desc;
+};
+
+static struct msblob2key_ctx_st *
+msblob2key_newctx(void *provctx, const struct keytype_desc_st *desc)
+{
+    struct msblob2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL) {
+        ctx->provctx = provctx;
+        ctx->desc = desc;
+    }
+    return ctx;
+}
+
+static void msblob2key_freectx(void *vctx)
+{
+    struct msblob2key_ctx_st *ctx = vctx;
+
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *msblob2key_gettable_params(ossl_unused void *provctx)
+{
+    static const OSSL_PARAM gettables[] = {
+        { OSSL_DECODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    return gettables;
+}
+
+static int msblob2key_get_params(OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "MSBLOB"))
+        return 0;
+
+    return 1;
+}
+
+static int msblob2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+                             OSSL_CALLBACK *data_cb, void *data_cbarg,
+                             OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    struct msblob2key_ctx_st *ctx = vctx;
+    BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+    const unsigned char *p;
+    unsigned char hdr_buf[16], *buf = NULL;
+    unsigned int bitlen, magic, length;
+    int isdss = -1;
+    int ispub = -1;
+    void *key = NULL;
+    int ok = 0;
+
+    if (BIO_read(in, hdr_buf, 16) != 16) {
+        ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
+        goto err;
+    }
+    p = hdr_buf;
+    if (ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, &ispub) <= 0)
+        goto err;
+
+    if ((isdss && ctx->desc->type != EVP_PKEY_DSA)
+        || (!isdss && ctx->desc->type != EVP_PKEY_RSA))
+        goto err;
+
+    length = ossl_blob_length(bitlen, isdss, ispub);
+    if (length > BLOB_MAX_LENGTH) {
+        ERR_raise(ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG);
+        goto err;
+    }
+    buf = OPENSSL_malloc(length);
+    if (buf == NULL) {
+        ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    p = buf;
+    if (BIO_read(in, buf, length) != (int)length) {
+        ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
+        goto err;
+    }
+
+    if ((selection == 0
+         || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        && !ispub
+        && ctx->desc->read_private_key != NULL) {
+        struct ossl_passphrase_data_st pwdata;
+
+        memset(&pwdata, 0, sizeof(pwdata));
+        if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
+            goto err;
+        p = buf;
+        key = ctx->desc->read_private_key(&p, bitlen, ispub);
+        if (selection != 0 && key == NULL)
+            goto next;
+    }
+    if (key == NULL && (selection == 0
+         || (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+        && ispub
+        && ctx->desc->read_public_key != NULL) {
+        p = buf;
+        key = ctx->desc->read_public_key(&p, bitlen, ispub);
+        if (selection != 0 && key == NULL)
+            goto next;
+    }
+
+    if (key != NULL && ctx->desc->adjust_key != NULL)
+        ctx->desc->adjust_key(key, ctx);
+
+ next:
+    /*
+     * We free resources here so it's not held up during the callback, because
+     * we know the process is recursive and the allocated chunks of memory
+     * add up.
+     */
+    OPENSSL_free(buf);
+    BIO_free(in);
+    buf = NULL;
+    in = NULL;
+
+    if (key != NULL) {
+        OSSL_PARAM params[4];
+        int object_type = OSSL_OBJECT_PKEY;
+
+        params[0] =
+            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+        params[1] =
+            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+                                             (char *)ctx->desc->name, 0);
+        /* The address of the key becomes the octet string */
+        params[2] =
+            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
+                                              &key, sizeof(key));
+        params[3] = OSSL_PARAM_construct_end();
+
+        ok = data_cb(params, data_cbarg);
+    }
+
+ err:
+    BIO_free(in);
+    OPENSSL_free(buf);
+    ctx->desc->free_key(key);
+
+    return ok;
+}
+
+static int
+msblob2key_export_object(void *vctx,
+                         const void *reference, size_t reference_sz,
+                         OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+    struct msblob2key_ctx_st *ctx = vctx;
+    OSSL_FUNC_keymgmt_export_fn *export =
+        ossl_prov_get_keymgmt_export(ctx->desc->fns);
+    void *keydata;
+
+    if (reference_sz == sizeof(keydata) && export != NULL) {
+        /* The contents of the reference is the address to our object */
+        keydata = *(void **)reference;
+
+        return export(keydata, OSSL_KEYMGMT_SELECT_ALL,
+                      export_cb, export_cbarg);
+    }
+    return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define dsa_decode_private_key  (b2i_of_void_fn *)ossl_b2i_DSA_after_header
+#define dsa_decode_public_key   (b2i_of_void_fn *)ossl_b2i_DSA_after_header
+#define dsa_adjust              NULL
+#define dsa_free                (void (*)(void *))DSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define rsa_decode_private_key  (b2i_of_void_fn *)ossl_b2i_RSA_after_header
+#define rsa_decode_public_key   (b2i_of_void_fn *)ossl_b2i_RSA_after_header
+
+static void rsa_adjust(void *key, struct msblob2key_ctx_st *ctx)
+{
+    ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+#define rsa_free                        (void (*)(void *))RSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define IMPLEMENT_MSBLOB(KEYTYPE, keytype)                              \
+    static const struct keytype_desc_st mstype##2##keytype##_desc = {   \
+        EVP_PKEY_##KEYTYPE, #KEYTYPE,                                   \
+        ossl_##keytype##_keymgmt_functions,                             \
+        keytype##_decode_private_key,                                   \
+        keytype##_decode_public_key,                                    \
+        keytype##_adjust,                                               \
+        keytype##_free                                                  \
+    };                                                                  \
+    static OSSL_FUNC_decoder_newctx_fn msblob2##keytype##_newctx;       \
+    static void *msblob2##keytype##_newctx(void *provctx)               \
+    {                                                                   \
+        return msblob2key_newctx(provctx, &mstype##2##keytype##_desc);  \
+    }                                                                   \
+    const OSSL_DISPATCH                                                 \
+    ossl_msblob_to_##keytype##_decoder_functions[] = {                  \
+        { OSSL_FUNC_DECODER_NEWCTX,                                     \
+          (void (*)(void))msblob2##keytype##_newctx },                  \
+        { OSSL_FUNC_DECODER_FREECTX,                                    \
+          (void (*)(void))msblob2key_freectx },                         \
+        { OSSL_FUNC_DECODER_GETTABLE_PARAMS,                            \
+          (void (*)(void))msblob2key_gettable_params },                 \
+        { OSSL_FUNC_DECODER_GET_PARAMS,                                 \
+          (void (*)(void))msblob2key_get_params },                      \
+        { OSSL_FUNC_DECODER_DECODE,                                     \
+          (void (*)(void))msblob2key_decode },                          \
+        { OSSL_FUNC_DECODER_EXPORT_OBJECT,                              \
+          (void (*)(void))msblob2key_export_object },                   \
+        { 0, NULL }                                                     \
+    }
+
+#ifndef OPENSSL_NO_DSA
+IMPLEMENT_MSBLOB(DSA, dsa);
+#endif
+IMPLEMENT_MSBLOB(RSA, rsa);
diff --git a/providers/implementations/encode_decode/decode_pvk2key.c b/providers/implementations/encode_decode/decode_pvk2key.c
new file mode 100644 (file)
index 0000000..3f2c80a
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/pem.h>         /* For public PVK functions */
+#include <openssl/x509.h>
+#include "internal/passphrase.h"
+#include "crypto/pem.h"          /* For internal PVK and "blob" headers */
+#include "crypto/rsa.h"
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+struct pvk2key_ctx_st;            /* Forward declaration */
+typedef int check_key_fn(void *, struct pvk2key_ctx_st *ctx);
+typedef void adjust_key_fn(void *, struct pvk2key_ctx_st *ctx);
+typedef void *b2i_PVK_of_bio_pw_fn(BIO *in, pem_password_cb *cb, void *u);
+typedef void free_key_fn(void *);
+struct keytype_desc_st {
+    int type;                 /* EVP key type */
+    const char *name;         /* Keytype */
+    const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
+
+    b2i_PVK_of_bio_pw_fn *read_private_key;
+    adjust_key_fn *adjust_key;
+    free_key_fn *free_key;
+};
+
+static OSSL_FUNC_decoder_freectx_fn pvk2key_freectx;
+static OSSL_FUNC_decoder_gettable_params_fn pvk2key_gettable_params;
+static OSSL_FUNC_decoder_get_params_fn pvk2key_get_params;
+static OSSL_FUNC_decoder_decode_fn pvk2key_decode;
+static OSSL_FUNC_decoder_export_object_fn pvk2key_export_object;
+
+/*
+ * Context used for DER to key decoding.
+ */
+struct pvk2key_ctx_st {
+    PROV_CTX *provctx;
+    const struct keytype_desc_st *desc;
+};
+
+static struct pvk2key_ctx_st *
+pvk2key_newctx(void *provctx, const struct keytype_desc_st *desc)
+{
+    struct pvk2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL) {
+        ctx->provctx = provctx;
+        ctx->desc = desc;
+    }
+    return ctx;
+}
+
+static void pvk2key_freectx(void *vctx)
+{
+    struct pvk2key_ctx_st *ctx = vctx;
+
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *pvk2key_gettable_params(ossl_unused void *provctx)
+{
+    static const OSSL_PARAM gettables[] = {
+        { OSSL_DECODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    return gettables;
+}
+
+static int pvk2key_get_params(OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "PVK"))
+        return 0;
+
+    return 1;
+}
+
+static int pvk2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+                         OSSL_CALLBACK *data_cb, void *data_cbarg,
+                         OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    struct pvk2key_ctx_st *ctx = vctx;
+    BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+    void *key = NULL;
+    int ok = 0;
+
+    if ((selection == 0
+         || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        && ctx->desc->read_private_key != NULL) {
+        struct ossl_passphrase_data_st pwdata;
+
+        memset(&pwdata, 0, sizeof(pwdata));
+        if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
+            goto end;
+        key = ctx->desc->read_private_key(in, ossl_pw_pem_password, &pwdata);
+        if (selection != 0 && key == NULL)
+            goto next;
+    }
+
+    if (key != NULL && ctx->desc->adjust_key != NULL)
+        ctx->desc->adjust_key(key, ctx);
+
+ next:
+    /*
+     * We free resources here so it's not held up during the callback, because
+     * we know the process is recursive and the allocated chunks of memory
+     * add up.
+     */
+    BIO_free(in);
+    in = NULL;
+
+    if (key != NULL) {
+        OSSL_PARAM params[4];
+        int object_type = OSSL_OBJECT_PKEY;
+
+        params[0] =
+            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+        params[1] =
+            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+                                             (char *)ctx->desc->name, 0);
+        /* The address of the key becomes the octet string */
+        params[2] =
+            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
+                                              &key, sizeof(key));
+        params[3] = OSSL_PARAM_construct_end();
+
+        ok = data_cb(params, data_cbarg);
+    }
+
+ end:
+    BIO_free(in);
+    ctx->desc->free_key(key);
+
+    return ok;
+}
+
+static int pvk2key_export_object(void *vctx,
+                                const void *reference, size_t reference_sz,
+                                OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+    struct pvk2key_ctx_st *ctx = vctx;
+    OSSL_FUNC_keymgmt_export_fn *export =
+        ossl_prov_get_keymgmt_export(ctx->desc->fns);
+    void *keydata;
+
+    if (reference_sz == sizeof(keydata) && export != NULL) {
+        /* The contents of the reference is the address to our object */
+        keydata = *(void **)reference;
+
+        return export(keydata, OSSL_KEYMGMT_SELECT_ALL,
+                      export_cb, export_cbarg);
+    }
+    return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define dsa_private_key_bio     (b2i_PVK_of_bio_pw_fn *)b2i_DSA_PVK_bio
+#define dsa_adjust              NULL
+#define dsa_free                (void (*)(void *))DSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define rsa_private_key_bio     (b2i_PVK_of_bio_pw_fn *)b2i_RSA_PVK_bio
+
+static void rsa_adjust(void *key, struct pvk2key_ctx_st *ctx)
+{
+    ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+#define rsa_free                (void (*)(void *))RSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define IMPLEMENT_MS(KEYTYPE, keytype)                                  \
+    static const struct keytype_desc_st                                 \
+    pvk2##keytype##_desc = {                                            \
+        EVP_PKEY_##KEYTYPE, #KEYTYPE,                                   \
+        ossl_##keytype##_keymgmt_functions,                             \
+        keytype##_private_key_bio,                                      \
+        keytype##_adjust,                                               \
+        keytype##_free                                                  \
+    };                                                                  \
+    static OSSL_FUNC_decoder_newctx_fn pvk2##keytype##_newctx;          \
+    static void *pvk2##keytype##_newctx(void *provctx)                  \
+    {                                                                   \
+        return pvk2key_newctx(provctx, &pvk2##keytype##_desc);          \
+    }                                                                   \
+    const OSSL_DISPATCH                                                 \
+    ossl_##pvk_to_##keytype##_decoder_functions[] = {                   \
+        { OSSL_FUNC_DECODER_NEWCTX,                                     \
+          (void (*)(void))pvk2##keytype##_newctx },                     \
+        { OSSL_FUNC_DECODER_FREECTX,                                    \
+          (void (*)(void))pvk2key_freectx },                            \
+        { OSSL_FUNC_DECODER_GETTABLE_PARAMS,                            \
+          (void (*)(void))pvk2key_gettable_params },                    \
+        { OSSL_FUNC_DECODER_GET_PARAMS,                                 \
+          (void (*)(void))pvk2key_get_params },                         \
+        { OSSL_FUNC_DECODER_DECODE,                                     \
+          (void (*)(void))pvk2key_decode },                             \
+        { OSSL_FUNC_DECODER_EXPORT_OBJECT,                              \
+          (void (*)(void))pvk2key_export_object },                      \
+        { 0, NULL }                                                     \
+    }
+
+#ifndef OPENSSL_NO_DSA
+IMPLEMENT_MS(DSA, dsa);
+#endif
+IMPLEMENT_MS(RSA, rsa);