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 <openssl/err.h>
19 : #include <openssl/decoder.h>
20 : #include <openssl/core_names.h>
21 : #include <openssl/param_build.h>
22 : #include <limits.h>
23 : #include <stdio.h>
24 : #include <stdlib.h>
25 : #include <string.h>
26 :
27 8634 : void crypto_sha256(const unsigned char *data, size_t len, unsigned char *out) {
28 8634 : if (EVP_Digest(data, len, out, NULL, EVP_sha256(), NULL) != 1) {
29 0 : fprintf(stderr, "crypto: EVP_Digest(sha256) failed\n");
30 0 : abort();
31 : }
32 8634 : }
33 :
34 1420 : int crypto_aes_set_encrypt_key(const unsigned char *key, int bits,
35 : CryptoAesKey *schedule) {
36 1420 : size_t key_len = (size_t)bits / 8;
37 1420 : memcpy(schedule->key, key, key_len);
38 1420 : schedule->bits = bits;
39 1420 : return 0;
40 : }
41 :
42 1404 : int crypto_aes_set_decrypt_key(const unsigned char *key, int bits,
43 : CryptoAesKey *schedule) {
44 1404 : size_t key_len = (size_t)bits / 8;
45 1404 : memcpy(schedule->key, key, key_len);
46 1404 : schedule->bits = bits;
47 1404 : return 0;
48 : }
49 :
50 2864170 : static const EVP_CIPHER *aes_ecb_cipher(int bits) {
51 2864170 : if (bits == 128) return EVP_aes_128_ecb();
52 2864170 : else if (bits == 192) return EVP_aes_192_ecb();
53 2864170 : else return EVP_aes_256_ecb();
54 : }
55 :
56 1432114 : void crypto_aes_encrypt_block(const unsigned char *in, unsigned char *out,
57 : const CryptoAesKey *schedule) {
58 1432114 : EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
59 1432114 : if (!ctx) { fprintf(stderr, "OOM: EVP_CIPHER_CTX_new\n"); abort(); }
60 1432114 : EVP_EncryptInit_ex(ctx, aes_ecb_cipher(schedule->bits),
61 1432114 : NULL, schedule->key, NULL);
62 1432114 : EVP_CIPHER_CTX_set_padding(ctx, 0);
63 :
64 1432114 : int out_len = 0;
65 1432114 : if (EVP_EncryptUpdate(ctx, out, &out_len, in, 16) != 1) {
66 0 : fprintf(stderr, "crypto: EVP_EncryptUpdate failed\n");
67 0 : abort();
68 : }
69 1432114 : int final_len = 0;
70 1432114 : if (EVP_EncryptFinal_ex(ctx, out + out_len, &final_len) != 1) {
71 0 : fprintf(stderr, "crypto: EVP_EncryptFinal_ex failed\n");
72 0 : abort();
73 : }
74 1432114 : EVP_CIPHER_CTX_free(ctx);
75 1432114 : }
76 :
77 1432056 : void crypto_aes_decrypt_block(const unsigned char *in, unsigned char *out,
78 : const CryptoAesKey *schedule) {
79 1432056 : EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
80 1432056 : if (!ctx) { fprintf(stderr, "OOM: EVP_CIPHER_CTX_new\n"); abort(); }
81 1432056 : EVP_DecryptInit_ex(ctx, aes_ecb_cipher(schedule->bits),
82 1432056 : NULL, schedule->key, NULL);
83 1432056 : EVP_CIPHER_CTX_set_padding(ctx, 0);
84 :
85 1432056 : int out_len = 0;
86 1432056 : if (EVP_DecryptUpdate(ctx, out, &out_len, in, 16) != 1) {
87 0 : fprintf(stderr, "crypto: EVP_DecryptUpdate failed\n");
88 0 : abort();
89 : }
90 1432056 : int final_len = 0;
91 1432056 : if (EVP_DecryptFinal_ex(ctx, out + out_len, &final_len) != 1) {
92 0 : fprintf(stderr, "crypto: EVP_DecryptFinal_ex failed\n");
93 0 : abort();
94 : }
95 1432056 : EVP_CIPHER_CTX_free(ctx);
96 1432056 : }
97 :
98 3316 : int crypto_rand_bytes(unsigned char *buf, size_t len) {
99 : /* QA-18: guard against size_t → int truncation that would leave the
100 : * tail of `buf` uninitialized. The project never asks for > INT_MAX
101 : * bytes in practice, so treat it as an impossible condition and abort,
102 : * matching the project's abort-on-impossible policy. */
103 3316 : if (len > INT_MAX) {
104 0 : fprintf(stderr, "crypto_rand_bytes: len too large\n");
105 0 : abort();
106 : }
107 3316 : return RAND_bytes(buf, (int)len) == 1 ? 0 : -1;
108 : }
109 :
110 : /* ---- SHA-1 ---- */
111 :
112 2034 : void crypto_sha1(const unsigned char *data, size_t len, unsigned char *out) {
113 2034 : if (EVP_Digest(data, len, out, NULL, EVP_sha1(), NULL) != 1) {
114 0 : fprintf(stderr, "crypto: EVP_Digest(sha1) failed\n");
115 0 : abort();
116 : }
117 2034 : }
118 :
119 4 : void crypto_sha512(const unsigned char *data, size_t len, unsigned char *out) {
120 4 : if (EVP_Digest(data, len, out, NULL, EVP_sha512(), NULL) != 1) {
121 0 : fprintf(stderr, "crypto: EVP_Digest(sha512) failed\n");
122 0 : abort();
123 : }
124 4 : }
125 :
126 44 : int crypto_pbkdf2_hmac_sha512(const unsigned char *password, size_t password_len,
127 : const unsigned char *salt, size_t salt_len,
128 : int iters,
129 : unsigned char *out, size_t out_len) {
130 44 : if (!password || !salt || !out || out_len == 0 || iters <= 0) return -1;
131 38 : if (password_len > INT_MAX || salt_len > INT_MAX || out_len > INT_MAX)
132 0 : return -1;
133 38 : int rc = PKCS5_PBKDF2_HMAC((const char *)password, (int)password_len,
134 : salt, (int)salt_len, iters,
135 : EVP_sha512(), (int)out_len, out);
136 38 : return rc == 1 ? 0 : -1;
137 : }
138 :
139 : /* ---- RSA (OpenSSL 3.0 EVP API) ---- */
140 :
141 : struct CryptoRsaKey {
142 : EVP_PKEY *pkey;
143 : };
144 :
145 : /*
146 : * Load an RSA public key from PEM, supporting both formats:
147 : * "-----BEGIN PUBLIC KEY-----" (PKCS#8 SubjectPublicKeyInfo)
148 : * "-----BEGIN RSA PUBLIC KEY-----" (PKCS#1 RSAPublicKey)
149 : * Uses OSSL_DECODER which handles both transparently (OpenSSL 3.0+).
150 : * Returns NULL on failure; caller should check ERR queue for diagnostics.
151 : */
152 8 : static EVP_PKEY *rsa_public_pkey_from_pem(const char *pem) {
153 8 : EVP_PKEY *pkey = NULL;
154 8 : OSSL_DECODER_CTX *dctx = OSSL_DECODER_CTX_new_for_pkey(
155 : &pkey, "PEM", NULL, "RSA",
156 : OSSL_KEYMGMT_SELECT_PUBLIC_KEY, NULL, NULL);
157 8 : if (!dctx) return NULL;
158 :
159 8 : BIO *bio = BIO_new_mem_buf(pem, (int)strlen(pem));
160 8 : if (!bio) { OSSL_DECODER_CTX_free(dctx); return NULL; }
161 :
162 8 : int ok = OSSL_DECODER_from_bio(dctx, bio);
163 8 : OSSL_DECODER_CTX_free(dctx);
164 8 : BIO_free(bio);
165 8 : return ok ? pkey : NULL;
166 : }
167 :
168 8 : CryptoRsaKey *crypto_rsa_load_public(const char *pem) {
169 8 : if (!pem) return NULL;
170 :
171 8 : EVP_PKEY *pkey = rsa_public_pkey_from_pem(pem);
172 8 : if (!pkey) return NULL;
173 :
174 8 : CryptoRsaKey *key = (CryptoRsaKey *)calloc(1, sizeof(CryptoRsaKey));
175 8 : if (!key) { EVP_PKEY_free(pkey); return NULL; }
176 8 : key->pkey = pkey;
177 8 : return key;
178 : }
179 :
180 10 : void crypto_rsa_free(CryptoRsaKey *key) {
181 10 : if (key) {
182 10 : EVP_PKEY_free(key->pkey);
183 10 : free(key);
184 : }
185 10 : }
186 :
187 2 : CryptoRsaKey *crypto_rsa_load_private(const char *pem) {
188 2 : if (!pem) return NULL;
189 :
190 2 : BIO *bio = BIO_new_mem_buf(pem, (int)strlen(pem));
191 2 : if (!bio) return NULL;
192 :
193 2 : EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
194 2 : BIO_free(bio);
195 :
196 2 : if (!pkey) return NULL;
197 :
198 2 : CryptoRsaKey *key = (CryptoRsaKey *)calloc(1, sizeof(CryptoRsaKey));
199 2 : if (!key) { EVP_PKEY_free(pkey); return NULL; }
200 2 : key->pkey = pkey;
201 2 : return key;
202 : }
203 :
204 2 : int crypto_rsa_private_decrypt(CryptoRsaKey *key, const unsigned char *data,
205 : size_t data_len, unsigned char *out, size_t *out_len) {
206 2 : if (!key || !data || !out || !out_len) return -1;
207 :
208 2 : EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->pkey, NULL);
209 2 : if (!ctx) return -1;
210 :
211 2 : if (EVP_PKEY_decrypt_init(ctx) <= 0) {
212 0 : EVP_PKEY_CTX_free(ctx);
213 0 : return -1;
214 : }
215 :
216 : /* RSA_NO_PADDING — mirrors the RSA_PAD scheme used by the client */
217 2 : if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) {
218 0 : EVP_PKEY_CTX_free(ctx);
219 0 : return -1;
220 : }
221 :
222 2 : if (EVP_PKEY_decrypt(ctx, out, out_len, data, data_len) <= 0) {
223 0 : EVP_PKEY_CTX_free(ctx);
224 0 : return -1;
225 : }
226 :
227 2 : EVP_PKEY_CTX_free(ctx);
228 2 : return 0;
229 : }
230 :
231 8 : int crypto_rsa_public_encrypt(CryptoRsaKey *key, const unsigned char *data,
232 : size_t data_len, unsigned char *out, size_t *out_len) {
233 8 : if (!key || !data || !out || !out_len) return -1;
234 :
235 8 : EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->pkey, NULL);
236 8 : if (!ctx) return -1;
237 :
238 8 : if (EVP_PKEY_encrypt_init(ctx) <= 0) {
239 0 : EVP_PKEY_CTX_free(ctx);
240 0 : return -1;
241 : }
242 :
243 : /* RSA_NO_PADDING — Telegram uses its own RSA_PAD scheme on top */
244 8 : if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) {
245 0 : EVP_PKEY_CTX_free(ctx);
246 0 : return -1;
247 : }
248 :
249 8 : if (EVP_PKEY_encrypt(ctx, out, out_len, data, data_len) <= 0) {
250 0 : EVP_PKEY_CTX_free(ctx);
251 0 : return -1;
252 : }
253 :
254 8 : EVP_PKEY_CTX_free(ctx);
255 8 : return 0;
256 : }
257 :
258 : /* ---- RSA fingerprint ---- */
259 :
260 : /*
261 : * Telegram fingerprint: lower 64 bits (little-endian) of
262 : * SHA1( TL_bytes(n_BE) || TL_bytes(e_BE) )
263 : *
264 : * TL bytes encoding: short form (len < 254) = 1-byte-len + data + 4-byte-pad;
265 : * long form (len >= 254) = 0xFE + 3-byte-LE-len + data + pad.
266 : *
267 : * Supports both PKCS#1 ("BEGIN RSA PUBLIC KEY") and
268 : * PKCS#8 ("BEGIN PUBLIC KEY") PEM formats.
269 : * Uses the OpenSSL 3.0 EVP / OSSL_PARAM API throughout — no deprecated calls.
270 : */
271 0 : int crypto_rsa_fingerprint(const char *pem, uint64_t *out) {
272 0 : if (!pem || !out) return -1;
273 :
274 0 : ERR_clear_error();
275 0 : EVP_PKEY *pkey = rsa_public_pkey_from_pem(pem);
276 0 : if (!pkey) {
277 0 : unsigned long e = ERR_peek_last_error();
278 0 : fprintf(stderr,
279 : "crypto_rsa_fingerprint: PEM parse failed%s%s\n"
280 : " Accepted formats: \"BEGIN PUBLIC KEY\" (PKCS#8) or "
281 : "\"BEGIN RSA PUBLIC KEY\" (PKCS#1)\n",
282 : e ? ": " : "",
283 0 : e ? ERR_reason_error_string(e) : "");
284 0 : return -1;
285 : }
286 :
287 : /* Extract n and e as BIGNUMs using the provider-neutral OSSL_PARAM API. */
288 0 : BIGNUM *n = NULL, *e = NULL;
289 0 : if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n) != 1 ||
290 0 : EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &e) != 1) {
291 0 : BN_free(n);
292 0 : BN_free(e);
293 0 : EVP_PKEY_free(pkey);
294 0 : return -1;
295 : }
296 0 : EVP_PKEY_free(pkey);
297 :
298 0 : int n_len = BN_num_bytes(n);
299 0 : int e_len = BN_num_bytes(e);
300 0 : if (n_len <= 0 || e_len <= 0) {
301 0 : BN_free(n); BN_free(e);
302 0 : return -1;
303 : }
304 :
305 : /*
306 : * TL bytes encoding of RSAPublicKey { n:bytes, e:bytes }.
307 : * Short form (len < 254): [1-byte len][data][pad to 4-byte boundary].
308 : * Long form (len >= 254): [0xFE][3-byte LE len][data][pad to 4 bytes].
309 : */
310 : /* n: long form (typically 256 bytes) — (4 + 256) = 260, already 4-aligned */
311 : /* e: short form (typically 3 bytes) — (1 + 3) = 4, already 4-aligned */
312 0 : size_t n_hdr = (n_len < 254) ? 1 : 4;
313 0 : size_t e_hdr = (e_len < 254) ? 1 : 4;
314 0 : size_t n_block = n_hdr + (size_t)n_len;
315 0 : size_t e_block = e_hdr + (size_t)e_len;
316 : /* Pad each block to a 4-byte boundary. */
317 0 : n_block = (n_block + 3) & ~(size_t)3;
318 0 : e_block = (e_block + 3) & ~(size_t)3;
319 :
320 0 : size_t buf_len = n_block + e_block;
321 0 : unsigned char *buf = calloc(1, buf_len); /* calloc fills padding with 0 */
322 0 : if (!buf) { BN_free(n); BN_free(e); return -1; }
323 :
324 : /* Encode n */
325 0 : if (n_len < 254) {
326 0 : buf[0] = (unsigned char)n_len;
327 0 : BN_bn2bin(n, buf + 1);
328 : } else {
329 0 : buf[0] = 0xFE;
330 0 : buf[1] = (unsigned char)(n_len & 0xFF);
331 0 : buf[2] = (unsigned char)((n_len >> 8) & 0xFF);
332 0 : buf[3] = (unsigned char)((n_len >> 16) & 0xFF);
333 0 : BN_bn2bin(n, buf + 4);
334 : }
335 :
336 : /* Encode e */
337 0 : size_t off = n_block;
338 0 : if (e_len < 254) {
339 0 : buf[off] = (unsigned char)e_len;
340 0 : BN_bn2bin(e, buf + off + 1);
341 : } else {
342 0 : buf[off] = 0xFE;
343 0 : buf[off + 1] = (unsigned char)(e_len & 0xFF);
344 0 : buf[off + 2] = (unsigned char)((e_len >> 8) & 0xFF);
345 0 : buf[off + 3] = (unsigned char)((e_len >> 16) & 0xFF);
346 0 : BN_bn2bin(e, buf + off + 4);
347 : }
348 :
349 0 : BN_free(n);
350 0 : BN_free(e);
351 :
352 : /* SHA1 → take lower 64 bits (last 8 bytes of 20-byte digest). */
353 : unsigned char sha1_out[20];
354 0 : crypto_sha1(buf, buf_len, sha1_out);
355 0 : free(buf);
356 :
357 : /* Lower 64 bits = bytes [12..19] interpreted as little-endian. */
358 0 : uint64_t fp = 0;
359 0 : for (int i = 0; i < 8; i++) {
360 0 : fp |= ((uint64_t)sha1_out[12 + i]) << (8 * i);
361 : }
362 0 : *out = fp;
363 0 : return 0;
364 : }
365 :
366 : /* ---- Big Number Arithmetic ---- */
367 :
368 : struct CryptoBnCtx {
369 : BN_CTX *ctx;
370 : };
371 :
372 32 : CryptoBnCtx *crypto_bn_ctx_new(void) {
373 32 : CryptoBnCtx *c = (CryptoBnCtx *)calloc(1, sizeof(CryptoBnCtx));
374 32 : if (!c) return NULL;
375 32 : c->ctx = BN_CTX_new();
376 32 : if (!c->ctx) { free(c); return NULL; }
377 32 : return c;
378 : }
379 :
380 32 : void crypto_bn_ctx_free(CryptoBnCtx *ctx) {
381 32 : if (ctx) {
382 32 : BN_CTX_free(ctx->ctx);
383 32 : free(ctx);
384 : }
385 32 : }
386 :
387 102 : int crypto_bn_mod_exp(unsigned char *result, size_t *res_len,
388 : const unsigned char *base, size_t base_len,
389 : const unsigned char *exp, size_t exp_len,
390 : const unsigned char *mod, size_t mod_len,
391 : CryptoBnCtx *ctx) {
392 102 : if (!result || !res_len || !base || !exp || !mod || !ctx) return -1;
393 :
394 102 : BIGNUM *bn_base = BN_bin2bn(base, (int)base_len, NULL);
395 102 : BIGNUM *bn_exp = BN_bin2bn(exp, (int)exp_len, NULL);
396 102 : BIGNUM *bn_mod = BN_bin2bn(mod, (int)mod_len, NULL);
397 102 : BIGNUM *bn_res = BN_new();
398 :
399 102 : if (!bn_base || !bn_exp || !bn_mod || !bn_res) {
400 0 : BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
401 0 : return -1;
402 : }
403 :
404 102 : int rc = BN_mod_exp(bn_res, bn_base, bn_exp, bn_mod, ctx->ctx);
405 102 : if (rc != 1) {
406 0 : BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
407 0 : return -1;
408 : }
409 :
410 102 : int bytes = BN_num_bytes(bn_res);
411 102 : if ((size_t)bytes > *res_len) {
412 0 : BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
413 0 : return -1;
414 : }
415 :
416 : /* Left-pad with zeros to fill mod_len */
417 102 : size_t actual = (size_t)bytes;
418 102 : if (actual < mod_len) {
419 2 : memset(result, 0, mod_len - actual);
420 : }
421 102 : BN_bn2bin(bn_res, result + (mod_len - actual));
422 102 : *res_len = mod_len;
423 :
424 102 : BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
425 102 : return 0;
426 : }
427 :
428 : /* Shared epilogue for mod_mul/add/sub: BN_bn2bin() into left-padded out. */
429 58 : static int bn_op_finalize(BIGNUM *bn_res,
430 : unsigned char *out, size_t *out_len,
431 : size_t pad_len) {
432 58 : int bytes = BN_num_bytes(bn_res);
433 58 : if ((size_t)bytes > *out_len) return -1;
434 58 : size_t actual = (size_t)bytes;
435 58 : if (actual < pad_len) memset(out, 0, pad_len - actual);
436 58 : BN_bn2bin(bn_res, out + (pad_len - actual));
437 58 : *out_len = pad_len;
438 58 : return 0;
439 : }
440 :
441 : typedef int (*bn_bin_op)(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
442 : const BIGNUM *m, BN_CTX *ctx);
443 :
444 58 : static int bn_mod_op(bn_bin_op op,
445 : unsigned char *result, size_t *res_len,
446 : const unsigned char *a, size_t a_len,
447 : const unsigned char *b, size_t b_len,
448 : const unsigned char *m, size_t m_len,
449 : CryptoBnCtx *ctx) {
450 58 : if (!result || !res_len || !a || !b || !m || !ctx) return -1;
451 58 : BIGNUM *ba = BN_bin2bn(a, (int)a_len, NULL);
452 58 : BIGNUM *bb = BN_bin2bn(b, (int)b_len, NULL);
453 58 : BIGNUM *bm = BN_bin2bn(m, (int)m_len, NULL);
454 58 : BIGNUM *br = BN_new();
455 58 : int rc = -1;
456 58 : if (ba && bb && bm && br && op(br, ba, bb, bm, ctx->ctx) == 1) {
457 58 : rc = bn_op_finalize(br, result, res_len, m_len);
458 : }
459 58 : BN_free(ba); BN_free(bb); BN_free(bm); BN_free(br);
460 58 : return rc;
461 : }
462 :
463 38 : int crypto_bn_mod_mul(unsigned char *result, size_t *res_len,
464 : const unsigned char *a, size_t a_len,
465 : const unsigned char *b, size_t b_len,
466 : const unsigned char *m, size_t m_len,
467 : CryptoBnCtx *ctx) {
468 38 : return bn_mod_op(BN_mod_mul, result, res_len,
469 : a, a_len, b, b_len, m, m_len, ctx);
470 : }
471 :
472 0 : int crypto_bn_mod_add(unsigned char *result, size_t *res_len,
473 : const unsigned char *a, size_t a_len,
474 : const unsigned char *b, size_t b_len,
475 : const unsigned char *m, size_t m_len,
476 : CryptoBnCtx *ctx) {
477 0 : return bn_mod_op(BN_mod_add, result, res_len,
478 : a, a_len, b, b_len, m, m_len, ctx);
479 : }
480 :
481 20 : int crypto_bn_mod_sub(unsigned char *result, size_t *res_len,
482 : const unsigned char *a, size_t a_len,
483 : const unsigned char *b, size_t b_len,
484 : const unsigned char *m, size_t m_len,
485 : CryptoBnCtx *ctx) {
486 20 : return bn_mod_op(BN_mod_sub, result, res_len,
487 : a, a_len, b, b_len, m, m_len, ctx);
488 : }
489 :
490 6 : int crypto_bn_ucmp(const unsigned char *a, size_t a_len,
491 : const unsigned char *b, size_t b_len) {
492 6 : BIGNUM *ba = BN_bin2bn(a, (int)a_len, NULL);
493 6 : BIGNUM *bb = BN_bin2bn(b, (int)b_len, NULL);
494 6 : int r = 0;
495 6 : if (ba && bb) r = BN_ucmp(ba, bb);
496 6 : BN_free(ba); BN_free(bb);
497 6 : return r < 0 ? -1 : (r > 0 ? 1 : 0);
498 : }
|