fde.c (43761B)
1 /* FDE - Full Disk Encryption for LuajitOS 2 * 3 * Cascaded encryption: AES-256-GCM (inner) + XChaCha20-Poly1305 (outer) 4 */ 5 6 #include "fde.h" 7 #include "ata.h" 8 #include "crypto/AES-256-GCM.h" 9 #include "crypto/XChaCha20-Poly1305.h" 10 #include "crypto/Argon2.h" 11 #include "crypto/CSPRNG.h" 12 #include "crypto/hashing/hash.h" 13 #include <string.h> 14 #include <stdlib.h> 15 #include <stdio.h> 16 17 /* External terminal function for debug output */ 18 extern void terminal_writestring(const char *str); 19 20 /* ============================================================================ 21 * Internal Helper Functions 22 * ========================================================================= */ 23 24 /* Constant-time memory comparison */ 25 static int secure_compare(const uint8_t *a, const uint8_t *b, size_t len) { 26 uint8_t diff = 0; 27 for (size_t i = 0; i < len; i++) { 28 diff |= a[i] ^ b[i]; 29 } 30 return diff == 0 ? 0 : -1; 31 } 32 33 /* Secure memory wipe */ 34 static void secure_wipe(void *ptr, size_t len) { 35 volatile uint8_t *p = (volatile uint8_t *)ptr; 36 while (len--) { 37 *p++ = 0; 38 } 39 } 40 41 /* Derive encryption keys from password using Argon2id */ 42 static int derive_keys(const char *password, size_t pass_len, 43 const uint8_t salt[FDE_SALT_SIZE], 44 uint8_t aes_key[FDE_KEY_SIZE], 45 uint8_t xchacha_key[FDE_KEY_SIZE]) { 46 uint8_t master_key[FDE_MASTER_KEY_SIZE]; 47 48 /* Use Argon2id with recommended parameters */ 49 int result = argon2id((const uint8_t *)password, pass_len, 50 salt, FDE_SALT_SIZE, 51 FDE_KDF_TIME, FDE_KDF_MEMORY, 52 master_key, FDE_MASTER_KEY_SIZE); 53 54 if (result != 0) { 55 secure_wipe(master_key, sizeof(master_key)); 56 return FDE_ERR_CRYPTO; 57 } 58 59 /* Split master key into two keys */ 60 memcpy(aes_key, master_key, FDE_KEY_SIZE); 61 memcpy(xchacha_key, master_key + FDE_KEY_SIZE, FDE_KEY_SIZE); 62 63 secure_wipe(master_key, sizeof(master_key)); 64 return FDE_OK; 65 } 66 67 /* Build AES nonce from sector number */ 68 static void build_aes_nonce(uint32_t sector, uint8_t nonce[FDE_NONCE_AES_SIZE]) { 69 memset(nonce, 0, FDE_NONCE_AES_SIZE); 70 /* Little-endian sector number in first 4 bytes */ 71 nonce[0] = (sector >> 0) & 0xFF; 72 nonce[1] = (sector >> 8) & 0xFF; 73 nonce[2] = (sector >> 16) & 0xFF; 74 nonce[3] = (sector >> 24) & 0xFF; 75 /* Remaining 8 bytes are zero - unique per sector */ 76 } 77 78 /* Build XChaCha nonce from sector number */ 79 static void build_xchacha_nonce(uint32_t sector, uint8_t nonce[FDE_NONCE_XCHACHA_SIZE]) { 80 memset(nonce, 0, FDE_NONCE_XCHACHA_SIZE); 81 /* Little-endian sector number in first 4 bytes */ 82 nonce[0] = (sector >> 0) & 0xFF; 83 nonce[1] = (sector >> 8) & 0xFF; 84 nonce[2] = (sector >> 16) & 0xFF; 85 nonce[3] = (sector >> 24) & 0xFF; 86 /* Remaining 20 bytes are zero - unique per sector */ 87 } 88 89 /* Calculate how many sectors needed for tag storage */ 90 static uint32_t calc_tag_sectors(uint32_t data_sectors) { 91 /* Each data sector needs 32 bytes of tags (16 AES + 16 XChaCha) */ 92 /* 512 bytes per sector / 32 bytes per tag = 16 tags per sector */ 93 return (data_sectors + 15) / 16; 94 } 95 96 /* Read sector tags (partition_start is added to make absolute disk address) */ 97 static int read_sector_tags(uint8_t bus, uint8_t drive, uint32_t partition_start, 98 uint32_t tag_start, uint32_t sector, fde_sector_tags_t *tags) { 99 /* Each tag sector holds tags for 16 data sectors */ 100 uint32_t tag_sector = partition_start + tag_start + (sector / 16); 101 uint32_t tag_offset = (sector % 16) * FDE_TAGS_PER_SECTOR; 102 103 uint8_t tag_buffer[FDE_SECTOR_SIZE]; 104 int result = ata_read_sectors(bus, drive, tag_sector, 1, tag_buffer); 105 if (result != ATA_OK) { 106 return FDE_ERR_IO; 107 } 108 109 memcpy(tags, tag_buffer + tag_offset, sizeof(fde_sector_tags_t)); 110 return FDE_OK; 111 } 112 113 /* Write sector tags (partition_start is added to make absolute disk address) */ 114 static int write_sector_tags(uint8_t bus, uint8_t drive, uint32_t partition_start, 115 uint32_t tag_start, uint32_t sector, const fde_sector_tags_t *tags) { 116 /* Each tag sector holds tags for 16 data sectors */ 117 uint32_t tag_sector = partition_start + tag_start + (sector / 16); 118 uint32_t tag_offset = (sector % 16) * FDE_TAGS_PER_SECTOR; 119 120 /* Read-modify-write */ 121 uint8_t tag_buffer[FDE_SECTOR_SIZE]; 122 int result = ata_read_sectors(bus, drive, tag_sector, 1, tag_buffer); 123 if (result != ATA_OK) { 124 char dbg[80]; 125 snprintf(dbg, sizeof(dbg), "[FDE] write_sector_tags: read failed, tag_sector=%d\n", (int)tag_sector); 126 terminal_writestring(dbg); 127 return FDE_ERR_IO; 128 } 129 130 memcpy(tag_buffer + tag_offset, tags, sizeof(fde_sector_tags_t)); 131 132 result = ata_write_sectors(bus, drive, tag_sector, 1, tag_buffer); 133 if (result != ATA_OK) { 134 char dbg[80]; 135 snprintf(dbg, sizeof(dbg), "[FDE] write_sector_tags: write failed, tag_sector=%d\n", (int)tag_sector); 136 terminal_writestring(dbg); 137 return FDE_ERR_IO; 138 } 139 140 return FDE_OK; 141 } 142 143 /* Encrypt a single sector (cascade mode) */ 144 static int encrypt_sector_cascade(const uint8_t aes_key[FDE_KEY_SIZE], 145 const uint8_t xchacha_key[FDE_KEY_SIZE], 146 uint32_t sector_num, 147 const uint8_t plaintext[FDE_SECTOR_SIZE], 148 uint8_t ciphertext[FDE_SECTOR_SIZE], 149 fde_sector_tags_t *tags) { 150 uint8_t intermediate[FDE_SECTOR_SIZE]; 151 uint8_t aes_nonce[FDE_NONCE_AES_SIZE]; 152 uint8_t xchacha_nonce[FDE_NONCE_XCHACHA_SIZE]; 153 154 /* Build nonces */ 155 build_aes_nonce(sector_num, aes_nonce); 156 build_xchacha_nonce(sector_num, xchacha_nonce); 157 158 /* Inner encryption: AES-256-GCM */ 159 aes256_gcm_context aes_ctx; 160 if (aes256_gcm_init(&aes_ctx, aes_key) != 0) { 161 return FDE_ERR_CRYPTO; 162 } 163 164 if (aes256_gcm_encrypt(&aes_ctx, aes_nonce, FDE_NONCE_AES_SIZE, 165 NULL, 0, /* No AAD */ 166 plaintext, FDE_SECTOR_SIZE, 167 intermediate, tags->aes_tag, FDE_TAG_SIZE) != 0) { 168 aes256_gcm_cleanup(&aes_ctx); 169 return FDE_ERR_CRYPTO; 170 } 171 aes256_gcm_cleanup(&aes_ctx); 172 173 /* Outer encryption: XChaCha20-Poly1305 */ 174 xchacha20_poly1305_context xchacha_ctx; 175 if (xchacha20_poly1305_init(&xchacha_ctx, xchacha_key, xchacha_nonce) != 0) { 176 secure_wipe(intermediate, sizeof(intermediate)); 177 return FDE_ERR_CRYPTO; 178 } 179 180 if (xchacha20_poly1305_encrypt(&xchacha_ctx, NULL, 0, /* No AAD */ 181 intermediate, FDE_SECTOR_SIZE, 182 ciphertext, tags->xchacha_tag) != 0) { 183 xchacha20_poly1305_cleanup(&xchacha_ctx); 184 secure_wipe(intermediate, sizeof(intermediate)); 185 return FDE_ERR_CRYPTO; 186 } 187 xchacha20_poly1305_cleanup(&xchacha_ctx); 188 189 secure_wipe(intermediate, sizeof(intermediate)); 190 return FDE_OK; 191 } 192 193 /* Decrypt a single sector (cascade mode) */ 194 static int decrypt_sector_cascade(const uint8_t aes_key[FDE_KEY_SIZE], 195 const uint8_t xchacha_key[FDE_KEY_SIZE], 196 uint32_t sector_num, 197 const uint8_t ciphertext[FDE_SECTOR_SIZE], 198 const fde_sector_tags_t *tags, 199 uint8_t plaintext[FDE_SECTOR_SIZE]) { 200 uint8_t intermediate[FDE_SECTOR_SIZE]; 201 uint8_t aes_nonce[FDE_NONCE_AES_SIZE]; 202 uint8_t xchacha_nonce[FDE_NONCE_XCHACHA_SIZE]; 203 204 /* Build nonces */ 205 build_aes_nonce(sector_num, aes_nonce); 206 build_xchacha_nonce(sector_num, xchacha_nonce); 207 208 /* Outer decryption: XChaCha20-Poly1305 */ 209 xchacha20_poly1305_context xchacha_ctx; 210 if (xchacha20_poly1305_init(&xchacha_ctx, xchacha_key, xchacha_nonce) != 0) { 211 return FDE_ERR_CRYPTO; 212 } 213 214 if (xchacha20_poly1305_decrypt(&xchacha_ctx, NULL, 0, /* No AAD */ 215 ciphertext, FDE_SECTOR_SIZE, 216 tags->xchacha_tag, 217 intermediate) != 0) { 218 xchacha20_poly1305_cleanup(&xchacha_ctx); 219 return FDE_ERR_AUTH_FAILED; 220 } 221 xchacha20_poly1305_cleanup(&xchacha_ctx); 222 223 /* Inner decryption: AES-256-GCM */ 224 aes256_gcm_context aes_ctx; 225 if (aes256_gcm_init(&aes_ctx, aes_key) != 0) { 226 secure_wipe(intermediate, sizeof(intermediate)); 227 return FDE_ERR_CRYPTO; 228 } 229 230 if (aes256_gcm_decrypt(&aes_ctx, aes_nonce, FDE_NONCE_AES_SIZE, 231 NULL, 0, /* No AAD */ 232 intermediate, FDE_SECTOR_SIZE, 233 tags->aes_tag, FDE_TAG_SIZE, 234 plaintext) != 0) { 235 aes256_gcm_cleanup(&aes_ctx); 236 secure_wipe(intermediate, sizeof(intermediate)); 237 return FDE_ERR_AUTH_FAILED; 238 } 239 aes256_gcm_cleanup(&aes_ctx); 240 241 secure_wipe(intermediate, sizeof(intermediate)); 242 return FDE_OK; 243 } 244 245 /* Encrypt a single sector (AES only) */ 246 static int encrypt_sector_aes(const uint8_t aes_key[FDE_KEY_SIZE], 247 uint32_t sector_num, 248 const uint8_t plaintext[FDE_SECTOR_SIZE], 249 uint8_t ciphertext[FDE_SECTOR_SIZE], 250 fde_sector_tags_t *tags) { 251 uint8_t aes_nonce[FDE_NONCE_AES_SIZE]; 252 build_aes_nonce(sector_num, aes_nonce); 253 254 aes256_gcm_context aes_ctx; 255 if (aes256_gcm_init(&aes_ctx, aes_key) != 0) { 256 return FDE_ERR_CRYPTO; 257 } 258 259 if (aes256_gcm_encrypt(&aes_ctx, aes_nonce, FDE_NONCE_AES_SIZE, 260 NULL, 0, 261 plaintext, FDE_SECTOR_SIZE, 262 ciphertext, tags->aes_tag, FDE_TAG_SIZE) != 0) { 263 aes256_gcm_cleanup(&aes_ctx); 264 return FDE_ERR_CRYPTO; 265 } 266 aes256_gcm_cleanup(&aes_ctx); 267 268 /* Clear unused tag */ 269 memset(tags->xchacha_tag, 0, FDE_TAG_SIZE); 270 return FDE_OK; 271 } 272 273 /* Decrypt a single sector (AES only) */ 274 static int decrypt_sector_aes(const uint8_t aes_key[FDE_KEY_SIZE], 275 uint32_t sector_num, 276 const uint8_t ciphertext[FDE_SECTOR_SIZE], 277 const fde_sector_tags_t *tags, 278 uint8_t plaintext[FDE_SECTOR_SIZE]) { 279 uint8_t aes_nonce[FDE_NONCE_AES_SIZE]; 280 build_aes_nonce(sector_num, aes_nonce); 281 282 aes256_gcm_context aes_ctx; 283 if (aes256_gcm_init(&aes_ctx, aes_key) != 0) { 284 return FDE_ERR_CRYPTO; 285 } 286 287 if (aes256_gcm_decrypt(&aes_ctx, aes_nonce, FDE_NONCE_AES_SIZE, 288 NULL, 0, 289 ciphertext, FDE_SECTOR_SIZE, 290 tags->aes_tag, FDE_TAG_SIZE, 291 plaintext) != 0) { 292 aes256_gcm_cleanup(&aes_ctx); 293 return FDE_ERR_AUTH_FAILED; 294 } 295 aes256_gcm_cleanup(&aes_ctx); 296 return FDE_OK; 297 } 298 299 /* Encrypt a single sector (XChaCha only) */ 300 static int encrypt_sector_xchacha(const uint8_t xchacha_key[FDE_KEY_SIZE], 301 uint32_t sector_num, 302 const uint8_t plaintext[FDE_SECTOR_SIZE], 303 uint8_t ciphertext[FDE_SECTOR_SIZE], 304 fde_sector_tags_t *tags) { 305 uint8_t xchacha_nonce[FDE_NONCE_XCHACHA_SIZE]; 306 build_xchacha_nonce(sector_num, xchacha_nonce); 307 308 xchacha20_poly1305_context xchacha_ctx; 309 if (xchacha20_poly1305_init(&xchacha_ctx, xchacha_key, xchacha_nonce) != 0) { 310 return FDE_ERR_CRYPTO; 311 } 312 313 if (xchacha20_poly1305_encrypt(&xchacha_ctx, NULL, 0, 314 plaintext, FDE_SECTOR_SIZE, 315 ciphertext, tags->xchacha_tag) != 0) { 316 xchacha20_poly1305_cleanup(&xchacha_ctx); 317 return FDE_ERR_CRYPTO; 318 } 319 xchacha20_poly1305_cleanup(&xchacha_ctx); 320 321 /* Clear unused tag */ 322 memset(tags->aes_tag, 0, FDE_TAG_SIZE); 323 return FDE_OK; 324 } 325 326 /* Decrypt a single sector (XChaCha only) */ 327 static int decrypt_sector_xchacha(const uint8_t xchacha_key[FDE_KEY_SIZE], 328 uint32_t sector_num, 329 const uint8_t ciphertext[FDE_SECTOR_SIZE], 330 const fde_sector_tags_t *tags, 331 uint8_t plaintext[FDE_SECTOR_SIZE]) { 332 uint8_t xchacha_nonce[FDE_NONCE_XCHACHA_SIZE]; 333 build_xchacha_nonce(sector_num, xchacha_nonce); 334 335 xchacha20_poly1305_context xchacha_ctx; 336 if (xchacha20_poly1305_init(&xchacha_ctx, xchacha_key, xchacha_nonce) != 0) { 337 return FDE_ERR_CRYPTO; 338 } 339 340 if (xchacha20_poly1305_decrypt(&xchacha_ctx, NULL, 0, 341 ciphertext, FDE_SECTOR_SIZE, 342 tags->xchacha_tag, 343 plaintext) != 0) { 344 xchacha20_poly1305_cleanup(&xchacha_ctx); 345 return FDE_ERR_AUTH_FAILED; 346 } 347 xchacha20_poly1305_cleanup(&xchacha_ctx); 348 return FDE_OK; 349 } 350 351 /* ============================================================================ 352 * Public API Implementation 353 * ========================================================================= */ 354 355 /* Internal format implementation - works on partition or whole disk */ 356 static int fde_format_internal(uint8_t bus, uint8_t drive, 357 uint32_t part_start, uint32_t part_size, 358 const char *password, size_t pass_len, 359 uint8_t cipher) { 360 if (!password || pass_len == 0) { 361 return FDE_ERR_INVALID_PARAM; 362 } 363 364 if (cipher > FDE_CIPHER_CASCADE) { 365 return FDE_ERR_INVALID_PARAM; 366 } 367 368 terminal_writestring("[FDE] Formatting with encryption...\n"); 369 370 { 371 char dbg[128]; 372 snprintf(dbg, sizeof(dbg), "[FDE] bus=%d drive=%d part_start=%d part_size=%d\n", 373 (int)bus, (int)drive, (int)part_start, (int)part_size); 374 terminal_writestring(dbg); 375 } 376 377 /* Initialize CSPRNG */ 378 csprng_global_init(); 379 380 /* Generate random salt */ 381 uint8_t salt[FDE_SALT_SIZE]; 382 random_bytes(salt, FDE_SALT_SIZE); 383 384 /* Derive encryption keys */ 385 terminal_writestring("[FDE] Deriving encryption keys (Argon2id)...\n"); 386 uint8_t aes_key[FDE_KEY_SIZE]; 387 uint8_t xchacha_key[FDE_KEY_SIZE]; 388 int result = derive_keys(password, pass_len, salt, aes_key, xchacha_key); 389 if (result != FDE_OK) { 390 terminal_writestring("[FDE] ERROR: Key derivation failed (need 4MB for Argon2)!\n"); 391 return result; 392 } 393 terminal_writestring("[FDE] Key derivation complete.\n"); 394 395 /* Generate random master key (for encrypted storage in header) */ 396 uint8_t master_key[FDE_MASTER_KEY_SIZE]; 397 memcpy(master_key, aes_key, FDE_KEY_SIZE); 398 memcpy(master_key + FDE_KEY_SIZE, xchacha_key, FDE_KEY_SIZE); 399 400 /* Calculate layout - relative to partition */ 401 uint32_t total_partition_sectors = part_size; 402 uint32_t header_sectors = 1; /* Sector 0 of partition */ 403 404 /* Reserve sectors for tags: each data sector needs 32 bytes of tags */ 405 /* With ~90% data efficiency: total = header + tags + data */ 406 /* tags = data / 16 (each tag sector holds 16 data sector tags) */ 407 /* So: total = 1 + data/16 + data = 1 + 17*data/16 */ 408 /* data = (total - 1) * 16 / 17 */ 409 uint32_t data_sectors = ((total_partition_sectors - header_sectors) * 16) / 17; 410 uint32_t tag_sectors = calc_tag_sectors(data_sectors); 411 412 /* These are relative to partition start */ 413 uint32_t tag_start = header_sectors; 414 uint32_t data_start = header_sectors + tag_sectors; 415 416 { 417 char dbg[120]; 418 snprintf(dbg, sizeof(dbg), "[FDE] Layout: total=%d data=%d tags=%d tag_start=%d data_start=%d\n", 419 (int)total_partition_sectors, (int)data_sectors, (int)tag_sectors, (int)tag_start, (int)data_start); 420 terminal_writestring(dbg); 421 } 422 423 /* Build header */ 424 fde_header_t header; 425 memset(&header, 0, sizeof(header)); 426 memcpy(header.magic, FDE_MAGIC, FDE_MAGIC_SIZE); 427 header.version = FDE_VERSION; 428 header.cipher_mode = cipher; 429 header.kdf_iterations = FDE_KDF_TIME; 430 header.total_sectors = data_sectors; 431 header.data_start_sector = data_start; 432 header.tag_start_sector = tag_start; 433 memcpy(header.salt, salt, FDE_SALT_SIZE); 434 435 /* Encrypt master key with derived keys (if cascade, encrypt with both) */ 436 if (cipher == FDE_CIPHER_CASCADE || cipher == FDE_CIPHER_AES_GCM) { 437 uint8_t aes_nonce[FDE_NONCE_AES_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 438 0xFF, 0xFF, 0xFF, 0xFF, 439 0xFF, 0xFF, 0xFF, 0xFF}; 440 aes256_gcm_context aes_ctx; 441 aes256_gcm_init(&aes_ctx, aes_key); 442 aes256_gcm_encrypt(&aes_ctx, aes_nonce, FDE_NONCE_AES_SIZE, 443 NULL, 0, 444 master_key, FDE_MASTER_KEY_SIZE, 445 header.encrypted_master_key, 446 header.encrypted_master_key + FDE_MASTER_KEY_SIZE, 447 FDE_TAG_SIZE); 448 aes256_gcm_cleanup(&aes_ctx); 449 } 450 451 if (cipher == FDE_CIPHER_CASCADE || cipher == FDE_CIPHER_XCHACHA) { 452 uint8_t xchacha_nonce[FDE_NONCE_XCHACHA_SIZE]; 453 memset(xchacha_nonce, 0xFF, FDE_NONCE_XCHACHA_SIZE); 454 455 uint8_t *data_to_encrypt = (cipher == FDE_CIPHER_CASCADE) ? 456 header.encrypted_master_key : master_key; 457 size_t data_len = (cipher == FDE_CIPHER_CASCADE) ? 458 FDE_MASTER_KEY_SIZE + FDE_TAG_SIZE : FDE_MASTER_KEY_SIZE; 459 460 xchacha20_poly1305_context xchacha_ctx; 461 xchacha20_poly1305_init(&xchacha_ctx, xchacha_key, xchacha_nonce); 462 xchacha20_poly1305_encrypt(&xchacha_ctx, NULL, 0, 463 data_to_encrypt, data_len, 464 header.encrypted_master_key, 465 header.encrypted_master_key + FDE_MASTER_KEY_SIZE + FDE_TAG_SIZE); 466 xchacha20_poly1305_cleanup(&xchacha_ctx); 467 } 468 469 /* Hash header (excluding hash field itself) */ 470 sha256((uint8_t *)&header, sizeof(header) - 32, header.header_hash); 471 472 /* Write header to disk - at partition start */ 473 result = ata_write_sectors(bus, drive, part_start + FDE_HEADER_SECTOR, 1, &header); 474 if (result != ATA_OK) { 475 secure_wipe(aes_key, sizeof(aes_key)); 476 secure_wipe(xchacha_key, sizeof(xchacha_key)); 477 secure_wipe(master_key, sizeof(master_key)); 478 return FDE_ERR_IO; 479 } 480 481 /* Initialize tag area with zeros */ 482 uint8_t zero_sector[FDE_SECTOR_SIZE]; 483 memset(zero_sector, 0, sizeof(zero_sector)); 484 for (uint32_t i = 0; i < tag_sectors; i++) { 485 result = ata_write_sectors(bus, drive, part_start + tag_start + i, 1, zero_sector); 486 if (result != ATA_OK) { 487 secure_wipe(aes_key, sizeof(aes_key)); 488 secure_wipe(xchacha_key, sizeof(xchacha_key)); 489 secure_wipe(master_key, sizeof(master_key)); 490 return FDE_ERR_IO; 491 } 492 } 493 494 secure_wipe(aes_key, sizeof(aes_key)); 495 secure_wipe(xchacha_key, sizeof(xchacha_key)); 496 secure_wipe(master_key, sizeof(master_key)); 497 498 terminal_writestring("[FDE] Formatted successfully\n"); 499 return FDE_OK; 500 } 501 502 /* Format a disk with FDE (whole disk) */ 503 int fde_format(uint8_t bus, uint8_t drive, 504 const char *password, size_t pass_len, 505 uint8_t cipher) { 506 /* Get drive info */ 507 ata_drive_info_t *info = ata_get_drive_info(bus, drive); 508 if (!info) { 509 return FDE_ERR_IO; 510 } 511 512 return fde_format_internal(bus, drive, 0, info->sectors, password, pass_len, cipher); 513 } 514 515 /* Format a partition with FDE */ 516 int fde_format_partition(uint8_t bus, uint8_t drive, 517 uint32_t part_start, uint32_t part_size, 518 const char *password, size_t pass_len, 519 uint8_t cipher) { 520 if (part_size < 100) { /* Need at least some minimum sectors */ 521 return FDE_ERR_INVALID_PARAM; 522 } 523 524 return fde_format_internal(bus, drive, part_start, part_size, password, pass_len, cipher); 525 } 526 527 /* Internal open implementation - works on partition or whole disk */ 528 static int fde_open_internal(fde_context_t *ctx, uint8_t bus, uint8_t drive, 529 uint32_t part_start, uint32_t part_size, 530 const char *password, size_t pass_len) { 531 if (!ctx || !password || pass_len == 0) { 532 return FDE_ERR_INVALID_PARAM; 533 } 534 535 /* Read header from partition start */ 536 fde_header_t header; 537 int result = ata_read_sectors(bus, drive, part_start + FDE_HEADER_SECTOR, 1, &header); 538 if (result != ATA_OK) { 539 return FDE_ERR_IO; 540 } 541 542 /* Verify magic */ 543 if (memcmp(header.magic, FDE_MAGIC, FDE_MAGIC_SIZE) != 0) { 544 return FDE_ERR_NOT_FORMATTED; 545 } 546 547 /* Verify header hash */ 548 uint8_t computed_hash[32]; 549 sha256((uint8_t *)&header, sizeof(header) - 32, computed_hash); 550 if (secure_compare(computed_hash, header.header_hash, 32) != 0) { 551 return FDE_ERR_CORRUPT; 552 } 553 554 /* Derive keys from password */ 555 uint8_t aes_key[FDE_KEY_SIZE]; 556 uint8_t xchacha_key[FDE_KEY_SIZE]; 557 result = derive_keys(password, pass_len, header.salt, aes_key, xchacha_key); 558 if (result != FDE_OK) { 559 return result; 560 } 561 562 /* Decrypt master key to verify password */ 563 uint8_t decrypted_key[FDE_MASTER_KEY_SIZE]; 564 uint8_t cipher = header.cipher_mode; 565 566 if (cipher == FDE_CIPHER_CASCADE) { 567 /* First decrypt with XChaCha (outer layer) */ 568 uint8_t xchacha_nonce[FDE_NONCE_XCHACHA_SIZE]; 569 memset(xchacha_nonce, 0xFF, FDE_NONCE_XCHACHA_SIZE); 570 571 uint8_t intermediate[FDE_MASTER_KEY_SIZE + FDE_TAG_SIZE]; 572 xchacha20_poly1305_context xchacha_ctx; 573 xchacha20_poly1305_init(&xchacha_ctx, xchacha_key, xchacha_nonce); 574 if (xchacha20_poly1305_decrypt(&xchacha_ctx, NULL, 0, 575 header.encrypted_master_key, 576 FDE_MASTER_KEY_SIZE + FDE_TAG_SIZE, 577 header.encrypted_master_key + FDE_MASTER_KEY_SIZE + FDE_TAG_SIZE, 578 intermediate) != 0) { 579 xchacha20_poly1305_cleanup(&xchacha_ctx); 580 secure_wipe(aes_key, sizeof(aes_key)); 581 secure_wipe(xchacha_key, sizeof(xchacha_key)); 582 return FDE_ERR_BAD_PASSWORD; 583 } 584 xchacha20_poly1305_cleanup(&xchacha_ctx); 585 586 /* Then decrypt with AES (inner layer) */ 587 uint8_t aes_nonce[FDE_NONCE_AES_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 588 0xFF, 0xFF, 0xFF, 0xFF, 589 0xFF, 0xFF, 0xFF, 0xFF}; 590 aes256_gcm_context aes_ctx; 591 aes256_gcm_init(&aes_ctx, aes_key); 592 if (aes256_gcm_decrypt(&aes_ctx, aes_nonce, FDE_NONCE_AES_SIZE, 593 NULL, 0, 594 intermediate, FDE_MASTER_KEY_SIZE, 595 intermediate + FDE_MASTER_KEY_SIZE, FDE_TAG_SIZE, 596 decrypted_key) != 0) { 597 aes256_gcm_cleanup(&aes_ctx); 598 secure_wipe(aes_key, sizeof(aes_key)); 599 secure_wipe(xchacha_key, sizeof(xchacha_key)); 600 secure_wipe(intermediate, sizeof(intermediate)); 601 return FDE_ERR_BAD_PASSWORD; 602 } 603 aes256_gcm_cleanup(&aes_ctx); 604 secure_wipe(intermediate, sizeof(intermediate)); 605 606 } else if (cipher == FDE_CIPHER_XCHACHA) { 607 uint8_t xchacha_nonce[FDE_NONCE_XCHACHA_SIZE]; 608 memset(xchacha_nonce, 0xFF, FDE_NONCE_XCHACHA_SIZE); 609 610 xchacha20_poly1305_context xchacha_ctx; 611 xchacha20_poly1305_init(&xchacha_ctx, xchacha_key, xchacha_nonce); 612 if (xchacha20_poly1305_decrypt(&xchacha_ctx, NULL, 0, 613 header.encrypted_master_key, 614 FDE_MASTER_KEY_SIZE, 615 header.encrypted_master_key + FDE_MASTER_KEY_SIZE + FDE_TAG_SIZE, 616 decrypted_key) != 0) { 617 xchacha20_poly1305_cleanup(&xchacha_ctx); 618 secure_wipe(aes_key, sizeof(aes_key)); 619 secure_wipe(xchacha_key, sizeof(xchacha_key)); 620 return FDE_ERR_BAD_PASSWORD; 621 } 622 xchacha20_poly1305_cleanup(&xchacha_ctx); 623 624 } else if (cipher == FDE_CIPHER_AES_GCM) { 625 uint8_t aes_nonce[FDE_NONCE_AES_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 626 0xFF, 0xFF, 0xFF, 0xFF, 627 0xFF, 0xFF, 0xFF, 0xFF}; 628 aes256_gcm_context aes_ctx; 629 aes256_gcm_init(&aes_ctx, aes_key); 630 if (aes256_gcm_decrypt(&aes_ctx, aes_nonce, FDE_NONCE_AES_SIZE, 631 NULL, 0, 632 header.encrypted_master_key, FDE_MASTER_KEY_SIZE, 633 header.encrypted_master_key + FDE_MASTER_KEY_SIZE, FDE_TAG_SIZE, 634 decrypted_key) != 0) { 635 aes256_gcm_cleanup(&aes_ctx); 636 secure_wipe(aes_key, sizeof(aes_key)); 637 secure_wipe(xchacha_key, sizeof(xchacha_key)); 638 return FDE_ERR_BAD_PASSWORD; 639 } 640 aes256_gcm_cleanup(&aes_ctx); 641 } else { 642 /* No encryption */ 643 memcpy(decrypted_key, header.encrypted_master_key, FDE_MASTER_KEY_SIZE); 644 } 645 646 /* Initialize context */ 647 memset(ctx, 0, sizeof(fde_context_t)); 648 ctx->bus = bus; 649 ctx->drive = drive; 650 ctx->is_open = 1; 651 ctx->cipher_mode = cipher; 652 ctx->partition_start = part_start; 653 ctx->partition_size = part_size; 654 ctx->total_sectors = header.total_sectors; 655 ctx->data_start_sector = header.data_start_sector; 656 ctx->tag_start_sector = header.tag_start_sector; 657 658 { 659 char dbg[150]; 660 snprintf(dbg, sizeof(dbg), "[FDE] Open: part_start=%d total=%d data_start=%d tag_start=%d cipher=%d\n", 661 (int)ctx->partition_start, (int)ctx->total_sectors, (int)ctx->data_start_sector, 662 (int)ctx->tag_start_sector, (int)ctx->cipher_mode); 663 terminal_writestring(dbg); 664 } 665 666 /* Store actual encryption keys (from decrypted master key) */ 667 memcpy(ctx->aes_key, decrypted_key, FDE_KEY_SIZE); 668 memcpy(ctx->xchacha_key, decrypted_key + FDE_KEY_SIZE, FDE_KEY_SIZE); 669 670 secure_wipe(aes_key, sizeof(aes_key)); 671 secure_wipe(xchacha_key, sizeof(xchacha_key)); 672 secure_wipe(decrypted_key, sizeof(decrypted_key)); 673 674 return FDE_OK; 675 } 676 677 /* Open an encrypted disk (whole disk) */ 678 int fde_open(fde_context_t *ctx, uint8_t bus, uint8_t drive, 679 const char *password, size_t pass_len) { 680 /* Get drive info */ 681 ata_drive_info_t *info = ata_get_drive_info(bus, drive); 682 if (!info) { 683 return FDE_ERR_IO; 684 } 685 686 return fde_open_internal(ctx, bus, drive, 0, info->sectors, password, pass_len); 687 } 688 689 /* Open an encrypted partition */ 690 int fde_open_partition(fde_context_t *ctx, uint8_t bus, uint8_t drive, 691 uint32_t part_start, 692 const char *password, size_t pass_len) { 693 /* Read header to get partition size from stored values */ 694 fde_header_t header; 695 int result = ata_read_sectors(bus, drive, part_start + FDE_HEADER_SECTOR, 1, &header); 696 if (result != ATA_OK) { 697 return FDE_ERR_IO; 698 } 699 700 /* Verify magic before proceeding */ 701 if (memcmp(header.magic, FDE_MAGIC, FDE_MAGIC_SIZE) != 0) { 702 return FDE_ERR_NOT_FORMATTED; 703 } 704 705 /* Calculate partition size from header data */ 706 uint32_t tag_sectors = calc_tag_sectors(header.total_sectors); 707 uint32_t part_size = 1 + tag_sectors + header.total_sectors; 708 709 return fde_open_internal(ctx, bus, drive, part_start, part_size, password, pass_len); 710 } 711 712 /* Close an encrypted disk */ 713 int fde_close(fde_context_t *ctx) { 714 if (!ctx) { 715 return FDE_ERR_INVALID_PARAM; 716 } 717 718 /* Wipe keys */ 719 secure_wipe(ctx->aes_key, sizeof(ctx->aes_key)); 720 secure_wipe(ctx->xchacha_key, sizeof(ctx->xchacha_key)); 721 ctx->is_open = 0; 722 723 return FDE_OK; 724 } 725 726 /* Read a decrypted sector */ 727 int fde_read_sector(fde_context_t *ctx, uint32_t sector, void *buffer) { 728 if (!ctx || !buffer) { 729 return FDE_ERR_INVALID_PARAM; 730 } 731 if (!ctx->is_open) { 732 return FDE_ERR_NOT_OPEN; 733 } 734 if (sector >= ctx->total_sectors) { 735 return FDE_ERR_INVALID_PARAM; 736 } 737 738 /* Read ciphertext from disk - add partition_start offset */ 739 uint8_t ciphertext[FDE_SECTOR_SIZE]; 740 uint32_t disk_sector = ctx->partition_start + ctx->data_start_sector + sector; 741 int result = ata_read_sectors(ctx->bus, ctx->drive, disk_sector, 1, ciphertext); 742 if (result != ATA_OK) { 743 return FDE_ERR_IO; 744 } 745 746 /* No encryption mode */ 747 if (ctx->cipher_mode == FDE_CIPHER_NONE) { 748 memcpy(buffer, ciphertext, FDE_SECTOR_SIZE); 749 return FDE_OK; 750 } 751 752 /* Read tags - pass partition_start */ 753 fde_sector_tags_t tags; 754 result = read_sector_tags(ctx->bus, ctx->drive, ctx->partition_start, 755 ctx->tag_start_sector, sector, &tags); 756 if (result != FDE_OK) { 757 return result; 758 } 759 760 /* Decrypt based on cipher mode */ 761 switch (ctx->cipher_mode) { 762 case FDE_CIPHER_CASCADE: 763 return decrypt_sector_cascade(ctx->aes_key, ctx->xchacha_key, 764 sector, ciphertext, &tags, buffer); 765 case FDE_CIPHER_AES_GCM: 766 return decrypt_sector_aes(ctx->aes_key, sector, ciphertext, &tags, buffer); 767 case FDE_CIPHER_XCHACHA: 768 return decrypt_sector_xchacha(ctx->xchacha_key, sector, ciphertext, &tags, buffer); 769 default: 770 return FDE_ERR_INVALID_PARAM; 771 } 772 } 773 774 /* Write and encrypt a sector */ 775 int fde_write_sector(fde_context_t *ctx, uint32_t sector, const void *buffer) { 776 if (!ctx || !buffer) { 777 terminal_writestring("[FDE] write_sector: invalid param\n"); 778 return FDE_ERR_INVALID_PARAM; 779 } 780 if (!ctx->is_open) { 781 terminal_writestring("[FDE] write_sector: not open\n"); 782 return FDE_ERR_NOT_OPEN; 783 } 784 if (sector >= ctx->total_sectors) { 785 terminal_writestring("[FDE] write_sector: sector out of range\n"); 786 return FDE_ERR_INVALID_PARAM; 787 } 788 789 uint8_t ciphertext[FDE_SECTOR_SIZE]; 790 fde_sector_tags_t tags; 791 int result; 792 793 /* No encryption mode */ 794 if (ctx->cipher_mode == FDE_CIPHER_NONE) { 795 memcpy(ciphertext, buffer, FDE_SECTOR_SIZE); 796 memset(&tags, 0, sizeof(tags)); 797 } else { 798 /* Encrypt based on cipher mode */ 799 switch (ctx->cipher_mode) { 800 case FDE_CIPHER_CASCADE: 801 result = encrypt_sector_cascade(ctx->aes_key, ctx->xchacha_key, 802 sector, buffer, ciphertext, &tags); 803 break; 804 case FDE_CIPHER_AES_GCM: 805 result = encrypt_sector_aes(ctx->aes_key, sector, buffer, ciphertext, &tags); 806 break; 807 case FDE_CIPHER_XCHACHA: 808 result = encrypt_sector_xchacha(ctx->xchacha_key, sector, buffer, ciphertext, &tags); 809 break; 810 default: 811 return FDE_ERR_INVALID_PARAM; 812 } 813 if (result != FDE_OK) { 814 terminal_writestring("[FDE] write_sector: encrypt failed\n"); 815 return result; 816 } 817 } 818 819 /* Write tags - pass partition_start */ 820 if (ctx->cipher_mode != FDE_CIPHER_NONE) { 821 result = write_sector_tags(ctx->bus, ctx->drive, ctx->partition_start, 822 ctx->tag_start_sector, sector, &tags); 823 if (result != FDE_OK) { 824 terminal_writestring("[FDE] write_sector: write_sector_tags failed\n"); 825 return result; 826 } 827 } 828 829 /* Write ciphertext to disk - add partition_start offset */ 830 uint32_t disk_sector = ctx->partition_start + ctx->data_start_sector + sector; 831 result = ata_write_sectors(ctx->bus, ctx->drive, disk_sector, 1, ciphertext); 832 if (result != ATA_OK) { 833 char dbg[100]; 834 snprintf(dbg, sizeof(dbg), "[FDE] write_sector: ata_write failed, sector=%d disk_sector=%d result=%d\n", 835 (int)sector, (int)disk_sector, result); 836 terminal_writestring(dbg); 837 return FDE_ERR_IO; 838 } 839 840 return FDE_OK; 841 } 842 843 /* Read multiple sectors */ 844 int fde_read_sectors(fde_context_t *ctx, uint32_t start, uint32_t count, void *buffer) { 845 uint8_t *buf = (uint8_t *)buffer; 846 for (uint32_t i = 0; i < count; i++) { 847 int result = fde_read_sector(ctx, start + i, buf + i * FDE_SECTOR_SIZE); 848 if (result != FDE_OK) { 849 return result; 850 } 851 } 852 return FDE_OK; 853 } 854 855 /* Write multiple sectors */ 856 int fde_write_sectors(fde_context_t *ctx, uint32_t start, uint32_t count, const void *buffer) { 857 const uint8_t *buf = (const uint8_t *)buffer; 858 for (uint32_t i = 0; i < count; i++) { 859 int result = fde_write_sector(ctx, start + i, buf + i * FDE_SECTOR_SIZE); 860 if (result != FDE_OK) { 861 return result; 862 } 863 } 864 return FDE_OK; 865 } 866 867 /* Check if disk is FDE formatted */ 868 int fde_is_formatted(uint8_t bus, uint8_t drive) { 869 fde_header_t header; 870 int result = ata_read_sectors(bus, drive, FDE_HEADER_SECTOR, 1, &header); 871 if (result != ATA_OK) { 872 return 0; 873 } 874 return memcmp(header.magic, FDE_MAGIC, FDE_MAGIC_SIZE) == 0; 875 } 876 877 /* Get disk info */ 878 int fde_get_info(uint8_t bus, uint8_t drive, uint8_t *cipher, uint32_t *sectors) { 879 fde_header_t header; 880 int result = ata_read_sectors(bus, drive, FDE_HEADER_SECTOR, 1, &header); 881 if (result != ATA_OK) { 882 return FDE_ERR_IO; 883 } 884 885 if (memcmp(header.magic, FDE_MAGIC, FDE_MAGIC_SIZE) != 0) { 886 return FDE_ERR_NOT_FORMATTED; 887 } 888 889 if (cipher) *cipher = header.cipher_mode; 890 if (sectors) *sectors = header.total_sectors; 891 892 return FDE_OK; 893 } 894 895 /* Get error string */ 896 const char *fde_error_string(int error) { 897 switch (error) { 898 case FDE_OK: return "Success"; 899 case FDE_ERR_INVALID_PARAM: return "Invalid parameter"; 900 case FDE_ERR_NO_MEMORY: return "Out of memory"; 901 case FDE_ERR_IO: return "I/O error"; 902 case FDE_ERR_CRYPTO: return "Cryptographic error"; 903 case FDE_ERR_BAD_PASSWORD: return "Invalid password"; 904 case FDE_ERR_CORRUPT: return "Data corruption detected"; 905 case FDE_ERR_NOT_FORMATTED: return "Disk not FDE formatted"; 906 case FDE_ERR_ALREADY_OPEN: return "Disk already open"; 907 case FDE_ERR_NOT_OPEN: return "Disk not open"; 908 case FDE_ERR_AUTH_FAILED: return "Authentication failed"; 909 default: return "Unknown error"; 910 } 911 } 912 913 /* Get cipher name */ 914 const char *fde_cipher_name(uint8_t cipher) { 915 switch (cipher) { 916 case FDE_CIPHER_NONE: return "None (plaintext)"; 917 case FDE_CIPHER_AES_GCM: return "AES-256-GCM"; 918 case FDE_CIPHER_XCHACHA: return "XChaCha20-Poly1305"; 919 case FDE_CIPHER_CASCADE: return "AES-256-GCM + XChaCha20-Poly1305"; 920 default: return "Unknown"; 921 } 922 } 923 924 /* ============================================================================ 925 * Lua Bindings 926 * ========================================================================= */ 927 928 #include "lua.h" 929 #include "lauxlib.h" 930 931 /* Global FDE contexts (max 4 disks) - non-static so diskfs can access */ 932 fde_context_t fde_contexts[4]; 933 934 static int get_ctx_index(uint8_t bus, uint8_t drive) { 935 return bus * 2 + drive; 936 } 937 938 /* fde.format(bus, drive, password, cipher) -> ok, err */ 939 static int lua_fde_format(lua_State *L) { 940 int bus = luaL_checkinteger(L, 1); 941 int drive = luaL_checkinteger(L, 2); 942 size_t pass_len; 943 const char *password = luaL_checklstring(L, 3, &pass_len); 944 int cipher = luaL_optinteger(L, 4, FDE_CIPHER_CASCADE); 945 946 int result = fde_format(bus, drive, password, pass_len, cipher); 947 948 if (result != FDE_OK) { 949 lua_pushboolean(L, 0); 950 lua_pushstring(L, fde_error_string(result)); 951 return 2; 952 } 953 954 lua_pushboolean(L, 1); 955 lua_pushnil(L); 956 return 2; 957 } 958 959 /* fde.open(bus, drive, password) -> ok, err */ 960 static int lua_fde_open(lua_State *L) { 961 int bus = luaL_checkinteger(L, 1); 962 int drive = luaL_checkinteger(L, 2); 963 size_t pass_len; 964 const char *password = luaL_checklstring(L, 3, &pass_len); 965 966 int idx = get_ctx_index(bus, drive); 967 if (idx < 0 || idx >= 4) { 968 lua_pushboolean(L, 0); 969 lua_pushstring(L, "Invalid bus/drive"); 970 return 2; 971 } 972 973 if (fde_contexts[idx].is_open) { 974 lua_pushboolean(L, 0); 975 lua_pushstring(L, "Disk already open"); 976 return 2; 977 } 978 979 int result = fde_open(&fde_contexts[idx], bus, drive, password, pass_len); 980 981 if (result != FDE_OK) { 982 lua_pushboolean(L, 0); 983 lua_pushstring(L, fde_error_string(result)); 984 return 2; 985 } 986 987 lua_pushboolean(L, 1); 988 lua_pushnil(L); 989 return 2; 990 } 991 992 /* fde.close(bus, drive) -> ok */ 993 static int lua_fde_close(lua_State *L) { 994 int bus = luaL_checkinteger(L, 1); 995 int drive = luaL_checkinteger(L, 2); 996 997 int idx = get_ctx_index(bus, drive); 998 if (idx < 0 || idx >= 4) { 999 lua_pushboolean(L, 0); 1000 return 1; 1001 } 1002 1003 fde_close(&fde_contexts[idx]); 1004 lua_pushboolean(L, 1); 1005 return 1; 1006 } 1007 1008 /* fde.read(bus, drive, sector, count) -> data, err */ 1009 static int lua_fde_read(lua_State *L) { 1010 int bus = luaL_checkinteger(L, 1); 1011 int drive = luaL_checkinteger(L, 2); 1012 uint32_t sector = luaL_checkinteger(L, 3); 1013 uint32_t count = luaL_optinteger(L, 4, 1); 1014 1015 int idx = get_ctx_index(bus, drive); 1016 if (idx < 0 || idx >= 4 || !fde_contexts[idx].is_open) { 1017 lua_pushnil(L); 1018 lua_pushstring(L, "Disk not open"); 1019 return 2; 1020 } 1021 1022 size_t buf_size = count * FDE_SECTOR_SIZE; 1023 char *buffer = malloc(buf_size); 1024 if (!buffer) { 1025 lua_pushnil(L); 1026 lua_pushstring(L, "Out of memory"); 1027 return 2; 1028 } 1029 1030 int result = fde_read_sectors(&fde_contexts[idx], sector, count, buffer); 1031 1032 if (result != FDE_OK) { 1033 free(buffer); 1034 lua_pushnil(L); 1035 lua_pushstring(L, fde_error_string(result)); 1036 return 2; 1037 } 1038 1039 lua_pushlstring(L, buffer, buf_size); 1040 free(buffer); 1041 lua_pushnil(L); 1042 return 2; 1043 } 1044 1045 /* fde.write(bus, drive, sector, data) -> ok, err */ 1046 static int lua_fde_write(lua_State *L) { 1047 int bus = luaL_checkinteger(L, 1); 1048 int drive = luaL_checkinteger(L, 2); 1049 uint32_t sector = luaL_checkinteger(L, 3); 1050 size_t data_len; 1051 const char *data = luaL_checklstring(L, 4, &data_len); 1052 1053 int idx = get_ctx_index(bus, drive); 1054 if (idx < 0 || idx >= 4 || !fde_contexts[idx].is_open) { 1055 lua_pushboolean(L, 0); 1056 lua_pushstring(L, "Disk not open"); 1057 return 2; 1058 } 1059 1060 if (data_len == 0 || data_len % FDE_SECTOR_SIZE != 0) { 1061 lua_pushboolean(L, 0); 1062 lua_pushstring(L, "Data must be multiple of 512 bytes"); 1063 return 2; 1064 } 1065 1066 uint32_t count = data_len / FDE_SECTOR_SIZE; 1067 int result = fde_write_sectors(&fde_contexts[idx], sector, count, data); 1068 1069 if (result != FDE_OK) { 1070 lua_pushboolean(L, 0); 1071 lua_pushstring(L, fde_error_string(result)); 1072 return 2; 1073 } 1074 1075 lua_pushboolean(L, 1); 1076 lua_pushnil(L); 1077 return 2; 1078 } 1079 1080 /* fde.isFormatted(bus, drive) -> bool */ 1081 static int lua_fde_is_formatted(lua_State *L) { 1082 int bus = luaL_checkinteger(L, 1); 1083 int drive = luaL_checkinteger(L, 2); 1084 1085 lua_pushboolean(L, fde_is_formatted(bus, drive)); 1086 return 1; 1087 } 1088 1089 /* fde.getInfo(bus, drive) -> info or nil, err */ 1090 static int lua_fde_get_info(lua_State *L) { 1091 int bus = luaL_checkinteger(L, 1); 1092 int drive = luaL_checkinteger(L, 2); 1093 1094 uint8_t cipher; 1095 uint32_t sectors; 1096 int result = fde_get_info(bus, drive, &cipher, §ors); 1097 1098 if (result != FDE_OK) { 1099 lua_pushnil(L); 1100 lua_pushstring(L, fde_error_string(result)); 1101 return 2; 1102 } 1103 1104 lua_newtable(L); 1105 1106 lua_pushstring(L, "cipher"); 1107 lua_pushinteger(L, cipher); 1108 lua_settable(L, -3); 1109 1110 lua_pushstring(L, "cipherName"); 1111 lua_pushstring(L, fde_cipher_name(cipher)); 1112 lua_settable(L, -3); 1113 1114 lua_pushstring(L, "sectors"); 1115 lua_pushinteger(L, sectors); 1116 lua_settable(L, -3); 1117 1118 lua_pushstring(L, "bytes"); 1119 lua_pushnumber(L, (lua_Number)sectors * FDE_SECTOR_SIZE); 1120 lua_settable(L, -3); 1121 1122 return 1; 1123 } 1124 1125 /* fde.formatPartition(bus, drive, partStart, partSize, password, cipher) -> ok, err */ 1126 static int lua_fde_format_partition(lua_State *L) { 1127 int bus = luaL_checkinteger(L, 1); 1128 int drive = luaL_checkinteger(L, 2); 1129 uint32_t part_start = luaL_checkinteger(L, 3); 1130 uint32_t part_size = luaL_checkinteger(L, 4); 1131 size_t pass_len; 1132 const char *password = luaL_checklstring(L, 5, &pass_len); 1133 int cipher = luaL_optinteger(L, 6, FDE_CIPHER_CASCADE); 1134 1135 int result = fde_format_partition(bus, drive, part_start, part_size, 1136 password, pass_len, cipher); 1137 1138 if (result != FDE_OK) { 1139 lua_pushboolean(L, 0); 1140 lua_pushstring(L, fde_error_string(result)); 1141 return 2; 1142 } 1143 1144 lua_pushboolean(L, 1); 1145 lua_pushnil(L); 1146 return 2; 1147 } 1148 1149 /* fde.openPartition(bus, drive, partStart, password) -> ok, err */ 1150 static int lua_fde_open_partition(lua_State *L) { 1151 int bus = luaL_checkinteger(L, 1); 1152 int drive = luaL_checkinteger(L, 2); 1153 uint32_t part_start = luaL_checkinteger(L, 3); 1154 size_t pass_len; 1155 const char *password = luaL_checklstring(L, 4, &pass_len); 1156 1157 int idx = get_ctx_index(bus, drive); 1158 if (idx < 0 || idx >= 4) { 1159 lua_pushboolean(L, 0); 1160 lua_pushstring(L, "Invalid bus/drive"); 1161 return 2; 1162 } 1163 1164 if (fde_contexts[idx].is_open) { 1165 lua_pushboolean(L, 0); 1166 lua_pushstring(L, "Disk already open"); 1167 return 2; 1168 } 1169 1170 int result = fde_open_partition(&fde_contexts[idx], bus, drive, part_start, 1171 password, pass_len); 1172 1173 if (result != FDE_OK) { 1174 lua_pushboolean(L, 0); 1175 lua_pushstring(L, fde_error_string(result)); 1176 return 2; 1177 } 1178 1179 lua_pushboolean(L, 1); 1180 lua_pushnil(L); 1181 return 2; 1182 } 1183 1184 /* Register FDE module */ 1185 static const luaL_Reg fde_funcs[] = { 1186 {"format", lua_fde_format}, 1187 {"formatPartition", lua_fde_format_partition}, 1188 {"open", lua_fde_open}, 1189 {"openPartition", lua_fde_open_partition}, 1190 {"close", lua_fde_close}, 1191 {"read", lua_fde_read}, 1192 {"write", lua_fde_write}, 1193 {"isFormatted", lua_fde_is_formatted}, 1194 {"getInfo", lua_fde_get_info}, 1195 {NULL, NULL} 1196 }; 1197 1198 int luaopen_fde(lua_State *L) { 1199 /* Initialize contexts */ 1200 memset(fde_contexts, 0, sizeof(fde_contexts)); 1201 1202 luaL_newlib(L, fde_funcs); 1203 1204 /* Add constants */ 1205 lua_pushinteger(L, FDE_CIPHER_NONE); 1206 lua_setfield(L, -2, "CIPHER_NONE"); 1207 1208 lua_pushinteger(L, FDE_CIPHER_AES_GCM); 1209 lua_setfield(L, -2, "CIPHER_AES"); 1210 1211 lua_pushinteger(L, FDE_CIPHER_XCHACHA); 1212 lua_setfield(L, -2, "CIPHER_XCHACHA"); 1213 1214 lua_pushinteger(L, FDE_CIPHER_CASCADE); 1215 lua_setfield(L, -2, "CIPHER_CASCADE"); 1216 1217 lua_pushinteger(L, FDE_SECTOR_SIZE); 1218 lua_setfield(L, -2, "SECTOR_SIZE"); 1219 1220 return 1; 1221 }