Skip to content

Commit a2f652f

Browse files
tiranclaude
andcommitted
gh-160040: Distrust compromised eMudhra intermediate CA keys
Several eMudhra intermediate CA keys were compromised and revoked. As defense in depth, reject any chain containing one of the affected public keys, so connections are blocked even when the root store (OS, container) has not yet dropped the issuing roots or certifi/pip are not updated. The denylist in `Modules/_ssl/distrusted_spki.h` matches the DER-encoded SPKI, so cross-signed certificates that reuse a key are blocked too. Mirrors Golang's [crypto/x509](https://go.dev/cl/851047) change. See https://emudhra.we-have-a-problem.com/. Signed-off-by: Christian Heimes <christian@python.org> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0d35e3b commit a2f652f

7 files changed

Lines changed: 260 additions & 3 deletions

File tree

‎Makefile.pre.in‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3461,7 +3461,7 @@ MODULE__HMAC_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HMAC_HEADERS) $(LIBHACL_
34613461
MODULE__HMAC_LDEPS=$(LIBHACL_HMAC_LIB_@LIBHACL_LDEPS_LIBTYPE@)
34623462

34633463
MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c
3464-
MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_openssl_mem.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h
3464+
MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_openssl_mem.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl/distrusted_spki.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h
34653465
MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/parts.h $(srcdir)/Modules/_testcapi/util.h
34663466
MODULE__TESTLIMITEDCAPI_DEPS=$(srcdir)/Modules/_testlimitedcapi/testcapi_long.h $(srcdir)/Modules/_testlimitedcapi/parts.h $(srcdir)/Modules/_testlimitedcapi/util.h
34673467
MODULE__TESTINTERNALCAPI_DEPS=$(srcdir)/Modules/_testinternalcapi/parts.h $(srcdir)/Parser/tokenizer/source.h $(srcdir)/Parser/tokenizer/types.h $(srcdir)/Python/ceval.h $(srcdir)/Modules/_testinternalcapi/test_targets.h $(srcdir)/Modules/_testinternalcapi/test_cases.c.h
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
:mod:`ssl` now rejects TLS certificate chains that contain any of a set of
2+
compromised eMudhra intermediate CA keys. A built-in denylist of the affected
3+
:rfc:`5280` ``SubjectPublicKeyInfo`` values is enforced during certificate
4+
verification, mirroring the mitigation added to other TLS stacks. The denylist
5+
matches on the public key, so it also covers the cross-signed certificates that
6+
reuse one of the distrusted keys.

‎Modules/_ssl.c‎

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,9 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
976976
int mode = SSL_get_verify_mode(self->ssl);
977977
if (mode & SSL_VERIFY_PEER) {
978978
mode |= SSL_VERIFY_POST_HANDSHAKE;
979-
SSL_set_verify(self->ssl, mode, NULL);
979+
/* Keep ctx verify callback (distrusted SPKI check) */
980+
SSL_set_verify(self->ssl, mode,
981+
SSL_get_verify_callback(self->ssl));
980982
}
981983
} else {
982984
/* client socket */
@@ -3369,6 +3371,44 @@ static PyType_Spec PySSLSocket_spec = {
33693371
* _SSLContext objects
33703372
*/
33713373

3374+
#include "_ssl/distrusted_spki.h"
3375+
3376+
/* True if the cert's SPKI is on the distrusted list */
3377+
static int
3378+
_cert_has_distrusted_spki(X509 *cert)
3379+
{
3380+
unsigned char *spki = NULL;
3381+
int spki_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &spki);
3382+
if (spki_len <= 0 || spki == NULL) {
3383+
return 0;
3384+
}
3385+
3386+
int distrusted = 0;
3387+
for (size_t i = 0; i < Py_ARRAY_LENGTH(_distrusted_spki); i++) {
3388+
if ((size_t)spki_len == _distrusted_spki[i].len
3389+
&& memcmp(spki, _distrusted_spki[i].der, _distrusted_spki[i].len) == 0)
3390+
{
3391+
distrusted = 1;
3392+
break;
3393+
}
3394+
}
3395+
OPENSSL_free(spki);
3396+
return distrusted;
3397+
}
3398+
3399+
/* Verify callback, called once per cert. Reject distrusted SPKIs, otherwise
3400+
* keep OpenSSL's result. */
3401+
static int
3402+
_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
3403+
{
3404+
X509 *cert = X509_STORE_CTX_get_current_cert(x509_ctx);
3405+
if (cert != NULL && _cert_has_distrusted_spki(cert)) {
3406+
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REJECTED);
3407+
return 0;
3408+
}
3409+
return preverify_ok;
3410+
}
3411+
33723412
static int
33733413
_set_verify_mode(PySSLContext *self, enum py_ssl_cert_requirements n)
33743414
{
@@ -3393,7 +3433,7 @@ _set_verify_mode(PySSLContext *self, enum py_ssl_cert_requirements n)
33933433
/* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for
33943434
* server sockets and SSL_set_post_handshake_auth() for client. */
33953435

3396-
SSL_CTX_set_verify(self->ctx, mode, NULL);
3436+
SSL_CTX_set_verify(self->ctx, mode, _ssl_verify_callback);
33973437
return 0;
33983438
}
33993439

‎Modules/_ssl/distrusted_spki.h‎

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/* Denylist of distrusted SubjectPublicKeyInfo (SPKI).
2+
*
3+
* Verification rejects any chain with a cert whose SPKI (DER-encoded
4+
* SubjectPublicKeyInfo) is in the _distrusted_spki table. We match the key,
5+
* not a cert fingerprint, so cross-signs that reuse the key are blocked too.
6+
* To block another key, add its SPKI DER and a table entry.
7+
*
8+
* Dump a cert's SPKI DER and its SHA-256 with OpenSSL:
9+
*
10+
* $ openssl x509 -in cert.pem -noout -pubkey | openssl pkey -pubin -outform DER > spki.der
11+
* $ xxd -i spki.der
12+
* $ openssl dgst -sha256 spki.der
13+
*/
14+
#ifndef Py_SSL_DISTRUSTED_SPKI_H
15+
#define Py_SSL_DISTRUSTED_SPKI_H
16+
17+
#include <stddef.h>
18+
19+
/* Compromised eMudhra intermediates, https://emudhra.we-have-a-problem.com/ */
20+
21+
/* CN=EM OV TLS CA - G3B,O=eMudhra BV,C=NL (secp384r1)
22+
* SPKI SHA-256: 43bffa19d6ef651451f6841e019931e96f6f2eaeff28ab4b6f1ce4576de2717b */
23+
static const unsigned char _distrusted_spki_em_ov_tls_ca_g3b[] = {
24+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
25+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
26+
0x73, 0x89, 0x7a, 0xa0, 0xeb, 0x3c, 0xfd, 0xca, 0x8b, 0x12, 0x40, 0x2f,
27+
0xfa, 0xf0, 0xab, 0xc4, 0xe1, 0x21, 0x2b, 0xaf, 0x8d, 0xee, 0x77, 0x8a,
28+
0xc9, 0xee, 0x2d, 0x6d, 0xb8, 0x3f, 0xac, 0x28, 0x0c, 0x5e, 0x05, 0x63,
29+
0x8a, 0xe7, 0xa2, 0xac, 0xd7, 0x1c, 0x32, 0x2a, 0x8e, 0x36, 0x1c, 0xde,
30+
0xb9, 0xf6, 0x9c, 0x43, 0x02, 0xb9, 0xdd, 0x8d, 0x73, 0xd1, 0xcc, 0x6c,
31+
0xaa, 0xb2, 0xcc, 0x0a, 0xee, 0x4e, 0x21, 0x6c, 0xe9, 0x79, 0xdd, 0x51,
32+
0x8a, 0x4a, 0xf4, 0xb6, 0x26, 0xe5, 0x68, 0x4c, 0xdd, 0x65, 0x80, 0x37,
33+
0xed, 0x0c, 0x85, 0x4e, 0x4c, 0xc1, 0x2f, 0x4c, 0xf8, 0x64, 0x6d, 0x0d,
34+
};
35+
36+
/* CN=EM DV TLS CA - G3C,O=eMudhra Inc,C=US (secp384r1)
37+
* SPKI SHA-256: 49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f */
38+
static const unsigned char _distrusted_spki_em_dv_tls_ca_g3c[] = {
39+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
40+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
41+
0x3a, 0x7a, 0x78, 0x62, 0xc8, 0xc4, 0x4d, 0x32, 0x4d, 0xdd, 0x69, 0x03,
42+
0x3a, 0x67, 0x82, 0xc0, 0xef, 0xee, 0xfe, 0x40, 0x55, 0xe6, 0x8a, 0x12,
43+
0x3f, 0xa0, 0x37, 0x1b, 0xa3, 0xbd, 0x91, 0x94, 0x51, 0x4e, 0xe1, 0x72,
44+
0xa4, 0x33, 0xe3, 0x63, 0x0f, 0x02, 0xbd, 0x18, 0x80, 0x37, 0xd9, 0xe5,
45+
0x14, 0x9f, 0x9b, 0x77, 0xdb, 0x94, 0xa2, 0x81, 0xdf, 0x98, 0x7b, 0x2f,
46+
0xea, 0x88, 0x8b, 0x74, 0xbd, 0xfe, 0x8f, 0xda, 0x4c, 0x10, 0x99, 0xee,
47+
0x6c, 0xa1, 0x95, 0x66, 0xfe, 0x3e, 0xf0, 0xe5, 0xf6, 0xb0, 0x9a, 0x55,
48+
0x6c, 0xcc, 0x8b, 0xa5, 0x05, 0x61, 0xdf, 0x1b, 0x2a, 0x44, 0x1d, 0x01,
49+
};
50+
51+
/* CN=EM EV TLS CA - G3C,O=eMudhra Inc,C=US (secp384r1)
52+
* SPKI SHA-256: 5c0660a2b164910d8ff589c18e14552e9e0ec237dcefe2cb736d41a4811f81a0 */
53+
static const unsigned char _distrusted_spki_em_ev_tls_ca_g3c[] = {
54+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
55+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
56+
0x2d, 0x56, 0xe8, 0x29, 0xa6, 0xfb, 0x82, 0xc6, 0x4a, 0x46, 0x97, 0xd9,
57+
0x60, 0xaf, 0x95, 0x1e, 0x90, 0x67, 0xae, 0x91, 0xf9, 0x26, 0xd3, 0x43,
58+
0x06, 0xda, 0x48, 0xa8, 0xaa, 0xeb, 0x78, 0x69, 0xb6, 0xa6, 0x7e, 0x6d,
59+
0x27, 0x42, 0x6e, 0x04, 0x42, 0xaa, 0x03, 0xb3, 0x6e, 0x19, 0x88, 0xdf,
60+
0xf6, 0x36, 0x51, 0x4e, 0xe1, 0xcf, 0x07, 0xf5, 0xba, 0x4b, 0x3f, 0xf2,
61+
0xa0, 0x66, 0x45, 0xae, 0x7d, 0x31, 0xd5, 0x86, 0x6a, 0xd3, 0x1a, 0xa3,
62+
0xd2, 0x46, 0xeb, 0x0b, 0x31, 0x77, 0xee, 0x99, 0xcb, 0x75, 0x66, 0xc5,
63+
0x16, 0xce, 0x74, 0x80, 0x8a, 0x66, 0x8f, 0x91, 0xf0, 0xc4, 0x7f, 0xca,
64+
};
65+
66+
/* CN=EM OV TLS CA - G3C,O=eMudhra Inc,C=US (secp384r1)
67+
* SPKI SHA-256: 63ecefee431d49523bc1136c4e72d0206a477086caf606cd5cf025686c109cf8 */
68+
static const unsigned char _distrusted_spki_em_ov_tls_ca_g3c[] = {
69+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
70+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
71+
0x13, 0x82, 0x51, 0xcd, 0x52, 0xac, 0x92, 0x98, 0xc1, 0xc8, 0xaa, 0xd9,
72+
0x77, 0x32, 0x1d, 0xeb, 0x97, 0xe7, 0x09, 0xbd, 0x0b, 0x4c, 0xa0, 0xac,
73+
0xa5, 0x5d, 0xc8, 0xad, 0x51, 0xdc, 0xfc, 0x9d, 0x15, 0x89, 0xa1, 0x59,
74+
0x7e, 0x3a, 0x51, 0x20, 0xe1, 0xef, 0xd6, 0x31, 0xc6, 0x3e, 0x18, 0x35,
75+
0xca, 0xca, 0xe2, 0x98, 0x69, 0xa6, 0x2e, 0x16, 0x31, 0xe8, 0xa2, 0x81,
76+
0x81, 0xab, 0x56, 0x61, 0x6d, 0xc4, 0x5d, 0x91, 0x8a, 0xbc, 0x09, 0xf3,
77+
0xab, 0x0e, 0x63, 0xcf, 0x79, 0x2a, 0xa4, 0xdc, 0xed, 0x73, 0x87, 0xbe,
78+
0x37, 0xbb, 0xa5, 0x69, 0x54, 0x9f, 0x1c, 0x02, 0xb2, 0x70, 0xed, 0x67,
79+
};
80+
81+
/* CN=EM OV CS CA - G3C,O=eMudhra Inc,C=US (secp384r1)
82+
* SPKI SHA-256: 92c063d67e8f14db71d288e11266ffdaf69cbf115f7bab14f3bc2f68d25e0e30 */
83+
static const unsigned char _distrusted_spki_em_ov_cs_ca_g3c[] = {
84+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
85+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
86+
0xb9, 0xf0, 0xe0, 0x63, 0x47, 0x02, 0xa6, 0x9c, 0xa6, 0xaa, 0xdd, 0x44,
87+
0x3c, 0xfd, 0x62, 0xc4, 0x1b, 0x9b, 0xa4, 0xb2, 0x09, 0x24, 0x69, 0x7d,
88+
0x79, 0x74, 0xe7, 0x69, 0x1a, 0x12, 0x6f, 0xb9, 0x6a, 0xfb, 0x3e, 0x34,
89+
0xc2, 0xfd, 0x3b, 0xc5, 0xf4, 0x83, 0x06, 0x1f, 0x26, 0x5d, 0xc2, 0xa5,
90+
0x84, 0x36, 0x34, 0x73, 0x23, 0x51, 0x04, 0x36, 0xb2, 0x8f, 0x04, 0xf3,
91+
0x86, 0x23, 0x63, 0x06, 0x79, 0x80, 0xe2, 0x50, 0x3e, 0x02, 0x53, 0x2d,
92+
0x65, 0x46, 0x23, 0x5a, 0x71, 0x65, 0xa2, 0x59, 0xa1, 0xef, 0x49, 0x6f,
93+
0x74, 0x1b, 0x5f, 0xde, 0xe7, 0x78, 0x72, 0x08, 0xe4, 0xb8, 0xfa, 0xef,
94+
};
95+
96+
/* CN=EM EV TLS CA - G3B,O=eMudhra BV,C=NL (secp384r1)
97+
* SPKI SHA-256: e0254c7fcdc265e2f0f8a428dabb75ab4ae039b4872d62f5ee57f0877d5a8a41 */
98+
static const unsigned char _distrusted_spki_em_ev_tls_ca_g3b[] = {
99+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
100+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
101+
0x25, 0xdb, 0xa8, 0x5a, 0x64, 0xd3, 0x97, 0xda, 0x0b, 0x10, 0x52, 0xe9,
102+
0xea, 0x54, 0x6e, 0x48, 0x7b, 0xef, 0xea, 0x9c, 0x9d, 0x9d, 0x68, 0xa0,
103+
0xc5, 0xea, 0xdc, 0xfe, 0xef, 0x75, 0xa6, 0x9b, 0xb5, 0xd6, 0xfc, 0x01,
104+
0xf4, 0x2b, 0xfb, 0xca, 0x6e, 0xb4, 0x0b, 0xcd, 0x64, 0xc0, 0x76, 0x7e,
105+
0x36, 0xff, 0xd0, 0x5d, 0x97, 0xa8, 0x13, 0xc5, 0xaa, 0x0c, 0x6c, 0x95,
106+
0x30, 0xe7, 0x9c, 0xf5, 0xf0, 0x86, 0x64, 0x68, 0xf1, 0x38, 0x06, 0x42,
107+
0x0a, 0xa5, 0xc8, 0x51, 0x84, 0x0b, 0x1e, 0x25, 0x25, 0x40, 0xe7, 0x90,
108+
0xe4, 0xb6, 0x54, 0xc3, 0x85, 0x2e, 0xd6, 0x4a, 0xf1, 0xe8, 0x65, 0x7d,
109+
};
110+
111+
/* CN=EM DV TLS CA - G3B,O=eMudhra BV,C=NL (secp384r1)
112+
* SPKI SHA-256: ebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105 */
113+
static const unsigned char _distrusted_spki_em_dv_tls_ca_g3b[] = {
114+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
115+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
116+
0xe7, 0x9e, 0xd9, 0xc0, 0x4a, 0x97, 0x73, 0xc4, 0x73, 0x5e, 0x51, 0x11,
117+
0x1d, 0xa8, 0x5a, 0xc2, 0xbd, 0x5e, 0x64, 0xab, 0x37, 0x4b, 0x80, 0xe1,
118+
0x2c, 0xbc, 0x20, 0x38, 0xec, 0x1e, 0x4f, 0x0e, 0x3b, 0x81, 0x2f, 0xc9,
119+
0x85, 0x9e, 0x1e, 0x67, 0xaf, 0xb0, 0x4e, 0x2f, 0x8b, 0xc9, 0x7f, 0x74,
120+
0x85, 0xba, 0x65, 0xaa, 0x91, 0x1e, 0xe8, 0xa4, 0xe4, 0x5b, 0x62, 0xd5,
121+
0xcc, 0x03, 0xd5, 0x6b, 0xe6, 0x78, 0xb3, 0xc3, 0x8c, 0xb4, 0xdc, 0x93,
122+
0x7c, 0xa2, 0x46, 0x0e, 0x23, 0x84, 0x53, 0xc8, 0x92, 0x93, 0x5d, 0x29,
123+
0xd4, 0x4f, 0x91, 0xe1, 0x56, 0x06, 0xb6, 0x3c, 0xce, 0xda, 0xb4, 0x68,
124+
};
125+
126+
/* CN=EM OV CS CA - G3B,O=eMudhra BV,C=NL (secp384r1)
127+
* SPKI SHA-256: ec4ffa1ad0ac88081314b7aaffef6a91f97cd57291bb5ba686c5415f5eb4f6b6 */
128+
static const unsigned char _distrusted_spki_em_ov_cs_ca_g3b[] = {
129+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
130+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
131+
0xaa, 0xbb, 0x0f, 0xb0, 0x66, 0x6a, 0xcf, 0xcf, 0x01, 0xfa, 0xd5, 0x2b,
132+
0xfc, 0xfd, 0x4c, 0x2b, 0xf2, 0xcb, 0x3c, 0x34, 0x97, 0xde, 0x73, 0x95,
133+
0x0e, 0x58, 0x19, 0xb6, 0x1f, 0xe6, 0x95, 0x09, 0x87, 0x41, 0xc8, 0xc4,
134+
0x91, 0x43, 0x9b, 0x34, 0xa0, 0x43, 0x1c, 0xaf, 0xfe, 0x5b, 0xf9, 0xb4,
135+
0xc1, 0xc8, 0xe8, 0xae, 0x00, 0x01, 0xf9, 0xf4, 0xdc, 0x8a, 0xd8, 0x2c,
136+
0x59, 0xf3, 0x63, 0x9e, 0x96, 0xcd, 0x6c, 0x3d, 0x4d, 0xb0, 0xce, 0xfe,
137+
0xc2, 0x6c, 0x3d, 0x62, 0x51, 0x7b, 0x37, 0x76, 0x13, 0x72, 0x74, 0x3f,
138+
0x8e, 0x8a, 0xdd, 0x77, 0xb1, 0x00, 0x64, 0xf2, 0xab, 0xe5, 0xe0, 0x50,
139+
};
140+
141+
static const struct {
142+
const unsigned char *der;
143+
size_t len;
144+
} _distrusted_spki[] = {
145+
{_distrusted_spki_em_ov_tls_ca_g3b, sizeof(_distrusted_spki_em_ov_tls_ca_g3b)},
146+
{_distrusted_spki_em_dv_tls_ca_g3c, sizeof(_distrusted_spki_em_dv_tls_ca_g3c)},
147+
{_distrusted_spki_em_ev_tls_ca_g3c, sizeof(_distrusted_spki_em_ev_tls_ca_g3c)},
148+
{_distrusted_spki_em_ov_tls_ca_g3c, sizeof(_distrusted_spki_em_ov_tls_ca_g3c)},
149+
{_distrusted_spki_em_ov_cs_ca_g3c, sizeof(_distrusted_spki_em_ov_cs_ca_g3c)},
150+
{_distrusted_spki_em_ev_tls_ca_g3b, sizeof(_distrusted_spki_em_ev_tls_ca_g3b)},
151+
{_distrusted_spki_em_dv_tls_ca_g3b, sizeof(_distrusted_spki_em_dv_tls_ca_g3b)},
152+
{_distrusted_spki_em_ov_cs_ca_g3b, sizeof(_distrusted_spki_em_ov_cs_ca_g3b)},
153+
};
154+
155+
#endif /* !Py_SSL_DISTRUSTED_SPKI_H */

‎PCbuild/_ssl.vcxproj‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
</ItemDefinitionGroup>
100100
<ItemGroup>
101101
<ClInclude Include="..\Modules\_openssl_mem.h" />
102+
<ClInclude Include="..\Modules\_ssl\distrusted_spki.h" />
102103
</ItemGroup>
103104
<ItemGroup>
104105
<ClCompile Include="..\Modules\_ssl.c" />

‎PCbuild/_ssl.vcxproj.filters‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
<ClInclude Include="..\Modules\_openssl_mem.h">
2121
<Filter>Header Files</Filter>
2222
</ClInclude>
23+
<ClInclude Include="..\Modules\_ssl\distrusted_spki.h">
24+
<Filter>Header Files</Filter>
25+
</ClInclude>
2326
</ItemGroup>
2427
<ItemGroup>
2528
<ResourceCompile Include="..\PC\python_nt.rc">
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env python3
2+
"""Check the _ssl SPKI denylist against the live eMudhra test services."""
3+
import socket
4+
import ssl
5+
6+
# emSign ECC Root CA - G3
7+
ROOT_PEM = """\
8+
-----BEGIN CERTIFICATE-----
9+
MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG
10+
EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo
11+
bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
12+
RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ
13+
TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s
14+
b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw
15+
djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0
16+
WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS
17+
fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB
18+
zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq
19+
hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB
20+
CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD
21+
+JbNR6iC8hZVdyR+EhCVBCyj
22+
-----END CERTIFICATE-----
23+
"""
24+
25+
ENDPOINTS = [
26+
"dv-g3b-emudhra.we-have-a-problem.com",
27+
"dv-g3b-aaa-emudhra.we-have-a-problem.com",
28+
]
29+
30+
31+
def check(host, port=443):
32+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
33+
ctx.load_verify_locations(cadata=ROOT_PEM) # trust only the emSign root
34+
try:
35+
with socket.create_connection((host, port), timeout=15) as raw:
36+
with ctx.wrap_socket(raw, server_hostname=host) as s:
37+
s.sendall(
38+
b"GET / HTTP/1.0\r\nHost: " + host.encode() + b"\r\n\r\n"
39+
)
40+
status = s.recv(64).split(b"\r\n", 1)[0].decode("latin1")
41+
cipher = s.cipher()[0]
42+
return f"CONNECTED ({status}, {cipher}) -- denylist did NOT fire!"
43+
except ssl.SSLCertVerificationError as exc:
44+
return (f"REJECTED as expected: verify_code={exc.verify_code} "
45+
f"{exc.verify_message!r}")
46+
47+
48+
if __name__ == "__main__":
49+
print("OpenSSL:", ssl.OPENSSL_VERSION)
50+
for host in ENDPOINTS:
51+
print(f"\n{host}:")
52+
print(" ", check(host))

0 commit comments

Comments
 (0)