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 : }
|