Issue #17134: Finalize interface to Windows' certificate store. Cert and
CRL enumeration are now two functions. enum_certificates() also returns
purpose flags as set of OIDs.
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 99386c0..7685ade 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -372,21 +372,45 @@
 
    .. versionadded:: 3.4
 
-.. function:: enum_cert_store(store_name, cert_type='certificate')
+.. function:: enum_certificates(store_name)
 
    Retrieve certificates from Windows' system cert store. *store_name* may be
    one of ``CA``, ``ROOT`` or ``MY``. Windows may provide additional cert
-   stores, too. *cert_type* is either ``certificate`` for X.509 certificates
-   or ``crl`` for X.509 certificate revocation lists.
+   stores, too.
 
-   The function returns a list of (bytes, encoding_type) tuples. The
-   encoding_type flag can be interpreted with :const:`X509_ASN_ENCODING` or
-   :const:`PKCS_7_ASN_ENCODING`.
+   The function returns a list of (cert_bytes, encoding_type, trust) tuples.
+   The encoding_type specifies the encoding of cert_bytes. It is either
+   :const:`x509_asn` for X.509 ASN.1 data or :const:`pkcs_7_asn` for
+   PKCS#7 ASN.1 data. Trust specifies the purpose of the certificate as a set
+   of OIDS or exactly ``True`` if the certificate is trustworthy for all
+   purposes.
+
+   Example::
+
+      >>> ssl.enum_certificates("CA")
+      [(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}),
+       (b'data...', 'x509_asn', True)]
 
    Availability: Windows.
 
    .. versionadded:: 3.4
 
+.. function:: enum_crls(store_name)
+
+   Retrieve CRLs from Windows' system cert store. *store_name* may be
+   one of ``CA``, ``ROOT`` or ``MY``. Windows may provide additional cert
+   stores, too.
+
+   The function returns a list of (cert_bytes, encoding_type, trust) tuples.
+   The encoding_type specifies the encoding of cert_bytes. It is either
+   :const:`x509_asn` for X.509 ASN.1 data or :const:`pkcs_7_asn` for
+   PKCS#7 ASN.1 data.
+
+   Availability: Windows.
+
+   .. versionadded:: 3.4
+
+
 Constants
 ^^^^^^^^^
 
@@ -657,15 +681,6 @@
 
    .. versionadded:: 3.4
 
-.. data:: X509_ASN_ENCODING
-          PKCS_7_ASN_ENCODING
-
-   Encoding flags for :func:`enum_cert_store`.
-
-   Availability: Windows.
-
-   .. versionadded:: 3.4
-
 
 SSL Sockets
 -----------
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 7ce097f..d4c7bad 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -144,7 +144,7 @@
     _PROTOCOL_NAMES[PROTOCOL_TLSv1_2] = "TLSv1.2"
 
 if sys.platform == "win32":
-    from _ssl import enum_cert_store, X509_ASN_ENCODING, PKCS_7_ASN_ENCODING
+    from _ssl import enum_certificates, enum_crls
 
 from socket import getnameinfo as _getnameinfo
 from socket import socket, AF_INET, SOCK_STREAM, create_connection
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 9996ff1..2190d9f 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -528,29 +528,44 @@
             self.assertEqual(paths.cafile, CERTFILE)
             self.assertEqual(paths.capath, CAPATH)
 
+    @unittest.skipUnless(sys.platform == "win32", "Windows specific")
+    def test_enum_certificates(self):
+        self.assertTrue(ssl.enum_certificates("CA"))
+        self.assertTrue(ssl.enum_certificates("ROOT"))
+
+        self.assertRaises(TypeError, ssl.enum_certificates)
+        self.assertRaises(WindowsError, ssl.enum_certificates, "")
+
+        names = set()
+        ca = ssl.enum_certificates("CA")
+        self.assertIsInstance(ca, list)
+        for element in ca:
+            self.assertIsInstance(element, tuple)
+            self.assertEqual(len(element), 3)
+            cert, enc, trust = element
+            self.assertIsInstance(cert, bytes)
+            self.assertIn(enc, {"x509_asn", "pkcs_7_asn"})
+            self.assertIsInstance(trust, (set, bool))
+            if isinstance(trust, set):
+                names.update(trust)
+
+        serverAuth = "1.3.6.1.5.5.7.3.1"
+        self.assertIn(serverAuth, names)
 
     @unittest.skipUnless(sys.platform == "win32", "Windows specific")
-    def test_enum_cert_store(self):
-        self.assertEqual(ssl.X509_ASN_ENCODING, 1)
-        self.assertEqual(ssl.PKCS_7_ASN_ENCODING, 0x00010000)
+    def test_enum_crls(self):
+        self.assertTrue(ssl.enum_crls("CA"))
+        self.assertRaises(TypeError, ssl.enum_crls)
+        self.assertRaises(WindowsError, ssl.enum_crls, "")
 
-        self.assertEqual(ssl.enum_cert_store("CA"),
-            ssl.enum_cert_store("CA", "certificate"))
-        ssl.enum_cert_store("CA", "crl")
-        self.assertEqual(ssl.enum_cert_store("ROOT"),
-            ssl.enum_cert_store("ROOT", "certificate"))
-        ssl.enum_cert_store("ROOT", "crl")
+        crls = ssl.enum_crls("CA")
+        self.assertIsInstance(crls, list)
+        for element in crls:
+            self.assertIsInstance(element, tuple)
+            self.assertEqual(len(element), 2)
+            self.assertIsInstance(element[0], bytes)
+            self.assertIn(element[1], {"x509_asn", "pkcs_7_asn"})
 
-        self.assertRaises(TypeError, ssl.enum_cert_store)
-        self.assertRaises(WindowsError, ssl.enum_cert_store, "")
-        self.assertRaises(ValueError, ssl.enum_cert_store, "CA", "wrong")
-
-        ca = ssl.enum_cert_store("CA")
-        self.assertIsInstance(ca, list)
-        self.assertIsInstance(ca[0], tuple)
-        self.assertEqual(len(ca[0]), 2)
-        self.assertIsInstance(ca[0][0], bytes)
-        self.assertIsInstance(ca[0][1], int)
 
     def test_asn1object(self):
         expected = (129, 'serverAuth', 'TLS Web Server Authentication',
diff --git a/Misc/NEWS b/Misc/NEWS
index 295b456..b5916f4 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -59,6 +59,10 @@
 Library
 -------
 
+- Issue #17134: Finalize interface to Windows' certificate store. Cert and
+  CRL enumeration are now two functions. enum_certificates() also returns
+  purpose flags as set of OIDs.
+
 - Issue #19555: Restore sysconfig.get_config_var('SO'), with a
   DeprecationWarning pointing people at $EXT_SUFFIX.
 
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 69b1eef..4768e44 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -3422,130 +3422,258 @@
     return result;
 }
 
-
 #ifdef _MSC_VER
-PyDoc_STRVAR(PySSL_enum_cert_store_doc,
-"enum_cert_store(store_name, cert_type='certificate') -> []\n\
+
+static PyObject*
+certEncodingType(DWORD encodingType)
+{
+    static PyObject *x509_asn = NULL;
+    static PyObject *pkcs_7_asn = NULL;
+
+    if (x509_asn == NULL) {
+        x509_asn = PyUnicode_InternFromString("x509_asn");
+        if (x509_asn == NULL)
+            return NULL;
+    }
+    if (pkcs_7_asn == NULL) {
+        pkcs_7_asn = PyUnicode_InternFromString("pkcs_7_asn");
+        if (pkcs_7_asn == NULL)
+            return NULL;
+    }
+    switch(encodingType) {
+    case X509_ASN_ENCODING:
+        Py_INCREF(x509_asn);
+        return x509_asn;
+    case PKCS_7_ASN_ENCODING:
+        Py_INCREF(pkcs_7_asn);
+        return pkcs_7_asn;
+    default:
+        return PyLong_FromLong(encodingType);
+    }
+}
+
+static PyObject*
+parseKeyUsage(PCCERT_CONTEXT pCertCtx, DWORD flags)
+{
+    CERT_ENHKEY_USAGE *usage;
+    DWORD size, error, i;
+    PyObject *retval;
+
+    if (!CertGetEnhancedKeyUsage(pCertCtx, flags, NULL, &size)) {
+        error = GetLastError();
+        if (error == CRYPT_E_NOT_FOUND) {
+            Py_RETURN_TRUE;
+        }
+        return PyErr_SetFromWindowsErr(error);
+    }
+
+    usage = (CERT_ENHKEY_USAGE*)PyMem_Malloc(size);
+    if (usage == NULL) {
+        return PyErr_NoMemory();
+    }
+
+    /* Now get the actual enhanced usage property */
+    if (!CertGetEnhancedKeyUsage(pCertCtx, flags, usage, &size)) {
+        PyMem_Free(usage);
+        error = GetLastError();
+        if (error == CRYPT_E_NOT_FOUND) {
+            Py_RETURN_TRUE;
+        }
+        return PyErr_SetFromWindowsErr(error);
+    }
+    retval = PySet_New(NULL);
+    if (retval == NULL) {
+        goto error;
+    }
+    for (i = 0; i < usage->cUsageIdentifier; ++i) {
+        if (usage->rgpszUsageIdentifier[i]) {
+            PyObject *oid;
+            int err;
+            oid = PyUnicode_FromString(usage->rgpszUsageIdentifier[i]);
+            if (oid == NULL) {
+                Py_CLEAR(retval);
+                goto error;
+            }
+            err = PySet_Add(retval, oid);
+            Py_DECREF(oid);
+            if (err == -1) {
+                Py_CLEAR(retval);
+                goto error;
+            }
+        }
+    }
+  error:
+    PyMem_Free(usage);
+    return retval;
+}
+
+PyDoc_STRVAR(PySSL_enum_certificates_doc,
+"enum_certificates(store_name) -> []\n\
 \n\
 Retrieve certificates from Windows' cert store. store_name may be one of\n\
 'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\
-cert_type must be either 'certificate' or 'crl'.\n\
-The function returns a list of (bytes, encoding_type) tuples. The\n\
+The function returns a list of (bytes, encoding_type, trust) tuples. The\n\
 encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\
-PKCS_7_ASN_ENCODING.");
+PKCS_7_ASN_ENCODING. The trust setting is either a set of OIDs or the\n\
+boolean True.");
 
 static PyObject *
-PySSL_enum_cert_store(PyObject *self, PyObject *args, PyObject *kwds)
+PySSL_enum_certificates(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    char *kwlist[] = {"store_name", "cert_type", NULL};
+    char *kwlist[] = {"store_name", NULL};
     char *store_name;
-    char *cert_type = "certificate";
     HCERTSTORE hStore = NULL;
+    PCCERT_CONTEXT pCertCtx = NULL;
+    PyObject *keyusage = NULL, *cert = NULL, *enc = NULL, *tup = NULL;
     PyObject *result = NULL;
-    PyObject *tup = NULL, *cert = NULL, *enc = NULL;
-    int ok = 1;
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_cert_store",
-                                     kwlist, &store_name, &cert_type)) {
-            return NULL;
-    }
-
-    if ((strcmp(cert_type, "certificate") != 0) &&
-        (strcmp(cert_type, "crl") != 0)) {
-        return PyErr_Format(PyExc_ValueError,
-                            "cert_type must be 'certificate' or 'crl', "
-                            "not %.100s", cert_type);
-    }
-
-    if ((result = PyList_New(0)) == NULL) {
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_certificates",
+                                     kwlist, &store_name)) {
         return NULL;
     }
-
-    if ((hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name)) == NULL) {
+    result = PyList_New(0);
+    if (result == NULL) {
+        return NULL;
+    }
+    hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name);
+    if (hStore == NULL) {
         Py_DECREF(result);
         return PyErr_SetFromWindowsErr(GetLastError());
     }
 
-    if (strcmp(cert_type, "certificate") == 0) {
-        PCCERT_CONTEXT pCertCtx = NULL;
-        while (pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) {
-            cert = PyBytes_FromStringAndSize((const char*)pCertCtx->pbCertEncoded,
-                                             pCertCtx->cbCertEncoded);
-            if (!cert) {
-                ok = 0;
-                break;
-            }
-            if ((enc = PyLong_FromLong(pCertCtx->dwCertEncodingType)) == NULL) {
-                ok = 0;
-                break;
-            }
-            if ((tup = PyTuple_New(2)) == NULL) {
-                ok = 0;
-                break;
-            }
-            PyTuple_SET_ITEM(tup, 0, cert); cert = NULL;
-            PyTuple_SET_ITEM(tup, 1, enc); enc = NULL;
-
-            if (PyList_Append(result, tup) < 0) {
-                ok = 0;
-                break;
-            }
-            Py_CLEAR(tup);
+    while (pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) {
+        cert = PyBytes_FromStringAndSize((const char*)pCertCtx->pbCertEncoded,
+                                            pCertCtx->cbCertEncoded);
+        if (!cert) {
+            Py_CLEAR(result);
+            break;
         }
-        if (pCertCtx) {
-            /* loop ended with an error, need to clean up context manually */
-            CertFreeCertificateContext(pCertCtx);
+        if ((enc = certEncodingType(pCertCtx->dwCertEncodingType)) == NULL) {
+            Py_CLEAR(result);
+            break;
         }
-    } else {
-        PCCRL_CONTEXT pCrlCtx = NULL;
-        while (pCrlCtx = CertEnumCRLsInStore(hStore, pCrlCtx)) {
-            cert = PyBytes_FromStringAndSize((const char*)pCrlCtx->pbCrlEncoded,
-                                             pCrlCtx->cbCrlEncoded);
-            if (!cert) {
-                ok = 0;
-                break;
-            }
-            if ((enc = PyLong_FromLong(pCrlCtx->dwCertEncodingType)) == NULL) {
-                ok = 0;
-                break;
-            }
-            if ((tup = PyTuple_New(2)) == NULL) {
-                ok = 0;
-                break;
-            }
-            PyTuple_SET_ITEM(tup, 0, cert); cert = NULL;
-            PyTuple_SET_ITEM(tup, 1, enc); enc = NULL;
-
-            if (PyList_Append(result, tup) < 0) {
-                ok = 0;
-                break;
-            }
-            Py_CLEAR(tup);
+        keyusage = parseKeyUsage(pCertCtx, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG);
+        if (keyusage == Py_True) {
+            Py_DECREF(keyusage);
+            keyusage = parseKeyUsage(pCertCtx, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG);
         }
-        if (pCrlCtx) {
-            /* loop ended with an error, need to clean up context manually */
-            CertFreeCRLContext(pCrlCtx);
+        if (keyusage == NULL) {
+            Py_CLEAR(result);
+            break;
         }
+        if ((tup = PyTuple_New(3)) == NULL) {
+            Py_CLEAR(result);
+            break;
+        }
+        PyTuple_SET_ITEM(tup, 0, cert);
+        cert = NULL;
+        PyTuple_SET_ITEM(tup, 1, enc);
+        enc = NULL;
+        PyTuple_SET_ITEM(tup, 2, keyusage);
+        keyusage = NULL;
+        if (PyList_Append(result, tup) < 0) {
+            Py_CLEAR(result);
+            break;
+        }
+        Py_CLEAR(tup);
+    }
+    if (pCertCtx) {
+        /* loop ended with an error, need to clean up context manually */
+        CertFreeCertificateContext(pCertCtx);
     }
 
     /* In error cases cert, enc and tup may not be NULL */
     Py_XDECREF(cert);
     Py_XDECREF(enc);
+    Py_XDECREF(keyusage);
     Py_XDECREF(tup);
 
     if (!CertCloseStore(hStore, 0)) {
         /* This error case might shadow another exception.*/
+        Py_XDECREF(result);
+        return PyErr_SetFromWindowsErr(GetLastError());
+    }
+    return result;
+}
+
+PyDoc_STRVAR(PySSL_enum_crls_doc,
+"enum_crls(store_name) -> []\n\
+\n\
+Retrieve CRLs from Windows' cert store. store_name may be one of\n\
+'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\
+The function returns a list of (bytes, encoding_type) tuples. The\n\
+encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\
+PKCS_7_ASN_ENCODING.");
+
+static PyObject *
+PySSL_enum_crls(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    char *kwlist[] = {"store_name", NULL};
+    char *store_name;
+    HCERTSTORE hStore = NULL;
+    PCCRL_CONTEXT pCrlCtx = NULL;
+    PyObject *crl = NULL, *enc = NULL, *tup = NULL;
+    PyObject *result = NULL;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_crls",
+                                     kwlist, &store_name)) {
+        return NULL;
+    }
+    result = PyList_New(0);
+    if (result == NULL) {
+        return NULL;
+    }
+    hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name);
+    if (hStore == NULL) {
         Py_DECREF(result);
         return PyErr_SetFromWindowsErr(GetLastError());
     }
-    if (ok) {
-        return result;
-    } else {
-        Py_DECREF(result);
-        return NULL;
+
+    while (pCrlCtx = CertEnumCRLsInStore(hStore, pCrlCtx)) {
+        crl = PyBytes_FromStringAndSize((const char*)pCrlCtx->pbCrlEncoded,
+                                            pCrlCtx->cbCrlEncoded);
+        if (!crl) {
+            Py_CLEAR(result);
+            break;
+        }
+        if ((enc = certEncodingType(pCrlCtx->dwCertEncodingType)) == NULL) {
+            Py_CLEAR(result);
+            break;
+        }
+        if ((tup = PyTuple_New(2)) == NULL) {
+            Py_CLEAR(result);
+            break;
+        }
+        PyTuple_SET_ITEM(tup, 0, crl);
+        crl = NULL;
+        PyTuple_SET_ITEM(tup, 1, enc);
+        enc = NULL;
+
+        if (PyList_Append(result, tup) < 0) {
+            Py_CLEAR(result);
+            break;
+        }
+        Py_CLEAR(tup);
     }
+    if (pCrlCtx) {
+        /* loop ended with an error, need to clean up context manually */
+        CertFreeCRLContext(pCrlCtx);
+    }
+
+    /* In error cases cert, enc and tup may not be NULL */
+    Py_XDECREF(crl);
+    Py_XDECREF(enc);
+    Py_XDECREF(tup);
+
+    if (!CertCloseStore(hStore, 0)) {
+        /* This error case might shadow another exception.*/
+        Py_XDECREF(result);
+        return PyErr_SetFromWindowsErr(GetLastError());
+    }
+    return result;
 }
-#endif
+
+#endif /* _MSC_VER */
 
 /* List of functions exported by this module. */
 
@@ -3567,8 +3695,10 @@
     {"get_default_verify_paths", (PyCFunction)PySSL_get_default_verify_paths,
      METH_NOARGS, PySSL_get_default_verify_paths_doc},
 #ifdef _MSC_VER
-    {"enum_cert_store", (PyCFunction)PySSL_enum_cert_store,
-     METH_VARARGS | METH_KEYWORDS, PySSL_enum_cert_store_doc},
+    {"enum_certificates", (PyCFunction)PySSL_enum_certificates,
+     METH_VARARGS | METH_KEYWORDS, PySSL_enum_certificates_doc},
+    {"enum_crls", (PyCFunction)PySSL_enum_crls,
+     METH_VARARGS | METH_KEYWORDS, PySSL_enum_crls_doc},
 #endif
     {"txt2obj", (PyCFunction)PySSL_txt2obj,
      METH_VARARGS | METH_KEYWORDS, PySSL_txt2obj_doc},
@@ -3811,12 +3941,6 @@
     PyModule_AddIntConstant(m, "VERIFY_X509_STRICT",
                             X509_V_FLAG_X509_STRICT);
 
-#ifdef _MSC_VER
-    /* Windows dwCertEncodingType */
-    PyModule_AddIntMacro(m, X509_ASN_ENCODING);
-    PyModule_AddIntMacro(m, PKCS_7_ASN_ENCODING);
-#endif
-
     /* Alert Descriptions from ssl.h */
     /* note RESERVED constants no longer intended for use have been removed */
     /* http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 */