Loading...
// SPDX-License-Identifier: GPL-2.0+
/*
 * Check a file including a preload header including a signature
 *
 * Copyright (c) 2025 Paul HENRYS <paul.henrys_ext@softathome.com>
 *
 * Binman makes it possible to generate a preload header signing part or the
 * complete file. The tool preload_check_sign allows to verify and authenticate
 * a file starting with a preload header.
 */
#include <stdio.h>
#include <unistd.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <image.h>

extern void image_pre_load_sig_set_info(struct image_sig_info *info);
extern int image_pre_load_sig(ulong addr);

static void usage(char *cmdname)
{
	fprintf(stderr, "Usage: %s -f file -k PEM key file\n"
			"          -f ==> set file which should be checked\n"
			"          -k ==> PEM key file\n"
			"          -a ==> algo (default: sha256,rsa2048)\n"
			"          -p ==> padding (default: pkcs-1.5)\n"
			"          -h ==> help\n",
		cmdname);
	exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
	int ret = 0;
	char cmdname[256];
	char *file = NULL;
	char *keyfile = NULL;
	int c;
	FILE *fp = NULL;
	FILE *fp_key = NULL;
	size_t bytes;
	long filesize;
	void *buffer = NULL;
	EVP_PKEY *pkey = NULL;
	char *algo = "sha256,rsa2048";
	char *padding = "pkcs-1.5";
	struct image_sig_info info = {0};

	strncpy(cmdname, *argv, sizeof(cmdname) - 1);
	cmdname[sizeof(cmdname) - 1] = '\0';
	while ((c = getopt(argc, argv, "f:k:a:p:h")) != -1)
		switch (c) {
		case 'f':
			file = optarg;
			break;
		case 'k':
			keyfile = optarg;
			break;
		case 'a':
			algo = optarg;
			break;
		case 'p':
			padding = optarg;
			break;
		default:
			usage(cmdname);
			break;
	}

	if (!file) {
		fprintf(stderr, "%s: Missing file\n", *argv);
		usage(*argv);
	}

	if (!keyfile) {
		fprintf(stderr, "%s: Missing key file\n", *argv);
		usage(*argv);
	}

	fp = fopen(file, "r");
	if (!fp) {
		fprintf(stderr, "Error opening file: %s\n", file);
		ret = EXIT_FAILURE;
		goto out;
	}

	fseek(fp, 0, SEEK_END);
	filesize = ftell(fp);
	rewind(fp);

	buffer = malloc(filesize);
	if (!buffer) {
		fprintf(stderr, "Memory allocation failed");
		ret = EXIT_FAILURE;
		goto out;
	}

	bytes = fread(buffer, 1, filesize, fp);
	if (bytes != filesize) {
		fprintf(stderr, "Error reading file\n");
		ret = EXIT_FAILURE;
		goto out;
	}

	fp_key = fopen(keyfile, "r");
	if (!fp_key) {
		fprintf(stderr, "Error opening file: %s\n", keyfile);
		ret = EXIT_FAILURE;
		goto out;
	}

	/* Attempt to read the private key */
	pkey = PEM_read_PrivateKey(fp_key, NULL, NULL, NULL);
	if (!pkey) {
		/* If private key reading fails, try reading as a public key */
		fseek(fp_key, 0, SEEK_SET);
		pkey = PEM_read_PUBKEY(fp_key, NULL, NULL, NULL);
	}
	if (!pkey) {
		fprintf(stderr, "Unable to retrieve the public key: %s\n",
			ERR_error_string(ERR_get_error(), NULL));
		ret = EXIT_FAILURE;
		goto out;
	}

	info.algo_name = algo;
	info.padding_name = padding;
	info.key = (uint8_t *)pkey;
	info.mandatory = 1;
	info.sig_size = EVP_PKEY_size(pkey);
	if (info.sig_size < 0) {
		fprintf(stderr, "Fail to retrieve the signature size: %s\n",
			ERR_error_string(ERR_get_error(), NULL));
		ret = EXIT_FAILURE;
		goto out;
	}

	/* Compute signature information */
	info.sig_info.name     = info.algo_name;
	info.sig_info.padding  = image_get_padding_algo(info.padding_name);
	info.sig_info.checksum = image_get_checksum_algo(info.sig_info.name);
	info.sig_info.crypto   = image_get_crypto_algo(info.sig_info.name);
	info.sig_info.key      = info.key;
	info.sig_info.keylen   = info.key_len;

	/* Check the signature */
	image_pre_load_sig_set_info(&info);
	ret = image_pre_load_sig((ulong)buffer);
out:
	if (fp)
		fclose(fp);
	if (fp_key)
		fclose(fp_key);
	if (info.key)
		EVP_PKEY_free(pkey);
	free(buffer);

	exit(ret);
}