1 /* 2 * Boost Software License - Version 1.0 3 * https://www.boost.org/LICENSE_1_0.txt 4 */ 5 module aegis.aegis256; 6 7 /// AEGIS C bindings 8 import c.aegisc; // @system 9 import core.stdc.string : memset; 10 11 /** 12 * AEGIS-256 encryption/decryption state. 13 * 14 * Manages the lifecycle of an `aegis256_state` object, ensuring proper initialization 15 * and cleanup in a `@nogc` and `@trusted` context. 16 * 17 * Examples: 18 * --- 19 * ubyte[32] key = [0x01, 0x02, ..., 0x20]; 20 * ubyte[32] nonce = [0x00, 0x01, ..., 0x1F]; 21 * ubyte[] ad = [0x41, 0x42, 0x43]; // "ABC" 22 * ubyte[] message = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello" 23 * ubyte[128] ciphertext; 24 * ubyte[16] mac; 25 * size_t written; 26 * 27 * auto state = Aegis256State(key, nonce, ad); 28 * state.encryptUpdate(ciphertext[], &written, message); 29 * state.encryptDetachedFinal(ciphertext[written .. $], &written, mac[], 16); 30 * --- 31 */ 32 struct Aegis256State 33 { 34 private aegis256_state state; // Underlying C state 35 private bool initialized; // Tracks initialization status 36 37 /** 38 * Initializes the AEGIS-256 state with the provided key, nonce, and associated data. 39 * 40 * Params: 41 * key = The encryption key (must be `aegis256_KEYBYTES` long). 42 * nonce = The nonce (must be `aegis256_NPUBBYTES` long). 43 * ad = The associated data (optional, can be empty). 44 * 45 * Throws: 46 * AssertError if key or nonce lengths are invalid. 47 */ 48 @nogc @trusted 49 this(const(ubyte)[] key, const(ubyte)[] nonce, const(ubyte)[] ad = null) 50 { 51 assert(key.length == aegis256_KEYBYTES, "Key length must be aegis256_KEYBYTES"); 52 assert(nonce.length == aegis256_NPUBBYTES, "Nonce length must be aegis256_NPUBBYTES"); 53 54 memset(&state, 0, aegis256_state.sizeof); 55 aegis256_state_init(&state, ad.ptr, ad.length, nonce.ptr, key.ptr); 56 initialized = true; 57 } 58 59 /** 60 * Destructor to clean up the state. 61 */ 62 @nogc @trusted ~this() 63 { 64 if (initialized) 65 { 66 memset(&state, 0, aegis256_state.sizeof); 67 initialized = false; 68 } 69 } 70 71 /** 72 * Disabled copy constructor to prevent state duplication. 73 */ 74 @disable this(this); 75 76 /** 77 * Encrypts a message chunk, producing ciphertext. 78 * 79 * Params: 80 * ciphertext = Buffer to store the ciphertext. 81 * written = Pointer to store the number of bytes written. 82 * message = The plaintext message to encrypt. 83 * 84 * Returns: 85 * 0 on success, or a negative error code on failure. 86 */ 87 @nogc @trusted 88 int encryptUpdate(ubyte[] ciphertext, size_t* written, const(ubyte)[] message) 89 { 90 assert(initialized, "State not initialized"); 91 assert(written !is null, "Written pointer cannot be null"); 92 return aegis256_state_encrypt_update(&state, ciphertext.ptr, ciphertext.length, 93 written, message.ptr, message.length); 94 } 95 96 /** 97 * Finalizes encryption in detached mode, producing the final ciphertext and MAC. 98 * 99 * Params: 100 * ciphertext = Buffer for remaining ciphertext. 101 * written = Pointer to store the number of bytes written. 102 * mac = Buffer for the MAC (must be `aegis256_ABYTES_MIN` to `aegis256_ABYTES_MAX`). 103 * maclen = Length of the MAC. 104 * 105 * Returns: 106 * 0 on success, or a negative error code on failure. 107 */ 108 @nogc @trusted 109 int encryptDetachedFinal(ubyte[] ciphertext, size_t* written, ubyte[] mac, size_t maclen) 110 { 111 assert(initialized, "State not initialized"); 112 assert(written !is null, "Written pointer cannot be null"); 113 assert(maclen >= aegis256_ABYTES_MIN && maclen <= aegis256_ABYTES_MAX, "Invalid MAC length"); 114 return aegis256_state_encrypt_detached_final(&state, ciphertext.ptr, ciphertext.length, 115 written, mac.ptr, maclen); 116 } 117 118 /** 119 * Decrypts a ciphertext chunk, producing plaintext. 120 * 121 * Params: 122 * plaintext = Buffer to store the plaintext. 123 * written = Pointer to store the number of bytes written. 124 * ciphertext = The ciphertext to decrypt. 125 * 126 * Returns: 127 * 0 on success, or a negative error code on failure. 128 */ 129 @nogc @trusted 130 int decryptDetachedUpdate(ubyte[] plaintext, size_t* written, const(ubyte)[] ciphertext) 131 { 132 assert(initialized, "State not initialized"); 133 assert(written !is null, "Written pointer cannot be null"); 134 return aegis256_state_decrypt_detached_update(&state, plaintext.ptr, plaintext.length, 135 written, ciphertext.ptr, ciphertext.length); 136 } 137 138 /** 139 * Finalizes decryption in detached mode, verifying the MAC and producing final plaintext. 140 * 141 * Params: 142 * plaintext = Buffer for remaining plaintext. 143 * written = Pointer to store the number of bytes written. 144 * mac = The MAC to verify (must be `aegis256_ABYTES_MIN` to `aegis256_ABYTES_MAX`). 145 * maclen = Length of the MAC. 146 * 147 * Returns: 148 * 0 on success, -1 if MAC verification fails, or another negative error code on failure. 149 */ 150 @nogc @trusted 151 int decryptDetachedFinal(ubyte[] plaintext, size_t* written, const(ubyte)[] mac, size_t maclen) 152 { 153 assert(initialized, "State not initialized"); 154 assert(written !is null, "Written pointer cannot be null"); 155 assert(maclen >= aegis256_ABYTES_MIN && maclen <= aegis256_ABYTES_MAX, "Invalid MAC length"); 156 return aegis256_state_decrypt_detached_final(&state, plaintext.ptr, plaintext.length, 157 written, mac.ptr, maclen); 158 } 159 } 160 161 /** 162 * AEGIS-256 MAC state. 163 * 164 * Manages the lifecycle of an `aegis256_mac_state` object in a `@nogc` and `@trusted` context. 165 * 166 * Examples: 167 * --- 168 * ubyte[32] key = [0x01, 0x02, ..., 0x20]; 169 * ubyte[32] nonce = [0x00, 0x01, ..., 0x1F]; 170 * ubyte[] message = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello" 171 * ubyte[16] mac; 172 * 173 * auto macState = Aegis256MACState(key, nonce); 174 * macState.update(message); 175 * macState.finalize(mac[], 16); 176 * --- 177 */ 178 struct Aegis256MACState 179 { 180 private aegis256_mac_state state; // Underlying C MAC state 181 private bool initialized; // Tracks initialization status 182 183 /** 184 * Initializes the AEGIS-256 MAC state with the provided key and nonce. 185 * 186 * Params: 187 * key = The key (must be `aegis256_KEYBYTES` long). 188 * nonce = The nonce (must be `aegis256_NPUBBYTES` long). 189 * 190 * Throws: 191 * AssertError if key or nonce lengths are invalid. 192 */ 193 @nogc @trusted 194 this(const(ubyte)[] key, const(ubyte)[] nonce) 195 { 196 assert(key.length == aegis256_KEYBYTES, "Key length must be aegis256_KEYBYTES"); 197 assert(nonce.length == aegis256_NPUBBYTES, "Nonce length must be aegis256_NPUBBYTES"); 198 199 memset(&state, 0, aegis256_mac_state.sizeof); 200 aegis256_mac_init(&state, key.ptr, nonce.ptr); 201 initialized = true; 202 } 203 204 /** 205 * Destructor to clean up the MAC state. 206 */ 207 @nogc @trusted ~this() 208 { 209 if (initialized) 210 { 211 aegis256_mac_reset(&state); 212 memset(&state, 0, aegis256_mac_state.sizeof); 213 initialized = false; 214 } 215 } 216 217 /** 218 * Disabled copy constructor to prevent state duplication. 219 */ 220 @disable this(this); 221 222 /** 223 * Updates the MAC state with a message chunk. 224 * 225 * Params: 226 * message = The message to process. 227 * 228 * Returns: 229 * 0 on success, or a negative error code on failure. 230 */ 231 @nogc @trusted 232 int update(const(ubyte)[] message) 233 { 234 assert(initialized, "MAC state not initialized"); 235 return aegis256_mac_update(&state, message.ptr, message.length); 236 } 237 238 /** 239 * Finalizes the MAC computation, producing the MAC. 240 * 241 * Params: 242 * mac = Buffer to store the MAC (must be `aegis256_ABYTES_MIN` to `aegis256_ABYTES_MAX`). 243 * maclen = Length of the MAC. 244 * 245 * Returns: 246 * 0 on success, or a negative error code on failure. 247 */ 248 @nogc @trusted 249 int finalize(ubyte[] mac, size_t maclen) 250 { 251 assert(initialized, "MAC state not initialized"); 252 assert(maclen >= aegis256_ABYTES_MIN && maclen <= aegis256_ABYTES_MAX, "Invalid MAC length"); 253 return aegis256_mac_final(&state, mac.ptr, maclen); 254 } 255 256 /** 257 * Verifies a MAC against the computed MAC. 258 * 259 * Params: 260 * mac = The MAC to verify (must be `aegis256_ABYTES_MIN` to `aegis256_ABYTES_MAX`). 261 * maclen = Length of the MAC. 262 * 263 * Returns: 264 * 0 if valid, -1 if verification fails, or another negative error code on failure. 265 */ 266 @nogc @trusted 267 int verify(const(ubyte)[] mac, size_t maclen) 268 { 269 assert(initialized, "MAC state not initialized"); 270 assert(maclen >= aegis256_ABYTES_MIN && maclen <= aegis256_ABYTES_MAX, "Invalid MAC length"); 271 return aegis256_mac_verify(&state, mac.ptr, maclen); 272 } 273 274 /** 275 * Resets the MAC state for reuse with the same key and nonce. 276 */ 277 @nogc @trusted 278 void reset() 279 { 280 assert(initialized, "MAC state not initialized"); 281 aegis256_mac_reset(&state); 282 } 283 } 284 285 /** 286 * AEGIS-256x2 encryption/decryption state. 287 * 288 * Manages the lifecycle of an `aegis256x2_state` object in a `@nogc` and `@trusted` context. 289 */ 290 struct Aegis256x2State 291 { 292 private aegis256x2_state state; // Underlying C state 293 private bool initialized; // Tracks initialization status 294 295 /** 296 * Initializes the AEGIS-256x2 state with the provided key, nonce, and associated data. 297 * 298 * Params: 299 * key = The encryption key (must be `aegis256x2_KEYBYTES` long). 300 * nonce = The nonce (must be `aegis256x2_NPUBBYTES` long). 301 * ad = The associated data (optional, can be empty). 302 * 303 * Throws: 304 * AssertError if key or nonce lengths are invalid. 305 */ 306 @nogc @trusted 307 this(const(ubyte)[] key, const(ubyte)[] nonce, const(ubyte)[] ad = null) 308 { 309 assert(key.length == aegis256x2_KEYBYTES, "Key length must be aegis256x2_KEYBYTES"); 310 assert(nonce.length == aegis256x2_NPUBBYTES, "Nonce length must be aegis256x2_NPUBBYTES"); 311 312 memset(&state, 0, aegis256x2_state.sizeof); 313 aegis256x2_state_init(&state, ad.ptr, ad.length, nonce.ptr, key.ptr); 314 initialized = true; 315 } 316 317 /** 318 * Destructor to clean up the state. 319 */ 320 @nogc @trusted ~this() 321 { 322 if (initialized) 323 { 324 memset(&state, 0, aegis256x2_state.sizeof); 325 initialized = false; 326 } 327 } 328 329 /** 330 * Disabled copy constructor to prevent state duplication. 331 */ 332 @disable this(this); 333 334 /** 335 * Encrypts a message chunk, producing ciphertext. 336 * 337 * Params: 338 * ciphertext = Buffer to store the ciphertext. 339 * written = Pointer to store the number of bytes written. 340 * message = The plaintext message to encrypt. 341 * 342 * Returns: 343 * 0 on success, or a negative error code on failure. 344 */ 345 @nogc @trusted 346 int encryptUpdate(ubyte[] ciphertext, size_t* written, const(ubyte)[] message) 347 { 348 assert(initialized, "State not initialized"); 349 assert(written !is null, "Written pointer cannot be null"); 350 return aegis256x2_state_encrypt_update(&state, ciphertext.ptr, ciphertext.length, 351 written, message.ptr, message.length); 352 } 353 354 /** 355 * Finalizes encryption in detached mode, producing the final ciphertext and MAC. 356 * 357 * Params: 358 * ciphertext = Buffer for remaining ciphertext. 359 * written = Pointer to store the number of bytes written. 360 * mac = Buffer for the MAC (must be `aegis256x2_ABYTES_MIN` to `aegis256x2_ABYTES_MAX`). 361 * maclen = Length of the MAC. 362 * 363 * Returns: 364 * 0 on success, or a negative error code on failure. 365 */ 366 @nogc @trusted 367 int encryptDetachedFinal(ubyte[] ciphertext, size_t* written, ubyte[] mac, size_t maclen) 368 { 369 assert(initialized, "State not initialized"); 370 assert(written !is null, "Written pointer cannot be null"); 371 assert(maclen >= aegis256x2_ABYTES_MIN && maclen <= aegis256x2_ABYTES_MAX, "Invalid MAC length"); 372 return aegis256x2_state_encrypt_detached_final(&state, ciphertext.ptr, ciphertext.length, 373 written, mac.ptr, maclen); 374 } 375 376 /** 377 * Decrypts a ciphertext chunk, producing plaintext. 378 * 379 * Params: 380 * plaintext = Buffer to store the plaintext. 381 * written = Pointer to store the number of bytes written. 382 * ciphertext = The ciphertext to decrypt. 383 * 384 * Returns: 385 * 0 on success, or a negative error code on failure. 386 */ 387 @nogc @trusted 388 int decryptDetachedUpdate(ubyte[] plaintext, size_t* written, const(ubyte)[] associated_data) 389 { 390 assert(initialized, "State not initialized"); 391 assert(written !is null, "Written pointer cannot be null"); 392 return aegis256x2_state_decrypt_detached_update(&state, plaintext.ptr, plaintext.length, 393 written, associated_data.ptr, associated_data.length); 394 } 395 396 /** 397 * Finalizes decryption in detached mode, verifying the MAC and producing final plaintext. 398 * 399 * Params: 400 * plaintext = Buffer for remaining plaintext. 401 * written = Pointer to store the number of bytes written. 402 * mac = The MAC to verify (must be `aegis256x2_ABYTES_MIN` to `aegis256x2_ABYTES_MAX`). 403 * maclen = Length of the MAC. 404 * 405 * Returns: 406 *laws 407 * 0 on success, -1 if MAC verification fails, or another negative error code on failure. 408 */ 409 @nogc @trusted 410 int decryptDetachedFinal(ubyte[] plaintext, size_t* written, const(ubyte)[] mac, size_t maclen) 411 { 412 assert(initialized, "State not initialized"); 413 assert(written !is null, "Written pointer cannot be null"); 414 assert(maclen >= aegis256x2_ABYTES_MIN && maclen <= aegis256x2_ABYTES_MAX, "Invalid MAC length"); 415 return aegis256x2_state_decrypt_detached_final(&state, plaintext.ptr, plaintext.length, 416 written, mac.ptr, maclen); 417 } 418 } 419 420 /** 421 * AEGIS-256x2 MAC state. 422 * 423 * Manages the lifecycle of an `aegis256x2_mac_state` object in a `@nogc` and `@trusted` context. 424 */ 425 struct Aegis256x2MACState 426 { 427 private aegis256x2_mac_state state; // Underlying C MAC state 428 private bool initialized; // Tracks initialization status 429 430 /** 431 * Initializes the AEGIS-256x2 MAC state with the provided key and nonce. 432 * 433 * Params: 434 * key = The key (must be `aegis256x2_KEYBYTES` long). 435 * nonce = The nonce (must be `aegis256x2_NPUBBYTES` long). 436 * 437 * Throws: 438 * AssertError if key or nonce lengths are invalid. 439 */ 440 @nogc @trusted 441 this(const(ubyte)[] key, const(ubyte)[] nonce) 442 { 443 assert(key.length == aegis256x2_KEYBYTES, "Key length must be aegis256x2_KEYBYTES"); 444 assert(nonce.length == aegis256x2_NPUBBYTES, "Nonce length must be aegis256x2_NPUBBYTES"); 445 446 memset(&state, 0, aegis256x2_mac_state.sizeof); 447 aegis256x2_mac_init(&state, key.ptr, nonce.ptr); 448 initialized = true; 449 } 450 451 /** 452 * Destructor to clean up the MAC state. 453 */ 454 @nogc @trusted ~this() 455 { 456 if (initialized) 457 { 458 aegis256x2_mac_reset(&state); 459 memset(&state, 0, aegis256x2_mac_state.sizeof); 460 initialized = false; 461 } 462 } 463 464 /** 465 * Disabled copy constructor to prevent state duplication. 466 */ 467 @disable this(this); 468 469 /** 470 * Updates the MAC state with a message chunk. 471 * 472 * Params: 473 * message = The message to process. 474 * 475 * Returns: 476 * 0 on success, or a negative error code on failure. 477 */ 478 @nogc @trusted 479 int update(const(ubyte)[] message) 480 { 481 assert(initialized, "MAC state not initialized"); 482 return aegis256x2_mac_update(&state, message.ptr, message.length); 483 } 484 485 /** 486 * Finalizes the MAC computation, producing the MAC. 487 * 488 * Params: 489 * mac = Buffer to store the MAC (must be `aegis256x2_ABYTES_MIN` to `aegis256x2_ABYTES_MAX`). 490 * maclen = Length of the MAC. 491 * 492 * Returns: 493 * 0 on success, or a negative error code on failure. 494 */ 495 @nogc @trusted 496 int finalize(ubyte[] mac, size_t maclen) 497 { 498 assert(initialized, "MAC state not initialized"); 499 assert(maclen >= aegis256x2_ABYTES_MIN && maclen <= aegis256x2_ABYTES_MAX, "Invalid MAC length"); 500 return aegis256x2_mac_final(&state, mac.ptr, maclen); 501 } 502 503 /** 504 * Verifies a MAC against the computed MAC. 505 * 506 * Params: 507 * mac = The MAC to verify (must be `aegis256x2_ABYTES_MIN` to `aegis256x2_ABYTES_MAX`). 508 * maclen = Length of the MAC. 509 * 510 * Returns: 511 * 0 if valid, -1 if verification fails, or another negative error code on failure. 512 */ 513 @nogc @trusted 514 int verify(const(ubyte)[] mac, size_t maclen) 515 { 516 assert(initialized, "MAC state not initialized"); 517 assert(maclen >= aegis256x2_ABYTES_MIN && maclen <= aegis256x2_ABYTES_MAX, "Invalid MAC length"); 518 return aegis256x2_mac_verify(&state, mac.ptr, maclen); 519 } 520 521 /** 522 * Resets the MAC state for reuse with the same key and nonce. 523 */ 524 @nogc @trusted 525 void reset() 526 { 527 assert(initialized, "MAC state not initialized"); 528 aegis256x2_mac_reset(&state); 529 } 530 } 531 532 /** 533 * AEGIS-256x4 encryption/decryption state. 534 * 535 * Manages the lifecycle of an `aegis256x4_state` object in a `@nogc` and `@trusted` context. 536 */ 537 struct Aegis256x4State 538 { 539 private aegis256x4_state state; // Underlying C state 540 private bool initialized; // Tracks initialization status 541 542 /** 543 * Initializes the AEGIS-256x4 state with the provided key, nonce, and associated data. 544 * 545 * Params: 546 * key = The encryption key (must be `aegis256x4_KEYBYTES` long). 547 * nonce = The nonce (must be `aegis256x4_NPUBBYTES` long). 548 * ad = The associated data (optional, can be empty). 549 * 550 * Throws: 551 * AssertError if key or nonce lengths are invalid. 552 */ 553 @nogc @trusted 554 this(const(ubyte)[] key, const(ubyte)[] nonce, const(ubyte)[] ad = null) 555 { 556 assert(key.length == aegis256x4_KEYBYTES, "Key length must be aegis256x4_KEYBYTES"); 557 assert(nonce.length == aegis256x4_NPUBBYTES, "Nonce length must be aegis256x4_NPUBBYTES"); 558 559 memset(&state, 0, aegis256x4_state.sizeof); 560 aegis256x4_state_init(&state, ad.ptr, ad.length, nonce.ptr, key.ptr); 561 initialized = true; 562 } 563 564 /** 565 * Destructor to clean up the state. 566 */ 567 @nogc @trusted ~this() 568 { 569 if (initialized) 570 { 571 memset(&state, 0, aegis256x4_state.sizeof); 572 initialized = false; 573 } 574 } 575 576 /** 577 * Disabled copy constructor to prevent state duplication. 578 */ 579 @disable this(this); 580 581 /** 582 * Encrypts a message chunk, producing ciphertext. 583 * 584 * Params: 585 * ciphertext = Buffer to store the ciphertext. 586 * written = Pointer to store the number of bytes written. 587 * message = The plaintext message to encrypt. 588 * 589 * Returns: 590 * 0 on success, or a negative error code on failure. 591 */ 592 @nogc @trusted 593 int encryptUpdate(ubyte[] ciphertext, size_t* written, const(ubyte)[] message) 594 { 595 assert(initialized, "State not initialized"); 596 assert(written !is null, "Written pointer cannot be null"); 597 return aegis256x4_state_encrypt_update(&state, ciphertext.ptr, ciphertext.length, 598 written, message.ptr, message.length); 599 } 600 601 /** 602 * Finalizes encryption in detached mode, producing the final ciphertext and MAC. 603 * 604 * Params: 605 * ciphertext = Buffer for remaining ciphertext. 606 * written = Pointer to store the number of bytes written. 607 * mac = Buffer for the MAC (must be `aegis256x4_ABYTES_MIN` to `aegis256x4_ABYTES_MAX`). 608 * maclen = Length of the MAC. 609 * 610 * Returns: 611 * 0 on success, or a negative error code on failure. 612 */ 613 @nogc @trusted 614 int encryptDetachedFinal(ubyte[] ciphertext, size_t* written, ubyte[] mac, size_t maclen) 615 { 616 assert(initialized, "State not initialized"); 617 assert(written !is null, "Written pointer cannot be null"); 618 assert(maclen >= aegis256x4_ABYTES_MIN && maclen <= aegis256x4_ABYTES_MAX, "Invalid MAC length"); 619 return aegis256x4_state_encrypt_detached_final(&state, ciphertext.ptr, ciphertext.length, 620 written, mac.ptr, maclen); 621 } 622 623 /** 624 * Decrypts a ciphertext chunk, producing plaintext. 625 * 626 * Params: 627 * plaintext = Buffer to store the plaintext. 628 * written = Pointer to store the number of bytes written. 629 * ciphertext = The ciphertext to decrypt. 630 * 631 * Returns: 632 * 0 on success, or a negative error code on failure. 633 */ 634 @nogc @trusted 635 int decryptDetachedUpdate(ubyte[] plaintext, size_t* written, const(ubyte)[] ciphertext) 636 { 637 assert(initialized, "State not initialized"); 638 assert(written !is null, "Written pointer cannot be null"); 639 return aegis256x4_state_decrypt_detached_update(&state, plaintext.ptr, plaintext.length, 640 written, ciphertext.ptr, ciphertext.length); 641 } 642 643 /** 644 * Finalizes decryption in detached mode, verifying the MAC and producing final plaintext. 645 * 646 * Params: 647 * plaintext = Buffer for remaining plaintext. 648 * written = Pointer to store the number of bytes written. 649 * mac = The MAC to verify (must be `aegis256x4_ABYTES_MIN` to `aegis256x4_ABYTES_MAX`). 650 * maclen = Length of the MAC. 651 * 652 * Returns: 653 * 0 on success, -1 if MAC verification fails, or another negative error code on failure. 654 */ 655 @nogc @trusted 656 int decryptDetachedFinal(ubyte[] plaintext, size_t* written, const(ubyte)[] mac, size_t maclen) 657 { 658 assert(initialized, "State not initialized"); 659 assert(written !is null, "Written pointer cannot be null"); 660 assert(maclen >= aegis256x4_ABYTES_MIN && maclen <= aegis256x4_ABYTES_MAX, "Invalid MAC length"); 661 return aegis256x4_state_decrypt_detached_final(&state, plaintext.ptr, plaintext.length, 662 written, mac.ptr, maclen); 663 } 664 } 665 666 /** 667 * AEGIS-256x4 MAC state. 668 * 669 * Manages the lifecycle of an `aegis256x4_mac_state` object in a `@nogc` and `@trusted` context. 670 */ 671 struct Aegis256x4MACState 672 { 673 private aegis256x4_mac_state state; // Underlying C MAC state 674 private bool initialized; // Tracks initialization status 675 676 /** 677 * Initializes the AEGIS-256x4 MAC state with the provided key and nonce. 678 * 679 * Params: 680 * key = The key (must be `aegis256x4_KEYBYTES` long). 681 * nonce = The nonce (must be `aegis256x4_NPUBBYTES` long). 682 * 683 * Throws: 684 * AssertError if key or nonce lengths are invalid. 685 */ 686 @nogc @trusted 687 this(const(ubyte)[] key, const(ubyte)[] nonce) 688 { 689 assert(key.length == aegis256x4_KEYBYTES, "Key length must be aegis256x4_KEYBYTES"); 690 assert(nonce.length == aegis256x4_NPUBBYTES, "Nonce length must be aegis256x4_NPUBBYTES"); 691 692 memset(&state, 0, aegis256x4_mac_state.sizeof); 693 aegis256x4_mac_init(&state, key.ptr, nonce.ptr); 694 initialized = true; 695 } 696 697 /** 698 * Destructor to clean up the MAC state. 699 */ 700 @nogc @trusted ~this() 701 { 702 if (initialized) 703 { 704 aegis256x4_mac_reset(&state); 705 memset(&state, 0, aegis256x4_mac_state.sizeof); 706 initialized = false; 707 } 708 } 709 710 /** 711 * Disabled copy constructor to prevent state duplication. 712 */ 713 @disable this(this); 714 715 /** 716 * Updates the MAC state with a message chunk. 717 * 718 * Params: 719 * message = The message to process. 720 * 721 * Returns: 722 * 0 on success, or a negative error code on failure. 723 */ 724 @nogc @trusted 725 int update(const(ubyte)[] message) 726 { 727 assert(initialized, "MAC state not initialized"); 728 return aegis256x4_mac_update(&state, message.ptr, message.length); 729 } 730 731 /** 732 * Finalizes the MAC computation, producing the MAC. 733 * 734 * Params: 735 * mac = Buffer to store the MAC (must be `aegis256x4_ABYTES_MIN` to `aegis256x4_ABYTES_MAX`). 736 * maclen = Length of the MAC. 737 * 738 * Returns: 739 * 0 on success, or a negative error code on failure. 740 */ 741 @nogc @trusted 742 int finalize(ubyte[] mac, size_t maclen) 743 { 744 assert(initialized, "MAC state not initialized"); 745 assert(maclen >= aegis256x4_ABYTES_MIN && maclen <= aegis256x4_ABYTES_MAX, "Invalid MAC length"); 746 return aegis256x4_mac_final(&state, mac.ptr, maclen); 747 } 748 749 /** 750 * Verifies a MAC against the computed MAC. 751 * 752 * Params: 753 * mac = The MAC to verify (must be `aegis256x4_ABYTES_MIN` to `aegis256x4_ABYTES_MAX`). 754 * maclen = Length of the MAC. 755 * 756 * Returns: 757 * 0 if valid, -1 if verification fails, or another negative error code on failure. 758 */ 759 @nogc @trusted 760 int verify(const(ubyte)[] mac, size_t maclen) 761 { 762 assert(initialized, "MAC state not initialized"); 763 assert(maclen >= aegis256x4_ABYTES_MIN && maclen <= aegis256x4_ABYTES_MAX, "Invalid MAC length"); 764 return aegis256x4_mac_verify(&state, mac.ptr, maclen); 765 } 766 767 /** 768 * Resets the MAC state for reuse with the same key and nonce. 769 */ 770 @nogc @trusted 771 void reset() 772 { 773 assert(initialized, "MAC state not initialized"); 774 aegis256x4_mac_reset(&state); 775 } 776 } 777 778 version (unittest) 779 { 780 @trusted 781 @("AEGIS256") 782 unittest 783 { 784 import std.random : uniform; 785 import core.stdc.string : memcmp; 786 787 // Initialize AEGIS library 788 assert(aegis_init() == 0, "AEGIS initialization failed"); 789 790 // Common test data 791 enum size_t bufferSize = 256; // Sufficient for test message and tail bytes 792 ubyte[aegis256_KEYBYTES] key; 793 foreach (ref k; key) 794 k = cast(ubyte) uniform(0, bufferSize); 795 ubyte[aegis256_NPUBBYTES] nonce; 796 foreach (ref n; nonce) 797 n = cast(ubyte) uniform(0, bufferSize); 798 ubyte[] ad = cast(ubyte[]) "ABC"; 799 ubyte[] message = cast(ubyte[]) "Hello"; 800 ubyte[bufferSize] ciphertext; 801 ubyte[aegis256_ABYTES_MIN] macMin; 802 ubyte[aegis256_ABYTES_MAX] macMax; 803 size_t written; 804 805 // Test Aegis256State (encryption/decryption) 806 { 807 // Encryption 808 auto encState = Aegis256State(key, nonce, ad); 809 written = 0; 810 assert(encState.encryptUpdate(ciphertext[], &written, message) == 0, 811 "Aegis256State: Encryption update failed"); 812 assert(written <= ciphertext.length, "Aegis256State: Written bytes exceed buffer"); 813 size_t totalWritten = written; 814 815 assert(encState.encryptDetachedFinal(ciphertext[totalWritten .. $], &written, 816 macMin[], aegis256_ABYTES_MIN) == 0, "Aegis256State: Encryption finalization failed"); 817 totalWritten += written; 818 assert(totalWritten <= ciphertext.length, "Aegis256State: Total written bytes exceed buffer"); 819 820 // Decryption 821 ubyte[bufferSize] decrypted; 822 size_t decWritten; 823 auto decState = Aegis256State(key, nonce, ad); 824 decWritten = 0; 825 assert(decState.decryptDetachedUpdate(decrypted[], &decWritten, ciphertext[0 .. totalWritten]) == 0, 826 "Aegis256State: Decryption update failed"); 827 assert(decWritten <= decrypted.length, "Aegis256State: Decrypted bytes exceed buffer"); 828 size_t totalDecWritten = decWritten; 829 830 assert(decState.decryptDetachedFinal(decrypted[totalDecWritten .. $], &decWritten, 831 macMin[], aegis256_ABYTES_MIN) == 0, "Aegis256State: Decryption finalization failed"); 832 totalDecWritten += decWritten; 833 assert(totalDecWritten <= decrypted.length, "Aegis256State: Total decrypted bytes exceed buffer"); 834 assert(totalDecWritten >= message.length, "Aegis256State: Decrypted length too short"); 835 assert(decrypted[0 .. message.length] == message, "Aegis256State: Decrypted message mismatch"); 836 } 837 838 // Test Aegis256MACState 839 { 840 auto macState = Aegis256MACState(key, nonce); 841 assert(macState.update(message) == 0, "Aegis256MACState: MAC update failed"); 842 assert(macState.finalize(macMin[], aegis256_ABYTES_MIN) == 0, 843 "Aegis256MACState: MAC finalization (min) failed"); 844 assert(macState.finalize(macMax[], aegis256_ABYTES_MAX) == 0, 845 "Aegis256MACState: MAC finalization (max) failed"); 846 847 // Verify MAC (streaming) 848 macState.reset(); 849 assert(macState.update(message) == 0, "Aegis256MACState: MAC update for verification failed"); 850 assert(macState.verify(macMin[], aegis256_ABYTES_MIN) == 0, 851 "Aegis256MACState: MAC verification (min) failed"); 852 853 // Test reset 854 macState.reset(); 855 assert(macState.update(message) == 0, "Aegis256MACState: MAC update after reset failed"); 856 ubyte[aegis256_ABYTES_MIN] newMac; 857 assert(macState.finalize(newMac[], aegis256_ABYTES_MIN) == 0, 858 "Aegis256MACState: MAC finalization after reset failed"); 859 assert(memcmp(macMin.ptr, newMac.ptr, aegis256_ABYTES_MIN) == 0, 860 "Aegis256MACState: MACs do not match after reset"); 861 862 // Test invalid MAC 863 macState.reset(); 864 assert(macState.update(message) == 0, "Aegis256MACState: MAC update for invalid verification failed"); 865 ubyte[aegis256_ABYTES_MIN] wrongMac = macMin; 866 wrongMac[0] ^= 0xFF; 867 assert(macState.verify(wrongMac[], aegis256_ABYTES_MIN) == -1, 868 "Aegis256MACState: Invalid MAC verification did not fail"); 869 } 870 } 871 }