From patchwork Sun Nov 26 13:24:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Glembotzki X-Patchwork-Id: 1868534 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20230601 header.b=utyO+TQa; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=PzhJuGNL; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::140; helo=mail-lf1-x140.google.com; envelope-from=swupdate+bncbdy5juxlviebbjmorwvqmgqehsqppsi@googlegroups.com; receiver=patchwork.ozlabs.org) Received: from mail-lf1-x140.google.com (mail-lf1-x140.google.com [IPv6:2a00:1450:4864:20::140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4SdTwZ0SBXz1yS6 for ; Mon, 27 Nov 2023 00:25:00 +1100 (AEDT) Received: by mail-lf1-x140.google.com with SMTP id 2adb3069b0e04-50baa81874bsf791710e87.3 for ; Sun, 26 Nov 2023 05:25:00 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1701005094; cv=pass; d=google.com; s=arc-20160816; b=ra5XIFvkqjTY1K8fbz1dJ6ValY6XYLTsk7pKh/AWExZphNVwcaz/DPqOfKuDJ05Nfg a0SOutzUCa/g1I20xyu5Zpqappjazehh3kcV8/oPWfIm8kW9e26oEgnlsp7jzhAN90rU 38Zwwi0q4UDSHxDYuCXH8kX7fs7AidGp42rzkKlHg4ZyyKC/Y/29DGjlbdLJKhqQMfkt cQhcBNnUtM8wywE3q0mL3nnVEmP2CL5WUDnh8+At394+2rsPcijAUBqAGw76P+mqMX/8 6SNjLCiUt6lPJeF67p8AYNmI23Lpv8cxGZvsS5rtNRDj7rfSnbJK7irq45k9jJpl/Gyu 8W5A== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:mime-version:message-id:date :subject:cc:to:from:sender:dkim-signature:dkim-signature; bh=vpEGVxL0XJrEVqNSEN7qDpAWRZSLyJ5Mcri7S9UL0+g=; fh=zydHuzCQWrku2OPQyZfraJZFcOpEXLQ/YBcu3QNiBd0=; b=tkvYTx5sxUSpHUzyzO5t6BQ33+/k7i3rmcL6XYC/rN9uwh/JnvEl+Z7RwXLHDWe6Ia 0Vm3ffIBC68NRn+Rj7/26D2QArRL/fPoZUFFueN09tz3IxBATAEd4SjfwVkxmuE9izCL crAo25i57r+sDOKr68x8xgLlTVXfMihkZmEi9ziNnJklzZ0gPxlKhsTSUkIgPDDSFy4+ v2fDzvXeV55k1/qNvzYGdqnZng5Vh2vtfjcDSsNgazrotXP8J9vrKh4XzmnOkj75XBNI bufzwsqa+2gQXSyzs7jgWw5VIwMRG2YTtqaQzdQY9WzTCoRcbBLJCflFP8C2Kal4vNjc H+cg== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=dGWZbq34; spf=pass (google.com: domain of m.glembo@gmail.com designates 2a00:1450:4864:20::633 as permitted sender) smtp.mailfrom=m.glembo@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20230601; t=1701005094; x=1701609894; darn=patchwork.ozlabs.org; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:x-original-authentication-results :x-original-sender:mime-version:message-id:date:subject:cc:to:from :sender:from:to:cc:subject:date:message-id:reply-to; bh=vpEGVxL0XJrEVqNSEN7qDpAWRZSLyJ5Mcri7S9UL0+g=; b=utyO+TQa2eU69G0nb5XYvfU5khDoSvOgTR3lxE+HbNMgC5V6aH2Zih4tywVwDf8TIG QTxoQ86pkAY5nu4hu1Xj3sJCnhGtC3xMGhpoXVp/vTyN6GibL6Vi7lNs5r8nr/xk/900 L1etrHev2DRaArLnn98aUvikgP/YXgdxkHRC7qornh+2nUcaJtKKpy6zX6Lfv0cfcwPL UIZymhAP8l3dJWxo6ey7F9sTbF3s+1QgsaBdN3sKngM4nfIkJwbe5LKK72Xf4eyaZcdp d8pshxzmZJSKiVGGJCzofDsSP6WynvuFjusJObzRjQIAXHFctdwdEOThPiCcPRfqRW+V cuNg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1701005094; x=1701609894; darn=patchwork.ozlabs.org; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:x-original-authentication-results :x-original-sender:mime-version:message-id:date:subject:cc:to:from :from:to:cc:subject:date:message-id:reply-to; bh=vpEGVxL0XJrEVqNSEN7qDpAWRZSLyJ5Mcri7S9UL0+g=; b=PzhJuGNLSLsPRpYCjyNUpQc8TaqhrIPuU4+cLFloLal/kBqtwwojsjoSjPzC+0eG7z /i0nd2t5y7L2Gh5VrRuAFVY0dEbF4xZNIQZIzTv5W28du1G9RsMxjoRSc3XdZG/dNIHX R0vHESJoXwjy3njbnbruTO90kuGbGjLZLlvYVkjjq243hMLJbU9BYw3P+F8ZLGlvApCv KCMdj6MjJBeXlVmzNaeYJf+ux2IjZMsgeYQhK7FdygS6YR7v8zCPITNvUw+zR6VoQbeN /f6ctbNO4E28rmj5v2wBTWary7/c3eB0Ee1BYF024g7XHstSPfAuhgAag2JFafW4osKY aASQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701005094; x=1701609894; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :x-spam-checked-in-group:list-id:mailing-list:precedence :x-original-authentication-results:x-original-sender:mime-version :message-id:date:subject:cc:to:from:x-beenthere:x-gm-message-state :sender:from:to:cc:subject:date:message-id:reply-to; bh=vpEGVxL0XJrEVqNSEN7qDpAWRZSLyJ5Mcri7S9UL0+g=; b=oxFCf0F0uG4kAWgM0xueNHMnhQN7T3gsJwJgfjNstDHuRi/keb2do7cBrfzd8JX+2J PklQSpgz/Nz9nhSKXBkutFAPYSJpomN94X0gHuqUpKOeCEQdrkl2HxPjlnZgaefR7y2Q P+yznudrHk49jDcs2xV2ssrYpghYQB0P4n8X3jE161voULfqmCGkWn68pUd3KhHoePS9 VJrU0zMw+omN4t+x5/Ew9f+j2lqCWZARoBveRL5wY0BHN+4mvv6Pz6Gf17QIGNfFUn+L GL/+OEbI8H/akEsYUU566ir5rMbAqF6teuvmDIZfsIHlA/rtxixXQkz59fSXOaX9KkXf Ts9Q== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOJu0Yx9NWsLm++oNGOrepqcD+s2ms31wUnmJaQm98s62ouEOx9Ol+l6 VPR2HeP7u1DL7x7YIlq0aEM= X-Google-Smtp-Source: AGHT+IHzslWOK1fGcnU3uy74xTvULpW0Fam7l8fLs7+45v+KjrHxqnX6C2urA39R0cdrf02bNPMXKw== X-Received: by 2002:ac2:4c48:0:b0:507:b7db:1deb with SMTP id o8-20020ac24c48000000b00507b7db1debmr7086936lfk.38.1701005094176; Sun, 26 Nov 2023 05:24:54 -0800 (PST) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a05:6512:3ca7:b0:4f9:5599:26a with SMTP id h39-20020a0565123ca700b004f95599026als195484lfv.2.-pod-prod-08-eu; Sun, 26 Nov 2023 05:24:51 -0800 (PST) X-Received: by 2002:a05:6512:2089:b0:509:4ab2:3635 with SMTP id t9-20020a056512208900b005094ab23635mr4822951lfr.59.1701005091260; Sun, 26 Nov 2023 05:24:51 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1701005091; cv=none; d=google.com; s=arc-20160816; b=Du4qhlpZJ+dH5YDcXXEBr+kGUC3O0ItxGCh6RNtebZ8WMTgC9u6WtBQdIL9Y5sgusy L04KXCrWlk1tsrkg+N0EWBUAcSNPuf+Yv6/Ndhy+N4E+3Ub9ec59jsKx4LznO/I+YznE LLSmT1GGqHs1NCXmFiLDEDBjUfaLCn9nnPUiLrj8lGx8J14VzEOwUwp898PJybm0QQ6/ PGoiKw3Np4v7QxayL8feS05mYKp1DQUrKtz4rR1vCXSVMS8rcU5N+OeNDK0KUkwOW/jz qEgdyyrvIykAVAYQxOwSqzvLSmIKCkG9J3JtSm8h6pqeJ7dZTAsplsfBXxGlFXtiXxax efbA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:dkim-signature; bh=Q7sePQoU1KrLbDSIzgXKetSg2h1ayrKDxv7Vcg6goNA=; fh=zydHuzCQWrku2OPQyZfraJZFcOpEXLQ/YBcu3QNiBd0=; b=EfHtvcUqVtUsmZVHpSZpqebufvj+VjVaISn/xd9gU8lB6qsc623ABjNOg+Bx/ESHrE XVrkuJKFNd1DZ1PDwD+Z7+eYo28PfVr49iPwvOEsQbpGAVk/BsXI2MWiR8dr3ReSePCc Zc89cvAj3cw6EhIE8B75vmNFvEtejhhcrka0vD9GKOGBcTN6krqdERBj0f2VPx7wLysS IKPY0gCWAa1jGwvAOrhxoBOHpKiRE3rdHg3SzTy54vLYoxd3zVefCa+s+4ZEhduoWslv HOcK3hNTsdxBZAwJmJkYYi3EKd1JMnyCLtMzQHFkICAXbrQGhgRK85LeVCE8H1ttXYwW ep2g== ARC-Authentication-Results: i=1; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=dGWZbq34; spf=pass (google.com: domain of m.glembo@gmail.com designates 2a00:1450:4864:20::633 as permitted sender) smtp.mailfrom=m.glembo@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from mail-ej1-x633.google.com (mail-ej1-x633.google.com. [2a00:1450:4864:20::633]) by gmr-mx.google.com with ESMTPS id k11-20020ac24f0b000000b0050aa9bd7f72si453757lfr.1.2023.11.26.05.24.51 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sun, 26 Nov 2023 05:24:51 -0800 (PST) Received-SPF: pass (google.com: domain of m.glembo@gmail.com designates 2a00:1450:4864:20::633 as permitted sender) client-ip=2a00:1450:4864:20::633; Received: by mail-ej1-x633.google.com with SMTP id a640c23a62f3a-a04196fc957so484703166b.2 for ; Sun, 26 Nov 2023 05:24:51 -0800 (PST) X-Received: by 2002:a17:907:3daa:b0:9a2:225a:8d01 with SMTP id he42-20020a1709073daa00b009a2225a8d01mr7376576ejc.7.1701005089997; Sun, 26 Nov 2023 05:24:49 -0800 (PST) Received: from PC-2635.irisgmbh.local (dslb-002-203-161-041.002.203.pools.vodafone-ip.de. [2.203.161.41]) by smtp.gmail.com with ESMTPSA id h18-20020a1709063b5200b009fdd2c6d042sm4630799ejf.148.2023.11.26.05.24.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Nov 2023 05:24:49 -0800 (PST) From: Michael Glembotzki To: swupdate@googlegroups.com Cc: Michael Glembotzki Subject: [swupdate] [PATCH 1/3] Add support for asymmetric decryption Date: Sun, 26 Nov 2023 14:24:15 +0100 Message-ID: <20231126132417.107606-1-Michael.Glembotzki@iris-sensing.com> X-Mailer: git-send-email 2.42.0 MIME-Version: 1.0 X-Original-Sender: m.glembo@gmail.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=dGWZbq34; spf=pass (google.com: domain of m.glembo@gmail.com designates 2a00:1450:4864:20::633 as permitted sender) smtp.mailfrom=m.glembo@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Precedence: list Mailing-list: list swupdate@googlegroups.com; contact swupdate+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: swupdate@googlegroups.com X-Google-Group-Id: 605343134186 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , This feature enables asymmetric image decryption. The asymmetrically encrypted AES file is decrypted and loaded from a Cryptographic Message Syntax file (DER). The AES file can be encrypted for any number of recipient devices. Signed-off-by: Michael Glembotzki --- Kconfig | 12 ++++ core/installer.c | 12 ++++ core/stream_interface.c | 70 ++++++++++++++++++++- core/swupdate.c | 44 +++++++++++-- corelib/Makefile | 3 + corelib/swupdate_cms_decrypt.c | 112 +++++++++++++++++++++++++++++++++ include/parsers.h | 9 +++ include/sslapi.h | 9 +++ include/swupdate.h | 1 + 9 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 corelib/swupdate_cms_decrypt.c diff --git a/Kconfig b/Kconfig index 2ae2e4b..a0ae5db 100644 --- a/Kconfig +++ b/Kconfig @@ -499,6 +499,18 @@ config ENCRYPTED_IMAGES comment "Image encryption needs an SSL implementation" depends on !SSL_IMPL_OPENSSL && !SSL_IMPL_WOLFSSL && !SSL_IMPL_MBEDTLS +config ASYM_ENCRYPTED_AESFILE + bool "Enable asymmetrically encrypted AES file with CMS / PKCS#7" + default n + depends on ENCRYPTED_IMAGES && SSL_IMPL_OPENSSL + help + This option enables asymmetric image decryption. The asymmetrically + encrypted AES file is decrypted and loaded from a Cryptographic Message + Syntax file (DER). The AES file can be encrypted for any number of + recipient devices. For security reasons the AES file should be rotated + regularly and the feature should be used together with signature + verification: SIGNED_IMAGES. + config ENCRYPTED_SW_DESCRIPTION bool "Even sw-description is encrypted" depends on ENCRYPTED_IMAGES diff --git a/core/installer.c b/core/installer.c index 20b5b51..7349779 100644 --- a/core/installer.c +++ b/core/installer.c @@ -497,6 +497,18 @@ void cleanup_files(struct swupdate_cfg *software) { free(fn); } #endif + +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + if (asprintf(&fn, "%s%s", TMPDIR, ENCRYPTED_AES_FILENAME) != ENOMEM_ASPRINTF) { + remove_sw_file(fn); + free(fn); + } + if (asprintf(&fn, "%s%s", TMPDIR, AES_FILENAME) != ENOMEM_ASPRINTF) { + remove_sw_file(fn); + free(fn); + } +#endif + } int preupdatecmd(struct swupdate_cfg *swcfg) diff --git a/core/stream_interface.c b/core/stream_interface.c index 0b78329..25f9457 100644 --- a/core/stream_interface.c +++ b/core/stream_interface.c @@ -45,11 +45,13 @@ #include "state.h" #include "bootloader.h" #include "hw-compatibility.h" +#include "sslapi.h" #define BUFF_SIZE 4096 #define PERCENT_LB_INDEX 4 enum { + STREAM_WAIT_ENC_AESFILE, STREAM_WAIT_DESCRIPTION, STREAM_WAIT_SIGNATURE, STREAM_DATA, @@ -85,7 +87,7 @@ static int extract_file_to_tmp(int fd, const char *fname, unsigned long *poffs, return -1; } if (strcmp(fdh.filename, fname)) { - TRACE("description file name not the first of the list: %s instead of %s", + TRACE("file: %s instead of %s next element in the list", fdh.filename, fname); return -1; @@ -148,6 +150,11 @@ static int extract_files(int fd, struct swupdate_cfg *software) bool installed_directly = false; bool encrypted_sw_desc = false; +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + char aes_file[MAX_IMAGE_FNAME]; + status = STREAM_WAIT_ENC_AESFILE; +#endif + #ifdef CONFIG_ENCRYPTED_SW_DESCRIPTION encrypted_sw_desc = true; #endif @@ -164,6 +171,28 @@ static int extract_files(int fd, struct swupdate_cfg *software) for (;;) { switch (status) { /* Waiting for the first Header */ +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + case STREAM_WAIT_ENC_AESFILE: + if (extract_file_to_tmp(fd, ENCRYPTED_AES_FILENAME, &offset, false) < 0) { + return -1; + } + + snprintf(output_file, sizeof(output_file), "%s%s", TMPDIR, ENCRYPTED_AES_FILENAME); + snprintf(aes_file, sizeof(aes_file), "%s%s", TMPDIR, AES_FILENAME); + if (swupdate_decrypt_file(software->dgst, output_file, aes_file)) { + ERROR("Decrypting AES file"); + return -1; + } + + if (load_decryption_key(aes_file)) { + ERROR("Key file does not contain a valid AES key"); + return -1; + } + + status = STREAM_WAIT_DESCRIPTION; + break; +#endif + case STREAM_WAIT_DESCRIPTION: if (extract_file_to_tmp(fd, SW_DESCRIPTION_FILENAME, &offset, encrypted_sw_desc) < 0 ) return -1; @@ -422,6 +451,7 @@ static int save_stream(int fdin, struct swupdate_cfg *software) goto no_copy_output; } +#ifndef CONFIG_ASYM_ENCRYPTED_AESFILE /* * Make an estimation for sw-description and signature. * Signature cannot be very big - if it is, it is an attack. @@ -430,6 +460,14 @@ static int save_stream(int fdin, struct swupdate_cfg *software) */ tmpsize = SWUPDATE_ALIGN(fdh.size + fdh.namesize + sizeof(struct new_ascii_header) + bufsize - len, bufsize); +#else + /* + * tmpsize has enough space for the encrypted-aesfile, sw-description and + * sw-description.sig + */ + tmpsize = SWUPDATE_ALIGN(fdh.size + fdh.namesize + sizeof(struct new_ascii_header) + 2 * bufsize - len, + bufsize); +#endif ret = copy_write(&tmpfd, buf, len); /* copy the first buffer */ if (ret < 0) { ret = -EIO; @@ -447,6 +485,28 @@ static int save_stream(int fdin, struct swupdate_cfg *software) lseek(tmpfd, 0, SEEK_SET); offset = 0; +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + if (extract_file_to_tmp(tmpfd, ENCRYPTED_AES_FILENAME, &offset, false) < 0) { + ret = -EINVAL; + goto no_copy_output; + } + + snprintf(output_file, sizeof(output_file), "%s%s", TMPDIR, ENCRYPTED_AES_FILENAME); + char aes_file[MAX_IMAGE_FNAME]; + snprintf(aes_file, sizeof(aes_file), "%s%s", TMPDIR, AES_FILENAME); + if (swupdate_decrypt_file(software->dgst, output_file, aes_file)) { + ERROR("Decrypting AES Key"); + ret = -1; + goto no_copy_output; + } + + if (load_decryption_key(aes_file)) { + ERROR("Key file does not contain a valid AES key"); + ret = -1; + goto no_copy_output; + } +#endif + if (extract_file_to_tmp(tmpfd, SW_DESCRIPTION_FILENAME, &offset, encrypted_sw_desc) < 0) { ERROR("%s cannot be extracted", SW_DESCRIPTION_FILENAME); ret = -EINVAL; @@ -507,6 +567,10 @@ no_copy_output: cleanup_files(software); +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + clear_aes_key(); +#endif + return ret; } @@ -703,6 +767,10 @@ void *network_initializer(void *data) /* release temp files we may have created */ cleanup_files(software); +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + clear_aes_key(); +#endif + #ifndef CONFIG_NOCLEANUP swupdate_remove_directory(SCRIPTS_DIR_SUFFIX); swupdate_remove_directory(DATADST_DIR_SUFFIX); diff --git a/core/swupdate.c b/core/swupdate.c index 6f9938e..9532d0a 100644 --- a/core/swupdate.c +++ b/core/swupdate.c @@ -101,8 +101,11 @@ static struct option long_options[] = { {"forced-signer-name", required_argument, NULL, '2'}, #endif #endif -#ifdef CONFIG_ENCRYPTED_IMAGES +#if defined(CONFIG_ENCRYPTED_IMAGES) && !defined(CONFIG_ASYM_ENCRYPTED_AESFILE) {"key-aes", required_argument, NULL, 'K'}, +#endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + {"recip-keypair", required_argument, NULL, 'r'}, #endif {"loglevel", required_argument, NULL, 'l'}, {"max-version", required_argument, NULL, '3'}, @@ -162,9 +165,12 @@ static void usage(char *programname) " --ca-path : path to the Certificate Authority (PEM)\n" #endif #endif -#ifdef CONFIG_ENCRYPTED_IMAGES +#if defined(CONFIG_ENCRYPTED_IMAGES) && !defined(CONFIG_ASYM_ENCRYPTED_AESFILE) " -K, --key-aes : the file contains the symmetric key to be used\n" " to decrypt images\n" +#endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + " -r, --recip-keypair : path to the recipient keypair (PEM)\n" #endif " -n, --dry-run : run SWUpdate without installing the software\n" " -N, --no-downgrading : not install a release older as \n" @@ -310,8 +316,14 @@ static int read_globals_settings(void *elem, void *data) "public-key-file", sw->publickeyfname); GET_FIELD_STRING(LIBCFG_PARSER, elem, "ca-path", sw->publickeyfname); +#if defined(CONFIG_ENCRYPTED_IMAGES) && !defined(CONFIG_ASYM_ENCRYPTED_AESFILE) GET_FIELD_STRING(LIBCFG_PARSER, elem, "aes-key-file", sw->aeskeyfname); +#endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + GET_FIELD_STRING(LIBCFG_PARSER, elem, + "recip-keypair", sw->recipkeypairfname); +#endif GET_FIELD_STRING(LIBCFG_PARSER, elem, "mtd-blacklist", sw->mtdblacklist); GET_FIELD_STRING(LIBCFG_PARSER, elem, @@ -497,9 +509,12 @@ int main(int argc, char **argv) public_key_mandatory = 1; #endif #endif -#ifdef CONFIG_ENCRYPTED_IMAGES +#if defined(CONFIG_ENCRYPTED_IMAGES) && !defined(CONFIG_ASYM_ENCRYPTED_AESFILE) strcat(main_options, "K:"); #endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + strcat(main_options, "r:"); +#endif memset(fname, 0, sizeof(fname)); @@ -656,12 +671,19 @@ int main(int argc, char **argv) strlcpy(swcfg.maximum_version, optarg, sizeof(swcfg.maximum_version)); break; -#ifdef CONFIG_ENCRYPTED_IMAGES +#if defined(CONFIG_ENCRYPTED_IMAGES) && !defined(CONFIG_ASYM_ENCRYPTED_AESFILE) case 'K': strlcpy(swcfg.aeskeyfname, optarg, sizeof(swcfg.aeskeyfname)); break; +#endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + case 'r': + strlcpy(swcfg.recipkeypairfname, + optarg, + sizeof(swcfg.recipkeypairfname)); + break; #endif case 'N': swcfg.no_downgrading = true; @@ -842,6 +864,19 @@ int main(int argc, char **argv) mtd_set_ubiblacklist(swcfg.mtdblacklist); #endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + if (strlen(swcfg.recipkeypairfname)) { + if (swupdate_dgst_add_recipient_keypair(&swcfg, swcfg.recipkeypairfname)) { + fprintf(stderr, + "Error: Recipient keypair cannot be initialized.\n"); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, + "Error: SWUpdate is built for asym encrypted images, provide a recipient key pair.\n"); + exit(EXIT_FAILURE); + } +#else /* * If an AES key is passed, load it to allow * to decrypt images @@ -853,6 +888,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } } +#endif lua_handlers_init(); diff --git a/corelib/Makefile b/corelib/Makefile index c9ca4aa..36d32ec 100644 --- a/corelib/Makefile +++ b/corelib/Makefile @@ -18,6 +18,9 @@ endif lib-$(CONFIG_SIGALG_RAWRSA) += swupdate_rsa_verify.o lib-$(CONFIG_SIGALG_RSAPSS) += swupdate_rsa_verify.o endif +ifeq ($(CONFIG_ASYM_ENCRYPTED_AESFILE),y) +lib-$(CONFIG_ENCRYPTED_IMAGES) += swupdate_cms_decrypt.o +endif ifeq ($(CONFIG_SSL_IMPL_OPENSSL),y) lib-$(CONFIG_SIGALG_CMS) += swupdate_cms_verify.o endif diff --git a/corelib/swupdate_cms_decrypt.c b/corelib/swupdate_cms_decrypt.c new file mode 100644 index 0000000..5af2508 --- /dev/null +++ b/corelib/swupdate_cms_decrypt.c @@ -0,0 +1,112 @@ +/* + * (C) Copyright 2023 + * Michael Glembotzki, iris-GmbH infrared & intelligent sensors, michael.glembotzki@iris-sensing.com. + * + * SPDX-License-Identifier: GPL-2.0-only + * + * Code mostly taken from openssl examples + */ +#include +#include "swupdate.h" +#include "sslapi.h" +#include "util.h" + +int swupdate_dgst_add_recipient_keypair(struct swupdate_cfg *sw, const char *keypair_file) { + X509 *rcert = NULL; + EVP_PKEY *rkey = NULL; + struct swupdate_digest *dgst = sw->dgst; + int ret = 0; + + if (!dgst) { + dgst = calloc(1, sizeof(*dgst)); + if (!dgst) { + ret = 1; + goto err; + } + } + + BIO *tbio = BIO_new_file(keypair_file, "r"); + if (!tbio) { + ERROR("%s cannot be opened", keypair_file); + ret = 1; + goto err; + } + + rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + if (!rcert) { + WARN("Recipient cert not found"); + } + BIO_reset(tbio); + + rkey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + BIO_free(tbio); + if (!rkey) { + ERROR("Recipient private key not found"); + ret = 1; + goto err; + } + + dgst->rcert = rcert; + dgst->rkey = rkey; + + return ret; + +err: + if (dgst) { + free(dgst); + } + return ret; +} + +int swupdate_decrypt_file(struct swupdate_digest *dgst, const char *infile, const char *outfile) { + BIO *in = NULL, *out = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 0; + + if (!dgst || !infile || !outfile) { + return 1; + } + + /* Open CMS message to decrypt */ + in = BIO_new_file(infile, "rb"); + if (!in) { + ERROR("%s cannot be opened", infile); + ret = 1; + goto err; + } + + /* Parse message */ + cms = d2i_CMS_bio(in, NULL); + if (!cms) { + ERROR("%s cannot be parsed as DER-encoded CMS blob", infile); + ret = 1; + goto err; + } + + out = BIO_new_file(outfile, "wb"); + if (!out) { + ERROR("%s cannot be opened", outfile); + ret = 1; + goto err; + } + + if (chmod(outfile, S_IRUSR | S_IWUSR)) { + ERROR("Setting file permissions"); + ret = 1; + goto err; + } + + /* Decrypt CMS message */ + if (!CMS_decrypt(cms, dgst->rkey, dgst->rcert, NULL, out, 0)) { + ERR_print_errors_fp(stderr); + ERROR("Decrypting %s failed", infile); + ret = 1; + goto err; + } + +err: + BIO_free(in); + BIO_free(out); + CMS_ContentInfo_free(cms); + return ret; +} diff --git a/include/parsers.h b/include/parsers.h index 0e94c2b..53c3ee0 100644 --- a/include/parsers.h +++ b/include/parsers.h @@ -15,6 +15,15 @@ #define SW_DESCRIPTION_FILENAME CONFIG_SWDESCRIPTION #endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE +#define AES_FILENAME "aes-file" +#ifndef CONFIG_SET_ENCRYPTED_AES_FILENAME +#define ENCRYPTED_AES_FILENAME "encrypted-aesfile" +#else +#define ENCRYPTED_AES_FILENAME CONFIG_SET_ENCRYPTED_AES_FILENAME +#endif +#endif + typedef int (*parser_fn)(struct swupdate_cfg *swcfg, const char *filename); int parse(struct swupdate_cfg *swcfg, const char *filename); diff --git a/include/sslapi.h b/include/sslapi.h index 9f5b061..0ccb672 100644 --- a/include/sslapi.h +++ b/include/sslapi.h @@ -106,6 +106,10 @@ struct swupdate_digest { #else EVP_CIPHER_CTX *ctxdec; #endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE + EVP_PKEY *rkey; /* recipient private key */ + X509 *rcert; /* recipient private cert */ +#endif }; #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -215,6 +219,11 @@ UNUSED static inline struct swupdate_digest *swupdate_DECRYPT_init( #define swupdate_DECRYPT_cleanup(p) #endif +#ifdef CONFIG_ASYM_ENCRYPTED_AESFILE +int swupdate_dgst_add_recipient_keypair(struct swupdate_cfg *sw, const char *keypair_file); +int swupdate_decrypt_file(struct swupdate_digest *dgst, const char *infile, const char *outfile); +#endif + #ifndef SSL_PURPOSE_DEFAULT #define SSL_PURPOSE_EMAIL_PROT -1 #define SSL_PURPOSE_CODE_SIGN -1 diff --git a/include/swupdate.h b/include/swupdate.h index c1f86b3..cdfb971 100644 --- a/include/swupdate.h +++ b/include/swupdate.h @@ -57,6 +57,7 @@ struct swupdate_cfg { char output[SWUPDATE_GENERAL_STRING_SIZE]; char publickeyfname[SWUPDATE_GENERAL_STRING_SIZE]; char aeskeyfname[SWUPDATE_GENERAL_STRING_SIZE]; + char recipkeypairfname[SWUPDATE_GENERAL_STRING_SIZE]; char postupdatecmd[SWUPDATE_GENERAL_STRING_SIZE]; char preupdatecmd[SWUPDATE_GENERAL_STRING_SIZE]; char minimum_version[SWUPDATE_GENERAL_STRING_SIZE];