summaryrefslogtreecommitdiffstats
path: root/src/signframework/getpubkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/signframework/getpubkey.c')
-rw-r--r--src/signframework/getpubkey.c651
1 files changed, 651 insertions, 0 deletions
diff --git a/src/signframework/getpubkey.c b/src/signframework/getpubkey.c
new file mode 100644
index 0000000..5925200
--- /dev/null
+++ b/src/signframework/getpubkey.c
@@ -0,0 +1,651 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "openssl/evp.h"
+#include "openssl/pem.h"
+
+/* local */
+#include "framework_utils.h"
+#include "cca_structures.h"
+#include "ossl_functions.h"
+#include "utils.h"
+#include "debug.h"
+
+/* global variables */
+
+FILE *messageFile = NULL; /* needed for utilities */
+int verbose = FALSE;
+int debug = FALSE;
+
+#define FORMAT_BINARY 0
+#define FORMAT_LE32 1
+#define FORMAT_PEM 2
+
+/* local prototypes */
+
+int pubkeyBinToC(unsigned long *pubkeyc_length,
+ char **pubkeyc,
+ unsigned long pubkey_length,
+ unsigned char *pubkey);
+
+int GetArgs(const char **outputBodyFilename,
+ const char **outputAttachmentFileName,
+ const char **projectLogFileName,
+ const char **sender,
+ const char **project,
+ const char **signproject,
+ unsigned int *format,
+ int *verbose,
+ int argc,
+ char **argv);
+
+void PrintUsage(void);
+
+/* see printUsage() for the program description */
+
+int main(int argc, char** argv)
+{
+ int rc = 0; /* general return code */
+
+ /* command line arguments */
+ const char *outputBodyFilename = NULL;
+ const char *outputAttachmentFileName = NULL;
+ const char *projectLogFileName = NULL; /* project audit log */
+ FILE *projectLogFile = NULL; /* closed @5 */
+ const char *sender = NULL;
+ const char *project = NULL;
+ const char *signproject = NULL;
+ unsigned int format = FORMAT_BINARY; /* return format */
+
+ unsigned char *keyToken = NULL; /* CCA key token, freed @6 */
+ size_t keyTokenLength;
+ RsaKeyTokenPublic rsaKeyTokenPublic; /* CCA public key structure */
+ char *pubkeyc = NULL; /* public key as EFI C code, freed @7 */
+ unsigned long pubkeyc_length;
+
+ const char *frameworkConfigFileName = NULL;
+ FILE *frameworkConfigFile = NULL; /* freed @1 */
+ char *projectConfigFilename = NULL; /* freed @2 */
+ FILE *projectConfigFile = NULL; /* closed @3 */
+ ProjectConfig projectConfig;
+ char *keyFileName = NULL; /* signing CCA key token file, freed @4 */
+
+ char lineBuffer[MAX_LINE_SIZE];
+
+ messageFile = stdout;
+ ProjectConfig_Init(&projectConfig); /* freed @9 */
+
+ /* command line argument defaults */
+ verbose = FALSE;
+ debug = FALSE;
+
+ /* get command line arguments */
+ if (rc == 0) {
+ rc = GetArgs(&outputBodyFilename,
+ &outputAttachmentFileName,
+ &projectLogFileName,
+ &sender,
+ &project,
+ &signproject,
+ &format,
+ &verbose,
+ argc, argv);
+ }
+ /* project audit log */
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile, "Opening audit log %s\n", projectLogFileName);
+ projectLogFile = fopen(projectLogFileName, "a"); /* closed @5 */
+ if (projectLogFile == NULL) {
+ fprintf(messageFile, "ERROR1015: Cannot open audit log %s, %s\n",
+ projectLogFileName, strerror(errno));
+ rc = ERROR_CODE;
+ }
+ }
+ /* update audit log, begin this entry */
+ if (projectLogFile != NULL) {
+ if (verbose) fprintf(messageFile, "Updating audit log\n");
+ File_LogTime(projectLogFile);
+ fprintf(projectLogFile, "\tSender: %s\n", sender);
+ fprintf(projectLogFile, "\tProject: %s\n", project);
+ fprintf(projectLogFile, "\tProgram: %s\n", argv[0]);
+ fprintf(projectLogFile, "\tSigning Project: %s\n", signproject);
+ }
+
+ /*
+ get parameters from the configuration files
+ */
+ /* get the file name of the framework configuration file from an environment variable */
+ if (rc == 0) {
+ frameworkConfigFileName = getenv("FRAMEWORK_CONFIG_FILE");
+ if (frameworkConfigFileName == NULL) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1014, FRAMEWORK_CONFIG_FILE environment variable not set\n");
+ rc = ERROR_CODE;
+ }
+ }
+ /* open the framework configuration file */
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile, "Framework configuration file %s\n",
+ frameworkConfigFileName);
+ frameworkConfigFile = fopen(frameworkConfigFileName, "r"); /* freed @1 */
+ if (frameworkConfigFile == NULL) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1016, Cannot open %s\n", frameworkConfigFileName);
+ rc = ERROR_CODE;
+ }
+ }
+ /* get the file name for project configuration file */
+ if (rc == 0) {
+ rc = File_MapNameToValue(&projectConfigFilename, /* freed @2 */
+ signproject,
+ lineBuffer,
+ MAX_LINE_SIZE,
+ frameworkConfigFile);
+ if (rc != 0) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1019, Cannot find project %s\n", signproject);
+ }
+ }
+
+ /* open the project configuration file */
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile, "Project configuration file %s\n",
+ projectConfigFilename);
+ projectConfigFile = fopen(projectConfigFilename, "r"); /* freed @3 */
+ if (projectConfigFile == NULL) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1017, Cannot open %s\n", projectConfigFilename);
+ rc = ERROR_CODE;
+ }
+ }
+ /* get the file name for the signing key */
+ if (rc == 0) {
+ rc = File_MapNameToValue(&keyFileName, /* freed @4 */
+ "key",
+ lineBuffer,
+ MAX_LINE_SIZE,
+ projectConfigFile);
+ }
+ /* log the keyFileName */
+ if (rc == 0) {
+ fprintf(projectLogFile, "\tSigning Key file: %s\n", keyFileName);
+ if (verbose) fprintf(messageFile, "Signing key file %s\n",
+ keyFileName);
+ }
+ /* check the sender authorization */
+ /* NOTE: There's nothing really secret about the public key. Anyone can read it. This step is
+ more a demo of how a project program could check secondary authorization. */
+ /* determine whether senders are needed */
+ if (rc == 0) {
+ rc = File_MapNameToBool(&(projectConfig.needSenders),
+ "needsenders",
+ lineBuffer,
+ MAX_LINE_SIZE,
+ projectConfigFile);
+ }
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile,
+ "Signing project needs senders: %d\n", projectConfig. needSenders);
+ }
+ /* read the list of authorized senders */
+ if (rc == 0) {
+ rc = File_GetNameValueArray(&(projectConfig.senders), /* freed by caller */
+ &(projectConfig.senderemails), /* freed by caller */
+ &(projectConfig.sendersCount), /* number of authorized senders */
+ lineBuffer,
+ MAX_LINE_SIZE,
+ projectConfigFile);
+ }
+ /* check the sender authorization */
+ if (rc == 0) {
+ rc = ProjectConfig_ValidateSender(sender,
+ &projectConfig, NULL);
+ if (rc != 0) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1018: %s is not authorized for project: %s\n",
+ sender, signproject);
+ fprintf(messageFile,
+ "Contact framework administrator\n");
+ }
+ }
+ /* get the key token */
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile, "Key token at %s\n",
+ keyFileName);
+ rc = File_ReadBinaryFile(&keyToken, &keyTokenLength, 4000, keyFileName); /* freed @1 */
+ }
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile, "Key token length %lu\n",
+ (unsigned long)keyTokenLength);
+ }
+ /* extract the public key from the key token */
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile, "Extract the public key from the key token\n");
+ rc = getPKA96PublicKey(&rsaKeyTokenPublic, /* output: CCA structure */
+ keyTokenLength,
+ keyToken, /* input: PKA96 key token */
+ 0);
+ }
+ /* verify the public key length */
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile, "Public key length %u\n",
+ rsaKeyTokenPublic.nByteLength);
+ }
+ /* write the public key binary to the output attachment */
+ if (rc == 0) {
+
+ EVP_PKEY *publicKey = NULL; /* OpenSSL public key, freed @1*/
+ RSA *rsaPubKey = NULL; /* OpenSSL public key, freed @2 */
+ FILE *pubkeyFile = NULL;
+
+ switch (format) {
+ case FORMAT_BINARY: /* return binary */
+ if (verbose) fprintf(messageFile, "Writing binary to output file %s\n",
+ outputAttachmentFileName);
+ rc = File_WriteBinaryFile(rsaKeyTokenPublic.n, rsaKeyTokenPublic.nByteLength,
+ outputAttachmentFileName);
+ break;
+ case FORMAT_LE32:
+ /* convert the binary big endian public key to EFI format C code */
+ if (rc == 0) {
+ rc = pubkeyBinToC(&pubkeyc_length,
+ &pubkeyc, /* freed @7 */
+ rsaKeyTokenPublic.nByteLength,
+ rsaKeyTokenPublic.n);
+ }
+ /* write the EFI format C code public key to a file */
+ if (rc == 0) {
+ if (verbose) fprintf(messageFile,
+ "Writing little endian 32-bit to output file %s\n",
+ outputAttachmentFileName);
+ rc = File_WriteBinaryFile((unsigned char *)pubkeyc,
+ pubkeyc_length-1, /* omit the nuil terminator */
+ outputAttachmentFileName);
+ }
+ break;
+ case FORMAT_PEM:
+ /* allocate the EVP structure */
+ if (rc == 0) {
+ OpenSSL_add_all_algorithms();
+ publicKey = EVP_PKEY_new(); /* freed @1 */
+ if (publicKey == NULL) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1011: Unable to create openssl EVP_PKEY structure");
+ rc = ERROR_CODE;
+ }
+ }
+ /* convert the raw public key to openssl RSA structure */
+ if (rc == 0) {
+ rc = osslBinToRSA(&rsaPubKey, /* freed %2 */
+ rsaKeyTokenPublic.e,
+ rsaKeyTokenPublic.eLength,
+ rsaKeyTokenPublic.n,
+ rsaKeyTokenPublic.nByteLength);
+ if (rc != 0) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1020: Unable to convert public key to openssl RSA");
+ rc = ERROR_CODE;
+ }
+ }
+ /* convert the RSA structure to EVP */
+ if (rc == 0) {
+ /* 1 success, 0 failure */
+ rc = EVP_PKEY_assign_RSA(publicKey, rsaPubKey);
+ if (rc != 1) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1022: Unable to convert public key to openssl EVP");
+ rc = ERROR_CODE;
+ }
+ else {
+ rc = 0;
+ }
+ }
+ if (rc == 0) {
+ pubkeyFile = fopen(outputAttachmentFileName,"wb");
+ if (pubkeyFile == NULL) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1023: Unable to open %s\n", outputAttachmentFileName);
+ rc = ERROR_CODE;
+ }
+ }
+ if (rc == 0) {
+ /* 1 success, 0 failure */
+ rc = PEM_write_PUBKEY(pubkeyFile, publicKey);
+ if (rc != 1) {
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1024: Unable to write public key to %s\n",
+ outputAttachmentFileName);
+ rc = ERROR_CODE;
+ }
+ else {
+ rc = 0;
+ }
+ }
+ if (publicKey != NULL) { /* @2, also frees the RSA structure */
+ EVP_PKEY_free(publicKey);
+ rsaPubKey = NULL;
+ }
+ if (rsaPubKey != NULL) {
+ RSA_free(rsaPubKey); /* @1, if not freed above */
+ }
+ break;
+ default:
+ File_Printf(projectLogFile, messageFile,
+ "ERROR1003: format has illegal value: %u\n", format);
+ rc = ERROR_CODE;
+ break;
+ }
+
+ }
+ if (rc == 0) {
+ PrintAll(projectLogFile, "\tPublic Key", rsaKeyTokenPublic.nByteLength, rsaKeyTokenPublic.n);
+ }
+ /* cleanup */
+ File_Printf(projectLogFile, messageFile, "Return code: %u\n", rc);
+ /* close the framework configuration file */
+ if (frameworkConfigFile != NULL) {
+ fclose(frameworkConfigFile); /* @1 */
+ }
+ free(projectConfigFilename); /* @2 */
+ /* close the project configuration file */
+ if (projectConfigFile != NULL) {
+ fclose(projectConfigFile); /* @3 */
+ }
+ free(keyFileName); /* @4 */
+ free(keyToken); /* @6 */
+ free(pubkeyc); /* @7 */
+ ProjectConfig_Delete(&projectConfig); /* @9 */
+ if (projectLogFile != NULL) {
+ fclose(projectLogFile); /* @5 */
+ }
+ if (messageFile != stdout) {
+ fflush(messageFile);
+ fclose(messageFile); /* @8 */
+ messageFile = stdout;
+ }
+ return rc;
+}
+
+/* convert a binary public key to EFI (little endian) C code
+ */
+
+int pubkeyBinToC(unsigned long *pubkeyc_length, /* in bytes */
+ char **pubkeyc,
+ unsigned long pubkey_length, /* in bytes */
+ unsigned char *pubkey)
+{
+ int rc = 0; /* general return code */
+ size_t max; /* total length of pubkeyc */
+ size_t preamble_length;
+ size_t postamble_length;
+ size_t i; /* counts words */
+ size_t j; /* counts bytes in a word */
+ unsigned long pubkey_length_words;
+
+ const char *preamble = "static UINTN mPlatformKey[] = \n{";
+ const char *postamble = "\n};\n";
+
+ if (rc == 0) {
+ pubkey_length_words = pubkey_length / 4;
+ *pubkeyc_length = 0;
+ preamble_length = strlen(preamble);
+ postamble_length = strlen(postamble);
+ max = preamble_length +
+ (pubkey_length * 8) + /* this is overly conservative, 4 bytes needs about 12
+ bytes */
+ postamble_length;
+ *pubkeyc = malloc(max);
+ if (*pubkeyc == NULL) {
+ printf("ERROR1004, failure to malloc %u bytes\n", (uint)max);
+ rc = ERROR_CODE;
+ }
+ }
+ /* copy the preamble */
+ if (rc == 0) {
+ strcpy((*pubkeyc) + *pubkeyc_length, preamble);
+ *pubkeyc_length += preamble_length;
+
+ for (i = 0 ; i < pubkey_length_words ; i ++) {
+ /* every 4 32-bit ints is a new line */
+ if ((i % 4) == 0) {
+ if (i != 0) { /* no comma before first int */
+ *pubkeyc_length += sprintf((*pubkeyc) + *pubkeyc_length, ",");
+ }
+ *pubkeyc_length += sprintf((*pubkeyc) + *pubkeyc_length, "\n 0x");
+ }
+ /* every 32-bit int is a new word */
+ else {
+ *pubkeyc_length += sprintf((*pubkeyc) + *pubkeyc_length, ", 0x");
+ }
+ /* print the 4 bytes in an int, starting at the lsb's */
+ for (j = 0 ; j < 4 ; j++) {
+ *pubkeyc_length += sprintf((*pubkeyc) + *pubkeyc_length, "%02x",
+ pubkey[((pubkey_length_words - i - 1) * 4) + j]);
+ }
+ }
+ /* copy the postamble */
+ strcpy((*pubkeyc) + *pubkeyc_length, postamble);
+ *pubkeyc_length += postamble_length + 1; /* +1 for the nul terminator */
+
+ if (verbose) fprintf(messageFile, "pubkeyBinToC: public key\n%s\n", *pubkeyc);
+ }
+ return rc;
+}
+
+int GetArgs(const char **outputBodyFilename,
+ const char **outputAttachmentFileName,
+ const char **projectLogFileName,
+ const char **sender,
+ const char **project,
+ const char **signproject,
+ unsigned int *format,
+ int *verbose,
+ int argc,
+ char **argv)
+{
+ long rc = 0;
+ int i;
+ FILE *tmpFile;
+
+ /* command line argument defaults */
+ *outputBodyFilename = NULL;
+ *outputAttachmentFileName = NULL;
+
+ /* get the command line arguments */
+ for (i=1 ; (i<argc) && (rc == 0) ; i++) {
+ if (strcmp(argv[i],"-obody") == 0) {
+ i++;
+ if (i < argc) {
+ *outputBodyFilename = argv[i];
+ rc = File_Open(&tmpFile, *outputBodyFilename, "a"); /* closed @8 */
+ /* switch messageFile from stdout ASAP so all messages get returned via email */
+ if (rc == 0) {
+ messageFile = tmpFile;
+ setvbuf(messageFile , 0, _IONBF, 0);
+ }
+ }
+ else {
+ fprintf(messageFile,
+ "ERROR1002: -obody option (output email body) needs a value\n");
+ rc = ERROR_CODE;
+ }
+ }
+ else if (strcmp(argv[i],"-log") == 0) {
+ i++;
+ if (i < argc) {
+ *projectLogFileName = argv[i];
+ }
+ else {
+ fprintf(messageFile,
+ "ERROR1005: -log option (audit log file name) needs a value\n");
+ rc = ERROR_CODE;
+ }
+ }
+ else if (strcmp(argv[i],"-sender") == 0) {
+ i++;
+ if (i < argc) {
+ *sender = argv[i];
+ }
+ else {
+ fprintf(messageFile,
+ "ERROR1006: -sender option needs a value\n");
+ rc = ERROR_CODE;
+ }
+ }
+ else if (strcmp(argv[i],"-project") == 0) {
+ i++;
+ if (i < argc) {
+ *project = argv[i];
+ }
+ else {
+ fprintf(messageFile,
+ "ERROR1007: -project option needs a value\n");
+ rc = ERROR_CODE;
+ }
+ }
+ else if (strcmp(argv[i],"-signproject") == 0) {
+ i++;
+ if (i < argc) {
+ *signproject = argv[i];
+ }
+ else {
+ fprintf(messageFile,
+ "ERROR1007: -signproject option needs a value\n");
+ rc = ERROR_CODE;
+ }
+ }
+ else if (strcmp(argv[i],"-do") == 0) {
+ i++;
+ if (i < argc) {
+ *outputAttachmentFileName = argv[i];
+ }
+ else {
+ fprintf(messageFile,
+ "ERROR1009: -do option needs a value\n");
+ rc = ERROR_CODE;
+ }
+ }
+ else if (strcmp(argv[i],"-format") == 0) {
+ i++;
+ if (strcmp(argv[i],"binary") == 0) {
+ *format = FORMAT_BINARY;
+ }
+ else if (strcmp(argv[i],"le32") == 0) {
+ *format = FORMAT_LE32;
+ }
+ else if (strcmp(argv[i],"pem") == 0) {
+ *format = FORMAT_PEM;
+ }
+ else {
+ fprintf(messageFile, "ERROR1005: -format has illegal value: %s\n", argv[i]);
+ rc = ERROR_CODE;
+ }
+ }
+ /* this allows the framework to probe whether the project specific program can be called.
+ The program should do nothing except return success. */
+ else if (strcmp(argv[i],"-h") == 0) {
+ PrintUsage();
+ exit(0);
+ }
+ else if (strcmp(argv[i],"-v") == 0) {
+ *verbose = TRUE;
+ }
+ /* This code intentionally does not have an 'else error' clause. The framework can in
+ general add command line arguments that are ignored by the project specific program. */
+ }
+ /* verify command line arguments */
+ if (rc == 0) {
+ if (*outputAttachmentFileName == NULL) {
+ fprintf(messageFile,
+ "ERROR1007: -do option missing\n");
+ rc = ERROR_CODE;
+ }
+ }
+ if (rc == 0) {
+ if (*projectLogFileName == NULL) {
+ fprintf(messageFile,
+ "ERROR1008: -log option missing\n");
+ rc = ERROR_CODE;
+ }
+ }
+ if (rc == 0) {
+ if (*sender == NULL) {
+ fprintf(messageFile,
+ "ERROR1010: -sender option missing\n");
+ rc = ERROR_CODE;
+ }
+ }
+ if (rc == 0) {
+ if (*project == NULL) {
+ fprintf(messageFile,
+ "ERROR1012: -project option missing\n");
+ rc = ERROR_CODE;
+ }
+ }
+ if (rc == 0) {
+ if (*signproject == NULL) {
+ fprintf(messageFile,
+ "ERROR1013: -signproject option missing\n");
+ rc = ERROR_CODE;
+ }
+ }
+ return rc;
+}
+
+void PrintUsage()
+{
+ fprintf(messageFile, "\n");
+ fprintf(messageFile,
+ "\tgetpubkey usage:\n"
+ "\n"
+ "Common arguments:\n"
+ "\n"
+ "\t-signproject - signing project name\n"
+ "\t[-format - return format (default binary)]\n"
+ "\t binary - returns public key in binary\n"
+ "\t le32 - returns public key as C code, 32-bit, little endian\n"
+ "\t pem - returns public key as pem\n"
+ "\t[-v - verbose logging]\n"
+ "\t[-h - print usage help]\n"
+ "\n"
+
+ "Email only arguments:\n"
+ "\n"
+ "\t-project - project name\n"
+ "\n"
+
+ "Command line only arguments:\n"
+ "\n"
+ "\t-obody - output email body file name (should be first argument)\n"
+ "\t-sender - email sender\n"
+ "\t-do - output attachment file name\n"
+ "\t-log - project audit log file name\n"
+
+ "\n"
+ "Email example: -project getpubkey -signproject athena\n"
+ "\n"
+ "Extracts the public key from the CCA key token in the key file\n"
+ "The resulting public key modulus is returned as binary or compilable\n"
+ "C code\n"
+ );
+ fprintf(messageFile, "\n");
+ return;
+}
OpenPOWER on IntegriCloud