Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions tools/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Path to mbedtls
MBEDTLS_DIR ?=
ifeq ($(strip $(MBEDTLS_DIR)),)
INCLUDES =
LIBPATH =
else
INCLUDES = -I$(MBEDTLS_DIR)/include
LIBPATH = -L$(MBEDTLS_DIR)/build/library
endif

LIBS = -lmbedx509 -lmbedtls -lmbedcrypto

# Compiler
CC = gcc
CFLAGS = -Wall -Wextra $(INCLUDES)
LDFLAGS = $(LIBPATH) $(LIBS)

# Source and executable
SRC = verify.c
OUT = verify

all: $(OUT)

$(OUT): $(SRC)
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)

clean:
rm -f $(OUT)

78 changes: 78 additions & 0 deletions tools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# verify-tool
## PKCS#7 Signature Verification Utility
This utility verifies PKCS#7 (CMS) signatures against content files using the Mbed TLS library. It supports both detached signatures and appended signatures. The tool also allows verification of signatures with PEM or DER certificates, and supports multi-signer PKCS#7 files.

### Features
* Verifies appended PKCS#7 signatures extracted from binaries.
* Supports multiple appended signatures.
* Supports detached PKCS#7 signatures (DER format).
* Supports signatures with one or multiple signers in the same .p7s file.
* Verifies each signature using the provided PEM or DER formatted certificate(s).
* For multi-signature verification, multiple PEM certificates can be concatenated into a single file. The utility will use all certificates in the file to verify the signatures.

### Prerequisites
* Mbed TLS library (latest version recommended)
* GCC or compatible C compiler
* OpenSSL is required for generating PKCS#7 signatures using the provided examples.

### Build Instructions
1. Clone or download this repository.
2. Ensure Mbed TLS is built and installed.
3. If Mbed TLS is installed in a custom location, export the MBEDTLS_DIR variable with the path. For example
```bash
export MBEDTLS_DIR=/your/mbedtls/path
```
4. Run `make`.This will build the verify executable.

### Usage
Detached PKCS#7 signature verification:
```bash
./verify -d <pkcs7 signature> <binary> <certificate>
```
Appended PKCS#7 signature verification:
```bash
./verify -a <binary_with_appended_sig> <certificate>
```
* `<pkcs7 signature>`: PKCS#7 signature file (DER format, e.g., .p7s)
* `<binary>`: Original binary file that was signed (e.g., an ELF file)
* `<certificate>`: Signer certificate (can be a single PEM or DER file, or multiple PEM certificates concatenated into one file for multi-signer signatures)
* `<binary_with_appended_sig>`: Binary file with PKCS#7 signature appended

### Detached Signature Examples
**Signing with a single private key and its corresponding certificate:**
```bash
openssl cms -sign -binary -nocerts -in core.elf -signer certificate.pem -inkey imprint.key -out core.p7s -outform DER -noattr -md sha256
```
**Single signature verification:**
```bash
./verify -d core.p7s core.elf certificate.pem
```
**Signing with multiple private keys and their corresponding certificates:**
```bash
openssl cms -sign -binary -nocerts -in core.elf -signer certificate.pem -inkey imprint.key -signer certificate2.pem -inkey imprint2.key -out core.p7s -outform DER -noattr -md sha256
```
**Multiple signatures verification (one at a time):**
```bash
./verify -d core.p7s core.elf certificate.pem
./verify -d core.p7s core.elf certificate2.pem
```
**Multiple signatures verification (concatenated PEM):**
```bash
cat certificate.pem certificate2.pem > multicert.pem
./verify -d core.p7s core.elf multicert.pem
```
### Appended Signature Examples
**Signing and Appending with sign-file:**
First, generate a detached signature (see above). Then, append it:
```bash
sign-file -s core.p7s sha256 /dev/null core.elf core.elf.signed
```
**Verification**
```bash
./verify -a core.elf.signed certificate.pem
```
### Notes
* This utility does not perform full certificate chain validation.



258 changes: 258 additions & 0 deletions tools/verify.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
#include "mbedtls/pkcs7.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/platform.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <arpa/inet.h>

#define SIG_MAGIC "~Module signature appended~\n"
#define SIG_MAGIC_SIZE (sizeof(SIG_MAGIC)-1)
#define PKEY_ID_PKCS7 2

struct module_signature {
uint8_t algo; /* Public-key crypto algorithm [0] */
uint8_t hash; /* Digest algorithm [0] */
uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
uint8_t signer_len; /* Length of signer's name [0] */
uint8_t key_id_len; /* Length of key identifier [0] */
uint8_t __pad[3];
uint32_t sig_len; /* Length of signature data */
} __attribute__((packed));

#define SIG_METADATA_SIZE (sizeof(struct module_signature))

/**
* Reads a file into a memory buffer
* @param filename, Path to file
* @param buf, Pointer to buffer holding file contents
* @param len, Pointer to variable receiving the buffer size
* @return 0 on success, nonzero on failure or error
*/
int read_file(const char *filename, unsigned char **buf, size_t *len)
{
FILE *f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Failed to open %s\n", filename);
return 1;
}
fseek(f, 0, SEEK_END);
*len = ftell(f);
if (*len == 0) {
fprintf(stderr, "Empty file %s\n", filename);
fclose(f);
return 1;
}
fseek(f, 0, SEEK_SET);
*buf = malloc(*len);
if (!*buf) {
fprintf(stderr, "Memory allocation failed\n");
fclose(f);
return 1;
}
size_t bytes_read = fread(*buf, 1, *len, f);
fclose(f);
if (bytes_read != *len) {
fprintf(stderr, "File read incomplete: expected %zu, got %zu\n", *len, bytes_read);
free(*buf);
*buf = NULL;
return 1;
}
return 0;
}

/**
* Extracts an appended PKCS#7 signature and metadata from a signed binary file buffer.
* @param buf, Pointer to full binary file data
* @param bufsize, Size of the binary buffer
* @param content_size, Size of signed content
* @param pkcs7_sig, pointer to buffer containing extracted PKCS#7 signature
* @param pkcs7_siglen, pointer to variable receiving signature size
* @return 0 on success; negative value on error
*/
int extract_appended_signature(const unsigned char *buf, size_t bufsize,
size_t *content_size, unsigned char **pkcs7_sig, size_t *pkcs7_siglen)
{
if (bufsize < SIG_MAGIC_SIZE) {
perror("file too short for signature magic");
return -1;
}
const unsigned char *magic_offset = buf + bufsize - SIG_MAGIC_SIZE;
if (memcmp(magic_offset, SIG_MAGIC, SIG_MAGIC_SIZE) != 0) {
perror("missing or invalid signature magic");
return -2;
}
if (bufsize < SIG_MAGIC_SIZE + SIG_METADATA_SIZE) {
perror("file too short for signature metadata");
return -3;
}
const unsigned char *meta_offset = magic_offset - SIG_METADATA_SIZE;
struct module_signature meta;
memcpy(&meta, meta_offset, SIG_METADATA_SIZE);
if (meta.id_type != PKEY_ID_PKCS7) {
perror("wrong signature type");
return -4;
}
uint32_t siglen = ntohl(meta.sig_len);
if (bufsize < SIG_MAGIC_SIZE + SIG_METADATA_SIZE + siglen) {
perror("file too short for PKCS#7 message");
return -5;
}
const unsigned char *pkcs7_offset = meta_offset - siglen;
*pkcs7_sig = malloc(siglen);
if (!*pkcs7_sig) {
perror("memory allocation failed");
return -6;
}
memcpy(*pkcs7_sig, pkcs7_offset, siglen);
*pkcs7_siglen = siglen;
*content_size = pkcs7_offset - buf;
return 0;
}

/**Display usage information**/
void usage()
{
printf("Usage:\n");
printf(" Detached signature: ./%s -d <pkcs7_signature> <binary> <certificate>\n", "verify");
printf(" Appended signature: ./%s -a <binary_with_appended_sig> <certificate>\n", "verify");
}

/**
* Verifies a detached PKCS#7 signature
* @param pkcs7_path, Path to PKCS#7 signature file
* @param bin_path, Path to original binary file
* @param cert_path, Path to PEM or DER certificate
* @return 0 if signature is valid; 1 on failure or errros
*/
int verify_detached(const char *pkcs7_path, const char *bin_path, const char *cert_path)
{
unsigned char *pkcs7_buf = NULL;
unsigned char *content_buf = NULL;
size_t pkcs7_len = 0;
size_t content_len = 0;
int ret = 1;
mbedtls_pkcs7 pkcs7;
mbedtls_x509_crt cert;

if (read_file(pkcs7_path, &pkcs7_buf, &pkcs7_len))
return 1;
if (read_file(bin_path, &content_buf, &content_len)) {
free(pkcs7_buf);
return 1;
}

mbedtls_pkcs7_init(&pkcs7);
mbedtls_x509_crt_init(&cert);
ret = mbedtls_pkcs7_parse_der(&pkcs7, pkcs7_buf, pkcs7_len);
if (ret < 0) {
fprintf(stderr, "Failed to parse PKCS7 structure: -0x%04x\n", -ret);
goto cleanup;
}

ret = psa_crypto_init();
if (ret != 0) {
fprintf(stderr, "psa_crypto_init() failed: -0x%04x\n", -ret);
goto cleanup;
}
ret = mbedtls_x509_crt_parse_file(&cert, cert_path);
if (ret != 0) {
fprintf(stderr, "Failed to load certificate: -0x%04x\n", -ret);
goto cleanup;
}
ret = mbedtls_pkcs7_signed_data_verify(&pkcs7, &cert, content_buf, content_len);
if (ret == 0)
printf("CMS PKCS7 signature verification successful\n");
else
printf("CMS PKCS7 signature verification failed: -0x%04x\n", -ret);

cleanup:
free(pkcs7_buf);
free(content_buf);
mbedtls_pkcs7_free(&pkcs7);
mbedtls_x509_crt_free(&cert);
return ret < 0 ? 1 : 0;
}

/**
* Verifies an appended PKCS#7 signature
* @param bin_path, Path to binary file with appended signature
* @param cert_path, Path to PEM or DER certificate
* @return 0 if signature is valid; 1 on failure or errors
*/
int verify_appended(const char *bin_path, const char *cert_path)
{
unsigned char *buf = NULL;
unsigned char *pkcs7_sig = NULL;
size_t bufsize = 0;
size_t content_len = 0;
size_t pkcs7_siglen = 0;
int ret = 1;
mbedtls_pkcs7 pkcs7;
mbedtls_x509_crt cert;
if (read_file(bin_path, &buf, &bufsize))
return 1;
ret = extract_appended_signature(buf, bufsize, &content_len, &pkcs7_sig, &pkcs7_siglen);
if (ret != 0) {
fprintf(stderr, "Failed to extract appended signature (error %d)\n", ret);
free(buf);
return 1;
}
mbedtls_pkcs7_init(&pkcs7);
mbedtls_x509_crt_init(&cert);
ret = mbedtls_pkcs7_parse_der(&pkcs7, pkcs7_sig, pkcs7_siglen);
if (ret < 0) {
fprintf(stderr, "Failed to parse PKCS7 structure: -0x%04x\n", -ret);
goto cleanup;
}
ret = psa_crypto_init();
if (ret != 0) {
fprintf(stderr, "psa_crypto_init() failed: -0x%04x\n", -ret);
goto cleanup;
}
ret = mbedtls_x509_crt_parse_file(&cert, cert_path);
if (ret != 0) {
fprintf(stderr, "Failed to load certificate: -0x%04x\n", -ret);
goto cleanup;
}
ret = mbedtls_pkcs7_signed_data_verify(&pkcs7, &cert, buf, content_len);
if (ret == 0)
printf("Appended PKCS7 signature verification successful\n");
else
printf("Appended PKCS7 signature verification failed: -0x%04x\n", -ret);

cleanup:
free(buf);
free(pkcs7_sig);
mbedtls_pkcs7_free(&pkcs7);
mbedtls_x509_crt_free(&cert);
return ret < 0 ? 1 : 0;
}

int main(int argc, char *argv[])
{
if (argc < 2) {
usage();
return 1;
}
if (strncmp(argv[1], "-d", 2) == 0 && argv[1][2] == '\0') {
if (argc != 5) {
usage();
return 1;
}
return verify_detached(argv[2], argv[3], argv[4]);
}
else if (strncmp(argv[1], "-a", 2) == 0 && argv[1][2] == '\0') {
if (argc != 4) {
usage();
return 1;
}
return verify_appended(argv[2], argv[3]);
}
else {
usage();
return 1;
}
}

Loading