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 9832 : void crypto_sha256(const unsigned char *data, size_t len, unsigned char *out) {
24 9832 : 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 9832 : }
29 :
30 1338 : int crypto_aes_set_encrypt_key(const unsigned char *key, int bits,
31 : CryptoAesKey *schedule) {
32 1338 : size_t key_len = (size_t)bits / 8;
33 1338 : memcpy(schedule->key, key, key_len);
34 1338 : schedule->bits = bits;
35 1338 : return 0;
36 : }
37 :
38 1312 : int crypto_aes_set_decrypt_key(const unsigned char *key, int bits,
39 : CryptoAesKey *schedule) {
40 1312 : size_t key_len = (size_t)bits / 8;
41 1312 : memcpy(schedule->key, key, key_len);
42 1312 : schedule->bits = bits;
43 1312 : return 0;
44 : }
45 :
46 2863730 : static const EVP_CIPHER *aes_ecb_cipher(int bits) {
47 2863730 : if (bits == 128) return EVP_aes_128_ecb();
48 2863730 : else if (bits == 192) return EVP_aes_192_ecb();
49 2863730 : else return EVP_aes_256_ecb();
50 : }
51 :
52 1431962 : void crypto_aes_encrypt_block(const unsigned char *in, unsigned char *out,
53 : const CryptoAesKey *schedule) {
54 1431962 : EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
55 1431962 : if (!ctx) { fprintf(stderr, "OOM: EVP_CIPHER_CTX_new\n"); abort(); }
56 1431962 : EVP_EncryptInit_ex(ctx, aes_ecb_cipher(schedule->bits),
57 1431962 : NULL, schedule->key, NULL);
58 1431962 : EVP_CIPHER_CTX_set_padding(ctx, 0);
59 :
60 1431962 : int out_len = 0;
61 1431962 : if (EVP_EncryptUpdate(ctx, out, &out_len, in, 16) != 1) {
62 0 : fprintf(stderr, "crypto: EVP_EncryptUpdate failed\n");
63 0 : abort();
64 : }
65 1431962 : int final_len = 0;
66 1431962 : 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 1431962 : EVP_CIPHER_CTX_free(ctx);
71 1431962 : }
72 :
73 1431768 : void crypto_aes_decrypt_block(const unsigned char *in, unsigned char *out,
74 : const CryptoAesKey *schedule) {
75 1431768 : EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
76 1431768 : if (!ctx) { fprintf(stderr, "OOM: EVP_CIPHER_CTX_new\n"); abort(); }
77 1431768 : EVP_DecryptInit_ex(ctx, aes_ecb_cipher(schedule->bits),
78 1431768 : NULL, schedule->key, NULL);
79 1431768 : EVP_CIPHER_CTX_set_padding(ctx, 0);
80 :
81 1431768 : int out_len = 0;
82 1431768 : if (EVP_DecryptUpdate(ctx, out, &out_len, in, 16) != 1) {
83 0 : fprintf(stderr, "crypto: EVP_DecryptUpdate failed\n");
84 0 : abort();
85 : }
86 1431768 : int final_len = 0;
87 1431768 : 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 1431768 : EVP_CIPHER_CTX_free(ctx);
92 1431768 : }
93 :
94 3250 : 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 3250 : if (len > INT_MAX) {
100 0 : fprintf(stderr, "crypto_rand_bytes: len too large\n");
101 0 : abort();
102 : }
103 3250 : return RAND_bytes(buf, (int)len) == 1 ? 0 : -1;
104 : }
105 :
106 : /* ---- SHA-1 ---- */
107 :
108 42 : void crypto_sha1(const unsigned char *data, size_t len, unsigned char *out) {
109 42 : 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 42 : }
114 :
115 4 : void crypto_sha512(const unsigned char *data, size_t len, unsigned char *out) {
116 4 : 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 4 : }
121 :
122 44 : 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 44 : if (!password || !salt || !out || out_len == 0 || iters <= 0) return -1;
127 38 : if (password_len > INT_MAX || salt_len > INT_MAX || out_len > INT_MAX)
128 0 : return -1;
129 38 : 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 38 : 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 8 : CryptoRsaKey *crypto_rsa_load_public(const char *pem) {
142 8 : if (!pem) return NULL;
143 :
144 8 : BIO *bio = BIO_new_mem_buf(pem, (int)strlen(pem));
145 8 : if (!bio) return NULL;
146 :
147 8 : EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
148 8 : BIO_free(bio);
149 :
150 8 : if (!pkey) return NULL;
151 :
152 8 : CryptoRsaKey *key = (CryptoRsaKey *)calloc(1, sizeof(CryptoRsaKey));
153 8 : if (!key) { EVP_PKEY_free(pkey); return NULL; }
154 8 : key->pkey = pkey;
155 8 : return key;
156 : }
157 :
158 10 : void crypto_rsa_free(CryptoRsaKey *key) {
159 10 : if (key) {
160 10 : EVP_PKEY_free(key->pkey);
161 10 : free(key);
162 : }
163 10 : }
164 :
165 2 : CryptoRsaKey *crypto_rsa_load_private(const char *pem) {
166 2 : if (!pem) return NULL;
167 :
168 2 : BIO *bio = BIO_new_mem_buf(pem, (int)strlen(pem));
169 2 : if (!bio) return NULL;
170 :
171 2 : EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
172 2 : BIO_free(bio);
173 :
174 2 : if (!pkey) return NULL;
175 :
176 2 : CryptoRsaKey *key = (CryptoRsaKey *)calloc(1, sizeof(CryptoRsaKey));
177 2 : if (!key) { EVP_PKEY_free(pkey); return NULL; }
178 2 : key->pkey = pkey;
179 2 : return key;
180 : }
181 :
182 2 : int crypto_rsa_private_decrypt(CryptoRsaKey *key, const unsigned char *data,
183 : size_t data_len, unsigned char *out, size_t *out_len) {
184 2 : if (!key || !data || !out || !out_len) return -1;
185 :
186 2 : EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->pkey, NULL);
187 2 : if (!ctx) return -1;
188 :
189 2 : 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 2 : 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 2 : 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 2 : EVP_PKEY_CTX_free(ctx);
206 2 : return 0;
207 : }
208 :
209 12 : int crypto_rsa_public_encrypt(CryptoRsaKey *key, const unsigned char *data,
210 : size_t data_len, unsigned char *out, size_t *out_len) {
211 12 : if (!key || !data || !out || !out_len) return -1;
212 :
213 12 : EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->pkey, NULL);
214 12 : if (!ctx) return -1;
215 :
216 12 : 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 12 : 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 12 : if (EVP_PKEY_encrypt(ctx, out, out_len, data, data_len) <= 0) {
228 4 : EVP_PKEY_CTX_free(ctx);
229 4 : return -1;
230 : }
231 :
232 8 : EVP_PKEY_CTX_free(ctx);
233 8 : return 0;
234 : }
235 :
236 : /* ---- Big Number Arithmetic ---- */
237 :
238 : struct CryptoBnCtx {
239 : BN_CTX *ctx;
240 : };
241 :
242 32 : CryptoBnCtx *crypto_bn_ctx_new(void) {
243 32 : CryptoBnCtx *c = (CryptoBnCtx *)calloc(1, sizeof(CryptoBnCtx));
244 32 : if (!c) return NULL;
245 32 : c->ctx = BN_CTX_new();
246 32 : if (!c->ctx) { free(c); return NULL; }
247 32 : return c;
248 : }
249 :
250 32 : void crypto_bn_ctx_free(CryptoBnCtx *ctx) {
251 32 : if (ctx) {
252 32 : BN_CTX_free(ctx->ctx);
253 32 : free(ctx);
254 : }
255 32 : }
256 :
257 102 : 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 102 : if (!result || !res_len || !base || !exp || !mod || !ctx) return -1;
263 :
264 102 : BIGNUM *bn_base = BN_bin2bn(base, (int)base_len, NULL);
265 102 : BIGNUM *bn_exp = BN_bin2bn(exp, (int)exp_len, NULL);
266 102 : BIGNUM *bn_mod = BN_bin2bn(mod, (int)mod_len, NULL);
267 102 : BIGNUM *bn_res = BN_new();
268 :
269 102 : 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 102 : int rc = BN_mod_exp(bn_res, bn_base, bn_exp, bn_mod, ctx->ctx);
275 102 : 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 102 : int bytes = BN_num_bytes(bn_res);
281 102 : 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 102 : size_t actual = (size_t)bytes;
288 102 : if (actual < mod_len) {
289 2 : memset(result, 0, mod_len - actual);
290 : }
291 102 : BN_bn2bin(bn_res, result + (mod_len - actual));
292 102 : *res_len = mod_len;
293 :
294 102 : BN_free(bn_base); BN_free(bn_exp); BN_free(bn_mod); BN_free(bn_res);
295 102 : return 0;
296 : }
297 :
298 : /* Shared epilogue for mod_mul/add/sub: BN_bn2bin() into left-padded out. */
299 58 : static int bn_op_finalize(BIGNUM *bn_res,
300 : unsigned char *out, size_t *out_len,
301 : size_t pad_len) {
302 58 : int bytes = BN_num_bytes(bn_res);
303 58 : if ((size_t)bytes > *out_len) return -1;
304 58 : size_t actual = (size_t)bytes;
305 58 : if (actual < pad_len) memset(out, 0, pad_len - actual);
306 58 : BN_bn2bin(bn_res, out + (pad_len - actual));
307 58 : *out_len = pad_len;
308 58 : 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 58 : 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 58 : if (!result || !res_len || !a || !b || !m || !ctx) return -1;
321 58 : BIGNUM *ba = BN_bin2bn(a, (int)a_len, NULL);
322 58 : BIGNUM *bb = BN_bin2bn(b, (int)b_len, NULL);
323 58 : BIGNUM *bm = BN_bin2bn(m, (int)m_len, NULL);
324 58 : BIGNUM *br = BN_new();
325 58 : int rc = -1;
326 58 : if (ba && bb && bm && br && op(br, ba, bb, bm, ctx->ctx) == 1) {
327 58 : rc = bn_op_finalize(br, result, res_len, m_len);
328 : }
329 58 : BN_free(ba); BN_free(bb); BN_free(bm); BN_free(br);
330 58 : return rc;
331 : }
332 :
333 38 : 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 38 : 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 20 : 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 20 : return bn_mod_op(BN_mod_sub, result, res_len,
357 : a, a_len, b, b_len, m, m_len, ctx);
358 : }
359 :
360 6 : int crypto_bn_ucmp(const unsigned char *a, size_t a_len,
361 : const unsigned char *b, size_t b_len) {
362 6 : BIGNUM *ba = BN_bin2bn(a, (int)a_len, NULL);
363 6 : BIGNUM *bb = BN_bin2bn(b, (int)b_len, NULL);
364 6 : int r = 0;
365 6 : if (ba && bb) r = BN_ucmp(ba, bb);
366 6 : BN_free(ba); BN_free(bb);
367 6 : return r < 0 ? -1 : (r > 0 ? 1 : 0);
368 : }
|