LCOV - code coverage report
Current view: top level - src/core - crypto.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 82.1 % 196 161
Test Date: 2026-04-20 19:54:24 Functions: 95.8 % 24 23

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file crypto.c
       6              :  * @brief Production OpenSSL implementation of crypto.h wrappers.
       7              :  *
       8              :  * Uses EVP interfaces for OpenSSL 3.0+ compatibility.
       9              :  * AES block operations use EVP with ECB mode and no padding.
      10              :  */
      11              : 
      12              : #include "crypto.h"
      13              : 
      14              : #include <openssl/evp.h>
      15              : #include <openssl/rand.h>
      16              : #include <openssl/bn.h>
      17              : #include <openssl/pem.h>
      18              : #include <limits.h>
      19              : #include <stdio.h>
      20              : #include <stdlib.h>
      21              : #include <string.h>
      22              : 
      23         4848 : void crypto_sha256(const unsigned char *data, size_t len, unsigned char *out) {
      24         4848 :     if (EVP_Digest(data, len, out, NULL, EVP_sha256(), NULL) != 1) {
      25            0 :         fprintf(stderr, "crypto: EVP_Digest(sha256) failed\n");
      26            0 :         abort();
      27              :     }
      28         4848 : }
      29              : 
      30          660 : int crypto_aes_set_encrypt_key(const unsigned char *key, int bits,
      31              :                                CryptoAesKey *schedule) {
      32          660 :     size_t key_len = (size_t)bits / 8;
      33          660 :     memcpy(schedule->key, key, key_len);
      34          660 :     schedule->bits = bits;
      35          660 :     return 0;
      36              : }
      37              : 
      38          647 : int crypto_aes_set_decrypt_key(const unsigned char *key, int bits,
      39              :                                CryptoAesKey *schedule) {
      40          647 :     size_t key_len = (size_t)bits / 8;
      41          647 :     memcpy(schedule->key, key, key_len);
      42          647 :     schedule->bits = bits;
      43          647 :     return 0;
      44              : }
      45              : 
      46      1431739 : static const EVP_CIPHER *aes_ecb_cipher(int bits) {
      47      1431739 :     if (bits == 128)      return EVP_aes_128_ecb();
      48      1431739 :     else if (bits == 192) return EVP_aes_192_ecb();
      49      1431739 :     else                  return EVP_aes_256_ecb();
      50              : }
      51              : 
      52       715919 : void crypto_aes_encrypt_block(const unsigned char *in, unsigned char *out,
      53              :                               const CryptoAesKey *schedule) {
      54       715919 :     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
      55       715919 :     if (!ctx) { fprintf(stderr, "OOM: EVP_CIPHER_CTX_new\n"); abort(); }
      56       715919 :     EVP_EncryptInit_ex(ctx, aes_ecb_cipher(schedule->bits),
      57       715919 :                        NULL, schedule->key, NULL);
      58       715919 :     EVP_CIPHER_CTX_set_padding(ctx, 0);
      59              : 
      60       715919 :     int out_len = 0;
      61       715919 :     if (EVP_EncryptUpdate(ctx, out, &out_len, in, 16) != 1) {
      62            0 :         fprintf(stderr, "crypto: EVP_EncryptUpdate failed\n");
      63            0 :         abort();
      64              :     }
      65       715919 :     int final_len = 0;
      66       715919 :     if (EVP_EncryptFinal_ex(ctx, out + out_len, &final_len) != 1) {
      67            0 :         fprintf(stderr, "crypto: EVP_EncryptFinal_ex failed\n");
      68            0 :         abort();
      69              :     }
      70       715919 :     EVP_CIPHER_CTX_free(ctx);
      71       715919 : }
      72              : 
      73       715820 : void crypto_aes_decrypt_block(const unsigned char *in, unsigned char *out,
      74              :                               const CryptoAesKey *schedule) {
      75       715820 :     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
      76       715820 :     if (!ctx) { fprintf(stderr, "OOM: EVP_CIPHER_CTX_new\n"); abort(); }
      77       715820 :     EVP_DecryptInit_ex(ctx, aes_ecb_cipher(schedule->bits),
      78       715820 :                        NULL, schedule->key, NULL);
      79       715820 :     EVP_CIPHER_CTX_set_padding(ctx, 0);
      80              : 
      81       715820 :     int out_len = 0;
      82       715820 :     if (EVP_DecryptUpdate(ctx, out, &out_len, in, 16) != 1) {
      83            0 :         fprintf(stderr, "crypto: EVP_DecryptUpdate failed\n");
      84            0 :         abort();
      85              :     }
      86       715820 :     int final_len = 0;
      87       715820 :     if (EVP_DecryptFinal_ex(ctx, out + out_len, &final_len) != 1) {
      88            0 :         fprintf(stderr, "crypto: EVP_DecryptFinal_ex failed\n");
      89            0 :         abort();
      90              :     }
      91       715820 :     EVP_CIPHER_CTX_free(ctx);
      92       715820 : }
      93              : 
      94         1601 : int crypto_rand_bytes(unsigned char *buf, size_t len) {
      95              :     /* QA-18: guard against size_t → int truncation that would leave the
      96              :      * tail of `buf` uninitialized. The project never asks for > INT_MAX
      97              :      * bytes in practice, so treat it as an impossible condition and abort,
      98              :      * matching the project's abort-on-impossible policy. */
      99         1601 :     if (len > INT_MAX) {
     100            0 :         fprintf(stderr, "crypto_rand_bytes: len too large\n");
     101            0 :         abort();
     102              :     }
     103         1601 :     return RAND_bytes(buf, (int)len) == 1 ? 0 : -1;
     104              : }
     105              : 
     106              : /* ---- SHA-1 ---- */
     107              : 
     108           21 : void crypto_sha1(const unsigned char *data, size_t len, unsigned char *out) {
     109           21 :     if (EVP_Digest(data, len, out, NULL, EVP_sha1(), NULL) != 1) {
     110            0 :         fprintf(stderr, "crypto: EVP_Digest(sha1) failed\n");
     111            0 :         abort();
     112              :     }
     113           21 : }
     114              : 
     115            2 : void crypto_sha512(const unsigned char *data, size_t len, unsigned char *out) {
     116            2 :     if (EVP_Digest(data, len, out, NULL, EVP_sha512(), NULL) != 1) {
     117            0 :         fprintf(stderr, "crypto: EVP_Digest(sha512) failed\n");
     118            0 :         abort();
     119              :     }
     120            2 : }
     121              : 
     122           22 : int crypto_pbkdf2_hmac_sha512(const unsigned char *password, size_t password_len,
     123              :                               const unsigned char *salt, size_t salt_len,
     124              :                               int iters,
     125              :                               unsigned char *out, size_t out_len) {
     126           22 :     if (!password || !salt || !out || out_len == 0 || iters <= 0) return -1;
     127           19 :     if (password_len > INT_MAX || salt_len > INT_MAX || out_len > INT_MAX)
     128            0 :         return -1;
     129           19 :     int rc = PKCS5_PBKDF2_HMAC((const char *)password, (int)password_len,
     130              :                                 salt, (int)salt_len, iters,
     131              :                                 EVP_sha512(), (int)out_len, out);
     132           19 :     return rc == 1 ? 0 : -1;
     133              : }
     134              : 
     135              : /* ---- RSA (OpenSSL 3.0 EVP API) ---- */
     136              : 
     137              : struct CryptoRsaKey {
     138              :     EVP_PKEY *pkey;
     139              : };
     140              : 
     141            4 : CryptoRsaKey *crypto_rsa_load_public(const char *pem) {
     142            4 :     if (!pem) return NULL;
     143              : 
     144            4 :     BIO *bio = BIO_new_mem_buf(pem, (int)strlen(pem));
     145            4 :     if (!bio) return NULL;
     146              : 
     147            4 :     EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
     148            4 :     BIO_free(bio);
     149              : 
     150            4 :     if (!pkey) return NULL;
     151              : 
     152            4 :     CryptoRsaKey *key = (CryptoRsaKey *)calloc(1, sizeof(CryptoRsaKey));
     153            4 :     if (!key) { EVP_PKEY_free(pkey); return NULL; }
     154            4 :     key->pkey = pkey;
     155            4 :     return key;
     156              : }
     157              : 
     158            5 : void crypto_rsa_free(CryptoRsaKey *key) {
     159            5 :     if (key) {
     160            5 :         EVP_PKEY_free(key->pkey);
     161            5 :         free(key);
     162              :     }
     163            5 : }
     164              : 
     165            1 : CryptoRsaKey *crypto_rsa_load_private(const char *pem) {
     166            1 :     if (!pem) return NULL;
     167              : 
     168            1 :     BIO *bio = BIO_new_mem_buf(pem, (int)strlen(pem));
     169            1 :     if (!bio) return NULL;
     170              : 
     171            1 :     EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
     172            1 :     BIO_free(bio);
     173              : 
     174            1 :     if (!pkey) return NULL;
     175              : 
     176            1 :     CryptoRsaKey *key = (CryptoRsaKey *)calloc(1, sizeof(CryptoRsaKey));
     177            1 :     if (!key) { EVP_PKEY_free(pkey); return NULL; }
     178            1 :     key->pkey = pkey;
     179            1 :     return key;
     180              : }
     181              : 
     182            1 : int crypto_rsa_private_decrypt(CryptoRsaKey *key, const unsigned char *data,
     183              :                                size_t data_len, unsigned char *out, size_t *out_len) {
     184            1 :     if (!key || !data || !out || !out_len) return -1;
     185              : 
     186            1 :     EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->pkey, NULL);
     187            1 :     if (!ctx) return -1;
     188              : 
     189            1 :     if (EVP_PKEY_decrypt_init(ctx) <= 0) {
     190            0 :         EVP_PKEY_CTX_free(ctx);
     191            0 :         return -1;
     192              :     }
     193              : 
     194              :     /* RSA_NO_PADDING — mirrors the RSA_PAD scheme used by the client */
     195            1 :     if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) {
     196            0 :         EVP_PKEY_CTX_free(ctx);
     197            0 :         return -1;
     198              :     }
     199              : 
     200            1 :     if (EVP_PKEY_decrypt(ctx, out, out_len, data, data_len) <= 0) {
     201            0 :         EVP_PKEY_CTX_free(ctx);
     202            0 :         return -1;
     203              :     }
     204              : 
     205            1 :     EVP_PKEY_CTX_free(ctx);
     206            1 :     return 0;
     207              : }
     208              : 
     209            6 : int crypto_rsa_public_encrypt(CryptoRsaKey *key, const unsigned char *data,
     210              :                               size_t data_len, unsigned char *out, size_t *out_len) {
     211            6 :     if (!key || !data || !out || !out_len) return -1;
     212              : 
     213            6 :     EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->pkey, NULL);
     214            6 :     if (!ctx) return -1;
     215              : 
     216            6 :     if (EVP_PKEY_encrypt_init(ctx) <= 0) {
     217            0 :         EVP_PKEY_CTX_free(ctx);
     218            0 :         return -1;
     219              :     }
     220              : 
     221              :     /* RSA_NO_PADDING — Telegram uses its own RSA_PAD scheme on top */
     222            6 :     if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) {
     223            0 :         EVP_PKEY_CTX_free(ctx);
     224            0 :         return -1;
     225              :     }
     226              : 
     227            6 :     if (EVP_PKEY_encrypt(ctx, out, out_len, data, data_len) <= 0) {
     228            2 :         EVP_PKEY_CTX_free(ctx);
     229            2 :         return -1;
     230              :     }
     231              : 
     232            4 :     EVP_PKEY_CTX_free(ctx);
     233            4 :     return 0;
     234              : }
     235              : 
     236              : /* ---- Big Number Arithmetic ---- */
     237              : 
     238              : struct CryptoBnCtx {
     239              :     BN_CTX *ctx;
     240              : };
     241              : 
     242           16 : CryptoBnCtx *crypto_bn_ctx_new(void) {
     243           16 :     CryptoBnCtx *c = (CryptoBnCtx *)calloc(1, sizeof(CryptoBnCtx));
     244           16 :     if (!c) return NULL;
     245           16 :     c->ctx = BN_CTX_new();
     246           16 :     if (!c->ctx) { free(c); return NULL; }
     247           16 :     return c;
     248              : }
     249              : 
     250           16 : void crypto_bn_ctx_free(CryptoBnCtx *ctx) {
     251           16 :     if (ctx) {
     252           16 :         BN_CTX_free(ctx->ctx);
     253           16 :         free(ctx);
     254              :     }
     255           16 : }
     256              : 
     257           51 : int crypto_bn_mod_exp(unsigned char *result, size_t *res_len,
     258              :                       const unsigned char *base, size_t base_len,
     259              :                       const unsigned char *exp, size_t exp_len,
     260              :                       const unsigned char *mod, size_t mod_len,
     261              :                       CryptoBnCtx *ctx) {
     262           51 :     if (!result || !res_len || !base || !exp || !mod || !ctx) return -1;
     263              : 
     264           51 :     BIGNUM *bn_base = BN_bin2bn(base, (int)base_len, NULL);
     265           51 :     BIGNUM *bn_exp  = BN_bin2bn(exp, (int)exp_len, NULL);
     266           51 :     BIGNUM *bn_mod  = BN_bin2bn(mod, (int)mod_len, NULL);
     267           51 :     BIGNUM *bn_res  = BN_new();
     268              : 
     269           51 :     if (!bn_base || !bn_exp || !bn_mod || !bn_res) {
     270            0 :         BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
     271            0 :         return -1;
     272              :     }
     273              : 
     274           51 :     int rc = BN_mod_exp(bn_res, bn_base, bn_exp, bn_mod, ctx->ctx);
     275           51 :     if (rc != 1) {
     276            0 :         BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
     277            0 :         return -1;
     278              :     }
     279              : 
     280           51 :     int bytes = BN_num_bytes(bn_res);
     281           51 :     if ((size_t)bytes > *res_len) {
     282            0 :         BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
     283            0 :         return -1;
     284              :     }
     285              : 
     286              :     /* Left-pad with zeros to fill mod_len */
     287           51 :     size_t actual = (size_t)bytes;
     288           51 :     if (actual < mod_len) {
     289            1 :         memset(result, 0, mod_len - actual);
     290              :     }
     291           51 :     BN_bn2bin(bn_res, result + (mod_len - actual));
     292           51 :     *res_len = mod_len;
     293              : 
     294           51 :     BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
     295           51 :     return 0;
     296              : }
     297              : 
     298              : /* Shared epilogue for mod_mul/add/sub: BN_bn2bin() into left-padded out. */
     299           29 : static int bn_op_finalize(BIGNUM *bn_res,
     300              :                            unsigned char *out, size_t *out_len,
     301              :                            size_t pad_len) {
     302           29 :     int bytes = BN_num_bytes(bn_res);
     303           29 :     if ((size_t)bytes > *out_len) return -1;
     304           29 :     size_t actual = (size_t)bytes;
     305           29 :     if (actual < pad_len) memset(out, 0, pad_len - actual);
     306           29 :     BN_bn2bin(bn_res, out + (pad_len - actual));
     307           29 :     *out_len = pad_len;
     308           29 :     return 0;
     309              : }
     310              : 
     311              : typedef int (*bn_bin_op)(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
     312              :                           const BIGNUM *m, BN_CTX *ctx);
     313              : 
     314           29 : static int bn_mod_op(bn_bin_op op,
     315              :                       unsigned char *result, size_t *res_len,
     316              :                       const unsigned char *a, size_t a_len,
     317              :                       const unsigned char *b, size_t b_len,
     318              :                       const unsigned char *m, size_t m_len,
     319              :                       CryptoBnCtx *ctx) {
     320           29 :     if (!result || !res_len || !a || !b || !m || !ctx) return -1;
     321           29 :     BIGNUM *ba = BN_bin2bn(a, (int)a_len, NULL);
     322           29 :     BIGNUM *bb = BN_bin2bn(b, (int)b_len, NULL);
     323           29 :     BIGNUM *bm = BN_bin2bn(m, (int)m_len, NULL);
     324           29 :     BIGNUM *br = BN_new();
     325           29 :     int rc = -1;
     326           29 :     if (ba && bb && bm && br && op(br, ba, bb, bm, ctx->ctx) == 1) {
     327           29 :         rc = bn_op_finalize(br, result, res_len, m_len);
     328              :     }
     329           29 :     BN_free(ba); BN_free(bb); BN_free(bm); BN_free(br);
     330           29 :     return rc;
     331              : }
     332              : 
     333           19 : int crypto_bn_mod_mul(unsigned char *result, size_t *res_len,
     334              :                        const unsigned char *a, size_t a_len,
     335              :                        const unsigned char *b, size_t b_len,
     336              :                        const unsigned char *m, size_t m_len,
     337              :                        CryptoBnCtx *ctx) {
     338           19 :     return bn_mod_op(BN_mod_mul, result, res_len,
     339              :                       a, a_len, b, b_len, m, m_len, ctx);
     340              : }
     341              : 
     342            0 : int crypto_bn_mod_add(unsigned char *result, size_t *res_len,
     343              :                        const unsigned char *a, size_t a_len,
     344              :                        const unsigned char *b, size_t b_len,
     345              :                        const unsigned char *m, size_t m_len,
     346              :                        CryptoBnCtx *ctx) {
     347            0 :     return bn_mod_op(BN_mod_add, result, res_len,
     348              :                       a, a_len, b, b_len, m, m_len, ctx);
     349              : }
     350              : 
     351           10 : int crypto_bn_mod_sub(unsigned char *result, size_t *res_len,
     352              :                        const unsigned char *a, size_t a_len,
     353              :                        const unsigned char *b, size_t b_len,
     354              :                        const unsigned char *m, size_t m_len,
     355              :                        CryptoBnCtx *ctx) {
     356           10 :     return bn_mod_op(BN_mod_sub, result, res_len,
     357              :                       a, a_len, b, b_len, m, m_len, ctx);
     358              : }
     359              : 
     360            3 : int crypto_bn_ucmp(const unsigned char *a, size_t a_len,
     361              :                     const unsigned char *b, size_t b_len) {
     362            3 :     BIGNUM *ba = BN_bin2bn(a, (int)a_len, NULL);
     363            3 :     BIGNUM *bb = BN_bin2bn(b, (int)b_len, NULL);
     364            3 :     int r = 0;
     365            3 :     if (ba && bb) r = BN_ucmp(ba, bb);
     366            3 :     BN_free(ba); BN_free(bb);
     367            3 :     return r < 0 ? -1 : (r > 0 ? 1 : 0);
     368              : }
        

Generated by: LCOV version 2.0-1