/*############################################################################
# Copyright 2016-2017 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
############################################################################*/
/*!
* \file
* \brief Verifysig example implementation.
*/
#include <argtable3.h>
#include <stdlib.h>
#include <string.h>
#include "epid/common/file_parser.h"
#include "epid/verifier/1.1/api.h"
#include "epid/verifier/api.h"
#include "src/verifysig.h"
#include "src/verifysig11.h"
#include "util/buffutil.h"
#include "util/convutil.h"
#include "util/envutil.h"
// Defaults
#define PROGRAM_NAME "verifysig"
#define PUBKEYFILE_DEFAULT "pubkey.bin"
#define PRIVRL_DEFAULT NULL
#define SIGRL_DEFAULT NULL
#define GRPRL_DEFAULT "grprl.bin"
#define VERIFIERRL_DEFAULT NULL
#define SIG_DEFAULT "sig.dat"
#define CACERT_DEFAULT "cacert.bin"
#define HASHALG_DEFAULT "SHA-512"
#define VPRECMPI_DEFAULT NULL
#define VPRECMPO_DEFAULT NULL
#define ARGPARSE_ERROR_MAX 20
#define ARGTABLE_SIZE 17
bool IsCaCertAuthorizedByRootCa(void const* data, size_t size) {
// Implementation of this function is out of scope of the sample.
// In an actual implementation Issuing CA certificate must be validated
// with CA Root certificate before using it in parse functions.
(void)data;
(void)size;
return true;
}
/// Main entrypoint
int main(int argc, char* argv[]) {
// intermediate return value for C style functions
int ret_value = EXIT_SUCCESS;
// intermediate return value for Intel(R) EPID functions
EpidStatus result = kEpidErr;
// User Settings
// Message string parameter
static char* msg_str = NULL;
size_t msg_size = 0;
char* msg_buf = NULL; // message loaded from msg_file
// Basename string parameter
static char* basename_str = NULL;
size_t basename_size = 0;
char* basename_buf = NULL; // basename loaded from basename_file
// Verbose flag parameter
static bool verbose_flag = false;
// Buffers and computed values
// Signature buffer
void* sig = NULL;
size_t sig_size = 0;
// PrivRl buffer
void* signed_priv_rl = NULL;
size_t signed_priv_rl_size = 0;
// SigRl buffer
void* signed_sig_rl = NULL;
size_t signed_sig_rl_size = 0;
// GrpRl buffer
void* signed_grp_rl = NULL;
size_t signed_grp_rl_size = 0;
// VerRl buffer
VerifierRl* ver_rl = NULL;
size_t ver_rl_size = 0;
// Group public key buffer
void* signed_pubkey = NULL;
size_t signed_pubkey_size = 0;
// Verifier pre-computed settings
void* verifier_precmp = NULL;
size_t vprecmpi_file_size = 0;
// CA certificate
EpidCaCertificate cacert = {0};
// Hash algorithm
static HashAlg hashalg = kInvalidHashAlg;
// Argument variables
struct arg_file* sig_file =
arg_file0(NULL, "sig", "FILE",
"load signature from FILE (default: " SIG_DEFAULT ")");
struct arg_str* msg = arg_str0(NULL, "msg", "MESSAGE",
"MESSAGE that was signed (default: empty)");
struct arg_file* msg_file = arg_file0(
NULL, "msgfile", "FILE", "FILE containing message that was signed");
struct arg_str* basename = arg_str0(
NULL, "bsn", "BASENAME", "BASENAME used in signature (default: random)");
struct arg_file* basename_file = arg_file0(
NULL, "bsnfile", "FILE", "FILE containing basename used in signature");
struct arg_file* privrl_file = arg_file0(
NULL, "privrl", "FILE", "load private key revocation list from FILE");
struct arg_file* sigrl_file = arg_file0(
NULL, "sigrl", "FILE", "load signature based revocation list from FILE");
struct arg_file* grprl_file = arg_file0(
NULL, "grprl", "FILE",
"load group revocation list from FILE (default: " GRPRL_DEFAULT ")");
struct arg_file* verrl_file = arg_file0(
NULL, "verifierrl", "FILE", "load verifier revocation list from FILE");
struct arg_file* pubkey_file = arg_file0(
NULL, "gpubkey", "FILE",
"load group public key from FILE (default: " PUBKEYFILE_DEFAULT ")");
struct arg_file* vprecmpi_file = arg_file0(
NULL, "vprecmpi", "FILE", "load pre-computed verifier data from FILE");
struct arg_file* vprecmpo_file = arg_file0(
NULL, "vprecmpo", "FILE", "write pre-computed verifier data to FILE");
struct arg_file* cacert_file = arg_file0(
NULL, "capubkey", "FILE",
"load IoT Issuing CA public key from FILE (default: " CACERT_DEFAULT ")");
struct arg_str* hashalg_str = arg_str0(
NULL, "hashalg", "{SHA-256 | SHA-384 | SHA-512 | SHA-512/256}",
"use specified hash algorithm for 2.0 groups (default: " HASHALG_DEFAULT
")");
struct arg_lit* help = arg_lit0(NULL, "help", "display this help and exit");
struct arg_lit* verbose =
arg_lit0("v", "verbose", "print status messages to stdout");
struct arg_end* end = arg_end(ARGPARSE_ERROR_MAX);
void* argtable[ARGTABLE_SIZE];
int nerrors;
/* initialize the argtable array with ptrs to the arg_xxx structures
* constructed above */
argtable[0] = sig_file;
argtable[1] = msg;
argtable[2] = msg_file;
argtable[3] = basename;
argtable[4] = basename_file;
argtable[5] = privrl_file;
argtable[6] = sigrl_file;
argtable[7] = grprl_file;
argtable[8] = verrl_file;
argtable[9] = pubkey_file;
argtable[10] = vprecmpi_file;
argtable[11] = vprecmpo_file;
argtable[12] = cacert_file;
argtable[13] = hashalg_str;
argtable[14] = help;
argtable[15] = verbose;
argtable[16] = end;
// set program name for logging
set_prog_name(PROGRAM_NAME);
do {
EpidVersion epid_version = kNumEpidVersions;
// Read command line args
/* verify the argtable[] entries were allocated sucessfully */
if (arg_nullcheck(argtable) != 0) {
/* NULL entries were detected, some allocations must have failed */
printf("%s: insufficient memory\n", PROGRAM_NAME);
ret_value = EXIT_FAILURE;
break;
}
/* set any command line default values prior to parsing */
sig_file->filename[0] = SIG_DEFAULT;
grprl_file->filename[0] = GRPRL_DEFAULT;
pubkey_file->filename[0] = PUBKEYFILE_DEFAULT;
cacert_file->filename[0] = CACERT_DEFAULT;
hashalg_str->sval[0] = HASHALG_DEFAULT;
/* Parse the command line as defined by argtable[] */
nerrors = arg_parse(argc, argv, argtable);
if (help->count > 0) {
log_fmt(
"Usage: %s [OPTION]...\n"
"Verify signature was created by group member in good standing\n"
"\n"
"Options:\n",
PROGRAM_NAME);
arg_print_glossary(stdout, argtable, " %-25s %s\n");
ret_value = EXIT_SUCCESS;
break;
}
if (verbose->count > 0) {
verbose_flag = ToggleVerbosity();
}
/* If the parser returned any errors then display them and exit */
if (nerrors > 0) {
/* Display the error details contained in the arg_end struct.*/
arg_print_errors(stderr, end, PROGRAM_NAME);
fprintf(stderr, "Try '%s --help' for more information.\n", PROGRAM_NAME);
ret_value = EXIT_FAILURE;
break;
}
if (msg->count > 0 && msg_file->count > 0) {
log_error("options --msg and --msgfile cannot be used together");
ret_value = EXIT_FAILURE;
break;
} else if (msg->count > 0) {
msg_str = (char*)msg->sval[0];
msg_size = strlen(msg_str);
} else if (msg_file->count > 0) {
msg_buf = NewBufferFromFile(msg_file->filename[0], &msg_size);
if (!msg_buf) {
ret_value = EXIT_FAILURE;
break;
}
msg_str = msg_buf;
} else {
msg_size = 0;
}
if (basename->count > 0 && basename_file->count > 0) {
log_error("options --bsn and --bsnfile cannot be used together");
ret_value = EXIT_FAILURE;
break;
} else if (basename->count > 0) {
basename_str = (char*)basename->sval[0];
basename_size = strlen(basename_str);
} else if (basename_file->count > 0) {
basename_buf =
NewBufferFromFile(basename_file->filename[0], &basename_size);
if (!basename_buf) {
log_error("Failed in reading basename from %s", basename_file);
ret_value = EXIT_FAILURE;
break;
}
basename_str = basename_buf;
} else {
basename_size = 0;
}
if (!StringToHashAlg(hashalg_str->sval[0], &hashalg)) {
log_error("invalid hashalg: %s", hashalg_str->sval[0]);
ret_value = EXIT_FAILURE;
break;
}
if (verbose_flag) {
log_msg("\nOption values:");
log_msg(" sig_file : %s", sig_file->filename[0]);
log_msg(" msg_str : %s", msg_str);
log_msg(" basename_str : %s", basename_str);
log_msg(" privrl_file : %s", privrl_file->filename[0]);
log_msg(" sigrl_file : %s", sigrl_file->filename[0]);
log_msg(" grprl_file : %s", grprl_file->filename[0]);
log_msg(" verrl_file : %s", verrl_file->filename[0]);
log_msg(" vprecmpi_file : %s", vprecmpi_file->filename[0]);
log_msg(" vprecmpo_file : %s", vprecmpo_file->filename[0]);
log_msg(" hashalg : %s", HashAlgToString(hashalg));
log_msg(" cacert_file : %s", cacert_file->filename[0]);
log_msg("");
}
// convert command line args to usable formats
// Signature
sig = NewBufferFromFile(sig_file->filename[0], &sig_size);
if (!sig) {
ret_value = EXIT_FAILURE;
break;
}
// PrivRl
if (privrl_file->count > 0) {
signed_priv_rl =
NewBufferFromFile(privrl_file->filename[0], &signed_priv_rl_size);
if (!signed_priv_rl) {
ret_value = EXIT_FAILURE;
break;
}
}
// SigRl
if (sigrl_file->count > 0) {
signed_sig_rl =
NewBufferFromFile(sigrl_file->filename[0], &signed_sig_rl_size);
if (!signed_sig_rl) {
ret_value = EXIT_FAILURE;
break;
}
}
// GrpRl
signed_grp_rl =
NewBufferFromFile(grprl_file->filename[0], &signed_grp_rl_size);
if (!signed_grp_rl) {
ret_value = EXIT_FAILURE;
break;
}
// VerRl
if (verrl_file->count > 0) {
ver_rl =
(VerifierRl*)NewBufferFromFile(verrl_file->filename[0], &ver_rl_size);
if (!ver_rl) {
ret_value = EXIT_FAILURE;
break;
}
}
// Group public key
signed_pubkey =
NewBufferFromFile(pubkey_file->filename[0], &signed_pubkey_size);
if (!signed_pubkey) {
ret_value = EXIT_FAILURE;
break;
}
// CA certificate
if (0 != ReadLoud(cacert_file->filename[0], &cacert, sizeof(cacert))) {
ret_value = EXIT_FAILURE;
break;
}
// Security note:
// Application must confirm that IoT Issuing CA
// certificate is authorized by IoT Root CA,
// e.g., signed by IoT Root CA.
if (!IsCaCertAuthorizedByRootCa(&cacert, sizeof(cacert))) {
log_error("CA certificate is not authorized");
ret_value = EXIT_FAILURE;
break;
}
// Detect Intel(R) EPID version
result = EpidParseFileHeader(signed_pubkey, signed_pubkey_size,
&epid_version, NULL);
if (kEpidNoErr != result || kNumEpidVersions <= epid_version) {
log_error("EPID version can not be detected");
ret_value = EXIT_FAILURE;
break;
}
// Configure hashalg based on group
if (kEpid1x == epid_version) {
if (kSha256 != hashalg && hashalg_str->count > 0) {
log_error(
"unsupported hash algorithm: %s only supported for 2.0 groups",
HashAlgToString(hashalg));
ret_value = EXIT_FAILURE;
break;
}
}
// Load Verifier pre-computed settings
if (vprecmpi_file->count > 0) {
vprecmpi_file_size = GetFileSize_S(vprecmpi_file->filename[0], SIZE_MAX);
verifier_precmp = AllocBuffer(vprecmpi_file_size);
if (0 != ReadLoud(vprecmpi_file->filename[0], verifier_precmp,
vprecmpi_file_size)) {
ret_value = EXIT_FAILURE;
break;
}
}
// Report Settings
if (verbose_flag) {
log_msg("==============================================");
log_msg("Verifying Message:");
log_msg("");
log_msg(" [in] Intel(R) EPID version: %s",
EpidVersionToString(epid_version));
log_msg("");
log_msg(" [in] Signature Len: %d", (int)sig_size);
log_msg(" [in] Signature: ");
PrintBuffer(sig, sig_size);
log_msg("");
log_msg(" [in] Message Len: %d", (int)msg_size);
log_msg(" [in] Message: ");
PrintBuffer(msg_str, msg_size);
log_msg("");
log_msg(" [in] BaseName Len: %d", (int)basename_size);
log_msg(" [in] BaseName: ");
PrintBuffer(basename_str, basename_size);
log_msg("");
log_msg(" [in] PrivRl Len: %d", (int)signed_priv_rl_size);
log_msg(" [in] PrivRl: ");
PrintBuffer(signed_priv_rl, signed_priv_rl_size);
log_msg("");
log_msg(" [in] SigRl Len: %d", (int)signed_sig_rl_size);
log_msg(" [in] SigRl: ");
PrintBuffer(signed_sig_rl, signed_sig_rl_size);
log_msg("");
log_msg(" [in] GrpRl Len: %d", (int)signed_grp_rl_size);
log_msg(" [in] GrpRl: ");
PrintBuffer(signed_grp_rl, signed_grp_rl_size);
log_msg("");
log_msg(" [in] VerRl Len: %d", (int)ver_rl_size);
log_msg(" [in] VerRl: ");
PrintBuffer(ver_rl, ver_rl_size);
log_msg("");
log_msg(" [in] Group Public Key: ");
PrintBuffer(signed_pubkey, sizeof(signed_pubkey_size));
log_msg("");
log_msg(" [in] Hash Algorithm: %s", HashAlgToString(hashalg));
if (vprecmpi_file->count > 0) {
log_msg("");
log_msg(" [in] Verifier PreComp: ");
PrintBuffer(verifier_precmp, vprecmpi_file_size);
}
log_msg("==============================================");
}
// Verify
if (kEpid2x == epid_version) {
if (verifier_precmp && vprecmpi_file_size != sizeof(VerifierPrecomp)) {
if (vprecmpi_file_size == sizeof(VerifierPrecomp) - sizeof(GroupId)) {
log_error(
"incorrect input precomp size: precomp format may have changed, "
"try regenerating it");
} else {
log_error("incorrect input precomp size");
}
ret_value = EXIT_FAILURE;
break;
}
result =
Verify(sig, sig_size, msg_str, msg_size, basename_str, basename_size,
signed_priv_rl, signed_priv_rl_size, signed_sig_rl,
signed_sig_rl_size, signed_grp_rl, signed_grp_rl_size, ver_rl,
ver_rl_size, signed_pubkey, signed_pubkey_size, &cacert,
hashalg, &verifier_precmp, &vprecmpi_file_size);
} else if (kEpid1x == epid_version) {
if (verifier_precmp &&
vprecmpi_file_size != sizeof(Epid11VerifierPrecomp)) {
log_error("incorrect input precomp size");
ret_value = EXIT_FAILURE;
break;
}
result = Verify11(sig, sig_size, msg_str, msg_size, basename_str,
basename_size, signed_priv_rl, signed_priv_rl_size,
signed_sig_rl, signed_sig_rl_size, signed_grp_rl,
signed_grp_rl_size, signed_pubkey, signed_pubkey_size,
&cacert, &verifier_precmp, &vprecmpi_file_size);
} else {
log_error("EPID version %s is not supported",
EpidVersionToString(epid_version));
ret_value = EXIT_FAILURE;
break;
}
// Report Result
if (kEpidNoErr == result) {
log_msg("signature verified successfully");
} else if (kEpidErr == result) {
log_error(
"signature verification failed: "
"member did not prove it was not revoked");
ret_value = result;
break;
} else {
log_error("signature verification failed: %s",
EpidStatusToString(result));
ret_value = result;
break;
}
// Store Verifier pre-computed settings
if (vprecmpo_file->count > 0) {
if (0 != WriteLoud(verifier_precmp, vprecmpi_file_size,
vprecmpo_file->filename[0])) {
ret_value = EXIT_FAILURE;
break;
}
}
// Success
ret_value = EXIT_SUCCESS;
} while (0);
// Free allocated buffers
if (sig) free(sig);
if (msg_buf) free(msg_buf);
if (basename_buf) free(basename_buf);
if (signed_priv_rl) free(signed_priv_rl);
if (signed_sig_rl) free(signed_sig_rl);
if (signed_grp_rl) free(signed_grp_rl);
if (ver_rl) free(ver_rl);
if (signed_pubkey) free(signed_pubkey);
if (verifier_precmp) free(verifier_precmp);
arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0]));
return ret_value;
}