pined-openssl 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: pined-openssl
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: cryptography>=46.0.5
File without changes
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "pined-openssl"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "cryptography>=46.0.5",
9
+ ]
10
+
11
+ [build-system]
12
+ requires = ["setuptools"]
13
+ build-backend = "setuptools.build_meta"
14
+
15
+ [tool.setuptools]
16
+ include-package-data = false
17
+
18
+ [[tool.setuptools.ext-modules]]
19
+ name = "pined.openssl.native"
20
+ sources = ["src/pined/openssl/native.cc"]
21
+ extra-compile-args=["-std=c++17"]
22
+ extra-link-args=["-lcrypto"]
23
+
24
+ [tool.pyright]
25
+ venv = ".venv"
26
+ venvPath = "."
27
+
28
+ [tool.ty.environment]
29
+ root = ["."]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,13 @@
1
+ from .pkcs12 import extract_certificates
2
+ from pathlib import Path
3
+ from argparse import ArgumentParser
4
+
5
+ if __name__ == "__main__":
6
+ parser = ArgumentParser(prog="pined.openssl")
7
+ parser.add_argument("pkcs12", type=Path)
8
+ parser.add_argument("password", type=str)
9
+ args = parser.parse_args()
10
+ with args.pkcs12.open("rb") as f:
11
+ certificates = extract_certificates(f.read(), args.password)
12
+ for c in certificates:
13
+ print(c)
@@ -0,0 +1,236 @@
1
+ #include <exception>
2
+ #include <optional>
3
+ #include <string>
4
+
5
+ #define PY_SSIZE_T_CLEAN
6
+ #include <Python.h>
7
+
8
+ #include <openssl/bio.h>
9
+ #include <openssl/pem.h>
10
+ #include <openssl/pkcs12.h>
11
+ #include <openssl/provider.h>
12
+
13
+ static PyObject *OpenSSLError = NULL;
14
+
15
+ static int native_module_exec(PyObject *m) {
16
+ if (OpenSSLError != NULL) {
17
+ PyErr_SetString(PyExc_ImportError,
18
+ "cannot initialize spam module more than once");
19
+ return -1;
20
+ }
21
+ OpenSSLError = PyErr_NewException("pkcs12.OpenSSLError", NULL, NULL);
22
+ if (PyModule_AddObjectRef(m, "OpenSSLError", OpenSSLError) < 0) {
23
+ return -1;
24
+ }
25
+
26
+ return 0;
27
+ }
28
+
29
+ namespace openssl {
30
+
31
+ class NoCertificateException : std::exception {};
32
+ class InvalidPassword : std::exception {};
33
+ struct Provider {
34
+ OSSL_PROVIDER *provider;
35
+ Provider(const std::string &name)
36
+ : provider(OSSL_PROVIDER_load(NULL, name.c_str())) {}
37
+ ~Provider() { OSSL_PROVIDER_unload(provider); }
38
+ };
39
+
40
+ struct X509Certificate {
41
+ ::X509 *certificate;
42
+
43
+ X509Certificate(::X509 *certificate) : certificate(certificate) {}
44
+
45
+ ~X509Certificate() { ::X509_free(certificate); }
46
+ };
47
+
48
+ struct BIO {
49
+ ::BIO *bio;
50
+
51
+ BIO() : bio(::BIO_new(::BIO_s_mem())) {}
52
+ BIO(const Py_buffer &buffer)
53
+ : bio(::BIO_new_mem_buf(buffer.buf, buffer.len)) {}
54
+ ~BIO() { ::BIO_free(bio); }
55
+
56
+ BIO &operator<<(const X509Certificate &certificate) {
57
+ int ret = PEM_write_bio_X509(bio, certificate.certificate);
58
+ return *this;
59
+ }
60
+ };
61
+
62
+ struct SafeBag {
63
+ ::PKCS12_SAFEBAG *bag;
64
+
65
+ SafeBag(::PKCS12_SAFEBAG *bag) : bag(bag) {}
66
+
67
+ std::optional<X509Certificate> get_certificate() {
68
+ X509 *cert = PKCS12_SAFEBAG_get1_cert(bag);
69
+ if (cert == NULL) {
70
+ return std::nullopt;
71
+ }
72
+ // вот тут нюанс -- тут будет конструирование X509Certificate по месту
73
+ // иначе копирование и вызов деструктора, а умные указатели мне пока
74
+ // использовать не хочется
75
+ return cert;
76
+ }
77
+
78
+ ~SafeBag() {}
79
+ };
80
+
81
+ template <typename T, typename U> class StackIterator {
82
+ T &p;
83
+ int i;
84
+
85
+ public:
86
+ using iterator_category = std::input_iterator_tag;
87
+ using value_type = U;
88
+ using difference_type = int;
89
+ using pointer = U *;
90
+ using reference = U &;
91
+ StackIterator(T &p, int i = 0) : p(p), i(i) {}
92
+ StackIterator &operator++() {
93
+ ++i;
94
+ return *this;
95
+ }
96
+ StackIterator operator++(int) {
97
+ auto retval = *this;
98
+ ++(*this);
99
+ return retval;
100
+ }
101
+ bool operator==(StackIterator other) const {
102
+ return &p == &(other.p) && i == other.i;
103
+ }
104
+ bool operator!=(StackIterator other) const { return !(*this == other); }
105
+ value_type operator*() const { return get_value(p, i); }
106
+ virtual value_type get_value(T &p, int i) const;
107
+ };
108
+
109
+ struct PKCS7_ {
110
+ ::PKCS7 *pkcs7;
111
+ std::string password;
112
+ STACK_OF(PKCS12_SAFEBAG) * bags;
113
+
114
+ PKCS7_(::PKCS7 *pkcs7, const std::string &password)
115
+ : pkcs7(pkcs7), password(password), bags(NULL) {
116
+ int bagnid = OBJ_obj2nid(pkcs7->type);
117
+ switch (bagnid) {
118
+ case NID_pkcs7_data:
119
+ bags = PKCS12_unpack_p7data(pkcs7);
120
+ break;
121
+ case NID_pkcs7_encrypted: {
122
+ bags =
123
+ PKCS12_unpack_p7encdata(pkcs7, password.c_str(), password.length());
124
+ if (bags == NULL) {
125
+ throw InvalidPassword();
126
+ }
127
+ break;
128
+ }
129
+ default:
130
+ break;
131
+ }
132
+ }
133
+ ~PKCS7_() { sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); }
134
+
135
+ class iterator : public StackIterator<PKCS7_, SafeBag> {
136
+ public:
137
+ iterator(PKCS7_ &p, int i = 0) : StackIterator<PKCS7_, SafeBag>(p, i) {}
138
+ value_type get_value(openssl::PKCS7_ &p, int i) const override {
139
+ return SafeBag(sk_PKCS12_SAFEBAG_value(p.bags, i));
140
+ }
141
+ };
142
+
143
+ iterator begin() { return iterator(*this, 0); }
144
+ iterator end() {
145
+ return iterator(*this, bags == NULL ? 0 : sk_PKCS12_SAFEBAG_num(bags));
146
+ }
147
+ };
148
+
149
+ struct PKCS12 {
150
+ ::PKCS12 *pkcs12;
151
+ STACK_OF(PKCS7) * asafes;
152
+ std::string password;
153
+
154
+ PKCS12(const openssl::BIO &data, const std::string &password)
155
+ : pkcs12(d2i_PKCS12_bio(data.bio, NULL)),
156
+ asafes(PKCS12_unpack_authsafes(pkcs12)), password(password) {}
157
+ ~PKCS12() {
158
+ sk_PKCS7_pop_free(asafes, PKCS7_free);
159
+ PKCS12_free(pkcs12);
160
+ }
161
+
162
+ class iterator : public StackIterator<PKCS12, PKCS7_> {
163
+ public:
164
+ iterator(PKCS12 &p, int i = 0) : StackIterator<PKCS12, PKCS7_>(p, i) {}
165
+ value_type get_value(PKCS12 &p, int i) const override {
166
+ return PKCS7_(sk_PKCS7_value(p.asafes, i), p.password);
167
+ }
168
+ };
169
+
170
+ iterator begin() { return iterator(*this, 0); }
171
+ iterator end() { return iterator(*this, sk_PKCS7_num(asafes)); }
172
+ };
173
+
174
+ } // namespace openssl
175
+
176
+ namespace python {
177
+ PyObject *make_bytes(const openssl::BIO &bio) {
178
+ char *data = NULL;
179
+ long len = BIO_get_mem_data(bio.bio, &data);
180
+ // if (len <= 0) {
181
+ // PyErr_SetString(OpenSSLError, "Failed to export certificates data");
182
+ // goto err;
183
+ // }
184
+
185
+ return PyBytes_FromStringAndSize(data, len);
186
+ }
187
+ } // namespace python
188
+
189
+ extern "C" {
190
+ static PyObject *extract_certificates(PyObject *self, PyObject *args) {
191
+ Py_buffer pkcs12_bytes;
192
+ const char *password;
193
+
194
+ if (!PyArg_ParseTuple(args, "y*s", &pkcs12_bytes, &password))
195
+ return NULL;
196
+
197
+ auto default_provider = openssl::Provider("default");
198
+ auto legacy_provider = openssl::Provider("legacy");
199
+ auto pkcs12_bio = openssl::BIO(pkcs12_bytes);
200
+ auto pkcs12 = openssl::PKCS12(pkcs12_bio, password);
201
+ auto output = openssl::BIO();
202
+
203
+ try {
204
+ for (auto pkcs7 : pkcs12) {
205
+ for (auto bag : pkcs7) {
206
+ if (auto certificate = bag.get_certificate()) {
207
+ output << *certificate;
208
+ }
209
+ }
210
+ }
211
+ } catch (openssl::InvalidPassword &_) {
212
+ PyErr_SetString(OpenSSLError, "Invalid password");
213
+ return NULL;
214
+ }
215
+
216
+ return python::make_bytes(output);
217
+ }
218
+ }
219
+
220
+ static PyMethodDef methods[] = {
221
+ {"extract_certificates", extract_certificates, METH_VARARGS, NULL},
222
+ {NULL, NULL, 0, NULL} /* sentinel */
223
+ };
224
+
225
+ static PyModuleDef_Slot slots[] = {{Py_mod_exec, (void *)native_module_exec},
226
+ {0, NULL}};
227
+
228
+ static struct PyModuleDef module = {
229
+ .m_base = PyModuleDef_HEAD_INIT,
230
+ .m_name = "native",
231
+ .m_size = 0,
232
+ .m_methods = methods,
233
+ .m_slots = slots,
234
+ };
235
+
236
+ PyMODINIT_FUNC PyInit_native(void) { return PyModuleDef_Init(&module); }
@@ -0,0 +1 @@
1
+ def extract_certificates(pkcs12: bytes, password: str) -> bytes: ...
@@ -0,0 +1,5 @@
1
+ from cryptography.x509 import load_pem_x509_certificates, Certificate
2
+ from .native import extract_certificates as _extract_certificates
3
+
4
+ def extract_certificates(pkcs12: bytes, password: str) -> list[Certificate]:
5
+ return load_pem_x509_certificates(_extract_certificates(pkcs12, password))
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: pined-openssl
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: cryptography>=46.0.5
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/pined/openssl/__init__.py
4
+ src/pined/openssl/__main__.py
5
+ src/pined/openssl/native.cc
6
+ src/pined/openssl/native.pyi
7
+ src/pined/openssl/pkcs12.py
8
+ src/pined_openssl.egg-info/PKG-INFO
9
+ src/pined_openssl.egg-info/SOURCES.txt
10
+ src/pined_openssl.egg-info/dependency_links.txt
11
+ src/pined_openssl.egg-info/requires.txt
12
+ src/pined_openssl.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ cryptography>=46.0.5