feat(tbor): implement AesGenerateKey + AesEncryptDecrypt (masked-key AES)#584
Open
vsonims wants to merge 1 commit into
Open
feat(tbor): implement AesGenerateKey + AesEncryptDecrypt (masked-key AES)#584vsonims wants to merge 1 commit into
vsonims wants to merge 1 commit into
Conversation
vsonims
force-pushed
the
tbor/unwrap-key
branch
2 times, most recently
from
July 19, 2026 03:32
b3e53f6 to
427588b
Compare
…AES) Add the TBOR AES crypto command pair, stacked on the RSA-AES key-unwrap commands, following the MBOR AES commands but with the TBOR masked-key (stateless) model. AesGenerateKey (opcode 0x15): generate a fresh random AES key (128 / 192 / 256 bits) and return it masked (AEAD-GCM-256) under the requested scope's masking key. Non-bulk sizes only (mirroring MBOR); no vault `key_id` / `key_tag`. The masked-blob length is fixed by the key size, so the handler reserves the response slot up front and masks the generated key straight into it (encoder `*_reserve` + `decode_mut`) — no scratch buffer and no copy. AesEncryptDecrypt (opcode 0x16): AES-CBC encrypt or decrypt a host-supplied message with a caller-held masked AES key. Fully zero-copy: the request `masked_key` is `#[tbor(mutable)]`, so the handler `decode_mut`s the request and `unmask`s the blob in place, then the non-in-place `aes_cbc_enc_dec` reads the request message and writes the ciphertext / chaining IV straight into the reserved response slots — no blob / key scratch copy and no message copy. Rejects non-AES key kinds (`InvalidKeyType`) and direction-permission mismatches (`InvalidPermissions`); the recovered key is wiped on every path. AES key import is already provided by `UnwrapKey` (0x14, key class Aes); an emu test exercises the unwrap -> encrypt/decrypt round-trip. Reuses the shared session/masking helpers (`validate_active_session`, `resolve_masking_key`) and the encoder reserve/fill support from the key-unwrap PR below it in the stack. Wires both opcodes through the fw dispatcher, the `is_known_opcode` / `is_in_session` / `needs_session_id_cross_check` classifiers, and `op::SessionCtrl::from_tbor_opcode`. Adds fw + host wire schemas, command docs, and emu tests: keygen round-trip across all sizes/scopes, encrypt->decrypt round-trip, CBC chaining IV, tamper / bad-length rejects, and the UnwrapKey->AES cross-command round-trip. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the TBOR AES crypto command pair, stacked on the RSA-AES
key-unwrap commands (#583), following the MBOR AES commands but with the
TBOR masked-key (stateless) model — no vault
key_id/key_tag.AesGenerateKey(opcode0x15)Generate a fresh random AES key (128 / 192 / 256 bits, non-bulk only,
mirroring MBOR) and return it masked (AEAD-GCM-256) under the
requested scope's masking key. The masked-blob length is fixed by the key
size, so the handler reserves the response slot up front and masks the
generated key straight into it (encoder
*_reserve+decode_mut) — noscratch buffer, no copy.
AesEncryptDecrypt(opcode0x16)AES-CBC encrypt or decrypt a host-supplied message with a caller-held
masked AES key. Fully zero-copy:
masked_keyis#[tbor(mutable)]→ the handlerdecode_mutsthe request and
unmasks the blob in place, using the recoveredkey directly (no blob / key scratch copy);
msg/ivslots are reserved, and the non-in-placeaes_cbc_enc_decreads the request message and writes the ciphertext /chaining IV straight into the response (no message copy).
Rejects non-AES key kinds (
InvalidKeyType) and direction-permissionmismatches (
InvalidPermissions); the recovered key is wiped on everypath (including the unmask-failure path).
AES key import
Already provided by
UnwrapKey(0x14, key classAes) in #583 — anemu test exercises the
UnwrapKey→AesEncryptDecryptround-trip.Wiring
Both opcodes are routed through the fw dispatcher, the
is_known_opcode/
is_in_session/needs_session_id_cross_checkclassifiers, andop::SessionCtrl::from_tbor_opcode(InSession, CO/CU). Reuses the sharedvalidate_active_session/resolve_masking_keyhelpers and the encoderreserve/fill support from #583.
Tests / validation
SD-scope + unknown-size rejects; encrypt→decrypt round-trip all sizes,
CBC chaining IV, tamper + bad-length rejects, and the
UnwrapKey→AEScross-command round-trip.
clippyclean, nightlyfmt+copyrightclean, Uno (thumbv7em) build passes.docs/tbor-ddi/commands/{aes_generate_key,aes_encrypt_decrypt}.md0x15,0x16).