1 /* 2 * Boost Software License - Version 1.0 3 * https://www.boost.org/LICENSE_1_0.txt 4 */ 5 module aegis.aegis128; 6 7 /// AEGIS C bindings 8 import c.aegisc; // @system 9 import core.stdc.string : memset; 10 11 /** 12 * AEGIS-128L encryption/decryption state. 13 * 14 * Manages the lifecycle of an `aegis128l_state` object, ensuring proper initialization 15 * and cleanup in a `@nogc` and `@trusted` context. 16 * 17 * Examples: 18 * --- 19 * ubyte[16] key = [0x01, 0x02, ..., 0x10]; 20 * ubyte[16] nonce = [0x00, 0x01, ..., 0x0F]; 21 * ubyte[] ad = [0x41, 0x42, 0x43]; // Associated data 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 = Aegis128LState(key, nonce, ad); 28 * state.encryptUpdate(ciphertext[], &written, message); 29 * state.encryptDetachedFinal(ciphertext[written .. $], &written, mac[], 16); 30 * --- 31 */ 32 struct Aegis128LState 33 { 34 private aegis128l_state state; // Underlying C state 35 private bool initialized; // Tracks initialization status 36 37 /** 38 * Initializes the AEGIS-128L state with the provided key, nonce, and associated data. 39 * 40 * Params: 41 * key = The encryption key (must be `aegis128l_KEYBYTES` long). 42 * nonce = The nonce (must be `aegis128l_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 == aegis128l_KEYBYTES, "Key length must be aegis128l_KEYBYTES"); 52 assert(nonce.length == aegis128l_NPUBBYTES, "Nonce length must be aegis128l_NPUBBYTES"); 53 54 memset(&state, 0, aegis128l_state.sizeof); 55 aegis128l_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, aegis128l_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 aegis128l_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 `aegis128l_ABYTES_MIN` to `aegis128l_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 >= aegis128l_ABYTES_MIN && maclen <= aegis128l_ABYTES_MAX, "Invalid MAC length"); 114 return aegis128l_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 aegis128l_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 `aegis128l_ABYTES_MIN` to `aegis128l_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 >= aegis128l_ABYTES_MIN && maclen <= aegis128l_ABYTES_MAX, "Invalid MAC length"); 156 return aegis128l_state_decrypt_detached_final(&state, plaintext.ptr, plaintext.length, 157 written, mac.ptr, maclen); 158 } 159 } 160 161 /** 162 * AEGIS-128L MAC state. 163 * 164 * Manages the lifecycle of an `aegis128l_mac_state` object in a `@nogc` and `@trusted` context. 165 * 166 * Examples: 167 * --- 168 * ubyte[16] key = [0x01, 0x02, ..., 0x10]; 169 * ubyte[16] nonce = [0x00, 0x01, ..., 0x0F]; 170 * ubyte[] message = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello" 171 * ubyte[16] mac; 172 * 173 * auto macState = Aegis128LMACState(key, nonce); 174 * macState.update(message); 175 * macState.finalize(mac[], 16); 176 * --- 177 */ 178 struct Aegis128LMACState 179 { 180 private aegis128l_mac_state state; // Underlying C MAC state 181 private bool initialized; // Tracks initialization status 182 183 /** 184 * Initializes the AEGIS-128L MAC state with the provided key and nonce. 185 * 186 * Params: 187 * key = The key (must be `aegis128l_KEYBYTES` long). 188 * nonce = The nonce (must be `aegis128l_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 == aegis128l_KEYBYTES, "Key length must be aegis128l_KEYBYTES"); 197 assert(nonce.length == aegis128l_NPUBBYTES, "Nonce length must be aegis128l_NPUBBYTES"); 198 199 memset(&state, 0, aegis128l_mac_state.sizeof); 200 aegis128l_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 aegis128l_mac_reset(&state); 212 memset(&state, 0, aegis128l_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 aegis128l_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 `aegis128l_ABYTES_MIN` to `aegis128l_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 >= aegis128l_ABYTES_MIN && maclen <= aegis128l_ABYTES_MAX, "Invalid MAC length"); 253 return aegis128l_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 `aegis128l_ABYTES_MIN` to `aegis128l_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 >= aegis128l_ABYTES_MIN && maclen <= aegis128l_ABYTES_MAX, "Invalid MAC length"); 271 return aegis128l_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 aegis128l_mac_reset(&state); 282 } 283 } 284 285 /** 286 * AEGIS-128x2 encryption/decryption state. 287 * 288 * Manages the lifecycle of an `aegis128x2_state` object in a `@nogc` and `@trusted` context. 289 * 290 * Examples: 291 * --- 292 * ubyte[16] key = [0x01, 0x02, ..., 0x10]; 293 * ubyte[16] nonce = [0x00, 0x01, ..., 0x0F]; 294 * ubyte[] ad = [0x41, 0x42, 0x43]; // Associated data 295 * ubyte[] message = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello" 296 * ubyte[128] ciphertext; 297 * ubyte[16] mac; 298 * size_t written; 299 * 300 * auto state = Aegis128x2State(key, nonce, ad); 301 * state.encryptUpdate(ciphertext[], &written, message); 302 * state.encryptDetachedFinal(ciphertext[written .. $], &written, mac[], 16); 303 * --- 304 */ 305 struct Aegis128x2State 306 { 307 private aegis128x2_state state; // Underlying C state 308 private bool initialized; // Tracks initialization status 309 310 /** 311 * Initializes the AEGIS-128x2 state with the provided key, nonce, and associated data. 312 * 313 * Params: 314 * key = The encryption key (must be `aegis128x2_KEYBYTES` long). 315 * nonce = The nonce (must be `aegis128x2_NPUBBYTES` long). 316 * ad = The associated data (optional, can be empty). 317 * 318 * Throws: 319 * AssertError if key or nonce lengths are invalid. 320 */ 321 @nogc @trusted 322 this(const(ubyte)[] key, const(ubyte)[] nonce, const(ubyte)[] ad = null) 323 { 324 assert(key.length == aegis128x2_KEYBYTES, "Key length must be aegis128x2_KEYBYTES"); 325 assert(nonce.length == aegis128x2_NPUBBYTES, "Nonce length must be aegis128x2_NPUBBYTES"); 326 327 memset(&state, 0, aegis128x2_state.sizeof); 328 aegis128x2_state_init(&state, ad.ptr, ad.length, nonce.ptr, key.ptr); 329 initialized = true; 330 } 331 332 /** 333 * Destructor to clean up the state. 334 */ 335 @nogc @trusted ~this() 336 { 337 if (initialized) 338 { 339 memset(&state, 0, aegis128x2_state.sizeof); 340 initialized = false; 341 } 342 } 343 344 /** 345 * Disabled copy constructor to prevent state duplication. 346 */ 347 @disable this(this); 348 349 /** 350 * Encrypts a message chunk, producing ciphertext. 351 * 352 * Params: 353 * ciphertext = Buffer to store the ciphertext. 354 * written = Pointer to store the number of bytes written. 355 * message = The plaintext message to encrypt. 356 * 357 * Returns: 358 * 0 on success, or a negative error code on failure. 359 */ 360 @nogc @trusted 361 int encryptUpdate(ubyte[] ciphertext, size_t* written, const(ubyte)[] message) 362 { 363 assert(initialized, "State not initialized"); 364 assert(written !is null, "Written pointer cannot be null"); 365 return aegis128x2_state_encrypt_update(&state, ciphertext.ptr, ciphertext.length, 366 written, message.ptr, message.length); 367 } 368 369 /** 370 * Finalizes encryption in detached mode, producing the final ciphertext and MAC. 371 * 372 * Params: 373 * ciphertext = Buffer for remaining ciphertext. 374 * written = Pointer to store the number of bytes written. 375 * mac = Buffer for the MAC (must be `aegis128x2_ABYTES_MIN` to `aegis128x2_ABYTES_MAX`). 376 * maclen = Length of the MAC. 377 * 378 * Returns: 379 * 0 on success, or a negative error code on failure. 380 */ 381 @nogc @trusted 382 int encryptDetachedFinal(ubyte[] ciphertext, size_t* written, ubyte[] mac, size_t maclen) 383 { 384 assert(initialized, "State not initialized"); 385 assert(written !is null, "Written pointer cannot be null"); 386 assert(maclen >= aegis128x2_ABYTES_MIN && maclen <= aegis128x2_ABYTES_MAX, "Invalid MAC length"); 387 return aegis128x2_state_encrypt_detached_final(&state, ciphertext.ptr, ciphertext.length, 388 written, mac.ptr, maclen); 389 } 390 391 /** 392 * Decrypts a ciphertext chunk, producing plaintext. 393 * 394 * Params: 395 * plaintext = Buffer to store the plaintext. 396 * written = Pointer to store the number of bytes written. 397 * ciphertext = The ciphertext to decrypt. 398 * 399 * Returns: 400 * 0 on success, or a negative error code on failure. 401 */ 402 @nogc @trusted 403 int decryptDetachedUpdate(ubyte[] plaintext, size_t* written, const(ubyte)[] ciphertext) 404 { 405 assert(initialized, "State not initialized"); 406 assert(written !is null, "Written pointer cannot be null"); 407 return aegis128x2_state_decrypt_detached_update(&state, plaintext.ptr, plaintext.length, 408 written, ciphertext.ptr, ciphertext.length); 409 } 410 411 /** 412 * Finalizes decryption in detached mode, verifying the MAC and producing final plaintext. 413 * 414 * Params: 415 * plaintext = Buffer for remaining plaintext. 416 * written = Pointer to store the number of bytes written. 417 * mac = The MAC to verify (must be `aegis128x2_ABYTES_MIN` to `aegis128x2_ABYTES_MAX`). 418 * maclen = Length of the MAC. 419 * 420 * Returns: 421 * 0 on success, -1 if MAC verification fails, or another negative error code on failure. 422 */ 423 @nogc @trusted 424 int decryptDetachedFinal(ubyte[] plaintext, size_t* written, const(ubyte)[] mac, size_t maclen) 425 { 426 assert(initialized, "State not initialized"); 427 assert(written !is null, "Written pointer cannot be null"); 428 assert(maclen >= aegis128x2_ABYTES_MIN && maclen <= aegis128x2_ABYTES_MAX, "Invalid MAC length"); 429 return aegis128x2_state_decrypt_detached_final(&state, plaintext.ptr, plaintext.length, 430 written, mac.ptr, maclen); 431 } 432 } 433 434 /** 435 * AEGIS-128x2 MAC state. 436 * 437 * Manages the lifecycle of an `aegis128x2_mac_state` object in a `@nogc` and `@trusted` context. 438 * 439 * Examples: 440 * --- 441 * ubyte[16] key = [0x01, 0x02, ..., 0x10]; 442 * ubyte[16] nonce = [0x00, 0x01, ..., 0x0F]; 443 * ubyte[] message = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello" 444 * ubyte[16] mac; 445 * 446 * auto macState = Aegis128x2MACState(key, nonce); 447 * macState.update(message); 448 * macState.finalize(mac[], 16); 449 * --- 450 */ 451 struct Aegis128x2MACState 452 { 453 private aegis128x2_mac_state state; // Underlying C MAC state 454 private bool initialized; // Tracks initialization status 455 456 /** 457 * Initializes the AEGIS-128x2 MAC state with the provided key and nonce. 458 * 459 * Params: 460 * key = The key (must be `aegis128x2_KEYBYTES` long). 461 * nonce = The nonce (must be `aegis128x2_NPUBBYTES` long). 462 * 463 * Throws: 464 * AssertError if key or nonce lengths are invalid. 465 */ 466 @nogc @trusted 467 this(const(ubyte)[] key, const(ubyte)[] nonce) 468 { 469 assert(key.length == aegis128x2_KEYBYTES, "Key length must be aegis128x2_KEYBYTES"); 470 assert(nonce.length == aegis128x2_NPUBBYTES, "Nonce length must be aegis128x2_NPUBBYTES"); 471 472 memset(&state, 0, aegis128x2_mac_state.sizeof); 473 aegis128x2_mac_init(&state, key.ptr, nonce.ptr); 474 initialized = true; 475 } 476 477 /** 478 * Destructor to clean up the MAC state. 479 */ 480 @nogc @trusted ~this() 481 { 482 if (initialized) 483 { 484 aegis128x2_mac_reset(&state); 485 memset(&state, 0, aegis128x2_mac_state.sizeof); 486 initialized = false; 487 } 488 } 489 490 /** 491 * Disabled copy constructor to prevent state duplication. 492 */ 493 @disable this(this); 494 495 /** 496 * Updates the MAC state with a message chunk. 497 * 498 * Params: 499 * message = The message to process. 500 * 501 * Returns: 502 * 0 on success, or a negative error code on failure. 503 */ 504 @nogc @trusted 505 int update(const(ubyte)[] message) 506 { 507 assert(initialized, "MAC state not initialized"); 508 return aegis128x2_mac_update(&state, message.ptr, message.length); 509 } 510 511 /** 512 * Finalizes the MAC computation, producing the MAC. 513 * 514 * Params: 515 * mac = Buffer to store the MAC (must be `aegis128x2_ABYTES_MIN` to `aegis128x2_ABYTES_MAX`). 516 * maclen = Length of the MAC. 517 * 518 * Returns: 519 * 0 on success, or a negative error code on failure. 520 */ 521 @nogc @trusted 522 int finalize(ubyte[] mac, size_t maclen) 523 { 524 assert(initialized, "MAC state not initialized"); 525 assert(maclen >= aegis128x2_ABYTES_MIN && maclen <= aegis128x2_ABYTES_MAX, "Invalid MAC length"); 526 return aegis128x2_mac_final(&state, mac.ptr, maclen); 527 } 528 529 /** 530 * Verifies a MAC against the computed MAC. 531 * 532 * Params: 533 * mac = The MAC to verify (must be `aegis128x2_ABYTES_MIN` to `aegis128x2_ABYTES_MAX`). 534 * maclen = Length of the MAC. 535 * 536 * Returns: 537 * 0 if valid, -1 if verification fails, or another negative error code on failure. 538 */ 539 @nogc @trusted 540 int verify(const(ubyte)[] mac, size_t maclen) 541 { 542 assert(initialized, "MAC state not initialized"); 543 assert(maclen >= aegis128x2_ABYTES_MIN && maclen <= aegis128x2_ABYTES_MAX, "Invalid MAC length"); 544 return aegis128x2_mac_verify(&state, mac.ptr, maclen); 545 } 546 547 /** 548 * Resets the MAC state for reuse with the same key and nonce. 549 */ 550 @nogc @trusted 551 void reset() 552 { 553 assert(initialized, "MAC state not initialized"); 554 aegis128x2_mac_reset(&state); 555 } 556 } 557 558 /** 559 * AEGIS-128x4 encryption/decryption state. 560 * 561 * Manages the lifecycle of an `aegis128x4_state` object in a `@nogc` and `@trusted` context. 562 * 563 * Examples: 564 * --- 565 * ubyte[16] key = [0x01, 0x02, ..., 0x10]; 566 * ubyte[16] nonce = [0x00, 0x01, ..., 0x0F]; 567 * ubyte[] ad = [0x41, 0x42, 0x43]; // Associated data 568 * ubyte[] message = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello" 569 * ubyte[128] ciphertext; 570 * ubyte[16] mac; 571 * size_t written; 572 * 573 * auto state = Aegis128x4State(key, nonce, ad); 574 * state.encryptUpdate(ciphertext[], &written, message); 575 * state.encryptDetachedFinal(ciphertext[written .. $], &written, mac[], 16); 576 * --- 577 */ 578 struct Aegis128x4State 579 { 580 private aegis128x4_state state; // Underlying C state 581 private bool initialized; // Tracks initialization status 582 583 /** 584 * Initializes the AEGIS-128x4 state with the provided key, nonce, and associated data. 585 * 586 * Params: 587 * key = The encryption key (must be `aegis128x4_KEYBYTES` long). 588 * nonce = The nonce (must be `aegis128x4_NPUBBYTES` long). 589 * ad = The associated data (optional, can be empty). 590 * 591 * Throws: 592 * AssertError if key or nonce lengths are invalid. 593 */ 594 @nogc @trusted 595 this(const(ubyte)[] key, const(ubyte)[] nonce, const(ubyte)[] ad = null) 596 { 597 assert(key.length == aegis128x4_KEYBYTES, "Key length must be aegis128x4_KEYBYTES"); 598 assert(nonce.length == aegis128x4_NPUBBYTES, "Nonce length must be aegis128x4_NPUBBYTES"); 599 600 memset(&state, 0, aegis128x4_state.sizeof); 601 aegis128x4_state_init(&state, ad.ptr, ad.length, nonce.ptr, key.ptr); 602 initialized = true; 603 } 604 605 /** 606 * Destructor to clean up the state. 607 */ 608 @nogc @trusted ~this() 609 { 610 if (initialized) 611 { 612 memset(&state, 0, aegis128x4_state.sizeof); 613 initialized = false; 614 } 615 } 616 617 /** 618 * Disabled copy constructor to prevent state duplication. 619 */ 620 @disable this(this); 621 622 /** 623 * Encrypts a message chunk, producing ciphertext. 624 * 625 * Params: 626 * ciphertext = Buffer to store the ciphertext. 627 * written = Pointer to store the number of bytes written. 628 * message = The plaintext message to encrypt. 629 * 630 * Returns: 631 * 0 on success, or a negative error code on failure. 632 */ 633 @nogc @trusted 634 int encryptUpdate(ubyte[] ciphertext, size_t* written, const(ubyte)[] message) 635 { 636 assert(initialized, "State not initialized"); 637 assert(written !is null, "Written pointer cannot be null"); 638 return aegis128x4_state_encrypt_update(&state, ciphertext.ptr, ciphertext.length, 639 written, message.ptr, message.length); 640 } 641 642 /** 643 * Finalizes encryption in detached mode, producing the final ciphertext and MAC. 644 * 645 * Params: 646 * ciphertext = Buffer for remaining ciphertext. 647 * written = Pointer to store the number of bytes written. 648 * mac = Buffer for the MAC (must be `aegis128x4_ABYTES_MIN` to `aegis128x4_ABYTES_MAX`). 649 * maclen = Length of the MAC. 650 * 651 * Returns: 652 * 0 on success, or a negative error code on failure. 653 */ 654 @nogc @trusted 655 int encryptDetachedFinal(ubyte[] ciphertext, size_t* written, ubyte[] mac, size_t maclen) 656 { 657 assert(initialized, "State not initialized"); 658 assert(written !is null, "Written pointer cannot be null"); 659 assert(maclen >= aegis128x4_ABYTES_MIN && maclen <= aegis128x4_ABYTES_MAX, "Invalid MAC length"); 660 return aegis128x4_state_encrypt_detached_final(&state, ciphertext.ptr, ciphertext.length, 661 written, mac.ptr, maclen); 662 } 663 664 /** 665 * Decrypts a ciphertext chunk, producing plaintext. 666 * 667 * Params: 668 * plaintext = Buffer to store the plaintext. 669 * written = Pointer to store the number of bytes written. 670 * ciphertext = The ciphertext to decrypt. 671 * 672 * Returns: 673 * 0 on success, or a negative error code on failure. 674 */ 675 @nogc @trusted 676 int decryptDetachedUpdate(ubyte[] plaintext, size_t* written, const(ubyte)[] ciphertext) 677 { 678 assert(initialized, "State not initialized"); 679 assert(written !is null, "Written pointer cannot be null"); 680 return aegis128x4_state_decrypt_detached_update(&state, plaintext.ptr, plaintext.length, 681 written, ciphertext.ptr, ciphertext.length); 682 } 683 684 /** 685 * Finalizes decryption in detached mode, verifying the MAC and producing final plaintext. 686 * 687 * Params: 688 * plaintext = Buffer for remaining plaintext. 689 * written = Pointer to store the number of bytes written. 690 * mac = The MAC to verify (must be `aegis128x4_ABYTES_MIN` to `aegis128x4_ABYTES_MAX`). 691 * maclen = Length of the MAC. 692 * 693 * Returns: 694 * 0 on success, -1 if MAC verification fails, or another negative error code on failure. 695 */ 696 @nogc @trusted 697 int decryptDetachedFinal(ubyte[] plaintext, size_t* written, const(ubyte)[] mac, size_t maclen) 698 { 699 assert(initialized, "State not initialized"); 700 assert(written !is null, "Written pointer cannot be null"); 701 assert(maclen >= aegis128x4_ABYTES_MIN && maclen <= aegis128x4_ABYTES_MAX, "Invalid MAC length"); 702 return aegis128x4_state_decrypt_detached_final(&state, plaintext.ptr, plaintext.length, 703 written, mac.ptr, maclen); 704 } 705 } 706 707 /** 708 * AEGIS-128x4 MAC state. 709 * 710 * Manages the lifecycle of an `aegis128x4_mac_state` object in a `@nogc` and `@trusted` context. 711 * 712 * Examples: 713 * --- 714 * ubyte[16] key = [0x01, 0x02, ..., 0x10]; 715 * ubyte[16] nonce = [0x00, 0x01, ..., 0x0F]; 716 * ubyte[] message = [0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello" 717 * ubyte[16] mac; 718 * 719 * auto macState = Aegis128x4MACState(key, nonce); 720 * macState.update(message); 721 * macState.finalize(mac[], 16); 722 * --- 723 */ 724 struct Aegis128x4MACState 725 { 726 private aegis128x4_mac_state state; // Underlying C MAC state 727 private bool initialized; // Tracks initialization status 728 729 /** 730 * Initializes the AEGIS-128x4 MAC state with the provided key and nonce. 731 * 732 * Params: 733 * key = The key (must be `aegis128x4_KEYBYTES` long). 734 * nonce = The nonce (must be `aegis128x4_NPUBBYTES` long). 735 * 736 * Throws: 737 * AssertError if key or nonce lengths are invalid. 738 */ 739 @nogc @trusted 740 this(const(ubyte)[] key, const(ubyte)[] nonce) 741 { 742 assert(key.length == aegis128x4_KEYBYTES, "Key length must be aegis128x4_KEYBYTES"); 743 assert(nonce.length == aegis128x4_NPUBBYTES, "Nonce length must be aegis128x4_NPUBBYTES"); 744 745 memset(&state, 0, aegis128x4_mac_state.sizeof); 746 aegis128x4_mac_init(&state, key.ptr, nonce.ptr); 747 initialized = true; 748 } 749 750 /** 751 * Destructor to clean up the MAC state. 752 */ 753 @nogc @trusted ~this() 754 { 755 if (initialized) 756 { 757 aegis128x4_mac_reset(&state); 758 memset(&state, 0, aegis128x4_mac_state.sizeof); 759 initialized = false; 760 } 761 } 762 763 /** 764 * Disabled copy constructor to prevent state duplication. 765 */ 766 @disable this(this); 767 768 /** 769 * Updates the MAC state with a message chunk. 770 * 771 * Params: 772 * message = The message to process. 773 * 774 * Returns: 775 * 0 on success, or a negative error code on failure. 776 */ 777 @nogc @trusted 778 int update(const(ubyte)[] message) 779 { 780 assert(initialized, "MAC state not initialized"); 781 return aegis128x4_mac_update(&state, message.ptr, message.length); 782 } 783 784 /** 785 * Finalizes the MAC computation, producing the MAC. 786 * 787 * Params: 788 * mac = Buffer to store the MAC (must be `aegis128x4_ABYTES_MIN` to `aegis128x4_ABYTES_MAX`). 789 * maclen = Length of the MAC. 790 * 791 * Returns: 792 * 0 on success, or a negative error code on failure. 793 */ 794 @nogc @trusted 795 int finalize(ubyte[] mac, size_t maclen) 796 { 797 assert(initialized, "MAC state not initialized"); 798 assert(maclen >= aegis128x4_ABYTES_MIN && maclen <= aegis128x4_ABYTES_MAX, "Invalid MAC length"); 799 return aegis128x4_mac_final(&state, mac.ptr, maclen); 800 } 801 802 /** 803 * Verifies a MAC against the computed MAC. 804 * 805 * Params: 806 * mac = The MAC to verify (must be `aegis128x4_ABYTES_MIN` to `aegis128x4_ABYTES_MAX`). 807 * maclen = Length of the MAC. 808 * 809 * Returns: 810 * 0 if valid, -1 if verification fails, or another negative error code on failure. 811 */ 812 @nogc @trusted 813 int verify(const(ubyte)[] mac, size_t maclen) 814 { 815 assert(initialized, "MAC state not initialized"); 816 assert(maclen >= aegis128x4_ABYTES_MIN && maclen <= aegis128x4_ABYTES_MAX, "Invalid MAC length"); 817 return aegis128x4_mac_verify(&state, mac.ptr, maclen); 818 } 819 820 /** 821 * Resets the MAC state for reuse with the same key and nonce. 822 */ 823 @nogc @trusted 824 void reset() 825 { 826 assert(initialized, "MAC state not initialized"); 827 aegis128x4_mac_reset(&state); 828 } 829 } 830 831 version (unittest) 832 { 833 @trusted 834 @("AEGIS128") 835 unittest 836 { 837 import std.random : uniform; 838 839 // Initialize AEGIS library 840 assert(aegis_init() == 0, "AEGIS initialization failed"); 841 842 // Common test data 843 enum size_t bufferSize = 256; // Sufficient for test message and tail bytes 844 ubyte[aegis128l_KEYBYTES] key; 845 foreach (ref k; key) 846 k = cast(ubyte) uniform(0, bufferSize); 847 ubyte[aegis128l_NPUBBYTES] nonce; 848 foreach (ref n; nonce) 849 n = cast(ubyte) uniform(0, bufferSize); 850 ubyte[] ad = cast(ubyte[]) "ABC".ptr; 851 ubyte[] message = cast(ubyte[]) "Hello".ptr; 852 ubyte[bufferSize] ciphertext; 853 ubyte[aegis128l_ABYTES_MIN] macMin; 854 ubyte[aegis128l_ABYTES_MAX] macMax; 855 size_t written; 856 857 // Test Aegis128LState (encryption/decryption) 858 { 859 // Encryption 860 auto encState = Aegis128LState(key, nonce, ad); 861 written = 0; 862 assert(encState.encryptUpdate(ciphertext[], &written, message) == 0, 863 "Aegis128LState: Encryption update failed"); 864 assert(written <= ciphertext.length, "Aegis128LState: Written bytes exceed buffer"); 865 size_t totalWritten = written; 866 867 assert(encState.encryptDetachedFinal(ciphertext[totalWritten .. $], &written, 868 macMin[], aegis128l_ABYTES_MIN) == 0, "Aegis128LState: Encryption finalization failed"); 869 totalWritten += written; 870 assert(totalWritten <= ciphertext.length, "Aegis128LState: Total written bytes exceed buffer"); 871 872 // Decryption 873 ubyte[bufferSize] decrypted; 874 size_t decWritten; 875 auto decState = Aegis128LState(key, nonce, ad); 876 decWritten = 0; 877 assert(decState.decryptDetachedUpdate(decrypted[], &decWritten, ciphertext[0 .. totalWritten]) == 0, 878 "Aegis128LState: Decryption update failed"); 879 assert(decWritten <= decrypted.length, "Aegis128LState: Decrypted bytes exceed buffer"); 880 size_t totalDecWritten = decWritten; 881 882 assert(decState.decryptDetachedFinal(decrypted[totalDecWritten .. $], &decWritten, 883 macMin[], aegis128l_ABYTES_MIN) == 0, "Aegis128LState: Decryption finalization failed"); 884 totalDecWritten += decWritten; 885 assert(totalDecWritten <= decrypted.length, "Aegis128LState: Total decrypted bytes exceed buffer"); 886 assert(totalDecWritten >= message.length, "Aegis128LState: Decrypted length too short"); 887 assert(decrypted[0 .. message.length] == message, "Aegis128LState: Decrypted message mismatch"); 888 } 889 890 // Test Aegis128LMACState 891 { 892 import core.stdc.string : memcmp; 893 894 auto macState = Aegis128LMACState(key, nonce); 895 assert(macState.update(message) == 0, "Aegis128LMACState: MAC update failed"); 896 assert(macState.finalize(macMin[], aegis128l_ABYTES_MIN) == 0, 897 "Aegis128LMACState: MAC finalization (min) failed"); 898 assert(macState.finalize(macMax[], aegis128l_ABYTES_MAX) == 0, 899 "Aegis128LMACState: MAC finalization (max) failed"); 900 901 // Verify MAC (streaming) 902 macState.reset(); 903 assert(macState.update(message) == 0, "Aegis128LMACState: MAC update for verification failed"); 904 assert(macState.verify(macMin[], aegis128l_ABYTES_MIN) == 0, 905 "Aegis128LMACState: MAC verification (min) failed"); 906 907 // Test reset 908 macState.reset(); 909 assert(macState.update(message) == 0, "Aegis128LMACState: MAC update after reset failed"); 910 ubyte[aegis128l_ABYTES_MIN] newMac; 911 assert(macState.finalize(newMac[], aegis128l_ABYTES_MIN) == 0, 912 "Aegis128LMACState: MAC finalization after reset failed"); 913 assert(memcmp(macMin.ptr, newMac.ptr, aegis128l_ABYTES_MIN) == 0, 914 "Aegis128LMACState: MACs do not match after reset"); 915 916 // Test invalid MAC 917 macState.reset(); 918 assert(macState.update(message) == 0, "Aegis128LMACState: MAC update for invalid verification failed"); 919 ubyte[aegis128l_ABYTES_MIN] wrongMac = macMin; 920 wrongMac[0] ^= 0xFF; 921 assert(macState.verify(wrongMac[], aegis128l_ABYTES_MIN) == -1, 922 "Aegis128LMACState: Invalid MAC verification did not fail"); 923 } 924 } 925 }