From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id C0E38C53219 for ; Wed, 29 Jul 2026 04:55:46 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 89A7881068; Wed, 29 Jul 2026 04:55:46 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id ZCvfPT8XMOtX; Wed, 29 Jul 2026 04:55:45 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.166.142; helo=lists1.osuosl.org; envelope-from=u-boot-bounces@lists.u-boot-project.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org EE155810C5 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lists.u-boot-project.org ; s=default; t=1785300945; bh=mrTX9BzRwOur4MEFRvMv/1t/iNZblNNSOVP8pZoURrQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=zYNqNpFFVG1LdBf4HqgaqvFm1nA/yl+IzviejnO7mmnqeaLXsIB05Jd5lu7vm16Sw VuiEyyY/WHCc2kvbg+tEylWMnhSFs8KOIPatNb5lvnrGlsd85GrMaSGCsVkFkRDrzQ scYYAqsxhYJkzl7F5/NYvKnnMR0bZ0+0twfFbVWvx9uBATOCpHi1vp8GWB0osswRci 4h7VWMYB5L55PB5Jr5ONiw/j9RtK2tVQh05Jn50s+K2pFGrquLUavsUtomJ29Lcf1o TQ3ZwhjA2Rmmj+1iNpNlFrFzz7FlEiYUHvOlfE3B2SPLAOGer4qvAcDb3QdZ0UqNwa aN3ahCd8JoFgw== Received: from lists1.osuosl.org (lists1.osuosl.org [140.211.166.142]) by smtp1.osuosl.org (Postfix) with ESMTP id EE155810C5; Wed, 29 Jul 2026 04:55:44 +0000 (UTC) Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists1.osuosl.org (Postfix) with ESMTP id C1B57788 for ; Tue, 28 Jul 2026 17:44:10 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id BBEBD60810 for ; Tue, 28 Jul 2026 17:44:10 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id jKZa7fPocnzL for ; Tue, 28 Jul 2026 17:44:09 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=2607:f8b0:4864:20::334; helo=mail-ot1-x334.google.com; envelope-from=james.hilliard1@gmail.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org 563DA608A9 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 563DA608A9 Received: from mail-ot1-x334.google.com (mail-ot1-x334.google.com [IPv6:2607:f8b0:4864:20::334]) by smtp3.osuosl.org (Postfix) with ESMTPS id 563DA608A9 for ; Tue, 28 Jul 2026 17:44:09 +0000 (UTC) Received: by mail-ot1-x334.google.com with SMTP id 46e09a7af769-7ec49608332so82325a34.3 for ; Tue, 28 Jul 2026 10:44:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785260648; x=1785865448; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=mrTX9BzRwOur4MEFRvMv/1t/iNZblNNSOVP8pZoURrQ=; b=N4vwY5Ntam040L83/LPGu+rDsw3O3FtXRBn9Jw9Ylvm4YXC3TnCMQlGDsY/sIFoaW0 UZsSIBUomEsreHY7FK4ZzRhLxwohkHpWorxmqryqM3oM8DSLm55J7FpJuaSSnSrhgRBx U+AJE1+03tMd4jnO/UHyZheAjPZsKXbyrlV7lRgihUAlmkpH5rf4Xp04UgDkmN5YpiBr FvGH4IIp6Pto/OwzjJOy/qz8zi2Matr4SKqp3kreHYCzxksIOunaT/XTg2M2sjCMBg4c 9eT/DcPqqmQDdeJpvsFb4GHCMBLCB9YifJL9FUbwcHAg0ndlP0bFMkGyjOLkZaRrp44A rpUg== X-Forwarded-Encrypted: i=1; AHgh+RqSfI5v29XVnuhKWTQOi78vc/DrTyRa87ok6NOod4OBw1RfTXvMmNluwzVGSP4bT6XpS2RnweU=@lists.u-boot-project.org X-Gm-Message-State: AOJu0YxnBYW3dzsFk5krtYLWtS/8CX+aWqG5iiFKSLV6Pi+Zy/Z5nRq4 yVQbe0P/5QlWEkuC0vYMwZIfyKVQlZDejUPQOb0u3bd8fB5qXKLM2DU+ X-Gm-Gg: AR+sD11sbB/WsBZHGMYaS5JIyH8Di2sPqtqunU9z/zkDQdCrUlphBoTT+dr9Oq49Hbv fb791lA5cHvEwaSuaLEtwkh1VGw2LoAI0gGuaAVrixnRmd8Ba45lSdkEosjubmgvsmZzjTnTy5I MGUpIPIv2IavtxOnaNxP7xwdMB+XfrTyFauz+rmqHT7k8QOA5sxLs+cmLNsuUtlG2oilmxMyE8T 3jJYP5RLLF3jZuOx6NEXPFeGf9ej70szKwkAMsCzQHXx/qqx1PNgd+NRQXVOP3iAdxY0A/3o8MY ngLxcEOR98SfdPg7IsnC+XhLYhw+Xsq+nioGJVxenkBelzb0UfTTYNmabkkb4KuZhFwnpsOf+HP FS/H+o91B6pIU529sLUks3Gf9i6jBllsSUXWFy4mM8LE9R7fs7clOSBaoCyXyjghlU0AF315ziR KMH6413fLeTpy59O9/I+S80ju0kIRIxiEN/rpLquPhM7ferHeEeyGCzd5k0mRK9Kfo/UvH0UOqV uvKfTOoFIMpfNWNKyCyq9i0Pj96kBizI+90y1lsmMu0kJ3iGxpEHhX+ X-Received: by 2002:a05:6830:2a17:b0:7eb:c933:eff7 with SMTP id 46e09a7af769-7effee90329mr1815796a34.9.1785260648055; Tue, 28 Jul 2026 10:44:08 -0700 (PDT) Received: from [127.0.1.1] (71-218-31-69.hlrn.qwest.net. [71.218.31.69]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f00d94389dsm271103a34.21.2026.07.28.10.44.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Jul 2026 10:44:07 -0700 (PDT) From: James Hilliard Date: Tue, 28 Jul 2026 11:41:50 -0600 Subject: [PATCH v6 07/13] boot: image: add FIT decrypt-to-buffer helper MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260728-submit-ce-series-v2-v6-7-80c1f7daffa5@gmail.com> References: <20260728-submit-ce-series-v2-v6-0-80c1f7daffa5@gmail.com> In-Reply-To: <20260728-submit-ce-series-v2-v6-0-80c1f7daffa5@gmail.com> To: Svyatoslav Ryhel , Ion Agorria , Aspeed BMC SW team , Joel Stanley , u-boot@lists.u-boot-project.org Cc: Chen-Yu Tsai , Samuel Holland , Tom Rini , Simon Glass , James Hilliard , Thierry Reding , Quentin Schulz , Quentin Schulz , Marek Vasut , Marek Vasut , Rasmus Villemoes , Rasmus Villemoes , Aristo Chen , Anton Ivanov , Daniel Golle , Francois Berder , Peng Fan , Neil Armstrong , Randolph Sapp , Jonas Karlman , Wolfgang Wallner , Alexey Charkov , Ilias Apalodimas , Heiko Schocher , "Kory Maincent (TI.com)" , Anshul Dalal , Johan Jonker , Francesco Valla , Heinrich Schuchardt , Michael Walle , Andre Przywara , Lukasz Majewski , Richard Genoud , Michael Trimarchi , E Shattow , Enric Balletbo i Serra , Mattijs Korpershoek , Lucas Dietrich , David Lechner , Julien Stephan , Kuan-Wei Chiu , Bastien Curutchet , Raymond Mao , Ryan Chen , Chia-Wei Wang , "Lucien.Jheng" , Mateusz Furdyna , Dinesh Maniyam , Heiko Stuebner , Vincent Jardin X-Mailer: b4 0.15.2 X-Mailman-Approved-At: Wed, 29 Jul 2026 04:55:22 +0000 X-Mailman-Original-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785260648; x=1785865448; darn=lists.u-boot-project.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=mrTX9BzRwOur4MEFRvMv/1t/iNZblNNSOVP8pZoURrQ=; b=jnmVcccAIBZogD6NO4yx5a7yee0pyyeSakJLXGfgidOtsARmKanQ5Yy12BA1EU/k6T QeI6vHbZesA5g52IC9MVXwLakp2h/+csgWvgYJNx1y8eR778eINElQNtKytjF8rCWUXh tLV9CHwE4dsY/cWSkTuIeKmEkWG7fD1cWHrHTWg/pYDklTq+lHqD5GQjDY0dpuOxNCmO bcqn+b9Of/GYlo3Wg9L7hr+3CVAlXcafyqpQTvbIgTLMCi7xCDj/XRAPm0tCqcVsPXgS qA/q5PiDcEm2AknMBujII0Cxbi2hMUd7AfFGON9mMoLeUGc0noQoLOs4yn412vVio9PX ItYQ== X-Mailman-Original-Authentication-Results: smtp3.osuosl.org; dmarc=pass (p=none dis=none) header.from=gmail.com X-Mailman-Original-Authentication-Results: smtp3.osuosl.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=jnmVcccA X-BeenThere: u-boot@lists.u-boot-project.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.u-boot-project.org Sender: "U-Boot" FIT cipher support currently allocates the output buffer inside the AES helper. SPL often needs to decrypt directly into a caller-selected buffer, for example a load buffer or a scratch buffer used before decompression. Add a decrypt_to callback to the FIT cipher algorithm and wire it up for AES. The existing allocating decrypt path becomes a wrapper around the new helper. Validate the FIT cipher key length, IV length and unciphered-size property while preparing decryption, and build lib/aes/ by phase when FIT_CIPHER is enabled so the target-side decrypt helper is available to SPL builds. Use the DM AES provider helper when enabled, retaining the software implementation only when no provider supports the operation. For U-Boot proper, use decrypt_to for in-place decryption when the FIT payload is already in writable RAM and has a payload hash. The encrypted data is no longer needed after hash verification, and this avoids a full-size allocation for encrypted payloads loaded into DRAM. When normal image verification is disabled, verify the payload hash before overwriting the ciphertext. A repeated load then fails the hash check before AES sees the plaintext. Keep using the allocating path for hashless payloads so their ciphertext remains reusable, and leave signed FIT metadata unchanged. On host builds, make both decrypt helpers return -ENOSYS instead of reporting success without producing output. Add sandbox coverage for out-of-place and in-place AES-256 decrypt and malformed key, IV and size inputs. Reviewed-by: Simon Glass Signed-off-by: James Hilliard --- Changes v5 -> v6: - Keep signed FIT metadata unchanged after in-place decryption - Require a payload hash for in-place decryption and verify it when normal verification is disabled, so repeated loads fail before AES processes plaintext (suggested by Simon Glass) - Return -ENOSYS from the allocating host decrypt stub (suggested by Simon Glass) - Preserve the existing fit_image_decrypt_data() declaration wrapping Changes v3 -> v4: - Validate cipher metadata before reading the key length - Preserve hard provider failures and fall back only when unsupported - Treat -EINVAL as a hard provider error - Fix disabled-feature declarations and use the public test prototype - Add in-place decrypt and malformed-input tests - Simplify the legacy allocating decrypt wrapper Changes v2 -> v3: - Flip the image_aes_decrypt_to() host-tool guard (suggested by Simon Glass) - Let image_aes_decrypt_to() be the single length-validation path (suggested by Simon Glass) - Document that AES CBC decrypt providers must support in-place decrypt (suggested by Simon Glass) Changes v1 -> v2: - Explain FIT cipher validation (suggested by Simon Glass) - Explain phase-keyed lib/aes builds (suggested by Simon Glass) - Return -ENOSYS without decrypt support (suggested by Simon Glass) - Use decrypt_to for U-Boot proper in-place decrypt --- boot/image-cipher.c | 47 ++++++++++++++++++----- boot/image-fit.c | 66 +++++++++++++++++++++++++++++--- include/image.h | 39 +++++++++++++++++++ include/u-boot/aes.h | 27 ++++++++++---- lib/Makefile | 2 +- lib/aes/aes-decrypt.c | 91 +++++++++++++++++++++++++++++++++++++-------- test/lib/Makefile | 3 ++ test/lib/test_aes_decrypt.c | 89 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 325 insertions(+), 39 deletions(-) diff --git a/boot/image-cipher.c b/boot/image-cipher.c index 9d389f26cea..e7ef72987cc 100644 --- a/boot/image-cipher.c +++ b/boot/image-cipher.c @@ -25,6 +25,7 @@ struct cipher_algo cipher_algos[] = { #endif .encrypt = image_aes_encrypt, .decrypt = image_aes_decrypt, + .decrypt_to = image_aes_decrypt_to, .add_cipher_data = image_aes_add_cipher_data }, { @@ -36,6 +37,7 @@ struct cipher_algo cipher_algos[] = { #endif .encrypt = image_aes_encrypt, .decrypt = image_aes_decrypt, + .decrypt_to = image_aes_decrypt_to, .add_cipher_data = image_aes_add_cipher_data }, { @@ -47,6 +49,7 @@ struct cipher_algo cipher_algos[] = { #endif .encrypt = image_aes_encrypt, .decrypt = image_aes_decrypt, + .decrypt_to = image_aes_decrypt_to, .add_cipher_data = image_aes_add_cipher_data } }; @@ -70,6 +73,7 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info, int cipher_noffset) { const void *fdt = gd_fdt_blob(); + int key_len, iv_len; const char *node_name; char node_path[128]; int noffset; @@ -94,7 +98,7 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info, return -1; } - info->iv = fdt_getprop(fit, cipher_noffset, "iv", NULL); + info->iv = fdt_getprop(fit, cipher_noffset, "iv", &iv_len); info->ivname = fdt_getprop(fit, cipher_noffset, "iv-name-hint", NULL); if (!info->iv && !info->ivname) { @@ -115,7 +119,7 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info, &info->size_unciphered); if (ret) { printf("Can't get size of unciphered data\n"); - return -1; + return ret; } /* @@ -136,20 +140,28 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info, } /* read key */ - info->key = fdt_getprop(fdt, noffset, "key", NULL); + info->key = fdt_getprop(fdt, noffset, "key", &key_len); if (!info->key) { printf("Can't get key in cipher node '%s'\n", node_path); return -1; } + if (key_len != info->cipher->key_len) { + printf("Bad key length in cipher node '%s'\n", node_path); + return -1; + } /* read iv */ if (!info->iv) { - info->iv = fdt_getprop(fdt, noffset, "iv", NULL); + info->iv = fdt_getprop(fdt, noffset, "iv", &iv_len); if (!info->iv) { printf("Can't get IV in cipher node '%s'\n", node_path); return -1; } } + if (iv_len != info->cipher->iv_len) { + printf("Bad IV length for cipher in image '%s'\n", node_name); + return -1; + } return 0; } @@ -165,11 +177,28 @@ int fit_image_decrypt_data(const void *fit, ret = fit_image_setup_decrypt(&info, fit, image_noffset, cipher_noffset); if (ret < 0) - goto out; + return ret; + + return info.cipher->decrypt(&info, data_ciphered, size_ciphered, + data_unciphered, size_unciphered); +} + +int fit_image_decrypt_data_to(const void *fit, + int image_noffset, int cipher_noffset, + const void *data_ciphered, size_t size_ciphered, + void *data_unciphered, size_t *size_unciphered) +{ + struct image_cipher_info info; + int ret; + + ret = fit_image_setup_decrypt(&info, fit, image_noffset, + cipher_noffset); + if (ret < 0) + return ret; - ret = info.cipher->decrypt(&info, data_ciphered, size_ciphered, - data_unciphered, size_unciphered); + if (!info.cipher->decrypt_to) + return -ENOSYS; - out: - return ret; + return info.cipher->decrypt_to(&info, data_ciphered, size_ciphered, + data_unciphered, size_unciphered); } diff --git a/boot/image-fit.c b/boot/image-fit.c index 9b39696de2d..bc964510624 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -1028,7 +1028,7 @@ int fit_image_get_data_size(const void *fit, int noffset, int *data_size) * * @fit: pointer to the FIT image header * @noffset: component image node offset - * @data_size: holds the data-size property + * @data_size: holds the data-size-unciphered property * * returns: * 0, on success @@ -1038,10 +1038,13 @@ int fit_image_get_data_size_unciphered(const void *fit, int noffset, size_t *data_size) { const fdt32_t *val; + int len; - val = fdt_getprop(fit, noffset, "data-size-unciphered", NULL); + val = fdt_getprop(fit, noffset, FIT_DATA_SIZE_UNCIPHERED_PROP, &len); if (!val) return -ENOENT; + if (len != sizeof(*val)) + return -EINVAL; *data_size = (size_t)fdt32_to_cpu(*val); @@ -1550,8 +1553,33 @@ int fit_all_image_verify(const void *fit) return 1; } +#ifndef USE_HOSTCC +static bool fit_image_has_hash(const void *fit, int image_noffset) +{ + int noffset; + + fdt_for_each_subnode(noffset, fit, image_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_HASH_NODENAME, + strlen(FIT_HASH_NODENAME))) + return true; + } + + return false; +} + +static bool fit_range_in_writable_ram(const void *data, size_t size) +{ + ulong start = map_to_sysmem(data); + + return start >= gd->ram_base && start <= gd->ram_top && + size <= gd->ram_top - start; +} +#endif + static int fit_image_uncipher(const void *fit, int image_noffset, - void **data, size_t *size) + bool verified, void **data, size_t *size) { int cipher_noffset, ret; void *dst; @@ -1562,15 +1590,40 @@ static int fit_image_uncipher(const void *fit, int image_noffset, if (cipher_noffset < 0) return 0; +#ifndef USE_HOSTCC + if (!tools_build()) { + /* + * Avoid a full-size allocation when the FIT payload is already + * in writable DRAM. Require a payload hash so an unverified + * repeated attempt detects the consumed ciphertext before + * decrypting it again. + */ + if (fit_range_in_writable_ram(*data, *size) && + fit_image_has_hash(fit, image_noffset)) { + if (!verified && !fit_image_verify(fit, image_noffset)) { + ret = -EACCES; + goto out; + } + ret = fit_image_decrypt_data_to(fit, image_noffset, + cipher_noffset, + *data, *size, *data, + &size_dst); + if (ret != -ENOSYS) + goto out; + } + } +#endif + ret = fit_image_decrypt_data(fit, image_noffset, cipher_noffset, *data, *size, &dst, &size_dst); if (ret) goto out; *data = dst; - *size = size_dst; +out: + if (!ret) + *size = size_dst; - out: return ret; } @@ -2299,7 +2352,8 @@ int fit_image_load(struct bootm_headers *images, ulong addr, /* Decrypt data before uncompress/move */ if (IS_ENABLED(CONFIG_FIT_CIPHER) && IMAGE_ENABLE_DECRYPT) { puts(" Decrypting Data ... "); - if (fit_image_uncipher(fit, noffset, &buf, &size)) { + if (fit_image_uncipher(fit, noffset, images->verify, + &buf, &size)) { puts("Error\n"); return -EACCES; } diff --git a/include/image.h b/include/image.h index 4149ebbcce9..879e58e2241 100644 --- a/include/image.h +++ b/include/image.h @@ -1179,6 +1179,7 @@ int booti_setup(ulong image, ulong *relocated_addr, ulong *size, #define FIT_DATA_POSITION_PROP "data-position" #define FIT_DATA_OFFSET_PROP "data-offset" #define FIT_DATA_SIZE_PROP "data-size" +#define FIT_DATA_SIZE_UNCIPHERED_PROP "data-size-unciphered" #define FIT_TIMESTAMP_PROP "timestamp" #define FIT_DESC_PROP "description" #define FIT_ARCH_PROP "arch" @@ -1875,11 +1876,45 @@ int fit_image_check_sig(const void *fit, int noffset, const void *data, size_t size, const void *key_blob, int required_keynode, char **err_msgp); +/** + * fit_image_decrypt_data() - Decrypt a FIT image payload + * + * @fit: FIT image + * @image_noffset: Offset of the image node to decrypt + * @cipher_noffset: Offset of the cipher node for the image + * @data: Encrypted image payload + * @size: Size of encrypted image payload + * @data_unciphered: Returns allocated decrypted payload + * @size_unciphered: Returns size of decrypted payload + * Return: 0 on success, <0 on error + */ int fit_image_decrypt_data(const void *fit, int image_noffset, int cipher_noffset, const void *data, size_t size, void **data_unciphered, size_t *size_unciphered); +/** + * fit_image_decrypt_data_to() - Decrypt a FIT image payload to a buffer + * + * @fit: FIT image + * @image_noffset: Offset of the image node to decrypt + * @cipher_noffset: Offset of the cipher node for the image + * @data: Encrypted image payload + * @size: Size of encrypted image payload + * @data_unciphered: Destination buffer for decrypted payload. The caller + * must provide at least @size bytes. + * @size_unciphered: Returns size of decrypted payload + * + * If @data and @data_unciphered are the same buffer, successful decryption + * overwrites the encrypted payload. + * + * Return: 0 on success, <0 on error + */ +int fit_image_decrypt_data_to(const void *fit, + int image_noffset, int cipher_noffset, + const void *data, size_t size, + void *data_unciphered, size_t *size_unciphered); + /** * fit_region_make_list() - Make a list of regions to hash * @@ -1973,6 +2008,10 @@ struct cipher_algo { int (*decrypt)(struct image_cipher_info *info, const void *cipher, size_t cipher_len, void **data, size_t *data_len); + + int (*decrypt_to)(struct image_cipher_info *info, + const void *cipher, size_t cipher_len, + void *data, size_t *data_len); }; int fit_image_cipher_get_algo(const void *fit, int noffset, char **algo); diff --git a/include/u-boot/aes.h b/include/u-boot/aes.h index acbc50b9e6f..8fd43f02adc 100644 --- a/include/u-boot/aes.h +++ b/include/u-boot/aes.h @@ -16,15 +16,16 @@ int image_aes_encrypt(struct image_cipher_info *info, int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest, void *fit, int node_noffset); #else -int image_aes_encrypt(struct image_cipher_info *info, - const unsigned char *data, int size, - unsigned char **cipher, int *cipher_len) +static inline int image_aes_encrypt(struct image_cipher_info *info, + const unsigned char *data, int size, + unsigned char **cipher, int *cipher_len) { return -ENXIO; } -int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest, - void *fit, int node_noffset) +static inline int image_aes_add_cipher_data(struct image_cipher_info *info, + void *keydest, void *fit, + int node_noffset) { return -ENXIO; } @@ -34,10 +35,20 @@ int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest, int image_aes_decrypt(struct image_cipher_info *info, const void *cipher, size_t cipher_len, void **data, size_t *size); +int image_aes_decrypt_to(struct image_cipher_info *info, + const void *cipher, size_t cipher_len, + void *data, size_t *size); #else -int image_aes_decrypt(struct image_cipher_info *info, - const void *cipher, size_t cipher_len, - void **data, size_t *size) +static inline int image_aes_decrypt(struct image_cipher_info *info, + const void *cipher, size_t cipher_len, + void **data, size_t *size) +{ + return -ENXIO; +} + +static inline int image_aes_decrypt_to(struct image_cipher_info *info, + const void *cipher, size_t cipher_len, + void *data, size_t *size) { return -ENXIO; } diff --git a/lib/Makefile b/lib/Makefile index 222378a8531..e8ec4660b38 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_ARCH_AT91) += at91/ obj-$(CONFIG_OPTEE_LIB) += optee/ obj-$(CONFIG_AES) += aes.o -obj-$(CONFIG_AES) += aes/ obj-$(CONFIG_$(PHASE_)BINMAN_FDT) += binman.o obj-$(CONFIG_FW_LOADER) += fw_loader.o @@ -89,6 +88,7 @@ obj-$(CONFIG_$(PHASE_)ASN1_DECODER_LEGACY) += asn1_decoder.o obj-$(CONFIG_$(PHASE_)ZLIB) += zlib/ obj-$(CONFIG_$(PHASE_)ZSTD) += zstd/ +obj-$(CONFIG_$(PHASE_)FIT_CIPHER) += aes/ obj-$(CONFIG_$(PHASE_)GZIP) += gunzip.o obj-$(CONFIG_$(PHASE_)LZO) += lzo/ obj-$(CONFIG_$(PHASE_)LZMA) += lzma/ diff --git a/lib/aes/aes-decrypt.c b/lib/aes/aes-decrypt.c index 741102a4723..8a9b5f01efb 100644 --- a/lib/aes/aes-decrypt.c +++ b/lib/aes/aes-decrypt.c @@ -4,37 +4,98 @@ */ #ifndef USE_HOSTCC +#include #include #endif #include #include +#ifndef USE_HOSTCC +static int image_aes_validate(struct image_cipher_info *info, + const void *cipher, size_t cipher_len, + void *data, size_t *size) +{ + if (!info || !info->cipher || !info->key || !info->iv || !cipher || + !data || !size) + return -EINVAL; + if (info->cipher->iv_len != AES_BLOCK_LENGTH || + (info->cipher->key_len != AES128_KEY_LENGTH && + info->cipher->key_len != AES192_KEY_LENGTH && + info->cipher->key_len != AES256_KEY_LENGTH)) + return -EINVAL; + if (!cipher_len || cipher_len % AES_BLOCK_LENGTH || + info->size_unciphered > cipher_len) + return -EINVAL; + + return 0; +} +#endif + +int image_aes_decrypt_to(struct image_cipher_info *info, + const void *cipher, size_t cipher_len, + void *data, size_t *size) +{ +#ifdef USE_HOSTCC + return -ENOSYS; +#else + unsigned int aes_blocks, key_len; + int ret; + + ret = image_aes_validate(info, cipher, cipher_len, data, size); + if (ret) + return ret; + key_len = info->cipher->key_len; + aes_blocks = cipher_len / AES_BLOCK_LENGTH; + + if (CONFIG_IS_ENABLED(DM_AES)) { + ret = dm_aes_cbc_decrypt_with_key(key_len * 8, (u8 *)info->key, + (u8 *)info->iv, (u8 *)cipher, + data, aes_blocks); + if (!ret) { + *size = info->size_unciphered; + return 0; + } + if (ret != -ENODEV && ret != -EOPNOTSUPP) + return ret; + } + + if (!IS_ENABLED(CONFIG_XPL_BUILD)) { + unsigned char key_exp[AES256_EXPAND_KEY_LENGTH]; + + /* First we expand the key. */ + aes_expand_key((u8 *)info->key, key_len, key_exp); + + aes_cbc_decrypt_blocks(key_len, key_exp, (u8 *)info->iv, + (u8 *)cipher, data, aes_blocks); + *size = info->size_unciphered; + return 0; + } + + return -ENOSYS; +#endif +} + int image_aes_decrypt(struct image_cipher_info *info, const void *cipher, size_t cipher_len, void **data, size_t *size) { -#ifndef USE_HOSTCC - unsigned char key_exp[AES256_EXPAND_KEY_LENGTH]; - unsigned int aes_blocks, key_len = info->cipher->key_len; +#ifdef USE_HOSTCC + return -ENOSYS; +#else + int ret; *data = malloc(cipher_len); if (!*data) { printf("Can't allocate memory to decrypt\n"); return -ENOMEM; } - *size = info->size_unciphered; - - memcpy(&key_exp[0], info->key, key_len); - - /* First we expand the key. */ - aes_expand_key((u8 *)info->key, key_len, key_exp); - /* Calculate the number of AES blocks to encrypt. */ - aes_blocks = DIV_ROUND_UP(cipher_len, AES_BLOCK_LENGTH); + ret = image_aes_decrypt_to(info, cipher, cipher_len, *data, size); + if (ret) { + free(*data); + *data = NULL; + } - aes_cbc_decrypt_blocks(key_len, key_exp, (u8 *)info->iv, - (u8 *)cipher, *data, aes_blocks); + return ret; #endif - - return 0; } diff --git a/test/lib/Makefile b/test/lib/Makefile index f25383a40e5..721d1470185 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -29,6 +29,9 @@ obj-$(CONFIG_ERRNO_STR) += test_errno_str.o obj-$(CONFIG_UT_LIB_ASN1) += asn1.o obj-$(CONFIG_UT_LIB_RSA) += rsa.o obj-$(CONFIG_AES) += test_aes.o +ifeq ($(CONFIG_FIT_CIPHER)$(CONFIG_DM_AES),yy) +obj-y += test_aes_decrypt.o +endif obj-$(CONFIG_SHA256) += test_sha256_hmac.o obj-$(CONFIG_HKDF_MBEDTLS) += test_sha256_hkdf.o obj-$(CONFIG_GETOPT) += getopt.o diff --git a/test/lib/test_aes_decrypt.c b/test/lib/test_aes_decrypt.c new file mode 100644 index 00000000000..3b498e23e4b --- /dev/null +++ b/test/lib/test_aes_decrypt.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for target-side FIT AES decryption + * + * Copyright (C) 2026 James Hilliard + */ + +#include +#include +#include +#include +#include +#include + +static int lib_test_image_aes_decrypt(struct unit_test_state *uts) +{ + u8 key[AES256_KEY_LENGTH] = { }; + u8 key_exp[AES256_EXPAND_KEY_LENGTH]; + u8 iv[AES_BLOCK_LENGTH] = { }; + u8 plain[2 * AES_BLOCK_LENGTH]; + u8 cipher[sizeof(plain)]; + u8 output[sizeof(plain)]; + struct cipher_algo algo = { + .name = "aes256", + .key_len = sizeof(key), + .iv_len = sizeof(iv), + }; + struct image_cipher_info info = { + .cipher = &algo, + .key = key, + .iv = iv, + .size_unciphered = sizeof(plain), + }; + size_t size; + int i, ret; + + for (i = 0; i < sizeof(key); i++) + key[i] = i; + for (i = 0; i < sizeof(iv); i++) + iv[i] = 0x80 + i; + for (i = 0; i < sizeof(plain); i++) + plain[i] = 0x40 + i; + + aes_expand_key(key, sizeof(key), key_exp); + aes_cbc_encrypt_blocks(sizeof(key), key_exp, iv, plain, cipher, + ARRAY_SIZE(cipher) / AES_BLOCK_LENGTH); + + size = 0; + ut_assertok(image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, + &size)); + ut_asserteq(sizeof(plain), size); + ut_asserteq_mem(plain, output, sizeof(plain)); + + memcpy(output, cipher, sizeof(cipher)); + size = 0; + ut_assertok(image_aes_decrypt_to(&info, output, sizeof(output), output, + &size)); + ut_asserteq(sizeof(plain), size); + ut_asserteq_mem(plain, output, sizeof(plain)); + + size = 0x55; + ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher) - 1, output, + &size); + ut_asserteq(-EINVAL, ret); + ut_asserteq(0x55, size); + + info.size_unciphered = sizeof(cipher) + 1; + ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, &size); + ut_asserteq(-EINVAL, ret); + info.size_unciphered = sizeof(plain); + + info.key = NULL; + ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, &size); + ut_asserteq(-EINVAL, ret); + info.key = key; + info.iv = NULL; + ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, &size); + ut_asserteq(-EINVAL, ret); + info.iv = iv; + info.cipher = NULL; + ret = image_aes_decrypt_to(&info, cipher, sizeof(cipher), output, &size); + ut_asserteq(-EINVAL, ret); + ret = image_aes_decrypt_to(NULL, cipher, sizeof(cipher), output, &size); + ut_asserteq(-EINVAL, ret); + + return 0; +} + +LIB_TEST(lib_test_image_aes_decrypt, 0); -- 2.53.0