EasyCrypto.h (28396B)
1 /* 2 * EasyCrypto - Simple API for Authenticated Encryption 3 * 4 * Provides easy-to-use macros for encryption with proper defaults. 5 * All encryption includes authentication (AEAD). 6 * 7 * Usage: 8 * ENCRYPT(key, data, length) - Uses AES-256-GCM by default (hardware accelerated) 9 * DECRYPT(key, encrypted_data, length) - Automatically verifies and decrypts 10 * 11 * With algorithm selection: 12 * ENCRYPT_AES(key, data, length) - AES-256-GCM (fastest with AES-NI) 13 * ENCRYPT_CHACHA(key, data, length) - ChaCha20-Poly1305 (fast without hardware) 14 * ENCRYPT_XCHACHA(key, data, length) - XChaCha20-Poly1305 (192-bit nonces) 15 * ENCRYPT_SERPENT(key, data, length) - Serpent-256-GCM (conservative) 16 * ENCRYPT_TWOFISH(key, data, length) - Twofish-256-GCM (Bruce Schneier's design) 17 */ 18 19 #ifndef EASYCRYPTO_H 20 #define EASYCRYPTO_H 21 22 #include "ChaCha20-Poly1305.h" 23 #include "XChaCha20-Poly1305.h" 24 #include "Salsa20.h" 25 #include "AES-256-GCM.h" 26 #include "AES-128-GCM.h" 27 #include "Serpent-256-GCM.h" 28 #include "Twofish-256-GCM.h" 29 #include "CSPRNG.h" 30 #include <stdlib.h> 31 #include <string.h> 32 33 #ifdef __cplusplus 34 extern "C" { 35 #endif 36 37 /* Encrypted data structure */ 38 typedef struct { 39 uint8_t *nonce; /* 12 bytes for ChaCha20/AES/Serpent/Twofish */ 40 uint8_t *ciphertext; /* Same length as plaintext */ 41 uint8_t *tag; /* 16 bytes authentication tag */ 42 size_t nonce_len; 43 size_t ciphertext_len; 44 size_t tag_len; 45 int algorithm; /* 0=ChaCha20, 1=AES, 2=Serpent, 3=Twofish */ 46 } encrypted_data; 47 48 /* Algorithm identifiers */ 49 #define ALGO_CHACHA20 0 50 #define ALGO_AES 1 51 #define ALGO_SERPENT 2 52 #define ALGO_TWOFISH 3 53 54 /** 55 * Encrypt data with AES-128-GCM 56 * Returns serialized format: [Nonce(12)][Tag(16)][Ciphertext(var)] 57 * 58 * @param key 128-bit (16 byte) encryption key 59 * @param plaintext Plaintext data 60 * @param plaintext_len Plaintext length 61 * @param output_len Output parameter for encrypted data length 62 * @return Encrypted data [Nonce][Tag][Ciphertext] (caller must free) or NULL on failure 63 */ 64 static inline void* easycrypto_encrypt_aes128( 65 const uint8_t *key, const uint8_t *plaintext, size_t plaintext_len, size_t *output_len) { 66 67 if (!key || !plaintext || !output_len) return NULL; 68 69 /* Calculate output size: 12 (nonce) + 16 (tag) + plaintext_len */ 70 size_t total_len = 12 + 16 + plaintext_len; 71 uint8_t *output = malloc(total_len); 72 if (!output) return NULL; 73 74 /* Generate random nonce */ 75 random_bytes(output, 12); 76 77 /* Encrypt */ 78 aes128_gcm_context ctx; 79 if (aes128_gcm_init(&ctx, key) != 0) { 80 free(output); 81 return NULL; 82 } 83 84 /* output layout: [nonce(12)][tag(16)][ciphertext] */ 85 uint8_t *tag = output + 12; 86 uint8_t *ciphertext = output + 28; 87 88 if (aes128_gcm_encrypt(&ctx, output, 12, NULL, 0, 89 plaintext, plaintext_len, 90 ciphertext, tag, 16) != 0) { 91 aes128_gcm_cleanup(&ctx); 92 free(output); 93 return NULL; 94 } 95 96 aes128_gcm_cleanup(&ctx); 97 *output_len = total_len; 98 return output; 99 } 100 101 /** 102 * Decrypt data with AES-128-GCM 103 * Expects format: [Nonce(12)][Tag(16)][Ciphertext(var)] 104 * 105 * @param key 128-bit (16 byte) encryption key 106 * @param encrypted Encrypted data [Nonce][Tag][Ciphertext] 107 * @param encrypted_len Length of encrypted data 108 * @param output_len Output parameter for plaintext length 109 * @return Decrypted data (caller must free) or NULL on failure 110 */ 111 static inline void* easycrypto_decrypt_aes128( 112 const uint8_t *key, const void *encrypted, size_t encrypted_len, size_t *output_len) { 113 114 if (!key || !encrypted || !output_len || encrypted_len < 28) return NULL; 115 116 const uint8_t *data = (const uint8_t*)encrypted; 117 118 /* Parse input: [nonce(12)][tag(16)][ciphertext] */ 119 const uint8_t *nonce = data; 120 const uint8_t *tag = data + 12; 121 const uint8_t *ciphertext = data + 28; 122 size_t ciphertext_len = encrypted_len - 28; 123 124 /* Allocate output */ 125 uint8_t *plaintext = malloc(ciphertext_len); 126 if (!plaintext) return NULL; 127 128 /* Decrypt */ 129 aes128_gcm_context ctx; 130 if (aes128_gcm_init(&ctx, key) != 0) { 131 free(plaintext); 132 return NULL; 133 } 134 135 if (aes128_gcm_decrypt(&ctx, nonce, 12, NULL, 0, 136 ciphertext, ciphertext_len, 137 tag, 16, plaintext) != 0) { 138 aes128_gcm_cleanup(&ctx); 139 memset(plaintext, 0, ciphertext_len); 140 free(plaintext); 141 return NULL; 142 } 143 144 aes128_gcm_cleanup(&ctx); 145 *output_len = ciphertext_len; 146 return plaintext; 147 } 148 149 /** 150 * Encrypt data with AES-256-GCM (default) 151 * Returns serialized format: [Nonce(12)][Tag(16)][Ciphertext(var)] 152 * 153 * @param key 256-bit (32 byte) encryption key 154 * @param plaintext Plaintext data 155 * @param plaintext_len Plaintext length 156 * @param output_len Output parameter for encrypted data length 157 * @return Encrypted data [Nonce][Tag][Ciphertext] (caller must free) or NULL on failure 158 */ 159 static inline void* easycrypto_encrypt_aes( 160 const uint8_t *key, const uint8_t *plaintext, size_t plaintext_len, size_t *output_len) { 161 162 if (!key || !plaintext || !output_len) return NULL; 163 164 /* Calculate output size: 12 (nonce) + 16 (tag) + plaintext_len */ 165 size_t total_len = 12 + 16 + plaintext_len; 166 uint8_t *output = malloc(total_len); 167 if (!output) return NULL; 168 169 /* Generate random nonce */ 170 random_bytes(output, 12); 171 172 /* Encrypt */ 173 aes256_gcm_context ctx; 174 if (aes256_gcm_init(&ctx, key) != 0) { 175 free(output); 176 return NULL; 177 } 178 179 /* output layout: [nonce(12)][tag(16)][ciphertext] */ 180 uint8_t *tag = output + 12; 181 uint8_t *ciphertext = output + 28; 182 183 if (aes256_gcm_encrypt(&ctx, output, 12, NULL, 0, 184 plaintext, plaintext_len, 185 ciphertext, tag, 16) != 0) { 186 aes256_gcm_cleanup(&ctx); 187 free(output); 188 return NULL; 189 } 190 191 aes256_gcm_cleanup(&ctx); 192 *output_len = total_len; 193 return output; 194 } 195 196 /** 197 * Decrypt data with AES-256-GCM 198 * Expects format: [Nonce(12)][Tag(16)][Ciphertext(var)] 199 * 200 * @param key 256-bit (32 byte) encryption key 201 * @param encrypted Encrypted data [Nonce][Tag][Ciphertext] 202 * @param encrypted_len Length of encrypted data 203 * @param output_len Output parameter for plaintext length 204 * @return Decrypted data (caller must free) or NULL on failure 205 */ 206 static inline void* easycrypto_decrypt_aes( 207 const uint8_t *key, const void *encrypted, size_t encrypted_len, size_t *output_len) { 208 209 if (!key || !encrypted || !output_len || encrypted_len < 28) return NULL; 210 211 const uint8_t *data = (const uint8_t*)encrypted; 212 213 /* Parse input: [nonce(12)][tag(16)][ciphertext] */ 214 const uint8_t *nonce = data; 215 const uint8_t *tag = data + 12; 216 const uint8_t *ciphertext = data + 28; 217 size_t ciphertext_len = encrypted_len - 28; 218 219 /* Allocate output */ 220 uint8_t *plaintext = malloc(ciphertext_len); 221 if (!plaintext) return NULL; 222 223 /* Decrypt */ 224 aes256_gcm_context ctx; 225 if (aes256_gcm_init(&ctx, key) != 0) { 226 free(plaintext); 227 return NULL; 228 } 229 230 if (aes256_gcm_decrypt(&ctx, nonce, 12, NULL, 0, 231 ciphertext, ciphertext_len, 232 tag, 16, plaintext) != 0) { 233 aes256_gcm_cleanup(&ctx); 234 memset(plaintext, 0, ciphertext_len); 235 free(plaintext); 236 return NULL; 237 } 238 239 aes256_gcm_cleanup(&ctx); 240 *output_len = ciphertext_len; 241 return plaintext; 242 } 243 244 /** 245 * Encrypt data with ChaCha20-Poly1305 246 * Returns serialized format: [Nonce(12)][Tag(16)][Ciphertext(var)] 247 * 248 * @param key 256-bit (32 byte) encryption key 249 * @param plaintext Plaintext data 250 * @param plaintext_len Plaintext length 251 * @param output_len Output parameter for encrypted data length 252 * @return Encrypted data [Nonce][Tag][Ciphertext] (caller must free) or NULL on failure 253 */ 254 static inline void* easycrypto_encrypt_chacha( 255 const uint8_t *key, const uint8_t *plaintext, size_t plaintext_len, size_t *output_len) { 256 257 if (!key || !plaintext || !output_len) return NULL; 258 259 /* Calculate output size: 12 (nonce) + 16 (tag) + plaintext_len */ 260 size_t total_len = 12 + 16 + plaintext_len; 261 uint8_t *output = malloc(total_len); 262 if (!output) return NULL; 263 264 /* Generate random nonce */ 265 random_bytes(output, 12); 266 267 /* Encrypt */ 268 chacha20_poly1305_context ctx; 269 if (chacha20_poly1305_init(&ctx, key, output) != 0) { 270 free(output); 271 return NULL; 272 } 273 274 /* output layout: [nonce(12)][tag(16)][ciphertext] */ 275 uint8_t *tag = output + 12; 276 uint8_t *ciphertext = output + 28; 277 278 if (chacha20_poly1305_encrypt(&ctx, NULL, 0, plaintext, plaintext_len, 279 ciphertext, tag) != 0) { 280 chacha20_poly1305_cleanup(&ctx); 281 free(output); 282 return NULL; 283 } 284 285 chacha20_poly1305_cleanup(&ctx); 286 *output_len = total_len; 287 return output; 288 } 289 290 /** 291 * Decrypt data with ChaCha20-Poly1305 292 * Expects format: [Nonce(12)][Tag(16)][Ciphertext(var)] 293 * 294 * @param key 256-bit (32 byte) encryption key 295 * @param encrypted Encrypted data [Nonce][Tag][Ciphertext] 296 * @param encrypted_len Length of encrypted data 297 * @param output_len Output parameter for plaintext length 298 * @return Decrypted data (caller must free) or NULL on failure 299 */ 300 static inline void* easycrypto_decrypt_chacha( 301 const uint8_t *key, const void *encrypted, size_t encrypted_len, size_t *output_len) { 302 303 if (!key || !encrypted || !output_len || encrypted_len < 28) return NULL; 304 305 const uint8_t *data = (const uint8_t*)encrypted; 306 307 /* Parse input: [nonce(12)][tag(16)][ciphertext] */ 308 const uint8_t *nonce = data; 309 const uint8_t *tag = data + 12; 310 const uint8_t *ciphertext = data + 28; 311 size_t ciphertext_len = encrypted_len - 28; 312 313 /* Allocate output */ 314 uint8_t *plaintext = malloc(ciphertext_len); 315 if (!plaintext) return NULL; 316 317 /* Decrypt */ 318 chacha20_poly1305_context ctx; 319 if (chacha20_poly1305_init(&ctx, key, nonce) != 0) { 320 free(plaintext); 321 return NULL; 322 } 323 324 if (chacha20_poly1305_decrypt(&ctx, NULL, 0, 325 ciphertext, ciphertext_len, 326 tag, plaintext) != 0) { 327 chacha20_poly1305_cleanup(&ctx); 328 memset(plaintext, 0, ciphertext_len); 329 free(plaintext); 330 return NULL; 331 } 332 333 chacha20_poly1305_cleanup(&ctx); 334 *output_len = ciphertext_len; 335 return plaintext; 336 } 337 338 /** 339 * Encrypt data with XChaCha20-Poly1305 340 * Returns serialized format: [Nonce(24)][Tag(16)][Ciphertext(var)] 341 * 342 * @param key 256-bit (32 byte) encryption key 343 * @param plaintext Plaintext data 344 * @param plaintext_len Plaintext length 345 * @param output_len Output parameter for encrypted data length 346 * @return Encrypted data [Nonce][Tag][Ciphertext] (caller must free) or NULL on failure 347 * 348 * Note: XChaCha20 uses 192-bit (24 byte) nonces - can be randomly generated safely 349 */ 350 static inline void* easycrypto_encrypt_xchacha( 351 const uint8_t *key, const uint8_t *plaintext, size_t plaintext_len, size_t *output_len) { 352 353 if (!key || !plaintext || !output_len) return NULL; 354 355 /* Calculate output size: 24 (nonce) + 16 (tag) + plaintext_len */ 356 size_t total_len = 24 + 16 + plaintext_len; 357 uint8_t *output = malloc(total_len); 358 if (!output) return NULL; 359 360 /* Generate random nonce (safe with 192 bits) */ 361 random_bytes(output, 24); 362 363 /* Encrypt */ 364 xchacha20_poly1305_context ctx; 365 if (xchacha20_poly1305_init(&ctx, key, output) != 0) { 366 free(output); 367 return NULL; 368 } 369 370 /* output layout: [nonce(24)][tag(16)][ciphertext] */ 371 uint8_t *tag = output + 24; 372 uint8_t *ciphertext = output + 40; 373 374 if (xchacha20_poly1305_encrypt(&ctx, NULL, 0, plaintext, plaintext_len, 375 ciphertext, tag) != 0) { 376 xchacha20_poly1305_cleanup(&ctx); 377 free(output); 378 return NULL; 379 } 380 381 xchacha20_poly1305_cleanup(&ctx); 382 *output_len = total_len; 383 return output; 384 } 385 386 /** 387 * Decrypt data with XChaCha20-Poly1305 388 * Expects format: [Nonce(24)][Tag(16)][Ciphertext(var)] 389 * 390 * @param key 256-bit (32 byte) encryption key 391 * @param encrypted Encrypted data [Nonce][Tag][Ciphertext] 392 * @param encrypted_len Length of encrypted data 393 * @param output_len Output parameter for plaintext length 394 * @return Decrypted data (caller must free) or NULL on failure 395 */ 396 static inline void* easycrypto_decrypt_xchacha( 397 const uint8_t *key, const void *encrypted, size_t encrypted_len, size_t *output_len) { 398 399 if (!key || !encrypted || !output_len || encrypted_len < 40) return NULL; 400 401 const uint8_t *data = (const uint8_t*)encrypted; 402 403 /* Parse input: [nonce(24)][tag(16)][ciphertext] */ 404 const uint8_t *nonce = data; 405 const uint8_t *tag = data + 24; 406 const uint8_t *ciphertext = data + 40; 407 size_t ciphertext_len = encrypted_len - 40; 408 409 /* Allocate output */ 410 uint8_t *plaintext = malloc(ciphertext_len); 411 if (!plaintext) return NULL; 412 413 /* Decrypt */ 414 xchacha20_poly1305_context ctx; 415 if (xchacha20_poly1305_init(&ctx, key, nonce) != 0) { 416 free(plaintext); 417 return NULL; 418 } 419 420 if (xchacha20_poly1305_decrypt(&ctx, NULL, 0, 421 ciphertext, ciphertext_len, 422 tag, plaintext) != 0) { 423 xchacha20_poly1305_cleanup(&ctx); 424 memset(plaintext, 0, ciphertext_len); 425 free(plaintext); 426 return NULL; 427 } 428 429 xchacha20_poly1305_cleanup(&ctx); 430 *output_len = ciphertext_len; 431 return plaintext; 432 } 433 434 /** 435 * Encrypt data with Twofish-256-GCM 436 * Returns serialized format: [Nonce(12)][Tag(16)][Ciphertext(var)] 437 * 438 * @param key 256-bit (32 byte) encryption key 439 * @param plaintext Plaintext data 440 * @param plaintext_len Plaintext length 441 * @param output_len Output parameter for encrypted data length 442 * @return Encrypted data [Nonce][Tag][Ciphertext] (caller must free) or NULL on failure 443 */ 444 static inline void* easycrypto_encrypt_twofish( 445 const uint8_t *key, const uint8_t *plaintext, size_t plaintext_len, size_t *output_len) { 446 447 if (!key || !plaintext || !output_len) return NULL; 448 449 /* Calculate output size: 12 (nonce) + 16 (tag) + plaintext_len */ 450 size_t total_len = 12 + 16 + plaintext_len; 451 uint8_t *output = malloc(total_len); 452 if (!output) return NULL; 453 454 /* Generate random nonce */ 455 random_bytes(output, 12); 456 457 /* Encrypt */ 458 twofish256_gcm_context ctx; 459 if (twofish256_gcm_init(&ctx, key) != 0) { 460 free(output); 461 return NULL; 462 } 463 464 /* output layout: [nonce(12)][tag(16)][ciphertext] */ 465 uint8_t *tag = output + 12; 466 uint8_t *ciphertext = output + 28; 467 468 if (twofish256_gcm_encrypt(&ctx, output, 12, NULL, 0, 469 plaintext, plaintext_len, 470 ciphertext, tag, 16) != 0) { 471 twofish256_gcm_cleanup(&ctx); 472 free(output); 473 return NULL; 474 } 475 476 twofish256_gcm_cleanup(&ctx); 477 *output_len = total_len; 478 return output; 479 } 480 481 /** 482 * Decrypt data with Twofish-256-GCM 483 * Expects format: [Nonce(12)][Tag(16)][Ciphertext(var)] 484 * 485 * @param key 256-bit (32 byte) encryption key 486 * @param encrypted Encrypted data [Nonce][Tag][Ciphertext] 487 * @param encrypted_len Length of encrypted data 488 * @param output_len Output parameter for plaintext length 489 * @return Decrypted data (caller must free) or NULL on failure 490 */ 491 static inline void* easycrypto_decrypt_twofish( 492 const uint8_t *key, const void *encrypted, size_t encrypted_len, size_t *output_len) { 493 494 if (!key || !encrypted || !output_len || encrypted_len < 28) return NULL; 495 496 const uint8_t *data = (const uint8_t*)encrypted; 497 498 /* Parse input: [nonce(12)][tag(16)][ciphertext] */ 499 const uint8_t *nonce = data; 500 const uint8_t *tag = data + 12; 501 const uint8_t *ciphertext = data + 28; 502 size_t ciphertext_len = encrypted_len - 28; 503 504 /* Allocate output */ 505 uint8_t *plaintext = malloc(ciphertext_len); 506 if (!plaintext) return NULL; 507 508 /* Decrypt */ 509 twofish256_gcm_context ctx; 510 if (twofish256_gcm_init(&ctx, key) != 0) { 511 free(plaintext); 512 return NULL; 513 } 514 515 if (twofish256_gcm_decrypt(&ctx, nonce, 12, NULL, 0, 516 ciphertext, ciphertext_len, 517 tag, 16, plaintext) != 0) { 518 twofish256_gcm_cleanup(&ctx); 519 memset(plaintext, 0, ciphertext_len); 520 free(plaintext); 521 return NULL; 522 } 523 524 twofish256_gcm_cleanup(&ctx); 525 *output_len = ciphertext_len; 526 return plaintext; 527 } 528 529 /** 530 * Encrypt data with Salsa20-Poly1305 531 * Returns serialized format: [Nonce(8)][Tag(16)][Ciphertext(var)] 532 * Note: Salsa20 uses 8-byte nonce (different from ChaCha20's 12 bytes) 533 * 534 * @param key 256-bit (32 byte) encryption key 535 * @param plaintext Plaintext data 536 * @param plaintext_len Plaintext length 537 * @param output_len Output parameter for encrypted data length 538 * @return Encrypted data [Nonce][Tag][Ciphertext] (caller must free) or NULL on failure 539 */ 540 static inline void* easycrypto_encrypt_salsa( 541 const uint8_t *key, const uint8_t *plaintext, size_t plaintext_len, size_t *output_len) { 542 543 if (!key || !plaintext || !output_len) return NULL; 544 545 /* Calculate output size: 8 (nonce) + 16 (tag) + plaintext_len */ 546 size_t total_len = 8 + 16 + plaintext_len; 547 uint8_t *output = malloc(total_len); 548 if (!output) return NULL; 549 550 /* Generate random nonce (8 bytes for Salsa20) */ 551 random_bytes(output, 8); 552 553 /* Encrypt */ 554 salsa20_poly1305_context ctx; 555 if (salsa20_poly1305_init(&ctx, key, output) != 0) { 556 free(output); 557 return NULL; 558 } 559 560 /* output layout: [nonce(8)][tag(16)][ciphertext] */ 561 uint8_t *tag = output + 8; 562 uint8_t *ciphertext = output + 24; 563 564 if (salsa20_poly1305_encrypt(&ctx, NULL, 0, plaintext, plaintext_len, 565 ciphertext, tag) != 0) { 566 salsa20_poly1305_cleanup(&ctx); 567 free(output); 568 return NULL; 569 } 570 571 salsa20_poly1305_cleanup(&ctx); 572 *output_len = total_len; 573 return output; 574 } 575 576 /** 577 * Decrypt data with Salsa20-Poly1305 578 * Expects format: [Nonce(8)][Tag(16)][Ciphertext(var)] 579 * 580 * @param key 256-bit (32 byte) encryption key 581 * @param encrypted Encrypted data [Nonce][Tag][Ciphertext] 582 * @param encrypted_len Length of encrypted data 583 * @param output_len Output parameter for plaintext length 584 * @return Decrypted data (caller must free) or NULL on failure 585 */ 586 static inline void* easycrypto_decrypt_salsa( 587 const uint8_t *key, const void *encrypted, size_t encrypted_len, size_t *output_len) { 588 589 if (!key || !encrypted || !output_len || encrypted_len < 24) return NULL; 590 591 const uint8_t *data = (const uint8_t*)encrypted; 592 593 /* Parse input: [nonce(8)][tag(16)][ciphertext] */ 594 const uint8_t *nonce = data; 595 const uint8_t *tag = data + 8; 596 const uint8_t *ciphertext = data + 24; 597 size_t ciphertext_len = encrypted_len - 24; 598 599 /* Allocate output */ 600 uint8_t *plaintext = malloc(ciphertext_len); 601 if (!plaintext) return NULL; 602 603 /* Decrypt */ 604 salsa20_poly1305_context ctx; 605 if (salsa20_poly1305_init(&ctx, key, nonce) != 0) { 606 free(plaintext); 607 return NULL; 608 } 609 610 if (salsa20_poly1305_decrypt(&ctx, NULL, 0, 611 ciphertext, ciphertext_len, 612 tag, plaintext) != 0) { 613 salsa20_poly1305_cleanup(&ctx); 614 memset(plaintext, 0, ciphertext_len); 615 free(plaintext); 616 return NULL; 617 } 618 619 salsa20_poly1305_cleanup(&ctx); 620 *output_len = ciphertext_len; 621 return plaintext; 622 } 623 624 /** 625 * Encrypt data with Serpent-256-GCM 626 * Returns serialized format: [Nonce(12)][Tag(16)][Ciphertext(var)] 627 * 628 * @param key 256-bit (32 byte) encryption key 629 * @param plaintext Plaintext data 630 * @param plaintext_len Plaintext length 631 * @param output_len Output parameter for encrypted data length 632 * @return Encrypted data [Nonce][Tag][Ciphertext] (caller must free) or NULL on failure 633 */ 634 static inline void* easycrypto_encrypt_serpent( 635 const uint8_t *key, const uint8_t *plaintext, size_t plaintext_len, size_t *output_len) { 636 637 if (!key || !plaintext || !output_len) return NULL; 638 639 /* Calculate output size: 12 (nonce) + 16 (tag) + plaintext_len */ 640 size_t total_len = 12 + 16 + plaintext_len; 641 uint8_t *output = malloc(total_len); 642 if (!output) return NULL; 643 644 /* Generate random nonce */ 645 random_bytes(output, 12); 646 647 /* Encrypt */ 648 serpent_gcm_context ctx; 649 if (serpent_gcm_init(&ctx, key) != 0) { 650 free(output); 651 return NULL; 652 } 653 654 /* output layout: [nonce(12)][tag(16)][ciphertext] */ 655 uint8_t *tag = output + 12; 656 uint8_t *ciphertext = output + 28; 657 658 if (serpent_gcm_encrypt(&ctx, output, 12, NULL, 0, 659 plaintext, plaintext_len, 660 ciphertext, tag, 16) != 0) { 661 serpent_gcm_cleanup(&ctx); 662 free(output); 663 return NULL; 664 } 665 666 serpent_gcm_cleanup(&ctx); 667 *output_len = total_len; 668 return output; 669 } 670 671 /** 672 * Decrypt data with Serpent-256-GCM 673 * Expects format: [Nonce(12)][Tag(16)][Ciphertext(var)] 674 * 675 * @param key 256-bit (32 byte) encryption key 676 * @param encrypted Encrypted data [Nonce][Tag][Ciphertext] 677 * @param encrypted_len Length of encrypted data 678 * @param output_len Output parameter for plaintext length 679 * @return Decrypted data (caller must free) or NULL on failure 680 */ 681 static inline void* easycrypto_decrypt_serpent( 682 const uint8_t *key, const void *encrypted, size_t encrypted_len, size_t *output_len) { 683 684 if (!key || !encrypted || !output_len || encrypted_len < 28) return NULL; 685 686 const uint8_t *data = (const uint8_t*)encrypted; 687 688 /* Parse input: [nonce(12)][tag(16)][ciphertext] */ 689 const uint8_t *nonce = data; 690 const uint8_t *tag = data + 12; 691 const uint8_t *ciphertext = data + 28; 692 size_t ciphertext_len = encrypted_len - 28; 693 694 /* Allocate output */ 695 uint8_t *plaintext = malloc(ciphertext_len); 696 if (!plaintext) return NULL; 697 698 /* Decrypt */ 699 serpent_gcm_context ctx; 700 if (serpent_gcm_init(&ctx, key) != 0) { 701 free(plaintext); 702 return NULL; 703 } 704 705 if (serpent_gcm_decrypt(&ctx, nonce, 12, NULL, 0, 706 ciphertext, ciphertext_len, 707 tag, 16, plaintext) != 0) { 708 serpent_gcm_cleanup(&ctx); 709 memset(plaintext, 0, ciphertext_len); 710 free(plaintext); 711 return NULL; 712 } 713 714 serpent_gcm_cleanup(&ctx); 715 *output_len = ciphertext_len; 716 return plaintext; 717 } 718 719 /** 720 * Free encrypted data structure 721 */ 722 static inline void encrypted_data_free(encrypted_data *data) { 723 if (!data) return; 724 if (data->nonce) free(data->nonce); 725 if (data->ciphertext) { 726 memset(data->ciphertext, 0, data->ciphertext_len); 727 free(data->ciphertext); 728 } 729 if (data->tag) free(data->tag); 730 free(data); 731 } 732 733 /** 734 * Serialize encrypted data to a flat byte array for storage 735 * Format: [Nonce (12 bytes)][Tag (16 bytes)][Ciphertext (variable)] 736 * 737 * Note: Algorithm selection must be tracked externally (not stored in data) 738 * 739 * @param data Encrypted data structure 740 * @param output_len Output parameter for serialized data length 741 * @return Serialized data (caller must free) or NULL on error 742 */ 743 static inline uint8_t* encrypted_data_serialize(const encrypted_data *data, size_t *output_len) { 744 if (!data || !output_len) return NULL; 745 746 /* Calculate total size: nonce + tag + ciphertext */ 747 size_t total_len = data->nonce_len + data->tag_len + data->ciphertext_len; 748 749 uint8_t *buffer = malloc(total_len); 750 if (!buffer) return NULL; 751 752 size_t offset = 0; 753 754 /* Write nonce */ 755 memcpy(buffer + offset, data->nonce, data->nonce_len); 756 offset += data->nonce_len; 757 758 /* Write tag */ 759 memcpy(buffer + offset, data->tag, data->tag_len); 760 offset += data->tag_len; 761 762 /* Write ciphertext */ 763 memcpy(buffer + offset, data->ciphertext, data->ciphertext_len); 764 765 *output_len = total_len; 766 return buffer; 767 } 768 769 /** 770 * Deserialize encrypted data from a flat byte array 771 * Format: [Nonce (12 bytes)][Tag (16 bytes)][Ciphertext (variable)] 772 * 773 * @param buffer Serialized data (minimum 28 bytes: 12 nonce + 16 tag + 0 ciphertext) 774 * @param buffer_len Length of serialized data 775 * @param algorithm Algorithm used (must be provided externally) 776 * @return Encrypted data structure (caller must free with encrypted_data_free) or NULL on error 777 */ 778 static inline encrypted_data* encrypted_data_deserialize(const uint8_t *buffer, size_t buffer_len, int algorithm) { 779 /* Fixed sizes for GCM mode */ 780 const size_t nonce_len = 12; 781 const size_t tag_len = 16; 782 783 /* Minimum size check */ 784 if (!buffer || buffer_len < (nonce_len + tag_len)) { 785 return NULL; 786 } 787 788 encrypted_data *data = malloc(sizeof(encrypted_data)); 789 if (!data) return NULL; 790 791 memset(data, 0, sizeof(encrypted_data)); 792 793 /* Set algorithm */ 794 data->algorithm = algorithm; 795 796 /* Calculate ciphertext length */ 797 data->ciphertext_len = buffer_len - nonce_len - tag_len; 798 799 size_t offset = 0; 800 801 /* Read nonce */ 802 data->nonce_len = nonce_len; 803 data->nonce = malloc(nonce_len); 804 if (!data->nonce) { 805 free(data); 806 return NULL; 807 } 808 memcpy(data->nonce, buffer + offset, nonce_len); 809 offset += nonce_len; 810 811 /* Read tag */ 812 data->tag_len = tag_len; 813 data->tag = malloc(tag_len); 814 if (!data->tag) { 815 free(data->nonce); 816 free(data); 817 return NULL; 818 } 819 memcpy(data->tag, buffer + offset, tag_len); 820 offset += tag_len; 821 822 /* Read ciphertext */ 823 data->ciphertext = malloc(data->ciphertext_len); 824 if (!data->ciphertext) { 825 free(data->nonce); 826 free(data->tag); 827 free(data); 828 return NULL; 829 } 830 memcpy(data->ciphertext, buffer + offset, data->ciphertext_len); 831 832 return data; 833 } 834 835 /* Convenience macros */ 836 #define ENCRYPT(key, plaintext, plaintext_len, output_len) \ 837 easycrypto_encrypt_aes(key, plaintext, plaintext_len, output_len) 838 #define DECRYPT(key, encrypted, encrypted_len, output_len) \ 839 easycrypto_decrypt_aes(key, encrypted, encrypted_len, output_len) 840 841 #define ENCRYPT_AES(key, plaintext, plaintext_len, output_len) \ 842 easycrypto_encrypt_aes(key, plaintext, plaintext_len, output_len) 843 #define DECRYPT_AES(key, encrypted, encrypted_len, output_len) \ 844 easycrypto_decrypt_aes(key, encrypted, encrypted_len, output_len) 845 846 #define ENCRYPT_AES128(key, plaintext, plaintext_len, output_len) \ 847 easycrypto_encrypt_aes128(key, plaintext, plaintext_len, output_len) 848 #define DECRYPT_AES128(key, encrypted, encrypted_len, output_len) \ 849 easycrypto_decrypt_aes128(key, encrypted, encrypted_len, output_len) 850 851 #define ENCRYPT_CHACHA(key, plaintext, plaintext_len, output_len) \ 852 easycrypto_encrypt_chacha(key, plaintext, plaintext_len, output_len) 853 #define DECRYPT_CHACHA(key, encrypted, encrypted_len, output_len) \ 854 easycrypto_decrypt_chacha(key, encrypted, encrypted_len, output_len) 855 856 #define ENCRYPT_XCHACHA(key, plaintext, plaintext_len, output_len) \ 857 easycrypto_encrypt_xchacha(key, plaintext, plaintext_len, output_len) 858 #define DECRYPT_XCHACHA(key, encrypted, encrypted_len, output_len) \ 859 easycrypto_decrypt_xchacha(key, encrypted, encrypted_len, output_len) 860 861 #define ENCRYPT_SERPENT(key, plaintext, plaintext_len, output_len) \ 862 easycrypto_encrypt_serpent(key, plaintext, plaintext_len, output_len) 863 #define DECRYPT_SERPENT(key, encrypted, encrypted_len, output_len) \ 864 easycrypto_decrypt_serpent(key, encrypted, encrypted_len, output_len) 865 866 #define ENCRYPT_TWOFISH(key, plaintext, plaintext_len, output_len) \ 867 easycrypto_encrypt_twofish(key, plaintext, plaintext_len, output_len) 868 #define DECRYPT_TWOFISH(key, encrypted, encrypted_len, output_len) \ 869 easycrypto_decrypt_twofish(key, encrypted, encrypted_len, output_len) 870 871 /* Salsa20-Poly1305 AEAD macros */ 872 #define ENCRYPT_SALSA(key, plaintext, plaintext_len, output_len) \ 873 easycrypto_encrypt_salsa(key, plaintext, plaintext_len, output_len) 874 #define DECRYPT_SALSA(key, encrypted, encrypted_len, output_len) \ 875 easycrypto_decrypt_salsa(key, encrypted, encrypted_len, output_len) 876 877 #ifdef __cplusplus 878 } 879 #endif 880 881 #endif /* EASYCRYPTO_H */