Files
James Stringer 303b34dab8 Win-on-Mac: encode the guest password in the daemon for all three configs
All three guest configs now obfuscate/hash the account credential in the daemon
and hand the disk-applier child only the non-plaintext result -- the plaintext
never crosses the process boundary, mirroring the Windows-on-Windows model.

  * Win-on-Mac: the daemon generates unattend.xml / setup.cmd / SetupComplete.cmd
    in-process via the shared asb_provision_* (base64 of UTF-16LE(pass+"Password"),
    the same encoder the Windows backend uses) and passes build-windows only
    --prov-dir; the plaintext --pass argv is gone. The child still detects the
    install.wim language and reports LANG:, and the daemon re-generates unattend.xml
    in-process with the detected tag -- the exact mirror of vhdx_create_thread.
  * Mac-on-Mac: the salted PBKDF2-SHA512 ShadowHash + kcpassword are computed in
    the daemon via the new shared tools/provision/mac_account_hash.{h,m}; the stage
    child takes --shadowhash-file / --kcpassword-file. --user-password-file remains
    a direct-CLI fallback.
  * Verified end-to-end on live Windows + macOS guests with a 12-character mixed
    Chinese/English password (ValidateCredentials / dscl -authonly), exercising the
    UTF-16 surrogate path.

Also in this checkpoint of accumulated win-on-mac work:

  * agent: report_idd_status re-runs on the device-check loop (force-on-connect +
    on-change) so display readiness tracks the live, self-healed VDD state instead
    of a single one-shot sample.
  * disk builder (tools/iso-patch-mac/engine): WIM reader gains the install-image
    default-language reader behind the language auto-detect; win_disk locale/apply
    refinements.
  * display + transport: idd_display Metal render + clipboard-reader paths;
    asb_ivshmem_transport slot-reclaim hardening.
  * housekeeping: remove the dev-only transport test harness (tools/transport/test/*);
    update headless-api test scripts; drop changelog-style comments.
2026-06-29 20:50:32 -04:00

115 lines
4.8 KiB
C

/*
* decompress_common -- shared primitives for the WIM codecs (xpress.c, lzx.c):
* a 16-bit-little-endian, MSB-first input bitstream and a single-level
* canonical-Huffman decode table, as required by the LZX/XPRESS bitstream
* format. See docs/appsandbox/codecs/lzx.md.
*/
#ifndef ASB_DECOMPRESS_COMMON_H
#define ASB_DECOMPRESS_COMMON_H
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
/* ---- input bitstream: 16-bit LE coding units, bits consumed MSB-first ---- */
typedef struct {
uint32_t bitbuf; /* left-justified: next bit to read is bit 31 */
unsigned bitsleft;
const uint8_t *next;
const uint8_t *end;
} bitstream_t;
static inline void bs_init(bitstream_t *bs, const uint8_t *p, size_t n) {
bs->bitbuf = 0; bs->bitsleft = 0; bs->next = p; bs->end = p + n;
}
/* Ensure >= n bits are available (n <= 17). Missing input reads as zero. */
static inline void bs_ensure(bitstream_t *bs, unsigned n) {
if (bs->bitsleft >= n) return;
if ((size_t)(bs->end - bs->next) >= 2) {
bs->bitbuf |= (uint32_t)(bs->next[0] | ((uint32_t)bs->next[1] << 8)) << (16 - bs->bitsleft);
bs->next += 2; bs->bitsleft += 16;
if (n == 17 && bs->bitsleft < 17) {
if ((size_t)(bs->end - bs->next) >= 2) {
bs->bitbuf |= (uint32_t)(bs->next[0] | ((uint32_t)bs->next[1] << 8)) << (16 - bs->bitsleft);
bs->next += 2; bs->bitsleft += 16;
} else { bs->bitsleft = 32; }
}
} else { bs->bitsleft = 32; } /* exhausted: treat remaining as zero */
}
static inline uint32_t bs_peek(const bitstream_t *bs, unsigned n) {
return n ? (bs->bitbuf >> (32 - n)) : 0;
}
static inline void bs_remove(bitstream_t *bs, unsigned n) {
bs->bitbuf <<= n; bs->bitsleft -= n;
}
static inline uint32_t bs_read(bitstream_t *bs, unsigned n) {
bs_ensure(bs, n); uint32_t v = bs_peek(bs, n); bs_remove(bs, n); return v;
}
static inline void bs_align(bitstream_t *bs) { bs->bitbuf = 0; bs->bitsleft = 0; }
static inline uint32_t bs_read_u32(bitstream_t *bs) {
if ((size_t)(bs->end - bs->next) < 4) return 0;
uint32_t v = bs->next[0] | ((uint32_t)bs->next[1] << 8) |
((uint32_t)bs->next[2] << 16) | ((uint32_t)bs->next[3] << 24);
bs->next += 4; return v;
}
static inline unsigned bs_bytes_left(const bitstream_t *bs) {
return (unsigned)(bs->end - bs->next);
}
/* ---- canonical Huffman decode table ----
* Single-level table of 2^max_len entries; each entry packs (symbol<<16)|len.
* Codewords are assigned in canonical (length, symbol) order, MSB-first. */
typedef struct { uint32_t *table; unsigned max_len; } huff_t;
/* Build from per-symbol code lengths (0 = unused). Returns 0/-1. */
static inline int huff_build(huff_t *h, const uint8_t *lens, unsigned n, unsigned max_len) {
h->max_len = max_len;
size_t tsz = (size_t)1 << max_len;
h->table = (uint32_t *)malloc(tsz * sizeof(uint32_t));
if (!h->table) return -1;
for (size_t i = 0; i < tsz; i++) h->table[i] = 0; /* len 0 => invalid */
unsigned counts[33] = { 0 };
for (unsigned s = 0; s < n; s++) {
if (lens[s] > max_len) { free(h->table); h->table = NULL; return -1; }
counts[lens[s]]++;
}
counts[0] = 0;
/* Reject over-subscribed code-length sets (Kraft inequality): a malformed set
* whose codewords exceed the 2^max_len code space would overflow next_code[] and
* write past the table (heap OOB). Incomplete (under-subscribed) sets are tolerated. */
{ int left = 1;
for (unsigned l = 1; l <= max_len; l++) { left <<= 1; left -= (int)counts[l];
if (left < 0) { free(h->table); h->table = NULL; return -1; } } }
uint32_t code = 0, next_code[33], total = 0;
for (unsigned l = 1; l <= max_len; l++) { next_code[l] = code; code = (code + counts[l]) << 1; total += counts[l]; }
if (total == 0) return 0; /* empty code: decode sym 0, 0 bits */
for (unsigned s = 0; s < n; s++) {
unsigned l = lens[s];
if (!l) continue;
uint32_t c = next_code[l]++;
uint32_t base = c << (max_len - l);
uint32_t cnt = 1u << (max_len - l);
uint32_t entry = ((uint32_t)s << 16) | l;
for (uint32_t k = 0; k < cnt; k++) h->table[base + k] = entry;
}
return 0;
}
static inline void huff_free(huff_t *h) { free(h->table); h->table = NULL; }
static inline unsigned huff_decode(bitstream_t *bs, const huff_t *h) {
bs_ensure(bs, h->max_len);
uint32_t e = h->table[bs_peek(bs, h->max_len)];
bs_remove(bs, e & 0xffff);
return e >> 16;
}
/* Overlap-safe LZ copy (offset may be < len). */
static inline void lz_copy(uint8_t *out, uint32_t off, uint32_t len) {
const uint8_t *src = out - off;
for (uint32_t i = 0; i < len; i++) out[i] = src[i];
}
#endif /* ASB_DECOMPRESS_COMMON_H */