%PDF- %PDF-
Direktori : /backups/router/usr/local/lib/python3.11/site-packages/aioquic/ |
Current File : //backups/router/usr/local/lib/python3.11/site-packages/aioquic/_crypto.c |
#define PY_SSIZE_T_CLEAN #include <Python.h> #include <openssl/err.h> #include <openssl/evp.h> #define MODULE_NAME "aioquic._crypto" #define AEAD_KEY_LENGTH_MAX 32 #define AEAD_NONCE_LENGTH 12 #define AEAD_TAG_LENGTH 16 #define PACKET_LENGTH_MAX 1500 #define PACKET_NUMBER_LENGTH_MAX 4 #define SAMPLE_LENGTH 16 #define CHECK_RESULT(expr) \ if (!(expr)) { \ ERR_clear_error(); \ PyErr_SetString(CryptoError, "OpenSSL call failed"); \ return NULL; \ } #define CHECK_RESULT_CTOR(expr) \ if (!(expr)) { \ ERR_clear_error(); \ PyErr_SetString(CryptoError, "OpenSSL call failed"); \ return -1; \ } static PyObject *CryptoError; /* AEAD */ typedef struct { PyObject_HEAD EVP_CIPHER_CTX *decrypt_ctx; EVP_CIPHER_CTX *encrypt_ctx; unsigned char buffer[PACKET_LENGTH_MAX]; unsigned char key[AEAD_KEY_LENGTH_MAX]; unsigned char iv[AEAD_NONCE_LENGTH]; unsigned char nonce[AEAD_NONCE_LENGTH]; } AEADObject; static PyObject *AEADType; static EVP_CIPHER_CTX * create_ctx(const EVP_CIPHER *cipher, int key_length, int operation) { EVP_CIPHER_CTX *ctx; int res; ctx = EVP_CIPHER_CTX_new(); CHECK_RESULT(ctx != 0); res = EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, operation); CHECK_RESULT(res != 0); res = EVP_CIPHER_CTX_set_key_length(ctx, key_length); CHECK_RESULT(res != 0); res = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, AEAD_NONCE_LENGTH, NULL); CHECK_RESULT(res != 0); return ctx; } static int AEAD_init(AEADObject *self, PyObject *args, PyObject *kwargs) { const char *cipher_name; const unsigned char *key, *iv; Py_ssize_t cipher_name_len, key_len, iv_len; if (!PyArg_ParseTuple(args, "y#y#y#", &cipher_name, &cipher_name_len, &key, &key_len, &iv, &iv_len)) return -1; const EVP_CIPHER *evp_cipher = EVP_get_cipherbyname(cipher_name); if (evp_cipher == 0) { PyErr_Format(CryptoError, "Invalid cipher name: %s", cipher_name); return -1; } if (key_len > AEAD_KEY_LENGTH_MAX) { PyErr_SetString(CryptoError, "Invalid key length"); return -1; } if (iv_len > AEAD_NONCE_LENGTH) { PyErr_SetString(CryptoError, "Invalid iv length"); return -1; } memcpy(self->key, key, key_len); memcpy(self->iv, iv, iv_len); self->decrypt_ctx = create_ctx(evp_cipher, key_len, 0); CHECK_RESULT_CTOR(self->decrypt_ctx != 0); self->encrypt_ctx = create_ctx(evp_cipher, key_len, 1); CHECK_RESULT_CTOR(self->encrypt_ctx != 0); return 0; } static void AEAD_dealloc(AEADObject *self) { EVP_CIPHER_CTX_free(self->decrypt_ctx); EVP_CIPHER_CTX_free(self->encrypt_ctx); PyTypeObject *tp = Py_TYPE(self); freefunc free = PyType_GetSlot(tp, Py_tp_free); free(self); Py_DECREF(tp); } static PyObject* AEAD_decrypt(AEADObject *self, PyObject *args) { const unsigned char *data, *associated; Py_ssize_t data_len, associated_len; int outlen, outlen2, res; uint64_t pn; if (!PyArg_ParseTuple(args, "y#y#K", &data, &data_len, &associated, &associated_len, &pn)) return NULL; if (data_len < AEAD_TAG_LENGTH || data_len > PACKET_LENGTH_MAX) { PyErr_SetString(CryptoError, "Invalid payload length"); return NULL; } memcpy(self->nonce, self->iv, AEAD_NONCE_LENGTH); for (int i = 0; i < 8; ++i) { self->nonce[AEAD_NONCE_LENGTH - 1 - i] ^= (uint8_t)(pn >> 8 * i); } res = EVP_CIPHER_CTX_ctrl(self->decrypt_ctx, EVP_CTRL_CCM_SET_TAG, AEAD_TAG_LENGTH, (void*)(data + (data_len - AEAD_TAG_LENGTH))); CHECK_RESULT(res != 0); res = EVP_CipherInit_ex(self->decrypt_ctx, NULL, NULL, self->key, self->nonce, 0); CHECK_RESULT(res != 0); res = EVP_CipherUpdate(self->decrypt_ctx, NULL, &outlen, associated, associated_len); CHECK_RESULT(res != 0); res = EVP_CipherUpdate(self->decrypt_ctx, self->buffer, &outlen, data, data_len - AEAD_TAG_LENGTH); CHECK_RESULT(res != 0); res = EVP_CipherFinal_ex(self->decrypt_ctx, NULL, &outlen2); if (res == 0) { PyErr_SetString(CryptoError, "Payload decryption failed"); return NULL; } return PyBytes_FromStringAndSize((const char*)self->buffer, outlen); } static PyObject* AEAD_encrypt(AEADObject *self, PyObject *args) { const unsigned char *data, *associated; Py_ssize_t data_len, associated_len; int outlen, outlen2, res; uint64_t pn; if (!PyArg_ParseTuple(args, "y#y#K", &data, &data_len, &associated, &associated_len, &pn)) return NULL; if (data_len > PACKET_LENGTH_MAX) { PyErr_SetString(CryptoError, "Invalid payload length"); return NULL; } memcpy(self->nonce, self->iv, AEAD_NONCE_LENGTH); for (int i = 0; i < 8; ++i) { self->nonce[AEAD_NONCE_LENGTH - 1 - i] ^= (uint8_t)(pn >> 8 * i); } res = EVP_CipherInit_ex(self->encrypt_ctx, NULL, NULL, self->key, self->nonce, 1); CHECK_RESULT(res != 0); res = EVP_CipherUpdate(self->encrypt_ctx, NULL, &outlen, associated, associated_len); CHECK_RESULT(res != 0); res = EVP_CipherUpdate(self->encrypt_ctx, self->buffer, &outlen, data, data_len); CHECK_RESULT(res != 0); res = EVP_CipherFinal_ex(self->encrypt_ctx, NULL, &outlen2); CHECK_RESULT(res != 0 && outlen2 == 0); res = EVP_CIPHER_CTX_ctrl(self->encrypt_ctx, EVP_CTRL_CCM_GET_TAG, AEAD_TAG_LENGTH, self->buffer + outlen); CHECK_RESULT(res != 0); return PyBytes_FromStringAndSize((const char*)self->buffer, outlen + AEAD_TAG_LENGTH); } static PyMethodDef AEAD_methods[] = { {"decrypt", (PyCFunction)AEAD_decrypt, METH_VARARGS, ""}, {"encrypt", (PyCFunction)AEAD_encrypt, METH_VARARGS, ""}, {NULL} }; static PyType_Slot AEADType_slots[] = { {Py_tp_dealloc, AEAD_dealloc}, {Py_tp_methods, AEAD_methods}, {Py_tp_doc, "AEAD objects"}, {Py_tp_init, AEAD_init}, {0, 0}, }; static PyType_Spec AEADType_spec = { MODULE_NAME ".AEADType", sizeof(AEADObject), 0, Py_TPFLAGS_DEFAULT, AEADType_slots }; /* HeaderProtection */ typedef struct { PyObject_HEAD EVP_CIPHER_CTX *ctx; int is_chacha20; unsigned char buffer[PACKET_LENGTH_MAX]; unsigned char mask[31]; unsigned char zero[5]; } HeaderProtectionObject; static PyObject *HeaderProtectionType; static int HeaderProtection_init(HeaderProtectionObject *self, PyObject *args, PyObject *kwargs) { const char *cipher_name; const unsigned char *key; Py_ssize_t cipher_name_len, key_len; int res; if (!PyArg_ParseTuple(args, "y#y#", &cipher_name, &cipher_name_len, &key, &key_len)) return -1; const EVP_CIPHER *evp_cipher = EVP_get_cipherbyname(cipher_name); if (evp_cipher == 0) { PyErr_Format(CryptoError, "Invalid cipher name: %s", cipher_name); return -1; } memset(self->mask, 0, sizeof(self->mask)); memset(self->zero, 0, sizeof(self->zero)); self->is_chacha20 = cipher_name_len == 8 && memcmp(cipher_name, "chacha20", 8) == 0; self->ctx = EVP_CIPHER_CTX_new(); CHECK_RESULT_CTOR(self->ctx != 0); res = EVP_CipherInit_ex(self->ctx, evp_cipher, NULL, NULL, NULL, 1); CHECK_RESULT_CTOR(res != 0); res = EVP_CIPHER_CTX_set_key_length(self->ctx, key_len); CHECK_RESULT_CTOR(res != 0); res = EVP_CipherInit_ex(self->ctx, NULL, NULL, key, NULL, 1); CHECK_RESULT_CTOR(res != 0); return 0; } static void HeaderProtection_dealloc(HeaderProtectionObject *self) { EVP_CIPHER_CTX_free(self->ctx); PyTypeObject *tp = Py_TYPE(self); freefunc free = PyType_GetSlot(tp, Py_tp_free); free(self); Py_DECREF(tp); } static int HeaderProtection_mask(HeaderProtectionObject *self, const unsigned char* sample) { int outlen; if (self->is_chacha20) { return EVP_CipherInit_ex(self->ctx, NULL, NULL, NULL, sample, 1) && EVP_CipherUpdate(self->ctx, self->mask, &outlen, self->zero, sizeof(self->zero)); } else { return EVP_CipherUpdate(self->ctx, self->mask, &outlen, sample, SAMPLE_LENGTH); } } static PyObject* HeaderProtection_apply(HeaderProtectionObject *self, PyObject *args) { const unsigned char *header, *payload; Py_ssize_t header_len, payload_len; int res; if (!PyArg_ParseTuple(args, "y#y#", &header, &header_len, &payload, &payload_len)) return NULL; int pn_length = (header[0] & 0x03) + 1; int pn_offset = header_len - pn_length; res = HeaderProtection_mask(self, payload + PACKET_NUMBER_LENGTH_MAX - pn_length); CHECK_RESULT(res != 0); memcpy(self->buffer, header, header_len); memcpy(self->buffer + header_len, payload, payload_len); if (self->buffer[0] & 0x80) { self->buffer[0] ^= self->mask[0] & 0x0F; } else { self->buffer[0] ^= self->mask[0] & 0x1F; } for (int i = 0; i < pn_length; ++i) { self->buffer[pn_offset + i] ^= self->mask[1 + i]; } return PyBytes_FromStringAndSize((const char*)self->buffer, header_len + payload_len); } static PyObject* HeaderProtection_remove(HeaderProtectionObject *self, PyObject *args) { const unsigned char *packet; Py_ssize_t packet_len; int pn_offset, res; if (!PyArg_ParseTuple(args, "y#I", &packet, &packet_len, &pn_offset)) return NULL; res = HeaderProtection_mask(self, packet + pn_offset + PACKET_NUMBER_LENGTH_MAX); CHECK_RESULT(res != 0); memcpy(self->buffer, packet, pn_offset + PACKET_NUMBER_LENGTH_MAX); if (self->buffer[0] & 0x80) { self->buffer[0] ^= self->mask[0] & 0x0F; } else { self->buffer[0] ^= self->mask[0] & 0x1F; } int pn_length = (self->buffer[0] & 0x03) + 1; uint32_t pn_truncated = 0; for (int i = 0; i < pn_length; ++i) { self->buffer[pn_offset + i] ^= self->mask[1 + i]; pn_truncated = self->buffer[pn_offset + i] | (pn_truncated << 8); } return Py_BuildValue("y#i", self->buffer, pn_offset + pn_length, pn_truncated); } static PyMethodDef HeaderProtection_methods[] = { {"apply", (PyCFunction)HeaderProtection_apply, METH_VARARGS, ""}, {"remove", (PyCFunction)HeaderProtection_remove, METH_VARARGS, ""}, {NULL} }; static PyType_Slot HeaderProtectionType_slots[] = { {Py_tp_dealloc, HeaderProtection_dealloc}, {Py_tp_methods, HeaderProtection_methods}, {Py_tp_doc, "HeaderProtection objects"}, {Py_tp_init, HeaderProtection_init}, {0, 0}, }; static PyType_Spec HeaderProtectionType_spec = { MODULE_NAME ".HeaderProtectionType", sizeof(HeaderProtectionObject), 0, Py_TPFLAGS_DEFAULT, HeaderProtectionType_slots }; static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, MODULE_NAME, /* m_name */ "Cryptography utilities.", /* m_doc */ -1, /* m_size */ NULL, /* m_methods */ NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ }; PyMODINIT_FUNC PyInit__crypto(void) { PyObject* m; m = PyModule_Create(&moduledef); if (m == NULL) return NULL; CryptoError = PyErr_NewException(MODULE_NAME ".CryptoError", PyExc_ValueError, NULL); Py_INCREF(CryptoError); PyModule_AddObject(m, "CryptoError", CryptoError); AEADType = PyType_FromSpec(&AEADType_spec); if (AEADType == NULL) return NULL; PyModule_AddObject(m, "AEAD", AEADType); HeaderProtectionType = PyType_FromSpec(&HeaderProtectionType_spec); if (HeaderProtectionType == NULL) return NULL; PyModule_AddObject(m, "HeaderProtection", HeaderProtectionType); // ensure required ciphers are initialised EVP_add_cipher(EVP_aes_128_ecb()); EVP_add_cipher(EVP_aes_128_gcm()); EVP_add_cipher(EVP_aes_256_ecb()); EVP_add_cipher(EVP_aes_256_gcm()); return m; }