diff options
99 files changed, 22908 insertions, 1 deletions
@@ -1,3 +1,29 @@ *.o +*~ src/client/sf_client src/client/sftp-test +src/signframework/audit +src/signframework/audit_archive +src/signframework/framework +src/signframework/framework_test +src/signframework/frameworkkey_generate +src/signframework/getpubkey +src/signframework/getpubkeyecc +src/signframework/json_regression_test/json_test +src/signframework/keygen +src/signframework/keygeneccp521 +src/signframework/password_change +src/signframework/password_generate +src/signframework/sender_validate +src/signframework/setclock +src/signframework/signecc/getkeysecc +src/signframework/signecc/signecc +src/signframework/*.bin +src/signframework/*.tok +src/signframework/*.pem +src/signframework/*.txt +src/signframework/*.cfg +src/signframework/logs/* +src/signframework/signecc/*.bin +src/signframework/signecc/*.tok +src/signframework/signecc/*.cfg @@ -1,3 +1,2 @@ # Power code signing framework -Read the PowerSigningProcess.pdf file for installation instructions
\ No newline at end of file diff --git a/src/signframework/adddropbox b/src/signframework/adddropbox new file mode 100755 index 0000000..0aca02c --- /dev/null +++ b/src/signframework/adddropbox @@ -0,0 +1,95 @@ +#!/bin/sh -e +# 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. + +if [ "$USER" != "root" ]; then + echo "adddropbox must be run as root" + exit 1; +fi + +if [ ! -e ./adddropbox ]; then + echo "adddropbox must be run from framework directory" + exit 1; +fi + +if [ "$2" = "" ]; then + echo "Syntax: adddropbox <userid> <SSH public key file>" + exit 1; +fi +USERID=$1 +PUBKEY=$2 + +if [ ! -e ./request_pub.pem ]; then + echo "Generating request encryption key pair" + openssl genpkey -algorithm RSA -out request_priv.pem -pkeyopt rsa_keygen_bits:2048 + chmod 600 request_priv.pem + openssl rsa -pubout -in request_priv.pem -out request_pub.pem +fi + + +REM=`echo $USERID | cut -c 9-` +if [ "$REM" != "" ]; then + echo "Userid character length limit is 8" + exit 1; +fi + +if [ ! -e $PUBKEY ]; then + echo "Public key file doesn't exist : $PUBKEY" + exit 1; +fi + +grep -q ssh-rsa $PUBKEY +if [ $? -ne 0 ]; then + echo "Public key doesn't appear to be in correct format" + exit 1; +fi + +if [ -e /home/dropbox/$USERID ]; then + echo "Dropbox for $USERID already exists" + exit 1; +fi + +# Ok, do this thing +echo "Creating new dropbox for $USERID" + +useradd -s /bin/false -G signers -M $USERID +mkdir /home/$USERID +chown root:$USERID /home/$USERID +chmod 750 /home/$USERID +mkdir /home/dropbox/$USERID/ +chown $USERID:signers /home/dropbox/$USERID +chmod 770 /home/dropbox/$USERID +cp ./request_pub.pem /home/dropbox/$USERID +mkdir /home/$USERID/dropbox +mount --bind /home/dropbox/$USERID/ /home/$USERID/dropbox +echo '/home/dropbox/$USERID/ /home/$USERID/dropbox none bind' >> /etc/fstab + +echo " +Match User $USERID + ChrootDirectory /home/$USERID + ForceCommand internal-sftp + AllowTcpForwarding no + PermitTunnel no + X11Forwarding no +" >> /etc/ssh/sshd_config + +cp $PUBKEY /etc/ssh/authorized_keys/$USERID +rm -f $PUBKEY + +systemctl restart sshd + +echo "Dropbox for $USERID setup" +echo "Next ensure a matching CCA profile exists and then run 'password_generate -profile $USERID -sender <signers email>'" + diff --git a/src/signframework/athena.cfg.sample b/src/signframework/athena.cfg.sample new file mode 100644 index 0000000..1e91039 --- /dev/null +++ b/src/signframework/athena.cfg.sample @@ -0,0 +1,41 @@ +# Athena project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=framework_test + +# File name for the project audit log + +log=logs/athena.log + +# project signing key file name + +needkey=true +key=athenakey.tok + + +# auxiliary project configuration file + +needauxcfg=true +auxcfg=athenaaux.cfg + +neediatt=true +needoatt=true +needpwd=true + +# Project administrator email address + +email=framework@email.com + +# Notification receivers (emails) + +notifs=1 +framework@email.com + +# Authorized senders (signers) (userid=email) + +needsenders=true +fred=fred@email.com + diff --git a/src/signframework/athenaaux.cfg.sample b/src/signframework/athenaaux.cfg.sample new file mode 100644 index 0000000..51225b4 --- /dev/null +++ b/src/signframework/athenaaux.cfg.sample @@ -0,0 +1,2 @@ + +sign_algorithm=rsa diff --git a/src/signframework/audit.c b/src/signframework/audit.c new file mode 100644 index 0000000..5756fc6 --- /dev/null +++ b/src/signframework/audit.c @@ -0,0 +1,726 @@ +/* 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. + */ + +/* This program implements the signer framework audit functions. They permit a remote user + to retrieve configuration and log files. */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> + +#include "utils.h" + +/* local prototypes */ + +long GetArgs(const char **sender, + const char **outputBodyFilename, + const char **outputAttachmentFilename, + const char **projectLogFileName, + int *frameworkConfig, + int *frameworkLog, + int *projectConfig, + int *projectAuxConfig, + int *projectLog, + int *useridConfig, + const char **project, + int *verbose, + int argc, + char **argv); + +void PrintUsage(void); +int processFrameworkConfig(const char *outputAttachmentFilename, + const char *frameworkConfigFileName, + FILE *projectLogFile); + +int processFrameworkLog(const char *outputAttachmentFilename, + const char *frameworkConfigFileName, + FILE *projectLogFile); + +int processProjectConfig(const char *outputAttachmentFilename, + const char *project, + const char *frameworkConfigFileName, + FILE *projectLogFile); + +int processProjectAuxConfig(const char *outputAttachmentFilename, + const char *project, + const char *frameworkConfigFileName, + FILE *projectLogFile); + +int processProjectLog(const char *outputAttachmentFilename, + const char *project, + const char *frameworkConfigFileName, + FILE *projectLogFile); + +int processUseridConfig(void); + +int printDirectory(const char* directory); + +/* This hard coded line size is safe because it is only used to parse configuration files, not + arbitrary user input */ + +#define MAX_LINE_SIZE 1024 + +/* messages are traced here */ +FILE *messageFile = NULL; +int verbose = FALSE; + +int main(int argc, char** argv) +{ + int rc = 0; + /* command line argument defaults */ + const char *outputBodyFilename = NULL; + const char *outputAttachmentFilename = NULL; + const char *sender = NULL; + const char *projectLogFileName = NULL; + FILE *projectLogFile = NULL; + time_t log_time; + int frameworkConfig = FALSE; + int frameworkLog = FALSE; + int projectConfig = FALSE; + int projectAuxConfig = FALSE; + int projectLog = FALSE; + int useridConfig = FALSE; + const char *project = NULL; + const char *frameworkConfigFileName = NULL; + + messageFile = stdout; /* default when running locally */ + + /* get caller's command line arguments */ + if (rc == 0) { + rc = GetArgs(&sender, + &outputBodyFilename, + &outputAttachmentFilename, + &projectLogFileName, + &frameworkConfig, + &frameworkLog, + &projectConfig, + &projectAuxConfig, + &projectLog, + &useridConfig, + &project, + &verbose, + argc, argv); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "audit: Running audit program\n"); + } + /* open the project audit logging */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Opening audit log %s\n", projectLogFileName); + projectLogFile = fopen(projectLogFileName, "a"); + if (projectLogFile == NULL) { + fprintf(messageFile, "ERROR0000: Cannot open audit log %s, %s\n", + projectLogFileName, strerror(errno)); + rc = ERROR_CODE; + } + } + /* update the project audit log, begin this entry */ + if (projectLogFile != NULL) { + if (verbose) fprintf(messageFile, "Updating audit log\n"); + log_time = time(NULL); + fprintf(projectLogFile, "\n%s", ctime(&log_time)); + fprintf(projectLogFile, "\tSender: %s\n", sender); + fprintf(projectLogFile, "\tProject: %s\n", project); + fprintf(projectLogFile, "\tProgram: %s\n", argv[0]); + } + /* get the file name of the framework configuration file from an environment variable */ + if (rc == 0) { + frameworkConfigFileName = getenv("FRAMEWORK_CONFIG_FILE"); + if (frameworkConfigFileName == NULL) { + fprintf(messageFile, + "audit: Error, FRAMEWORK_CONFIG_FILE environment variable not set\n"); + rc = ERROR_CODE; + } + } + /* branch based on the input command flag that is set */ + if (rc == 0) { + /* return the framework configuration file */ + if (frameworkConfig) { + fprintf(projectLogFile, "\tOption: Framework configuration file\n"); + rc = processFrameworkConfig(outputAttachmentFilename, + frameworkConfigFileName, + projectLogFile); + } + /* return the framework log file */ + else if (frameworkLog) { + fprintf(projectLogFile, "\tOption: Framework log file\n"); + rc = processFrameworkLog(outputAttachmentFilename, + frameworkConfigFileName, + projectLogFile); + } + /* return the project configuration file */ + else if (projectConfig) { + fprintf(projectLogFile, "\tOption: Project configuration file: %s\n", project); + rc = processProjectConfig(outputAttachmentFilename, project, + frameworkConfigFileName, + projectLogFile); + } + /* return the project auxiliary configuration file */ + else if (projectAuxConfig) { + fprintf(projectLogFile, "\tOption: Project auxiliary configuration file: %s\n", project); + rc = processProjectAuxConfig(outputAttachmentFilename, project, + frameworkConfigFileName, + projectLogFile); + } + /* return the project log file */ + else if (projectLog) { + fprintf(projectLogFile, "\tOption: Project log file: %s\n", project); + rc = processProjectLog(outputAttachmentFilename, project, + frameworkConfigFileName, + projectLogFile); + } + /* return the userid configuration */ + else if (useridConfig) { + rc = processUseridConfig(); + } + /* this should never occur */ + else { + fprintf(projectLogFile, "\tError: Missing option\n"); + PrintUsage(); + rc = ERROR_CODE; + } + } + fprintf(messageFile, "Return code: %u\n", rc); + if (messageFile != stdout) { + fflush(messageFile); + fclose(messageFile); + } + messageFile = stdout; + return rc; +} + +/* return the framework configuration file */ + +int processFrameworkConfig(const char *outputAttachmentFilename, + const char *frameworkConfigFileName, + FILE *projectLogFile) +{ + int rc = 0; + + projectLogFile = projectLogFile; + if (verbose) fprintf(messageFile, "audit: Returning framework config file: %s\n", + frameworkConfigFileName); + if (rc == 0) { + rc = File_Copy(outputAttachmentFilename, frameworkConfigFileName); + } + return rc; +} + +/* return the framework log file */ + +int processFrameworkLog(const char *outputAttachmentFilename, + const char *frameworkConfigFileName, + FILE *projectLogFile) +{ + int rc = 0; + FILE *frameworkConfigFile = NULL; /* freed @1 */ + char *auditLogFilename = NULL; /* freed @2 */ + char lineBuffer[MAX_LINE_SIZE]; + + /* open the framework configuration file */ + if (rc == 0) { + frameworkConfigFile = fopen(frameworkConfigFileName, "r"); /* freed @2 */ + if (frameworkConfigFile == NULL) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot open %s\n", frameworkConfigFileName); + rc = ERROR_CODE; + } + } + /* get the file name for the framework audit log */ + if (rc == 0) { + rc = File_MapNameToValue(&auditLogFilename, /* freed @1 */ + "log", + lineBuffer, + MAX_LINE_SIZE, + frameworkConfigFile); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot find framework audit log file name\n"); + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "audit: Returning framework audit log file: %s\n", + auditLogFilename); + rc = File_Copy(outputAttachmentFilename, auditLogFilename); + } + /* close the framework configuration file */ + if (frameworkConfigFile != NULL) { + fclose(frameworkConfigFile); /* @1 */ + } + free(auditLogFilename); /* @2 */ + return rc; +} + +/* return the project configuration file */ + +int processProjectConfig(const char *outputAttachmentFilename, + const char *project, + const char *frameworkConfigFileName, + FILE *projectLogFile) +{ + int rc = 0; + FILE *frameworkConfigFile = NULL; /* freed @1 */ + char *projectFilename = NULL; /* freed @2 */ + char lineBuffer[MAX_LINE_SIZE]; + + if (verbose) fprintf(messageFile, "audit: Returning configuration file for project: %s\n", + project); + /* open the framework configuration file */ + if (rc == 0) { + frameworkConfigFile = fopen(frameworkConfigFileName, "r"); /* freed @1 */ + if (frameworkConfigFile == NULL) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot open %s\n", frameworkConfigFileName); + rc = ERROR_CODE; + } + } + /* get the file name for project configuration file */ + if (rc == 0) { + rc = File_MapNameToValue(&projectFilename, /* freed @2 */ + project, + lineBuffer, + MAX_LINE_SIZE, + frameworkConfigFile); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot find project %s\n", project); + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "audit: Project configuration file: %s\n", + projectFilename); + rc = File_Copy(outputAttachmentFilename, projectFilename); + } + /* close the framework configuration file */ + if (frameworkConfigFile != NULL) { + fclose(frameworkConfigFile); /* @1 */ + } + free(projectFilename); /* @2 */ + return rc; +} + +/* return the project auxiliary configuration file */ + +int processProjectAuxConfig(const char *outputAttachmentFilename, + const char *project, + const char *frameworkConfigFileName, + FILE *projectLogFile) +{ + int rc = 0; + FILE *frameworkConfigFile = NULL; /* closed @1 */ + FILE *projectConfigFile = NULL; /* closed @2 */ + char *projectConfigFilename = NULL; /* freed @3 */ + char *projectAuxConfigFilename = NULL; /* freed @4 */ + char lineBuffer[MAX_LINE_SIZE]; + + if (verbose) fprintf(messageFile, + "audit: Returning auxiliary configuration file for project: %s\n", + project); + /* open the framework configuration file */ + if (rc == 0) { + frameworkConfigFile = fopen(frameworkConfigFileName, "r"); /* closed @1 */ + if (frameworkConfigFile == NULL) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot open %s\n", frameworkConfigFileName); + rc = ERROR_CODE; + } + } + /* get the file name for project configuration file */ + if (rc == 0) { + rc = File_MapNameToValue(&projectConfigFilename, /* freed @3 */ + project, + lineBuffer, + MAX_LINE_SIZE, + frameworkConfigFile); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot find project %s\n", project); + } + } + /* open the project configuration file */ + if (rc == 0) { + projectConfigFile = fopen(projectConfigFilename, "r"); /* closed @2 */ + if (projectConfigFile == NULL) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot open %s\n", projectConfigFilename); + rc = ERROR_CODE; + } + } + /* get the file name for the project auxiliary configuration file */ + if (rc == 0) { + rc = File_MapNameToValue(&projectAuxConfigFilename, /* freed @4 */ + "auxcfg", + lineBuffer, + MAX_LINE_SIZE, + projectConfigFile); + /* the project auxiliary configuration file is optional */ + if (rc == 0) { + /* File_Copy() copies the source file to the destination */ + if (verbose) fprintf(messageFile, "audit: Project auxiliary configuration file: %s\n", + projectAuxConfigFilename); + rc = File_Copy(outputAttachmentFilename, projectAuxConfigFilename); + } + else { + File_Printf(projectLogFile, messageFile, + "Error, Project does not include an auxiliary configuration file: %s\n", + project); + } + } + /* close the framework configuration file */ + if (frameworkConfigFile != NULL) { + fclose(frameworkConfigFile ); /* @1 */ + } + /* close the project configuration file */ + if (projectConfigFile != NULL) { + fclose(projectConfigFile); /* @2 */ + } + free(projectConfigFilename); /* @3 */ + free(projectAuxConfigFilename); /* @4 */ + return rc; +} + +/* return the project log file */ + +int processProjectLog(const char *outputAttachmentFilename, + const char *project, + const char *configFileName, + FILE *projectLogFile) +{ + int rc = 0; + FILE *configFile = NULL; /* freed @1 */ + char *projectFilename = NULL; /* freed @2 */ + FILE *projectFile = NULL; /* freed @3 */ + char *auditLogFilename = NULL; /* freed @4 */ + char lineBuffer[MAX_LINE_SIZE]; + + if (verbose) fprintf(messageFile, "audit: Returning audit log file for project: %s\n", + project); + /* open the framework configuration file */ + if (rc == 0) { + configFile = fopen(configFileName, "r"); /* freed @1 */ + if (configFile == NULL) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot open %s\n", configFileName); + rc = ERROR_CODE; + } + } + /* get the file name for project configuration file */ + if (rc == 0) { + rc = File_MapNameToValue(&projectFilename, /* freed @2 */ + project, + lineBuffer, + MAX_LINE_SIZE, + configFile); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "Error, Cannot find project %s\n", project); + } + } + /* open the project configuration file */ + if (rc == 0) { + projectFile = fopen(projectFilename, "r"); /* freed @3 */ + if (projectFile == NULL) { + File_Printf(projectLogFile, messageFile, + "Error opening project configuration file: %s\n", + projectFilename); + rc = ERROR_CODE; + } + } + /* get the file name for the project audit log */ + if (rc == 0) { + rc = File_MapNameToValue(&auditLogFilename, /* freed @4 */ + "log", + lineBuffer, + MAX_LINE_SIZE, + projectFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "audit: Project audit log file: %s\n", + auditLogFilename); + rc = File_Copy(outputAttachmentFilename, auditLogFilename); + } + /* close the framework configuration file */ + if (configFile != NULL) { + fclose(configFile); /* @1 */ + } + free(projectFilename); /* @2 */ + if (projectFile != NULL) { + fclose(projectFile); /* @3 */ + } + free(auditLogFilename); /* @4 */ + return rc; +} + +int processUseridConfig(void) +{ + int rc = 0; + + fprintf(messageFile, "audit: Displaying home directories existing: \n"); + rc = printDirectory("/home/"); + + if (rc == 0) { + fprintf(messageFile, "\naudit: Displaying dropbox directories existing: \n"); + rc = printDirectory ("/home/dropbox/"); + } + + if (rc == 0) { + fprintf(messageFile, "\naudit: Displaying ssh public keys installed: \n"); + rc = printDirectory ("/etc/ssh/authorized_keys"); + } + + return rc; +} + +int printDirectory(const char* directory) +{ + int rc = 0; + + DIR *dir; + struct dirent *dire; + dir = opendir (directory); + + if (NULL != dir) + { + while (NULL != (dire = readdir(dir))) { + if (strcmp(dire->d_name,".") && + strcmp(dire->d_name,"..")) { + fprintf(messageFile,"\t%s\n",dire->d_name); + } + } + closedir (dir); + } + else + { + fprintf (messageFile, "Couldn't open the directory : %s\n",directory); + rc = 1; + } + return rc; +} + +/* GetArgs() gets the command line arguments from the framework caller + + */ + +long GetArgs(const char **sender, + const char **outputBodyFilename, + const char **outputAttachmentFilename, + const char **projectLogFileName, + int *frameworkConfig, + int *frameworkLog, + int *projectConfig, + int *projectAuxConfig, + int *projectLog, + int *useridConfig, + const char **project, + int *verbose, + int argc, + char **argv) +{ + long rc = 0; + int i; + FILE *tmpFile; + unsigned int paramCount; /* only one of -fc, -pc, -paux, -fl, -pl must be + specified */ + + /* command line argument defaults */ + *sender = NULL; + *outputBodyFilename = NULL; + *outputAttachmentFilename = NULL; + *project = NULL; + *verbose = FALSE; + /* command flags */ + *frameworkConfig = FALSE; + *frameworkLog = FALSE; + *projectConfig = FALSE; + *projectAuxConfig = FALSE; + *projectLog = FALSE; + *useridConfig = FALSE; + + paramCount = 0; /* count of received command flags */ + + /* 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]; + /* since audit is partly for debugging, open the output body for append. If verbose + tracing is on, both the framework and audit tracing will be returned. */ + rc = File_Open(&tmpFile, *outputBodyFilename, "a"); + /* switch messageFile from stdout ASAP */ + if (rc == 0) { + messageFile = tmpFile; + } + } + else { + fprintf(messageFile, + "audit: Error, -obody option (output email body) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-sender") == 0) { + i++; + if (i < argc) { + *sender = argv[i]; + } + else { + fprintf(messageFile, "audit: Error, -sender option (sender) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-do") == 0) { + i++; + if (i < argc) { + *outputAttachmentFilename = argv[i]; + } + else { + fprintf(messageFile, + "audit: Error, -do option (output attachment) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-fc") == 0) { + *frameworkConfig = TRUE; + paramCount++; + } + else if (strcmp(argv[i],"-pc") == 0) { + i++; + if (i < argc) { + *projectConfig = TRUE; + *project = argv[i]; + paramCount++; + } + else { + fprintf(messageFile, "audit: Error, -pc option (project name) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-paux") == 0) { + i++; + if (i < argc) { + *projectAuxConfig = TRUE; + *project = argv[i]; + paramCount++; + } + else { + fprintf(messageFile, "audit: Error, -paux option (project name) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-fl") == 0) { + *frameworkLog = TRUE; + paramCount++; + } + else if (strcmp(argv[i],"-pl") == 0) { + i++; + if (i < argc) { + *projectLog = TRUE; + *project = argv[i]; + paramCount++; + } + else { + fprintf(messageFile, "audit: Error, -pl option (project name) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-userc") == 0) { + *useridConfig = TRUE; + paramCount++; + } + else if (strcmp(argv[i],"-project") == 0) { + i++; + if (i < argc) { + /* project name, unused */ + } + else { + fprintf(messageFile, + "audit: Error, -project option (project name) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-log") == 0) { + i++; + if (i < argc) { + *projectLogFileName = argv[i]; + } + else { + fprintf(messageFile, + "audit: Error, -log option (audit log name) needs a value\n"); + 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. */ + } + if (rc == 0) { + if (*outputAttachmentFilename == NULL) { + fprintf(messageFile, "audit: Error, -do option must be specified\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (paramCount != 1) { + fprintf(messageFile, + "audit: Error, only one of -fc, -fl, -pc, -pl, -paux must be specified\n"); + rc = ERROR_CODE; + } + } + return rc; +} + +void PrintUsage() +{ + fprintf(messageFile, + "\n" + "\taudit usage:\n" + "\n" + "Common arguments:\n" + "\n" + "\t-fc - return framework configuration file\n" + "\t-fl - return framework audit log file\n" + "\t-pc <project> - return project configuration file\n" + "\t-paux <project> - return project auxiliary configuration file\n" + "\t-pl <project> - return project audit log file\n" + "\t-userc - Display userid configuration\n" + "\t[-v] - verbose logging\n" + "\t[-h] - print usage help\n" + "\n" + "Request 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 - audit log file name\n" + "\n" + ); + return; +} diff --git a/src/signframework/audit.cfg.sample b/src/signframework/audit.cfg.sample new file mode 100644 index 0000000..296faa1 --- /dev/null +++ b/src/signframework/audit.cfg.sample @@ -0,0 +1,30 @@ +# Audit configuration file + +# The parameters must be in this order +# +# program=program executable filename + +program=audit + +# File name for the audit log + +log=logs/audit.log + +# files to be added to the program + +needkey=false +needauxcfg=false +neediatt=false +needoatt=true +needpwd=false + +# Project administrator +email=framework@email.com + +# Notification receivers (email addresses) +notifs=0 + +# Authorized senders (administrators) (userid=email) +needsenders=true +joe=joe@email.com + diff --git a/src/signframework/audit_archive.c b/src/signframework/audit_archive.c new file mode 100644 index 0000000..5013d60 --- /dev/null +++ b/src/signframework/audit_archive.c @@ -0,0 +1,346 @@ +/* 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. + */ + +/* This program sends the framework audit log to the framework administrators. + + It iterates through all the project configuration files, sending the audit log to the project + administrator. + + The intent is to run this as a periodic cron job, authough it can be run at any time from the + command line. +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include "framework_utils.h" +#include "mail.h" +#include "utils.h" +#include "debug.h" + +/* local prototypes */ + +int AuditArchive_Parse(char **outputBodyFilename, + char **outputAttachmentFilename, + char *configFilename); +int GetArgs(int argc, + char **argv, + char **configFilename); +void PrintUsage(void); + +/* global variables */ + +FILE *messageFile = NULL; /* needed for utilities */ +int verbose = TRUE; +int debug = FALSE; + +int main(int argc, char** argv) +{ + int rc = 0; + size_t i; + char *subject = NULL; /* freed @1 */ + char *configFilename = NULL; + FrameworkConfig frameworkConfig; /* freed @2 */ + char *outputBodyFilename = NULL; /* freed @5 */ + FILE *outputBodyFile = NULL; /* closed @4 */ + char *outputAttachmentFilename = NULL; /* freed @6 */ + const char projectSubject1[] = "Signing server "; + const char projectSubject2[] = " framework audit log"; + const char projectSubject3[] = " audit log for project: "; + const char *hostname = NULL; + + /* this is a stand alone program, so trace always goes to stdout */ + messageFile = stdout; + + FrameworkConfig_Init(&frameworkConfig); /* freed @2 */ + /* + get the command line arguments + */ + if (rc == 0) { + rc = GetArgs(argc, argv, + &configFilename); + } + if (rc == 0) { + hostname = getenv("HOSTNAME"); + if (hostname == NULL) { + fprintf(messageFile, "Error getting environment variable HOSTNAME\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "\naudit_archive: hostname %s\n", hostname); + } + /* + get parameters from the framework configuration file + */ + if (rc == 0) { + rc = FrameworkConfig_Parse(TRUE, /* need master key */ + FALSE, /* do not validate */ + &frameworkConfig); /* freed @2 */ + } + /* get parameters from the audit archive configuration file */ + if (rc == 0) { + rc = AuditArchive_Parse(&outputBodyFilename, /* freed @5 */ + &outputAttachmentFilename, /* freed @6 */ + configFilename); + } + /* + for each framework administrator + */ + for (i = 0 ; (rc == 0) && (i < frameworkConfig.frameworkAdminCount) ; i++) { + if (verbose) fprintf(messageFile, + "\naudit_archive: Sending framework audit log %s to %s\n\n", + frameworkConfig.frameworkLogFilename, + frameworkConfig.frameworkAdmins[i]); + /* open the email body file */ + if (rc == 0) { + outputBodyFile = fopen(outputBodyFilename, "w"); + if (outputBodyFile == NULL) { + fprintf(messageFile, "Error opening %s, %s\n", + outputBodyFilename, strerror(errno)); + rc = ERROR_CODE; + } + } + /* construct the email body */ + if (rc == 0) { + fprintf(outputBodyFile, + "This is a validation message from the signing server.\n\n" + "You are the framework administrator\n\n" + "Attached is a copy of the audit log for your archives.\n\n" + ); + } + /* close the email response body file */ + if (outputBodyFile != NULL) { + fclose(outputBodyFile); /* @4 */ + outputBodyFile = NULL; + } + /* construct the attachment. Copy because Notes needs a full path name. */ + if (rc == 0) { + rc = File_Copy(outputAttachmentFilename, + frameworkConfig.frameworkLogFilename); + } + /* construct the subject */ + if (rc == 0) { + subject = realloc(subject, + sizeof(projectSubject1) + + strlen(hostname) + + sizeof(projectSubject2)); + strcpy(subject, projectSubject1); + strcat(subject, hostname); + strcat(subject, projectSubject2); + } + /* send the email response */ + if (rc == 0) { + /* send the message to the framework administrator */ + rc = SendMailFileWithAttachment(&frameworkConfig, + frameworkConfig.frameworkAdmins[i], + subject, + outputBodyFilename, + outputAttachmentFilename); + if (rc != 0) { + fprintf(messageFile, "SendMail failed, status %u\n", rc); + rc = ERROR_CODE; + } + } + } + /* for each project */ + for (i = 0 ; (rc == 0) && (i < frameworkConfig.projectLength) ; i++) { + + if (verbose) fprintf(messageFile, + "\naudit_archive: Loading project configuration file %s\n\n", + frameworkConfig.projectConfigFilenames[i]); + /* get parameters from the project configuration file */ + if (rc == 0) { + rc = ProjectConfig_Parse(frameworkConfig.projectConfigFiles[i], + FALSE, /* do not validate */ + frameworkConfig.projectConfigFilenames[i], + &frameworkConfig); + } + /* open the email response file */ + if (rc == 0) { + outputBodyFile = fopen(outputBodyFilename, "w"); + if (outputBodyFile == NULL) { + fprintf(messageFile, "Error opening %s, %s\n", + outputBodyFilename, strerror(errno)); + rc = ERROR_CODE; + } + } + /* construct the email body */ + if (rc == 0) { + fprintf(outputBodyFile, + "This is a validation message from the signing server.\n\n" + "You are the project administrator for project: %s\n\n" + "Attached is a copy of the audit log for your archives.\n\n", + frameworkConfig.projectNames[i] + ); + } + /* close the email response file */ + if (outputBodyFile != NULL) { + fclose(outputBodyFile); + outputBodyFile = NULL; + } + /* construct the attachment. Copy because Notes needs a full path name. */ + if (rc == 0) { + rc = File_Copy(outputAttachmentFilename, + frameworkConfig.projectConfigFiles[i]->projectLogFilename); + } + /* construct the subject */ + if (rc == 0) { + subject = realloc(subject, + sizeof(projectSubject1) + + strlen(hostname) + + sizeof(projectSubject3) + + strlen(frameworkConfig.projectNames[i])); + strcpy(subject, projectSubject1); + strcat(subject, hostname); + strcat(subject, projectSubject3); + strcat(subject, frameworkConfig.projectNames[i]); + } + /* send the email response */ + if (rc == 0) { + /* send the message to the project administrator */ + rc = SendMailFileWithAttachment(&frameworkConfig, + frameworkConfig.projectConfigFiles[i]->emailProject, + subject, + outputBodyFilename, + outputAttachmentFilename); + if (rc != 0) { + fprintf(messageFile, "SendMail failed, status %u\n", rc); + rc = ERROR_CODE; + } + } + } + /* cleanup */ + free(subject); /* @1 */ + FrameworkConfig_Delete(&frameworkConfig); /* @2 */ + free(outputBodyFilename); /* @5 */ + free(outputAttachmentFilename); /* @6 */ + return rc; +} + +int AuditArchive_Parse(char **outputBodyFilename, /* freed by caller */ + char **outputAttachmentFilename, /* freed by caller */ + char *configFilename) +{ + int rc = 0; + FILE *configFile = NULL; + char lineBuffer[4000]; + + if (rc == 0) { + rc = File_Open(&configFile, configFilename, "r"); /* closed @1 */ + } + /* output body file name */ + if (rc == 0) { + rc = File_MapNameToValue(outputBodyFilename, /* freed by caller */ + "out_body", + lineBuffer, + sizeof(lineBuffer), + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "audit_archive: Output body file name: %s\n", + *outputBodyFilename); + } + /* output attachment file name */ + if (rc == 0) { + rc = File_MapNameToValue(outputAttachmentFilename, /* freed by caller */ + "out_attachment", + lineBuffer, + sizeof(lineBuffer), + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "audit_archive: Output body file name: %s\n", + *outputBodyFilename); + } + if (configFile != NULL) { + fclose(configFile); /* @1 */ + } + if (rc != 0) { + fprintf(messageFile, + "audit_archive: Error, rc %d\n", rc); + } + return rc; +} + +/* GetArgs() gets the command line arguments + + Returns ERROR_CODE on error. +*/ + +int GetArgs(int argc, + char **argv, + char **configFilename) +{ + int rc = 0; + int i; + + /* command line argument defaults */ + + verbose = FALSE; + *configFilename = NULL; + + /* get the command line arguments */ + for (i=1 ; (i<argc) && (rc == 0) ; i++) { + if (strcmp(argv[i],"-cfg") == 0) { + i++; + if (i < argc) { + *configFilename = argv[i]; + } + else { + fprintf(messageFile, + "audit_archive: Error, -cfg option needs a value\n"); + rc = ERROR_CODE; + } + + } + else if (strcmp(argv[i],"-h") == 0) { + PrintUsage(); + rc = ERROR_CODE; + } + else if (strcmp(argv[i],"-v") == 0) { + verbose = TRUE; + } + else { + fprintf(messageFile, "audit_archive: Error, %s is not a valid option\n", argv[i]); + PrintUsage(); + rc = ERROR_CODE; + } + } + /* check for missing connand line parameters */ + if (*configFilename == NULL) { + fprintf(messageFile, "audit_archive: Error, missing -cfg option\n"); + } + return rc; +} + +void PrintUsage() +{ + fprintf(messageFile, "\n"); + fprintf(messageFile, "audit_archive:\n" + "\t-cfg - configuration file\n" + "\t[-v - verbose tracing]\n" + "\t[-h - print usage help]\n"); + fprintf(messageFile, "\n"); + fprintf(messageFile, "Sends the audit log to the administrators\n"); + fprintf(messageFile, "\n"); + return; +} diff --git a/src/signframework/audit_archive.cfg.sample b/src/signframework/audit_archive.cfg.sample new file mode 100644 index 0000000..4989ed6 --- /dev/null +++ b/src/signframework/audit_archive.cfg.sample @@ -0,0 +1,10 @@ +# Audit Archive program configuration file + +# File names for the output email body + +out_body=audit_archive.txt + +# Rooted !!! file name for the output attachment + +out_attachment=/home/framework/signframework/src/signframework/audit_archive.bin + diff --git a/src/signframework/cca_functions.c b/src/signframework/cca_functions.c new file mode 100644 index 0000000..4cb8ef4 --- /dev/null +++ b/src/signframework/cca_functions.c @@ -0,0 +1,2184 @@ +/* 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 <time.h> + +/* CCA library */ +#if defined AIX +#include <csufincl.h> +#elif defined Linux +#include <csulincl.h> +#else +#error "Must define either AIX or Linux" +#endif + +/* local */ +#include "cca_functions.h" +#include "ossl_functions.h" +#include "debug.h" +#include "utils.h" + +extern FILE* messageFile; +extern int verbose; + +/* local prototypes */ + +int PadCCAString(unsigned char *out, const char *in, size_t length); + + +/* PadCCAString() pads the input string with trailing spaces. This pattern is used by the CCA + profile ID. + + The array 'in' must be of size 'length'. + + 'out' is not a C string, in that it does not have a NUL terminator. +*/ + +int PadCCAString(unsigned char *out, const char *in, size_t length) +{ + int rc = 0; + size_t inLength; + + if (rc == 0) { + inLength = strlen(in); + if (inLength > length) { + fprintf(messageFile, "Error, Illegal CCA profile ID %s\n", in); + rc = ERROR_CODE; + } + } + if (rc == 0) { + memset(out, ' ', length); + memcpy(out, in, inLength); + } + return rc; +} + +/* Login_Control() logs in or out a user profile + + logIn TRUE: log in + logIn FALSE: log out +*/ + +int Login_Control(int logIn, + const char *userName, + const char *password) + +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[16]; /* rule array can be either 1 or 2 8-byte values */ + unsigned char user_id[8]; + unsigned char auth_params[1]; + long auth_params_length; + unsigned char dummy[1]; /* dummy data for logout */ + long auth_data_length; + unsigned char *auth_data; + + /* pad with trailing spaces */ + if (rc == 0) { + rc = PadCCAString(user_id, userName, sizeof(user_id)); + } + if (rc == 0) { + auth_params_length = 0; + auth_params[0] ='\0'; + + if (logIn) { + if (verbose) fprintf(messageFile, "Login_Control: Log in the user profile\n"); + rule_array_count = 2; + memcpy(rule_array,"LOGON ", 8); + memcpy(rule_array + 8,"PPHRASE ", 8); + auth_data_length = strlen(password); + auth_data = (unsigned char *)password; + } + else { + if (verbose) fprintf(messageFile, "Login_Control: Log out the user profile\n"); + rule_array_count = 1; + memcpy(rule_array,"LOGOFF ", 8); + auth_data_length = 0; /* must be 0 even though password not used */ + auth_data = dummy; + } + CSUALCT(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + user_id, /* profile user ID */ + &auth_params_length, /* auth_params_length */ + auth_params, /* auth_params, cannot be NULL */ + &auth_data_length, /* auth_data_length */ + auth_data); /* auth_data, cannot be NULL */ + + if (verbose || (return_code != 0)) { + fprintf(messageFile, " Login_Control: CSUALCT return_code %08lx reason_code %08lx\n", + return_code, reason_code); + fprintf(messageFile, " Login_Control: CSUALCT CCA profile (user name): %s\n", userName); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + } + return rc; +} + +/* Password_ToMechanism() constructs a CCA mechanism in the format required by the + Access_Control_Initialization verb verb_data_2. + + 'password' is a cleartext C string + + ThE format is: + + length (2) length of the following fields, 32 bytes 0x0020 + mechanism ID (2) passphrase is 0x0001 + mechanism strength (2) 0x0180 + expiration date (4) 0x07da (2010) 0x06 June 0x01 1st + attributes (4) - renewable 0x80 00 00 00 + mechanism data (20) - SHA1 hash of password +*/ + +int Password_ToMechanism(unsigned char **mechanism, + size_t *mechanismLength, + unsigned int passwordExpire, + const char *password) +{ + int rc = 0; + time_t currentTime; /* right now */ + struct tm *timeTm; + time_t newTime; /* passwordExpire months from now */ + + if (rc == 0) { + *mechanismLength = 34; + rc = Malloc_Safe(mechanism, *mechanismLength, *mechanismLength); + } + /* get the current time as a time_t */ + if (rc == 0) { + currentTime = time(NULL); + if (currentTime == (time_t)-1) { + fprintf(messageFile, "Error, Server cannot get current time\n"); + rc = ERROR_CODE; + } + } + /* convert to a tm structure */ + if (rc == 0) { + timeTm = localtime(¤tTime); + if (timeTm == NULL) { + fprintf(messageFile, "Error, Server cannot convert current time\n"); + rc = ERROR_CODE; + } + } + /* add the number of months until the password expires */ + if (rc == 0) { + timeTm->tm_mon += passwordExpire; + /* convert the structure, adjusting to legal values */ + newTime = mktime(timeTm); + if (newTime == (time_t)-1) { + fprintf(messageFile, "Error, Server cannot calculate password expiration date\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + /* length */ + (*mechanism)[0] = 0x00; + (*mechanism)[1] = 0x20; + /* mechanism is passphrase */ + (*mechanism)[2] = 0x00; + (*mechanism)[3] = 0x01; + /* strength, the maximum allowed is 255 decimal */ + (*mechanism)[4] = 0x00; + (*mechanism)[5] = 0xff; + /* expiration date */ + /* year, C is 1900 based, CCA is 0 based */ + (*mechanism)[6] = (char)(((timeTm->tm_year + 1900) & 0xff00) >> 8); + (*mechanism)[7] = (char)(( timeTm->tm_year + 1900) & 0x00ff); + /* month, because C is 0 based and CCA is 1 based */ + (*mechanism)[8] = (char)((timeTm->tm_mon + 1) & 0x00ff); /* month */ + (*mechanism)[9] = (char)(timeTm->tm_mday & 0xff); /* day */ + /* attributes */ + (*mechanism)[10] = 0x80; + (*mechanism)[11] = 0x00; + (*mechanism)[12] = 0x00; + (*mechanism)[13] = 0x00; + /* SHA-1 hash of password */ + Ossl_SHA1(&((*mechanism)[14]), + strlen(password), password, + 0, NULL); + } + return rc; +} + +/* Access_Control_Initialization() changes the password for the specified CCA profile (user) + + passwordExpire gives the expiration period in months. +*/ + +int Access_Control_Initialization(const char *profileID, + unsigned int passwordExpire, + const char *password) +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 2; + unsigned char rule_array[16]; + unsigned char user_id[8]; + long userIDLength; + unsigned char *mechanism = NULL; + size_t mechanismLength; + + + /* pad with trailing spaces */ + if (rc == 0) { + userIDLength = sizeof(user_id); + rc = PadCCAString(user_id, profileID, sizeof(user_id)); + } + /* construct the CCA mechanism */ + if (rc == 0) { + rc = Password_ToMechanism(&mechanism, + &mechanismLength, + passwordExpire, + password); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Access_Control_Initialization: Changing password for profile %s\n", + profileID); + memcpy(rule_array, "CHG-AD ", 8); /* change a user password */ + memcpy(rule_array + 8,"PROTECTD", 8); /* proof that the user has authenticated */ + + CSUAACI(&return_code, + &reason_code, + &exit_data_length, + NULL, /* exit data */ + &rule_array_count, + rule_array, + &userIDLength, + user_id, /* 8 chacter profile ID */ + (long *)&mechanismLength, + mechanism); + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Access_Control_Initialization: CSUAACI return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + + } + free(mechanism); + return rc; +} + +/* Crypto_Facility_SetClock() sets the card clock to the current time. + + This should never be used. It's here only because my (Ken Goldman) 4764 has a broken clock that + drifts excessively. I hacked a repair by setting the clock every hour using a cron job. +*/ + +int Crypto_Facility_SetClock() +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 1; + unsigned char rule_array[8]; + unsigned char verb_data[17]; /* YYYYMMDDHHmmSSWW + nul */ + long verb_data_length = sizeof(verb_data) - 1; + time_t gmt; + long len; /* length of string */ + + if (rc == 0) { + gmt = time(NULL); + len = strftime((char *)verb_data, verb_data_length + 1, "%Y%m%d%H%M%S0%w", gmtime(&gmt)); + verb_data[15] += 1; /* C is 0 based, CCA is 1 based */ + if (verbose) fprintf(messageFile, "Crypto_Facility_SetClock: Time is %s\n", verb_data); + if (len != verb_data_length) { + fprintf(messageFile, "Error, TIme string length %ld is not %ld\n", + len, verb_data_length); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Crypto_Facility_SetClock: Resetting clock time\n"); + memcpy(rule_array, "SETCLOCK", 8); + + CSUACFC(&return_code, + &reason_code, + &exit_data_length, + NULL, /* exit data */ + &rule_array_count, + rule_array, + &verb_data_length, + verb_data); + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Crypto_Facility_SetClock: CSUACFC return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + } + return rc; +} + +/* Random_Number_Generate_Long() gets a random number from the card. + + */ + +int Random_Number_Generate_Long(unsigned char *random_number, + size_t random_number_length_in) +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 1; + unsigned char rule_array[8]; + + long seed_length = 0; + long random_number_length = random_number_length_in; + + if (rc == 0) { + memcpy(rule_array,"RANDOM ", 8); +#if 0 + if (verbose) fprintf(messageFile, "Random_Number_Generate_Long: \n"); +#endif + /* get random numbers */ + CSNBRNGL(&return_code, + &reason_code, + &exit_data_length, + NULL, /* exit data */ + &rule_array_count, + rule_array, + &seed_length, + NULL, /* seed */ + &random_number_length, + random_number); +#if 0 + if (return_code == 0) { + if (verbose) PrintAll(messageFile, + " Random_Number_Generate_Long: ", + random_number_length , + random_number); + } +#endif + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Random_Number_Generate_Long: CSNBRNGL return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (random_number_length_in != (unsigned long)random_number_length) { + rc = ERROR_CODE; + } + } + return rc; +} + +/* Key_Generate() generates an AES key + + generated_key_identifier_1 must be a 64-byte array +*/ + +int Key_Generate(unsigned char *generated_key_identifier_1) /* output: key token */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + + unsigned char key_form[8]; + unsigned char key_length[8]; + unsigned char key_type_1[8]; + unsigned char key_type_2[8]; + unsigned char KEK_key_identifier_1[64]; + unsigned char KEK_key_identifier_2[64]; + unsigned char generated_key_identifier_2[64]; + + memcpy(key_form, "OP ", 8); + memcpy(key_length, "KEYLN16 ", 8); + memcpy(key_type_1, "AESDATA ", 8); + memcpy(key_type_2, " ", 8); + memset(KEK_key_identifier_1, 0x00, 64); + memset(KEK_key_identifier_2, 0x00, 64); + memset(generated_key_identifier_1, 0x00, 64); + memset(generated_key_identifier_2, 0x00, 64); + + if (verbose) fprintf(messageFile, "Key_Generate: generate an AES key\n"); + + /* generate an AES key */ + CSNBKGN(&return_code, + &reason_code, + &exit_data_length, + NULL, /* exit data */ + key_form, + key_length, + key_type_1, + key_type_2, + KEK_key_identifier_1, + KEK_key_identifier_2, + generated_key_identifier_1, + generated_key_identifier_2); + +#if 0 + if (return_code == 0) { + if (verbose) PrintAll(messageFile, + " Key_Generate: key token", + 64, + generated_key_identifier_1); + } +#endif + if (verbose || (return_code != 0)) { + fprintf(messageFile, " Key_Generate: CSNBKGN return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + return rc; +} + +/* PKA_Key_Token_Build() builds a skeleton RSA 2048-bit key token + + */ + +/* key_values_structure for skeleton key token */ +static const char rsaCrtStruct[] = {0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01}; /* RSA 2048 65537 */ +static const char rsaCrtStruct4096[] = {0x10, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01}; /* RSA 4096 65537 */ + +/* PKA_Key_Token_Build() builds a skeleton for an RSA bitSize key. If encrypt is FALSE, restricts + to a signing key. +*/ + +int PKA_Key_Token_Build(long *token_length, /* i/o: skeleton key token length */ + unsigned char *token, /* output: skeleton key token */ + unsigned int bitSize, + int encrypt) +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[16]; /* rule array can be either 1 or 2 8-byte values */ + long key_values_structure_length; /* key parameter values */ + unsigned char key_values_structure[2500]; /* maximum length */ + long key_name_length; + long reserved_1_length; + long reserved_2_length; + long reserved_3_length; + long reserved_4_length; + long reserved_5_length; + + if (verbose) fprintf(messageFile, "PKA_Key_Token_Build: create a skeleton key token\n"); + + if (rc == 0) { + exit_data_length = 0; /* must be 0 */ + rule_array_count = 2; + + memcpy(rule_array, "RSA-CRT ", 8); /* store in CRT */ + if (!encrypt) { /* if encrypt disallowed */ + if (verbose) fprintf(messageFile, "PKA_Key_Token_Build: sign only\n"); + memcpy(rule_array + 8, "SIG-ONLY", 8); /* signing key */ + } + else { + if (verbose) fprintf(messageFile, "PKA_Key_Token_Build: sign and encrypt\n"); + memcpy(rule_array + 8, "KEY-MGMT", 8); /* signing key */ + } + switch (bitSize) { + case 2048: + memcpy(key_values_structure, rsaCrtStruct, + sizeof(rsaCrtStruct)); /* RSA 2048 65537 CRT */ + key_values_structure_length = sizeof(rsaCrtStruct); + break; + case 4096: + memcpy(key_values_structure, rsaCrtStruct4096, + sizeof(rsaCrtStruct4096)); /* RSA 4096 65537 CRT */ + key_values_structure_length = sizeof(rsaCrtStruct4096); + break; + default: + if (verbose) { + fprintf(messageFile, " PKA_Key_Token_Build: Illegal bitSize %u\n", bitSize); + } + rc = ERROR_CODE; + break; + } + } + + if (rc == 0) { + + key_name_length = 0; + + reserved_1_length = 0; + reserved_2_length = 0; + reserved_3_length = 0; + reserved_4_length = 0; + reserved_5_length = 0; + + if (verbose) fprintf(messageFile, "PKA_Key_Token_Build: rule array count %lu\n", + rule_array_count); + /* create skeleton */ + CSNDPKB(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + &key_values_structure_length, + key_values_structure, + &key_name_length, + rule_array, /* key_name, even though the length is 0, the API + does not accept a NULL pointer here */ + &reserved_1_length, + NULL, /* reserved_1 */ + &reserved_2_length, + NULL, /* reserved_2 */ + &reserved_3_length, + NULL, /* reserved_3 */ + &reserved_4_length, + NULL, /* reserved_4 */ + &reserved_5_length, + NULL, /* reserved_5 */ + token_length, + token); /* output skeleton key token */ + if (verbose || (return_code != 0)) { + fprintf(messageFile, " PKA_Key_Token_Build: CSNDPKB return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + } + return rc; +} + +/* PKA_Key_Generate() generates an RSA key pair using the skeleton key token + + */ + +int PKA_Key_Generate(long *generated_key_identifier_length, /* i/o: key token */ + unsigned char *generated_key_identifier, /* output */ + long skeleton_key_token_length, /* input */ + unsigned char *skeleton_key_token) /* input */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[16]; /* rule array can be either 1 or 2 8-byte values */ + long regeneration_data_length; + unsigned char transport_key_identifier[64]; + + if (verbose) fprintf(messageFile, "PKA_Key_Generate: generate a key pair\n"); + + exit_data_length = 0; /* must be 0 */ + + rule_array_count = 1; + memcpy(rule_array, "MASTER ", 8); /* encipher with the master key */ + + regeneration_data_length = 0; /* base key on random seed */ + + memset(transport_key_identifier, 0, + sizeof(transport_key_identifier)); /* not used with MASTER */ + + generated_key_identifier[0] = 0; /* put output key token here */ + + /* generate a key based on skeleton token */ + CSNDPKG(&return_code, + &reason_code, + &exit_data_length, + NULL, /* exit data */ + &rule_array_count, + rule_array, + ®eneration_data_length, + NULL, /* regeneration_data */ + &skeleton_key_token_length, + skeleton_key_token, + transport_key_identifier, + generated_key_identifier_length, + generated_key_identifier); + if (verbose || (return_code != 0)) { + fprintf(messageFile, " PKA_Key_Generate: CSNDPKG return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + if (return_code == 0) { + if (verbose) PrintAll(messageFile, + " PKA_Key_Generate: key token", + *generated_key_identifier_length, generated_key_identifier); + } + return rc; +} + +/* Digital_Signature_Generate() generates a digital signature + + 'signature_field' is the output signature. + 'hash' is the hash of the data to be signed. + 'PKA_private_key' is a PKA96 key pair, the CCA key token +*/ + +int Digital_Signature_Generate(unsigned long *signature_field_length, /* i/o */ + unsigned long *signature_bit_length, /* output */ + unsigned char *signature_field, /* output */ + unsigned long PKA_private_key_length, /* input */ + unsigned char *PKA_private_key, /* input */ + unsigned long hash_length, /* input */ + unsigned char *hash) /* input */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[16]; /* rule array can be either 1 or 2 8-byte values */ + + if (verbose) fprintf(messageFile, + "Digital_Signature_Generate: generate the digital signature\n"); + if (verbose) PrintAll(messageFile, + " Digital_Signature_Generate: message hash", hash_length, hash); + + exit_data_length = 0; /* must be 0 */ + + rule_array_count = 1; + memcpy(rule_array,"PKCS-1.1", 8); /* PKCS#1 padding */ + + CSNDDSG(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + (long *)&PKA_private_key_length, + PKA_private_key, + (long *)&hash_length, + hash, + (long *)signature_field_length, + (long *)signature_bit_length, + signature_field); + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Digital_Signature_Generate: CSNDDSG return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + if (return_code == 0) { + if (verbose) PrintAll(messageFile, + " Digital_Signature_Generate: signature", + *signature_field_length, signature_field); + } + return rc; +} + +/* Digital_Signature_Verify() verifies the signature using the coprocessor. + + 'key_token' can be either the public/private key pair or the public key. + 'hash' is a hash of the data to be verified. + 'signature_field' is the signature to be verified. +*/ + +int Digital_Signature_Verify(unsigned long signature_field_length, /* input */ + unsigned char *signature_field, /* input */ + unsigned long key_token_length, /* input */ + unsigned char *key_token, /* input */ + unsigned long hash_length, /* input */ + unsigned char *hash) /* input */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[16]; /* rule array can be either 1 or 2 8-byte values */ + + if (verbose) fprintf(messageFile, + "Digital_Signature_Verify: " + "verify the digital signature using the coprocessor\n"); + + exit_data_length = 0; /* must be 0 */ + + rule_array_count = 1; + memcpy(rule_array,"PKCS-1.1", 8); /* PKCS#1 padding */ + + CSNDDSV(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + (long *)&key_token_length, + key_token, + (long *)&hash_length, + hash, + (long *)&signature_field_length, + signature_field); + + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Digital_Signature_Verify: CSNDDSV return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + return rc; +} + +/* PKA_Decrypt decrypts the input data using an RSA private key */ + +int PKA_Decrypt(unsigned long *cleartext_length, /* i/o < 512 */ + unsigned char *cleartext, /* output */ + unsigned long PKA_private_key_length, /* input */ + unsigned char *PKA_private_key, /* input */ + unsigned long ciphertext_length, /* input */ + unsigned char *ciphertext) /* input */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[16]; /* rule array can be either 1 or 2 8-byte values */ + + if (verbose) fprintf(messageFile, + "PKA_Decrypt: Private key decrypt\n"); + if (verbose) PrintAll(messageFile, + " PKA_Decrypt: ciphertext", ciphertext_length, ciphertext); + + exit_data_length = 0; /* must be 0 */ + long data_structure_length = 0; + + rule_array_count = 1; + memcpy(rule_array,"PKCS-1.2", 8); /* PKCS#1 padding */ + + CSNDPKD(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + (long *)&ciphertext_length, /* source_encrypted_key_length */ + ciphertext, /* source_encrypted_key */ + &data_structure_length, + NULL, + (long *)&PKA_private_key_length, /* private_key_identifier_length */ + PKA_private_key, /* private_key_identifier */ + (long *)cleartext_length, /* clear_target_key_length */ + cleartext); /* clear_target_key */ + + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " PKA_Decrypt: CSNDPKD return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + return rc; +} + +/* PKA_Encrypt decrypts the input data using an RSA private key */ + +int PKA_Encrypt(unsigned long *ciphertext_length, /* output */ + unsigned char *ciphertext, /* i/o */ + unsigned long PKA_public_key_length, /* input */ + unsigned char *PKA_public_key, /* input */ + unsigned long cleartext_length, /* input */ + unsigned char *cleartext) /* input < 512 */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[16]; /* rule array can be either 1 or 2 8-byte values */ + + if (verbose) fprintf(messageFile, + "PKA_Encrypt: Private key decrypt\n"); + if (verbose) PrintAll(messageFile, + " PKA_Encrypt: cleartext", cleartext_length, cleartext); + + exit_data_length = 0; /* must be 0 */ + long data_structure_length = 0; + + rule_array_count = 1; + memcpy(rule_array,"PKCS-1.2", 8); /* PKCS#1 padding */ + + CSNDPKE(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + (long *)&cleartext_length, /* clear_source_data_length */ + cleartext, /* clear_source_data */ + &data_structure_length, + NULL, + (long *)&PKA_public_key_length, /* public_key_identifier_length */ + PKA_public_key, /* public_key_identifier */ + (long *)ciphertext_length, /* target_data_length */ + ciphertext); /* target_data */ + + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " PKA_Encrypt: CSNDPKE return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + return rc; +} + +/* Symmetric_Algorithm_Encipher() encrypts cleartext to ciphertext using key_identifier */ + +int Symmetric_Algorithm_Encipher(long *ciphertext_length, + unsigned char **ciphertext, /* freed by caller */ + long cleartext_length, + unsigned char *cleartext, + unsigned char *initialization_vector, + const unsigned char *key_identifier) +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 4; + unsigned char rule_array[32]; /* 4 8-byte values */ + long key_identifier_length = 64; /* internal key token */ + long key_parms_length = 0; + long block_size = 16; + long initialization_vector_length = 16; + long chain_data_length = 32; + unsigned char chain_data[32]; + long optional_data_length = 0; + + if (rc == 0) { + memcpy(rule_array, "AES ", 8); /* AES key */ + memcpy(rule_array + 8, "PKCS-PAD", 8); /* pad with 1-16 bytes */ + memcpy(rule_array + 16, "KEYIDENT", 8); /* internal key token */ + memcpy(rule_array + 24, "INITIAL ", 8); /* select IV */ + + memset(chain_data, 0, 32); + + /* add space for PKCS padding */ + *ciphertext_length = ((cleartext_length + 16)/16) * 16; +#if 1 + /* FIXME hack. The GA 4765 csulcca returns up to 16 bytes more than it should */ + *ciphertext = malloc((*ciphertext_length) + 16); +#else + *ciphertext = malloc(*ciphertext_length); +#endif + if (*ciphertext == NULL) { + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Symmetric_Algorithm_Encipher: AES Encipher the data\n"); + + /* AES encrypt */ + CSNBSAE(&return_code, + &reason_code, + &exit_data_length, + NULL, /* exit data */ + &rule_array_count, + rule_array, + &key_identifier_length, + (unsigned char *)key_identifier, + &key_parms_length, + NULL, /* key_parms */ + &block_size, + &initialization_vector_length, + initialization_vector, + &chain_data_length, + chain_data, + &cleartext_length, + cleartext, + ciphertext_length, + *ciphertext, + &optional_data_length, + NULL); /* optional_data */ +#if 0 + if (return_code == 0) { + if (verbose) PrintAll(messageFile, + " Symmetric_Algorithm_Encipher: ciphertext", + *ciphertext_length, + *ciphertext); + } +#endif + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Symmetric_Algorithm_Encipher: CSNBSAE return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + } + return rc; +} + +/* Symmetric_Algorithm_Decipher() decrypts ciphertext to cleartext using key_identifier */ + +int Symmetric_Algorithm_Decipher(long *cleartext_length, + unsigned char **cleartext, /* freed by caller */ + long ciphertext_length, + unsigned char *ciphertext, + unsigned char *initialization_vector, + const unsigned char *key_identifier) +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 4; + unsigned char rule_array[32]; /* 4 8-byte values */ + long key_identifier_length = 64; /* internal key token */ + long key_parms_length = 0; + long block_size = 16; + long initialization_vector_length = 16; + long chain_data_length = 32; + unsigned char chain_data[32]; + long optional_data_length = 0; + + if (rc == 0) { + memcpy(rule_array, "AES ", 8); /* AES key */ + memcpy(rule_array + 8, "PKCS-PAD", 8); /* pad with 1-16 bytes */ + memcpy(rule_array + 16, "KEYIDENT", 8); /* internal key token */ + memcpy(rule_array + 24, "INITIAL ", 8); /* select IV */ + + memset(chain_data, 0, 32); + + *cleartext_length = ciphertext_length; + *cleartext = malloc(*cleartext_length ); + + if (*cleartext == NULL) { + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Symmetric_Algorithm_Decipher: AES Decipher the data\n"); + + /* AES decrypt */ + CSNBSAD(&return_code, + &reason_code, + &exit_data_length, + NULL, /* exit data */ + &rule_array_count, + rule_array, + &key_identifier_length, + (unsigned char *)key_identifier, + &key_parms_length, + NULL, /* key_parms */ + &block_size, + &initialization_vector_length, + initialization_vector, + &chain_data_length, + chain_data, + &ciphertext_length, + ciphertext, + cleartext_length, + *cleartext, + &optional_data_length, + NULL); /* optional_data */ +#if 0 + if (return_code == 0) { + if (verbose) PrintAll(messageFile, + " Symmetric_Algorithm_Decipher: cleartext", + *cleartext_length, + *cleartext); + } +#endif + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Symmetric_Algorithm_Decipher: CSNBSAD return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + } + return rc; +} + +/* CCA_PrintError() prints the CCA text message based on return_code and reason_code. + + The message text was taken from: + + IBM PCI Cryptographic Coprocessor + CCA Basic Services Reference and Guide + Release 2.41, Revised September 2003 + for IBM 4758 Models 002 and 023 +*/ + +void CCA_PrintError(long return_code, + long reason_code) +{ + switch (return_code) { + case 0x00: + CCA_PrintReturn00(reason_code); + break; + case 0x04: + CCA_PrintReturn04(reason_code); + break; + case 0x08: + CCA_PrintReturn08(reason_code); + break; + case 0x0c: + CCA_PrintReturn0c(reason_code); + break; + case 0x10: + CCA_PrintReturn10(reason_code); + break; + default: + fprintf(messageFile, "Unknown return code: %08lx\n", return_code); + } + return; +} + +void CCA_PrintReturn00(long reason_code) +{ + switch (reason_code) { + case 0x000: + fprintf(messageFile, "The verb completed processing successfully.\n"); + break; + case 0x002: + fprintf(messageFile, "One or more bytes of a key do not have odd parity.\n"); + break; + case 0x008: + fprintf(messageFile, "No value is present to be processed.\n"); + break; + case 0x097: + fprintf(messageFile, "The key token supplies the MAC length or MACLEN4 is the default\n" + "for key tokens that contain MAC or MACVER keys.\n"); + break; + case 0x2BD: + fprintf(messageFile, "A new master-key value was found to have duplicate thirds.\n"); + break; + case 0x2BE: + fprintf(messageFile, "A provided master-key part did not have odd parity.\n"); + break; + case 0x2711: + fprintf(messageFile, "A key encrypted under the old master-key was used.\n"); + break; + default: + fprintf(messageFile, "Unknown return code 00 reason code: %08lx\n", reason_code); + } + return; +} +void CCA_PrintReturn04(long reason_code) +{ + switch (reason_code) { + + case 0x001: + fprintf(messageFile, "The verification test failed.\n"); + break; + case 0x00D: + fprintf(messageFile, "The key token has an initialization vector,\n" + "and the initialization_vector parameter value is nonzero.\n" + "The verb uses the value in the key token.\n"); + break; + case 0x010: + fprintf(messageFile, "The rule array and the rule-array count are too small\n" + "to contain the complete result.\n"); + break; + case 0x011: + fprintf(messageFile, "The requested ID is not present in any profile in the\n" + "specified cryptographic hardware component.\n"); + break; + case 0x013: + fprintf(messageFile, "The financial PIN in a PIN block is not verified.\n"); + break; + case 0x09E: + fprintf(messageFile, "The Key_Token_Change, Key_Record_Delete, or Key_Record_Write\n" + "verbs did not process any records.\n"); + break; + case 0x0A6: + fprintf(messageFile, "The control vector is not valid because of parity bits,\n" + "anti-variant bits, or inconsistent KEK bits, or because\n" + "bits 59 to 62 are not zero.\n"); + break; + case 0x0B3: + fprintf(messageFile, + "The control-vector keywords that are in the rule array are ignored.\n"); + break; + case 0x11B: + fprintf(messageFile, "The Cryptographic Coprocessor battery is low.\n"); + break; + case 0x11F: + fprintf(messageFile, "The PIN-block format is not consistent.\n"); + break; + case 0x1AD: + fprintf(messageFile, "The digital signature is not verified. The verb completed\n" + "its processing normally.\n"); + break; + case 0x400: + fprintf(messageFile, "Sufficient shares have been processed to create a new master-key.\n"); + break; + case 0x7F7: + fprintf(messageFile, "At least one control vector bit cannot be parsed.\n"); + break; + case 0x7FA: + fprintf(messageFile, "The supplied passphrase is invalid.\n"); + break; + default: + fprintf(messageFile, "Unknown return code 04 reason code: %08lx\n", reason_code); + } + return; +} +void CCA_PrintReturn08(long reason_code) +{ + switch (reason_code) { + + case 0x00C: + fprintf(messageFile, "The token-validation value in an external key token is not valid.\n"); + break; + case 0x016: + fprintf(messageFile, "The ID number in the request field is not valid.\n"); + break; + case 0x017: + fprintf(messageFile, "An access to the data area is outside the data-area boundary.\n"); + break; + case 0x018: + fprintf(messageFile, "The master-key verification pattern is not valid.\n"); + break; + case 0x019: + fprintf(messageFile, "The value that the text_length parameter specifies is not valid.\n"); + break; + case 0x01A: + fprintf(messageFile, "The value of the PIN is not valid.\n"); + break; + case 0x01D: + fprintf(messageFile, "The token-validation value in an internal key token is not valid.\n"); + break; + case 0x01E: + fprintf(messageFile, "No record with a matching key label is in key storage.\n"); + break; + case 0x01F: + fprintf(messageFile, "The control vector does not specify a DATA key.\n"); + break; + case 0x020: + fprintf(messageFile, "A key label format is not valid.\n"); + break; + case 0x021: + fprintf(messageFile, "A rule array or other parameter specifies a keyword that is not\n" + "valid.\n"); + break; + case 0x022: + fprintf(messageFile, "A rule-array keyword combination is not valid.\n"); + break; + case 0x023: + fprintf(messageFile, "A rule-array count is not valid.\n"); + break; + case 0x024: + fprintf(messageFile, "The action command must be specified in the rule array.\n"); + break; + case 0x025: + fprintf(messageFile, "The object type must be specified in the rule array.\n"); + break; + case 0x027: + fprintf(messageFile, "A control vector violation occurred. Check all control vectors\n" + "employed with the verb. For security reasons, no detail is provided.\n"); + break; + case 0x028: + fprintf(messageFile, "The service code does not contain numerical character data.\n"); + break; + case 0x029: + fprintf(messageFile, "The keyword specified by the key_form parameter is not valid.\n"); + break; + case 0x02A: + fprintf(messageFile, "The expiration date is not valid.\n"); + break; + case 0x02B: + fprintf(messageFile, "The length specified by the key_token_length parameter or the\n" + "keyword specified by the key_length parameter is not valid.\n"); + break; + case 0x02C: + fprintf(messageFile, "A record with a matching key label already exists in key storage.\n"); + break; + case 0x02D: + fprintf(messageFile, "The input character string cannot be found in the code table.\n"); + break; + case 0x02E: + fprintf(messageFile, "The card-validation value (CVV) is not valid.\n"); + break; + case 0x02F: + fprintf(messageFile, "A source key token is unusable because it contains data that is\n" + "not valid or is undefined.\n"); + break; + case 0x030: + fprintf(messageFile, "One or more keys has a master-key verification pattern that is " + "not valid.\n"); + break; + case 0x031: + fprintf(messageFile, "A key-token version number found in a key token is not supported.\n"); + break; + case 0x032: + fprintf(messageFile, "The key-serial-number specified in the rule array is not valid.\n"); + break; + case 0x033: + fprintf(messageFile, "The value that the text_length parameter identifies is not a\n" + "multiple of the cryptographic algorithm block length.\n"); + break; + case 0x036: + fprintf(messageFile, "The value that the pad_character parameter specifies is not valid.\n"); + break; + case 0x037: + fprintf(messageFile, "The initialization vector in the key token is enciphered.\n"); + break; + case 0x038: + fprintf(messageFile, "The master-key verification pattern in the OCV is not valid.\n"); + break; + case 0x03A: + fprintf(messageFile, "The parity of the operating key is not valid.\n"); + break; + case 0x03B: + fprintf(messageFile, "Control information (for example, the processing method or the\n" + "pad character) in the key token conflicts with that in the rule array.\n"); + break; + case 0x03C: + fprintf(messageFile, "A cryptographic request with the FIRST or MIDDLE keywords and a\n" + "text length less than 8 bytes is not valid.\n"); + break; + case 0x03D: + fprintf(messageFile, "The keyword specified by the key_type parameter is not valid.\n"); + break; + case 0x03E: + fprintf(messageFile, "The source key is not present.\n"); + break; + case 0x03F: + fprintf(messageFile, "A key token has an invalid token header (for example, not an\n" + "internal token).\n"); + break; + case 0x040: + fprintf(messageFile, "The key is not permitted to perform the requested operation. A\n" + "likely cause is that key distribution usage is not enabled for the key.\n"); + break; + case 0x041: + fprintf(messageFile, "The key token failed consistency checking.\n"); + break; + case 0x042: + fprintf(messageFile, "The recovered encryption block failed validation checking.\n"); + break; + case 0x043: + fprintf(messageFile, "RSA encryption failed.\n"); + break; + case 0x044: + fprintf(messageFile, "RSA decryption failed.\n"); + break; + case 0x048: + fprintf(messageFile, "The value that the size parameter specifies is not valid (too small,\n" + "too large, negative, or zero).\n"); + break; + case 0x051: + fprintf(messageFile, "The modulus length (key size) exceeds the allowable maximum.\n"); + break; + case 0x055: + fprintf(messageFile, "The date or the time value is not valid.\n"); + break; + case 0x05A: + fprintf(messageFile, "Access control checking failed. See the Required commands section\n" + "for the failing verb.\n"); + break; + case 0x05B: + fprintf(messageFile, "The time that was sent in your logon request was more than five\n" + "minutes different from the clock in the secure module.\n"); + break; + case 0x05C: + fprintf(messageFile, "The user profile is expired.\n"); + break; + case 0x05D: + fprintf(messageFile, "The user profile has not yet reached its activation date.\n"); + break; + case 0x05E: + fprintf(messageFile, "The authentication data (for example, passphrase) is expired.\n"); + break; + case 0x05F: + fprintf(messageFile, "Access to the data is not authorized.\n"); + break; + case 0x060: + fprintf(messageFile, "An error occurred reading or writing the secure clock.\n"); + break; + case 0x064: + fprintf(messageFile, "The PIN length is not valid.\n"); + break; + case 0x065: + fprintf(messageFile, "The PIN check length is not valid. It must be in the range from\n" + "4 to the PIN length inclusive.\n"); + break; + case 0x066: + fprintf(messageFile, "The value of the decimalization table is not valid.\n"); + break; + case 0x067: + fprintf(messageFile, "The value of the validation data is not valid.\n"); + break; + case 0x068: + fprintf(messageFile, "The value of the customer-selected PIN is not valid, or the PIN\n" + "length does not match the value specified by the PIN_length parameter or defined\n" + "by the PIN-block format specified in the PIN profile.\n"); + break; + case 0x069: + fprintf(messageFile, "The value of the transaction_security_parameter is not valid.\n"); + break; + case 0x06A: + fprintf(messageFile, "The PIN-block format keyword is not valid.\n"); + break; + case 0x06B: + fprintf(messageFile, "The format control keyword is not valid.\n"); + break; + case 0x06C: + fprintf(messageFile, "The value or the placement of the padding data is not valid.\n"); + break; + case 0x06D: + fprintf(messageFile, "The extraction method keyword is not valid.\n"); + break; + case 0x06E: + fprintf(messageFile, "The value of the PAN data is not numeric character data.\n"); + break; + case 0x06F: + fprintf(messageFile, "The sequence number is not valid.\n"); + break; + case 0x070: + fprintf(messageFile, "The PIN offset is not valid.\n"); + break; + case 0x072: + fprintf(messageFile, "The PVV value is not valid.\n"); + break; + case 0x074: + fprintf(messageFile, "The clear PIN value is not valid. For example, digits other\n" + "than 0 - 9 were found.\n"); + break; + case 0x078: + fprintf(messageFile, "An origin or destination identifier is not valid.\n"); + break; + case 0x079: + fprintf(messageFile, "The value specified by the inbound_key or source_key parameter\n" + "is not valid.\n"); + break; + case 0x07A: + fprintf(messageFile, "The value specified by the inbound_KEK_count or outbound_count\n" + "parameter is not valid.\n"); + break; + case 0x07D: + fprintf(messageFile, "A PKA92-encrypted key having the same EID as the local node cannot\nz" + "be imported.\n"); + break; + case 0x081: + fprintf(messageFile, "Required rule-array keyword not found.\n"); + break; + case 0x099: + fprintf(messageFile, "The text length exceeds the system limits.\n"); + break; + case 0x09A: + fprintf(messageFile, "The key token that the key_identifier parameter specifies is\n" + "not an internal key-token or a key label.\n"); + break; + case 0x09B: + fprintf(messageFile, "The value that the generated_key_identifier parameter specifies is\n" + "not valid, or it is not consistent with the value that the key_form parameter\n" + "specifies.\n"); + break; + case 0x09C: + fprintf(messageFile, "A keyword is not valid with the specified parameters.\n"); + break; + case 0x09D: + fprintf(messageFile, "The key-token type is not specified in the rule array.\n"); + break; + case 0x09F: + fprintf(messageFile, "The keyword supplied with the option parameter is not valid.\n"); + break; + case 0x0A0: + fprintf(messageFile, "The key type and the key length are not consistent.\n"); + break; + case 0x0A1: + fprintf(messageFile, "The value that the dataset_name_length parameter specifies is not\n" + "valid.\n"); + break; + case 0x0A2: + fprintf(messageFile, "The offset value is not valid.\n"); + break; + case 0x0A3: + fprintf(messageFile, "The value that the dataset_name parameter specifies is not valid.\n"); + break; + case 0x0A4: + fprintf(messageFile, "The starting address of the output area falls inside the input\n" + "area.\n"); + break; + case 0x0A5: + fprintf(messageFile, "The carryover_character_count that is specified in the chaining " + "vector is not valid.\n"); + break; + case 0x0A8: + fprintf(messageFile, "A hexadecimal MAC value contains characters that are not valid,\n" + "or the MAC on a request or reply failed because the user session key in the\n" + "host and the adapter card do not match.\n"); + break; + case 0x0A9: + fprintf(messageFile, "The MDC_Generate text length is in error.\n"); + break; + case 0x0AA: + fprintf(messageFile, "The value of the mechanism strength in the passphrase authentication\n" + "data structure of the user profile is less than the minimum authorization level\n" + "required.\n"); + break; + case 0x0AB: + fprintf(messageFile, "The control_array_count value is not valid.\n"); + break; + case 0x0AF: + fprintf(messageFile, "The key token cannot be parsed because no control vector is\n" + "present.\n"); + break; + case 0x0B4: + fprintf(messageFile, "A key token presented for parsing is null.\n"); + break; + case 0x0B5: + fprintf(messageFile, "The key token is not valid. The first byte is not valid, or an\n" + "incorrect token type was presented.\n"); + break; + case 0x0B7: + fprintf(messageFile, "The key type is not consistent with the key type of the control\n" + "vector.\n"); + break; + case 0x0B8: + fprintf(messageFile, "A required pointer is null.\n"); + break; + case 0x0B9: + fprintf(messageFile, "A disk I/O error occurred: perhaps the file is in-use, does not\n" + "exist, and so forth.\n"); + break; + case 0x0BA: + fprintf(messageFile, "The key-type field in the control vector is not valid.\n"); + break; + case 0x0BB: + fprintf(messageFile, "The requested MAC length (MACLEN4, MACLEN6, MACLEN8) is not\n" + "consistent with the control vector (key-A, key-B).\n"); + break; + case 0x0BF: + fprintf(messageFile, "The requested MAC length (MACLEN6, MACLEN8) is not consistent with\n" + "the control vector (MAC-LN-4).\n"); + break; + case 0x0C0: + fprintf(messageFile, "A key-storage record contains a record validation value that is not\n" + "valid.\n"); + break; + case 0x0CC: + fprintf(messageFile, "A memory allocation failed. This can occur in the host and in the\n" + "coprocessor. Try closing other host tasks. If the problem persists, contact the\n" + "IBM support center.\n"); + break; + case 0x0CD: + fprintf(messageFile, "The X9.23 ciphering method is not consistent with the use of the\n" + "CONTINUE keyword.\n"); + break; + case 0x143: + fprintf(messageFile, "The ciphering method that the Decipher verb used does not match the\n" + "ciphering method that the Encipher verb used.\n"); + break; + case 0x14F: + fprintf(messageFile, "Either the specified cryptographic hardware component or the\n" + "environment cannot implement this function.\n"); + break; + case 0x154: + fprintf(messageFile, "One of the input control vectors has odd parity.\n"); + break; + case 0x157: + fprintf(messageFile, "Either the data block or the buffer for the block is too small,\n" + "or a variable has caused an attempt to create an internal data structure that\n" + "is too large.\n"); + break; + case 0x176: + fprintf(messageFile, "Less data was supplied than expected or less data exists than was\n" + "requested.\n"); + break; + case 0x179: + fprintf(messageFile, "A key-storage error occurred.\n"); + break; + case 0x17E: + fprintf(messageFile, "A time-limit violation occurred.\n"); + break; + case 0x181: + fprintf(messageFile, "The cryptographic hardware component reported that the data passed\n" + "as part of a command is not valid for that command.\n"); + break; + case 0x183: + fprintf(messageFile, "The cryptographic hardware component reported that the user ID or\n" + "role ID is not valid.\n"); + break; + case 0x189: + fprintf(messageFile, "The command was not processed because the profile cannot be used.\n"); + break; + case 0x18A: + fprintf(messageFile, "The command was not processed because the expiration date was\n" + "exceeded.\n"); + break; + case 0x18D: + fprintf(messageFile, "The command was not processed because the active profile requires\n" + "the user to be verified first.\n"); + break; + case 0x18E: + fprintf(messageFile, "The command was not processed because the maximum PIN or password\n" + "failure limit is exceeded.\n"); + break; + case 0x197: + fprintf(messageFile, "There is a PIN-block consistency-check-error.\n"); + break; + case 0x1B7: + fprintf(messageFile, "Key cannot be completed because all required key parts have not\n" + "yet been accumulated, or key is already complete.\n"); + break; + case 0x1B9: + fprintf(messageFile, "Key part cannot be added because key is complete.\n"); + break; + case 0x1BA: + fprintf(messageFile, "DES keys with replicated halves are not allowed.\n"); + break; + case 0x25D: + fprintf(messageFile, "The number of output bytes is greater than the number that is\n" + "permitted.\n"); + break; + case 0x2BF: + fprintf(messageFile, "A new master-key value is one of the weak DES keys.\n"); + break; + case 0x2C0: + fprintf(messageFile, "A new master key cannot have the same master-key version number\n" + "or master-key verification pattern as the current master-key.\n"); + break; + case 0x2C1: + fprintf(messageFile, "Both exporter keys specify the same key-encrypting key.\n"); + break; + case 0x2C2: + fprintf(messageFile, "Pad count in deciphered data is not valid.\n"); + break; + case 0x2C3: + fprintf(messageFile, "The master-key registers are not in the state required for the\n" + "requested function.\n"); + break; + case 0x2C9: + fprintf(messageFile, "The algorithm or function is not available on this hardware (DES\n" + "on a CDMF-only system, or Triple-DES on DES-only or CDMF-only system)\n"); + break; + case 0x2CA: + fprintf(messageFile, "A reserved parameter must be a null pointer or an expected value.\n"); + break; + case 0x2CB: + fprintf(messageFile, "A parameter that must have a value of zero is not valid.\n"); + break; + case 0x2CE: + fprintf(messageFile, "The hash value of the data block in the decrypted RSA-OAEP block\n" + "does not match the hash of the decrypted data block.\n"); + break; + case 0x2CF: + fprintf(messageFile, "The block format (BT) field in the decrypted RSA-OAEP block does\n" + "not have the correct value.\n"); + break; + case 0x2D0: + fprintf(messageFile, "The initial byte (I) in the decrypted RSA-OAEP block does not have\n" + "a valid value.\n"); + break; + case 0x2D1: + fprintf(messageFile, "The V field in the decrypted RSA-OAEP does not have the correct\n" + "value.\n"); + break; + case 0x2F0: + fprintf(messageFile, "The key-storage file path is not usable.\n"); + break; + case 0x2F1: + fprintf(messageFile, "Opening the key-storage file failed.\n"); + break; + case 0x2F2: + fprintf(messageFile, "An internal call to the key_test command failed.\n"); + break; + case 0x2F4: + fprintf(messageFile, "Creation of the key-storage file failed.\n"); + break; + case 0x2F8: + fprintf(messageFile, "An RSA key-modulus length in bits or in bytes is not valid.\n"); + break; + case 0x2F9: + fprintf(messageFile, "An RSA-key exponent length is not valid.\n"); + break; + case 0x2FA: + fprintf(messageFile, "A length in the key value structure is not valid.\n"); + break; + case 0x2FB: + fprintf(messageFile, "The section identification number within a key token is not valid.\n"); + break; + case 0x302: + fprintf(messageFile, "The PKA key-token has a field that is not valid.\n"); + break; + case 0x303: + fprintf(messageFile, "The user is not logged on.\n"); + break; + case 0x304: + fprintf(messageFile, "The requested role does not exist.\n"); + break; + case 0x305: + fprintf(messageFile, "The requested profile does not exist.\n"); + break; + case 0x306: + fprintf(messageFile, "The profile already exists.\n"); + break; + case 0x307: + fprintf(messageFile, "The supplied data is not replaceable.\n"); + break; + case 0x308: + fprintf(messageFile, "The requested ID is already logged on.\n"); + break; + case 0x309: + fprintf(messageFile, "The authentication data is not valid.\n"); + break; + case 0x30A: + fprintf(messageFile, "The checksum for the role is in error.\n"); + break; + case 0x30B: + fprintf(messageFile, "The checksum for the profile is in error.\n"); + break; + case 0x30C: + fprintf(messageFile, "There is an error in the profile data.\n"); + break; + case 0x30D: + fprintf(messageFile, "There is an error in the role data.\n"); + break; + case 0x30E: + fprintf(messageFile, "The function-control-vector header is not valid.\n"); + break; + case 0x30F: + fprintf(messageFile, "The command is not permitted by the function-control-vector value.\n"); + break; + case 0x310: + fprintf(messageFile, "The operation you requested cannot be performed because the user\n" + "profile is in use.\n"); + break; + case 0x311: + fprintf(messageFile, "The operation you requested cannot be performed because the role\n" + "is in use.\n"); + break; + case 0x401: + fprintf(messageFile, "The registered public key or retained private key name already\n" + "exists.\n"); + break; + case 0x402: + fprintf(messageFile, "The key name (registered public key or retained private key) does\n" + "not exist.\n"); + break; + case 0x403: + fprintf(messageFile, "Environment identifier data is already set.\n"); + break; + case 0x404: + fprintf(messageFile, "Master key share data is already set.\n"); + break; + case 0x405: + fprintf(messageFile, "There is an error in the EID data.\n"); + break; + case 0x406: + fprintf(messageFile, "There is an error in using the master key share data.\n"); + break; + case 0x407: + fprintf(messageFile, "There is an error in using registered public key or retained\n" + "private key data.\n"); + break; + case 0x408: + fprintf(messageFile, "There is an error in using registered public key hash data.\n"); + break; + case 0x409: + fprintf(messageFile, "The public key hash was not registered.\n"); + break; + case 0x40A: + fprintf(messageFile, "The public key was not registered.\n"); + break; + case 0x40B: + fprintf(messageFile, "The public key certificate signature was not verified.\n"); + break; + case 0x40D: + fprintf(messageFile, "There is a master key shares distribution error.\n"); + break; + case 0x40E: + fprintf(messageFile, "The public key hash is not marked for cloning.\n"); + break; + case 0x40F: + fprintf(messageFile, "The registered public key hash does not match the registered hash.\n"); + break; + case 0x410: + fprintf(messageFile, "The master key share enciphering key could not be enciphered.\n"); + break; + case 0x411: + fprintf(messageFile, "The master key share enciphering key could not be deciphered.\n"); + break; + case 0x412: + fprintf(messageFile, "The master key share digital signature generate failed.\n"); + break; + case 0x413: + fprintf(messageFile, "The master key share digital signature verify failed.\n"); + break; + case 0x414: + fprintf(messageFile, "There is an error in reading VPD data from the adapter.\n"); + break; + case 0x415: + fprintf(messageFile, "Encrypting the cloning information failed.\n"); + break; + case 0x416: + fprintf(messageFile, "Decrypting the cloning information failed.\n"); + break; + case 0x417: + fprintf(messageFile, "There is an error loading new master key from master key shares.\n"); + break; + case 0x418: + fprintf(messageFile, "The clone information has one or more sections that are not valid.\n"); + break; + case 0x419: + fprintf(messageFile, "The master key share index is not valid.\n"); + break; + case 0x41A: + fprintf(messageFile, "The public-key encrypted-key is rejected because the EID with the\n" + "key is the same as the EID for this node.\n"); + break; + case 0x41B: + fprintf(messageFile, "The private key is rejected because the key is not flagged for use\n" + "in master-key cloning.\n"); + break; + case 0x41C: + fprintf(messageFile, "Token identifier of the header section is in the\n" + "range X'20' - X'FF'.\n"); + break; + case 0x41D: + fprintf(messageFile, "The Active flag in section X'14' of the trusted block is not " + "disabled.\n"); + break; + case 0x41E: + fprintf(messageFile, "Token identifier of the header section is not external X'1E'.\n"); + break; + case 0x41F: + fprintf(messageFile, "The Active flag in section X'14' of the trusted block is not\n" + "enabled.\n"); + break; + case 0x420: + fprintf(messageFile, "Token identifier of the header section is not internal X'1F'.\n"); + break; + case 0x421: + fprintf(messageFile, "Trusted block rule section X'12' rule ID does not match input " + "parameter rule ID.\n"); + break; + case 0x422: + fprintf(messageFile, "Trusted block contains a value that is too small or too large.\n"); + break; + case 0x423: + fprintf(messageFile, "A trusted block parameter that must have a value of zero (or a\n" + "grouping of bits set to zero} is not valid.\n"); + break; + case 0x424: + fprintf(messageFile, "Trusted block public-key section failed consistency checking.\n"); + break; + case 0x425: + fprintf(messageFile, "Trusted block contains at least one extraneous section or\n" + "subsection (TLV).\n"); + break; + case 0x426: + fprintf(messageFile, "Trusted block has at least one missing section or\n" + "subsection (TLV).\n"); + break; + case 0x427: + fprintf(messageFile, "Trusted block contains at least one duplicate section or\n" + "subsection (TLV).\n"); + break; + case 0x428: + fprintf(messageFile, "The expiration date of the trusted block is expired (compared\n" + "to the cryptographic coprocessor clock).\n"); + break; + case 0x429: + fprintf(messageFile, "The expiration date of the trusted block precedes the activation\n" + "date.\n"); + break; + case 0x42A: + fprintf(messageFile, "Trusted block public key modulus bit length is not consistent with\n" + "the byte length. The bit length must be less than or equal to 8 * byte length,\n" + "and greater than 8 (byte length - 1).\n"); + break; + case 0x42B: + fprintf(messageFile, "Trusted block public key modulus length in bits exceeds maximum\nzz" + "allowed bit length as defined by the function control vector (FCV).\n"); + break; + case 0x42C: + fprintf(messageFile, "One or more trusted block sections or TLV objects contains data\n" + "that is not valid (for example, invalid label data in label section X'13').\n"); + break; + case 0x42D: + fprintf(messageFile, "Trusted block verification attempted by function other than\n" + "CSNDDSV, CSNDKTC, CSNBKPI, CSNDRKX, or CSNDTBC.\n"); + break; + case 0x42E: + fprintf(messageFile, "Trusted block rule ID contained within the rule section contains\n" + "one or more invalid characters.\n"); + break; + case 0x42F: + fprintf(messageFile, "The key length or control vector of the source key does not match\n" + "the rule section in the trusted block that was selected by the rule ID input\n" + "parameter.\n"); + break; + case 0x430: + fprintf(messageFile, "The activation date is not valid.\n"); + break; + case 0x431: + fprintf(messageFile, "The source-key label does not match the template in the export\n" + "key DES token parameters TLV object of the selected trusted block rule section.\n"); + break; + case 0x432: + fprintf(messageFile, "The control-vector value specified in the common export key\n" + "parameters TLV object in the selected rule section of the trusted block contains\n" + "a control vector that is not valid.\n"); + break; + case 0x433: + fprintf(messageFile, "The source-key label template in the export key DES token parameters\n" + "TLV object in the selected rule section of the trusted block contains a label\n" + "template that is not valid.\n"); + break; + case 0x435: + fprintf(messageFile, "Key wrapping option input error.\n"); + break; + case 0x436: + fprintf(messageFile, "Key wrapping Security Relevant Data Item (SRDI) error.\n"); + break; + case 0x44C: + fprintf(messageFile, "There is a general hardware device driver execution error.\n"); + break; + case 0x44D: + fprintf(messageFile, "There is a hardware device driver parameter that is not valid.\n"); + break; + case 0x44E: + fprintf(messageFile, "There is a hardware device driver non-valid buffer length.\n"); + break; + case 0x44F: + fprintf(messageFile, "The hardware device driver has too many opens. The device cannot\n" + "open now.\n"); + break; + case 0x450: + fprintf(messageFile, "The hardware device driver is denied access.\n"); + break; + case 0x451: + fprintf(messageFile, "The hardware device driver device is busy and cannot perform the\n" + "request now.\n"); + break; + case 0x452: + fprintf(messageFile, "The hardware device driver buffer is too small and the received\nz" + "data is truncated.\n"); + break; + case 0x453: + fprintf(messageFile, "The hardware device driver request is interrupted and the request\n" + "is aborted.\n"); + break; + case 0x454: + fprintf(messageFile, "The hardware device driver detected a security tamper event.\n"); + break; + case 0x7F2: + fprintf(messageFile, "The environment variable that was used to set the default\n" + "coprocessor is not valid, or does not exist for a coprocessor in the system.\n"); + break; + case 0x7F4: + fprintf(messageFile, "The contents of a chaining vector are not valid. Ensure that the\n" + "chaining vector was not modified by your application program.\n"); + break; + case 0x7F6: + fprintf(messageFile, "No RSA private key information is provided.\n"); + break; + case 0x7F9: + fprintf(messageFile, "A default coprocessor environment variable is not valid.\n"); + break; + case 0x802: + fprintf(messageFile, "The current-key serial number (CKSN) field in the PIN_profile " + "variable is not valid (not hexadecimal or too many 1 bits).\n"); + break; + case 0x803: + fprintf(messageFile, "There is a non-valid message length in the OAEP-decoded\n" + "zinformation.\n"); + break; + case 0x805: + fprintf(messageFile, "No message found in the OAEP-decoded data.\n"); + break; + case 0x806: + fprintf(messageFile, "There is a non-valid RSA Enciphered Key cryptogram: OAEP optional\n" + "encoding parameters failed validation.\n"); + break; + case 0x807: + fprintf(messageFile, "The RSA public key is too small to encrypt the symmetric " + "(AES or DES) key.\n"); + break; + case 0x80E: + fprintf(messageFile, "The active role does not permit you to change the characteristic\n" + "of a double-length key in the Key_Part_Import parameter.\n"); + break; + case 0x811: + fprintf(messageFile, "The specified key token is not null.\n"); + break; + case 0x829: + fprintf(messageFile, "There is an inconsistency in the specification of a cryptographic\n" + "algorithm. The verb contains multiple keywords or parameters that indicate the\n" + "algorithm to be used, and at least one of these specifies or implies a different\n" + "algorithm from the others.\n"); + break; + case 0x82F: + fprintf(messageFile, "The key_type value is not compatible with the key_form value.\n"); + break; + case 0x831: + fprintf(messageFile, "The key_length value is not compatible with the key_type value.\n"); + break; + case 0x832: + fprintf(messageFile, "Either an AES key-token contains an invalid clear-key bit length\n" + "(not 128, 192, or 256), or an external DES key-token with a token version\n" + "number of X'01' has an invalid key-length flag.\n"); + break; + case 0x833: + fprintf(messageFile, "Byte length of encrypted key in AES key-token is invalid.\n"); + break; + case 0x83A: + fprintf(messageFile, "An input/output error occurred while accessing the logged on\n" + "users table.\n"); + break; + case 0x83E: + fprintf(messageFile, "Invalid wrapping type.\n"); + break; + case 0x83F: + fprintf(messageFile, "Control vector enhanced bit (bit 56) conflicts with key\n" + "wrapping keyword.\n"); + break; + case 0x841: + fprintf(messageFile, "A key token contains invalid payload.\n"); + break; + case 0x842: + fprintf(messageFile, "Clear-key bit length is out of range.\n"); + break; + case 0x843: + fprintf(messageFile, "Input key token cannot have a key present when importing the\n" + "first key part; skeleton key token is required.\n"); + break; + case 0xBB9: + fprintf(messageFile, "The RSA-OAEP block contains a PIN block and the verb did not\n" + "request PINBLOCK processing.\n"); + break; + case 0xBC5: + fprintf(messageFile, "The LRC checksum in the AES key-token does not match the LRC\n" + "checksum of the clear key.\n"); + break; + case 0x1770: + fprintf(messageFile, "The specified device is already allocated.\n"); + break; + case 0x1771: + fprintf(messageFile, "No device is allocated.\n"); + break; + case 0x1772: + fprintf(messageFile, "The specified device does not exist.\n"); + break; + case 0x1773: + fprintf(messageFile, "The specified device is an improper type.\n"); + break; + case 0x1774: + fprintf(messageFile, "Use of the specified device is not authorized for this user.\n"); + break; + case 0x1775: + fprintf(messageFile, "The specified device is not varied online.\n"); + break; + case 0x1776: + fprintf(messageFile, "The specified device is in a damaged state.\n"); + break; + case 0x1777: + fprintf(messageFile, "The key-storage file is not designated.\n"); + break; + case 0x1778: + fprintf(messageFile, "The key-storage file is not found.\n"); + break; + case 0x1779: + fprintf(messageFile, "The specified key-storage file is either the wrong type or the\n" + "wrong format.\n"); + break; + case 0x177A: + fprintf(messageFile, "The user is not authorized to use the key-storage file.\n"); + break; + case 0x177B: + fprintf(messageFile, "The specified CCA verb request is not permitted from a secondary\n" + "thread.\n"); + break; + case 0x177C: + fprintf(messageFile, "A cryptographic resource is already allocated.\n"); + break; + case 0x177D: + fprintf(messageFile, "The length of the cryptographic resource name is not valid.\n"); + break; + case 0x177E: + fprintf(messageFile, "The cryptographic resource name is not valid, or does not refer\n" + "to a coprocessor that is available in the system.\n"); + break; + default: + fprintf(messageFile, "Unknown return code 08 reason code: %08lx\n", reason_code); + } + return; +} + +void CCA_PrintReturn0c(long reason_code) +{ + switch (reason_code) { + case 0x05D: + fprintf(messageFile, "The security server is not available or not loaded.\n"); + break; + case 0x061: + fprintf(messageFile, + "File space in key storage is insufficient to complete the operation.\n"); + break; + case 0x0C4: + fprintf(messageFile, + "The device driver, the security server, or the directory server is not\n" + "installed, or is not active, or in AIX, file permissions are not valid\n" + "for your application.\n"); + break; + case 0x0C5: + fprintf(messageFile, "A key-storage file I/O error occurred, or a file was not found.\n"); + break; + case 0x0CE: + fprintf(messageFile, + "The key-storage file is not valid, or the master-key verification failed.\n" + "There is an unlikely but possible synchronization problem with the\n" + "Master_Key_Process verb.\n"); + break; + case 0x0CF: + fprintf(messageFile, "The verification method flags in the profile are not valid.\n"); + break; + case 0x144: + fprintf(messageFile, + "There was insufficient memory available to process your request, either\n" + "memory in the host computer, or memory inside the Coprocessor including\n" + "the Flash EPROM used to store keys, profiles, and other application data.\n"); + break; + case 0x152: + fprintf(messageFile, "This cryptographic hardware device driver is not installed or is not\n" + "responding, or the CCA code is not loaded in the Coprocessor.\n"); + break; + case 0x153: + fprintf(messageFile, "A system error occurred in interprocess communication routine.\n"); + break; + case 0x2FC: + fprintf(messageFile, "The master key(s) are not loaded and therefore a key could not be\n" + "recovered or enciphered.\n"); + break; + case 0x300: + fprintf(messageFile, + "One or more paths for key-storage directory operations is improperly specified.\n"); + break; + case 0x7FD: + fprintf(messageFile, "The CCA software was unable to claim a semaphore. The system may be\n" + "short of resources.\n"); + break; + case 0x7FE: + fprintf(messageFile, "The CCA software was unable to list all of the keys. The limit of\n" + "500 000 keys may have been reached.\n"); + break; + default: + fprintf(messageFile, "Unknown return code 0c reason code: %08lx\n", reason_code); + } + return; +} +void CCA_PrintReturn10(long reason_code) +{ + switch (reason_code) { + case 0x063: + fprintf(messageFile, "An unrecoverable error occurred in the security server; contact your\n" + "IBM service representative.\n"); + break; + case 0x150: + fprintf(messageFile, + "An error occurred in a cryptographic hardware or software component.\n"); + break; + case 0x151: + fprintf(messageFile, "A device software error occurred.\n"); + break; + case 0x1BC: + fprintf(messageFile, "The verb-unique-data had an invalid length.\n"); + break; + case 0x22C: + fprintf(messageFile, "The request parameter block failed consistency checking.\n"); + break; + case 0x2C4: + fprintf(messageFile, "Inconsistent data was returned from the cryptographic engine.\n"); + break; + case 0x2C5: + fprintf(messageFile, + "Cryptographic engine internal error, could not access the master-key data.\n"); + break; + case 0x2C6: + fprintf(messageFile, + "An unrecoverable error occurred while attempting to update master-key data\n" + "items.\n"); + break; + case 0x2C8: + fprintf(messageFile, "An unexpected error occurred in the master-key manager.\n"); + break; + case 0x301: + fprintf(messageFile, "The host system code or the CCA application in the Coprocessor\n" + "encountered an unexpected error and was unable to process the request.\n" + "Windows NT and 2000, and OS/2 support is limited to 32 concurrent requests.\n"); + break; + case 0x7FF: + fprintf(messageFile, "Unable to transfer Request Data from host to Coprocessor.\n"); + break; + case 0x809: + fprintf(messageFile, "Internal error: memory allocation failure.\n"); + break; + case 0x80A: + fprintf(messageFile, "Internal error: unexpected return code from OAEP routines.\n"); + break; + case 0x80B: + fprintf(messageFile, "Internal error: OAEP SHA-1 request failure.\n"); + break; + case 0x80D: + fprintf(messageFile, + "Internal error in CSNDSYI, OAEP-decode: enciphered message too long.\n"); + break; + default: + fprintf(messageFile, "Unknown return code 10 reason code: %08lx\n", reason_code); + } + return; +} diff --git a/src/signframework/cca_functions.h b/src/signframework/cca_functions.h new file mode 100644 index 0000000..bdabb9b --- /dev/null +++ b/src/signframework/cca_functions.h @@ -0,0 +1,92 @@ +/* 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. + */ + +#ifndef CCA_FUNCTIONS_H +#define CCA_FUNCTIONS_H + +#define CCA_KEY_IDENTIFIER_LENGTH 64 /* See CCA manual Key_Generate */ + +int Login_Control(int logIn, + const char *userName, + const char *password); +int Access_Control_Initialization(const char *profileID, + unsigned int passwordExpire, + const char *password); +int Crypto_Facility_SetClock(void); +int Password_ToMechanism(unsigned char **mechanism, + size_t *mechanismLength, + unsigned int passwordExpire, + const char *password); + +int Key_Generate(unsigned char *generated_key_identifier_1); +int PKA_Key_Token_Build(long *token_length, + unsigned char *token, + unsigned int bitSize, + int encrypt); +int Random_Number_Generate_Long(unsigned char *random_number, + size_t random_number_length_in); +int PKA_Decrypt(unsigned long *cleartext_length, + unsigned char *cleartext, + unsigned long PKA_private_key_length, + unsigned char *PKA_private_key, + unsigned long ciphertext_length, + unsigned char *ciphertext); +int PKA_Encrypt(unsigned long *ciphertext_length, + unsigned char *ciphertext, + unsigned long PKA_public_key_length, + unsigned char *PKA_public_key, + unsigned long cleartext_length, + unsigned char *cleartext); + +int Symmetric_Algorithm_Encipher(long *ciphertext_length, + unsigned char **ciphertext, + long cleartext_length, + unsigned char *cleartext, + unsigned char *initialization_vector, + const unsigned char *key_identifier); +int Symmetric_Algorithm_Decipher(long *cleartext_length, + unsigned char **cleartext, + long ciphertext_length, + unsigned char *ciphertext, + unsigned char *initialization_vector, + const unsigned char *key_identifier); +int PKA_Key_Generate(long *generated_key_identifier_length, + unsigned char *generated_key_identifier, + long skeleton_key_token_length, + unsigned char *skeleton_key_token); +int Digital_Signature_Generate(unsigned long *signature_field_length, + unsigned long *signature_bit_length, + unsigned char *signature_field, + unsigned long PKA_private_key_length, + unsigned char *PKA_private_key, + unsigned long hash_length, + unsigned char *hash); +int Digital_Signature_Verify(unsigned long signature_field_length, + unsigned char *signature_field, + unsigned long key_token_length, + unsigned char *key_token, + unsigned long hash_length, + unsigned char *hash); + +void CCA_PrintError(long return_code, + long reason_code); +void CCA_PrintReturn00(long reason_code); +void CCA_PrintReturn04(long reason_code); +void CCA_PrintReturn08(long reason_code); +void CCA_PrintReturn0c(long reason_code); +void CCA_PrintReturn10(long reason_code); + +#endif diff --git a/src/signframework/cca_functions_ecc.c b/src/signframework/cca_functions_ecc.c new file mode 100644 index 0000000..4008c3e --- /dev/null +++ b/src/signframework/cca_functions_ecc.c @@ -0,0 +1,231 @@ +/* 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 <csulincl.h> + +#include "cca_functions.h" +#include "cca_functions_ecc.h" +#include "debug.h" +#include "utils.h" + +extern FILE* messageFile; +extern int verbose; + +/* key_values_structure for skeleton key token */ +static const char eccP521Struct[] = {0x00, /* prime curve */ + 0x00, /* reserved */ + 0x02, 0x09, /* P-521 */ + 0x00, 0x00, /* length of the private key d */ + 0x00, 0x00, /* length of the public key */ +}; + +/* PKA_Key_Token_Build_ECC() builds a skeleton RSA 2048-bit key token + + */ + +int PKA_Key_Token_Build_ECCP521(long *token_length, /* i/o: skeleton key token length */ + unsigned char *token) /* output: skeleton key token */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[16]; /* rule array can be either 1 or 2 8-byte values */ + long key_values_structure_length; /* key parameter values */ + unsigned char key_values_structure[2500]; /* maximum length */ + long key_name_length; + long reserved_1_length; + long reserved_2_length; + long reserved_3_length; + long reserved_4_length; + long reserved_5_length; + + if (verbose) fprintf(messageFile, "PKA_Key_Token_Build_ECCP521: create a skeleton key token\n"); + + rule_array_count = 2; + memcpy(rule_array, "ECC-PAIR", 8); /* ECC public and private key pair */ + memcpy(rule_array + 8, "SIG-ONLY", 8); /* signing key */ + + key_values_structure_length = 8; + memcpy(key_values_structure, eccP521Struct, + sizeof(eccP521Struct)); /* ECC P-521 */ + + key_name_length = 0; + + reserved_1_length = 0; + reserved_2_length = 0; + reserved_3_length = 0; + reserved_4_length = 0; + reserved_5_length = 0; + + /* create skeleton */ + CSNDPKB(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + &key_values_structure_length, + key_values_structure, + &key_name_length, + rule_array, /* key_name, even though the length is 0, the API + does not accept a NULL pointer here */ + &reserved_1_length, + NULL, /* reserved_1 */ + &reserved_2_length, + NULL, /* reserved_2 */ + &reserved_3_length, + NULL, /* reserved_3 */ + &reserved_4_length, + NULL, /* reserved_4 */ + &reserved_5_length, + NULL, /* reserved_5 */ + token_length, + token); /* output skeleton key token */ + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " PKA_Key_Token_Build_ECCP521: CSNDPKB return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + return rc; +} + +/* Digital_Signature_Generate_ECC() generates a digital signature + + 'signature_field' is the output signature. + 'hash' is the hash of the data to be signed. + 'PKA_private_key' is a PKA96 key pair, the CCA key token +*/ + +int Digital_Signature_Generate_ECC(unsigned long *signature_field_length, /* i/o */ + unsigned long *signature_bit_length, /* output */ + unsigned char *signature_field, /* output */ + unsigned long PKA_private_key_length, /* input */ + unsigned char *PKA_private_key, /* input */ + unsigned long hash_length, /* input */ + unsigned char *hash) /* input */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[8]; /* rule array */ + + if (verbose) fprintf(messageFile, + "Digital_Signature_Generate_ECC: generate the digital signature\n"); + if (verbose) PrintAll(messageFile, + " Digital_Signature_Generate_ECC: message hash", hash_length, hash); + + exit_data_length = 0; /* must be 0 */ + + rule_array_count = 1; + memcpy(rule_array,"ECDSA ", 8); + + CSNDDSG(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + (long *)&PKA_private_key_length, + PKA_private_key, + (long *)&hash_length, + hash, + (long *)signature_field_length, + (long *)signature_bit_length, + signature_field); + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Digital_Signature_Generate_ECC: CSNDDSG return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + if (return_code == 0) { + if (verbose) PrintAll(messageFile, + " Digital_Signature_Generate_ECC: signature", + *signature_field_length, signature_field); + } + return rc; +} + +/* Digital_Signature_Verify_ECC() verifies the signature using the coprocessor. + + 'key_token' can be either the public/private key pair or the public key. + 'hash' is a hash of the data to be verified. + 'signature_field' is the signature to be verified. +*/ + +int Digital_Signature_Verify_ECC(unsigned long signature_field_length, /* input */ + unsigned char *signature_field, /* input */ + unsigned long key_token_length, /* input */ + unsigned char *key_token, /* input */ + unsigned long hash_length, /* input */ + unsigned char *hash) /* input */ +{ + int rc = 0; + long return_code = 0; + long reason_code = 0; + long exit_data_length = 0; + long rule_array_count = 0; + unsigned char rule_array[8]; /* rule array */ + + if (verbose) fprintf(messageFile, + "Digital_Signature_Verify_ECC: " + "verify the digital signature using the coprocessor\n"); + + exit_data_length = 0; /* must be 0 */ + + rule_array_count = 1; + memcpy(rule_array,"ECDSA ", 8); + + CSNDDSV(&return_code, + &reason_code, + &exit_data_length, + NULL, + &rule_array_count, + rule_array, + (long *)&key_token_length, + key_token, + (long *)&hash_length, + hash, + (long *)&signature_field_length, + signature_field); + + if (verbose || (return_code != 0)) { + fprintf(messageFile, + " Digital_Signature_Verify_ECC: CSNDDSV return_code %08lx reason_code %08lx\n", + return_code, reason_code); + } + if (return_code != 0) { + CCA_PrintError(return_code, reason_code); + rc = ERROR_CODE; + } + return rc; +} + diff --git a/src/signframework/cca_functions_ecc.h b/src/signframework/cca_functions_ecc.h new file mode 100644 index 0000000..a8ab997 --- /dev/null +++ b/src/signframework/cca_functions_ecc.h @@ -0,0 +1,36 @@ +/* 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. + */ + +#ifndef CCA_FUNCTIONS_ECC_H +#define CCA_FUNCTIONS_ECC_H + +int PKA_Key_Token_Build_ECCP521(long *token_length, + unsigned char *token); +int Digital_Signature_Generate_ECC(unsigned long *signature_field_length, + unsigned long *signature_bit_length, + unsigned char *signature_field, + unsigned long PKA_private_key_length, + unsigned char *PKA_private_key, + unsigned long hash_length, + unsigned char *hash); +int Digital_Signature_Verify_ECC(unsigned long signature_field_length, + unsigned char *signature_field, + unsigned long key_token_length, + unsigned char *key_token, + unsigned long hash_length, + unsigned char *hash); + +#endif diff --git a/src/signframework/cca_structures.c b/src/signframework/cca_structures.c new file mode 100644 index 0000000..38e1e0a --- /dev/null +++ b/src/signframework/cca_structures.c @@ -0,0 +1,995 @@ +/* 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> + +/* AIX specific */ +#include <sys/types.h> +#include <netinet/in.h> + +/* local */ +#include "cca_structures.h" +#include "utils.h" +#include "debug.h" + +extern FILE* messageFile; +extern int verbose; + +/* getPKA96PublicKey() returns a CCA RsaKeyTokenPublic structure with the members filled in from the + binary PKA96 key token. + +*/ + +long getPKA96PublicKey(RsaKeyTokenPublic *rsaKeyTokenPublic, + long keyTokenLength, + unsigned char *keyToken, + unsigned int bitSize) +{ + long rc = 0; + int hasPrivKey; /* boolean */ + RsaKeyTokenHeader rsaKeyTokenHeader; + RsaKeyTokenPrivate rsaKeyTokenPrivate; + + /* parse the PKA96 key token header */ + if (rc == 0) { + rc = parsePKA96KeyTokenHeader(&rsaKeyTokenHeader, + &keyTokenLength, + &keyToken); + } + /* if there's a private key section, parse it to get the public modulus n */ + if (rc == 0) { + if ((keyTokenLength > 0) && (*keyToken == RSA_PRIVATE_KEY_CRT)) { + rc = parsePKA96KeyTokenPrivateKey(rsaKeyTokenPublic, + &rsaKeyTokenPrivate, + &keyTokenLength, + &keyToken, + bitSize); + hasPrivKey = TRUE; /* flag to remember that the modulus n came from the private key */ + } + else { + hasPrivKey = FALSE; + } + } + /* check that the next section is a public key token */ + if (rc == 0) { + if (keyTokenLength == 0) { + if (verbose) fprintf(messageFile, + "getPKA96PublicKey: Error, no public key section\n"); + return -1; + } + } + /* parse the public key to get e and possibly n + + If the key token is a public key, the public modulus n and exponent are parsed here. + + If the key token is a private key, the public part has the exponent but not the modulus. + parsePKA96KeyTokenPrivateKey() put the modulus n and its length in the structure. It should + not be overwritten by the zero length here. + */ + if (rc == 0) { + rc = parsePKA96KeyTokenPublicKey(rsaKeyTokenPublic, + &keyTokenLength, + &keyToken, + hasPrivKey); + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "getPKA96PublicKey: public key", + rsaKeyTokenPublic->nByteLength, rsaKeyTokenPublic->n); + } + return rc; +} + +/* parsePKA96KeyTokenHeader() returns a CCA RsaKeyTokenHeader structure with the members filled in + from the binary PKA96 key token. + + keyTokenLength is decremented and keyToken is incremented as binary data is consumed. +*/ + +long parsePKA96KeyTokenHeader(RsaKeyTokenHeader *rsaKeyTokenHeader, + long *keyTokenLength, + unsigned char **keyToken) +{ + /* tokenId */ + if (*keyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenHeader: Error parsing tokenId \n"); + return -1; + } + rsaKeyTokenHeader->tokenId = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if ((rsaKeyTokenHeader->tokenId != PKA_EXTERNAL_TOKEN) && + (rsaKeyTokenHeader->tokenId != PKA_INTERNAL_TOKEN)) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenHeader: Error, unknown tokenId %02x\n", + rsaKeyTokenHeader->tokenId); + return -1; + } + /* version */ + if (*keyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenHeader: Error parsing version\n"); + return -1; + } + rsaKeyTokenHeader->version = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (rsaKeyTokenHeader->version != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenHeader: Error, unknown version %02x\n", + rsaKeyTokenHeader->version); + return -1; + } + /* tokenLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenHeader: Error parsing tokenLength \n"); + return -1; + } + rsaKeyTokenHeader->tokenLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + /* reserved */ + if (*keyTokenLength < 4) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenHeader: Error parsing reserved\n"); + return -1; + } + rsaKeyTokenHeader->reserved = ntohl(*(unsigned long *)*keyToken); + *keyToken += 4; + *keyTokenLength -= 4; + if (rsaKeyTokenHeader->reserved != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenHeader: Error, reserved %08lx\n", + rsaKeyTokenHeader->reserved); + return -1; + } + if (verbose) printPKA96KeyTokenHeader(rsaKeyTokenHeader); + return 0; +} + +/* parsePKA96KeyTokenPublicKey() returns a CCA RsaKeyTokenPublic structure + with the members filled in from the binary PKA96 public key token. + + If 'hasPrivKey' is FALSE, the modulus n and its length are extracted here. + + If 'hasPrivKey' is TRUE, the stream is part of a private/public key token, and the modulus n has + already been extracted from the private key part. Here, the modulus n length should be zero, + and is not used. + + keyTokenLength is decremented and keyToken is incremented as binary data is consumed. +*/ + +long parsePKA96KeyTokenPublicKey(RsaKeyTokenPublic *rsaKeyTokenPublic, + long *pubKeyTokenLength, + unsigned char **pubKeyToken, + int hasPrivKey) +{ + unsigned short nByteLengthPub; /* parsed here */ + + /* sectionId */ + if (*pubKeyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing sectionId\n"); + return -1; + } + rsaKeyTokenPublic->sectionId = **pubKeyToken; + *pubKeyToken += 1; + *pubKeyTokenLength -= 1; + if (rsaKeyTokenPublic->sectionId != RSA_PUBLIC_SECTION) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error, unknown sectionId %02x\n", + rsaKeyTokenPublic->sectionId); + return -1; + } + /* version */ + if (*pubKeyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing version\n"); + return -1; + } + rsaKeyTokenPublic->version = **pubKeyToken; + *pubKeyToken += 1; + *pubKeyTokenLength -= 1; + if (rsaKeyTokenPublic->version != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error, unknown version %02x\n", + rsaKeyTokenPublic->version); + return -1; + } + /* sectionLength */ + if (*pubKeyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing sectionLength\n"); + return -1; + } + rsaKeyTokenPublic->sectionLength = ntohs(*(unsigned short *)*pubKeyToken); + *pubKeyToken += 2; + *pubKeyTokenLength -= 2; + /* reserved[2] */ + if (*pubKeyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing reserved\n"); + return -1; + } + rsaKeyTokenPublic->reserved[0] = **pubKeyToken; + *pubKeyToken += 1; + rsaKeyTokenPublic->reserved[1] = **pubKeyToken; + *pubKeyToken += 1; + *pubKeyTokenLength -= 2; + /* eLength */ + if (*pubKeyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing eLength\n"); + return -1; + } + rsaKeyTokenPublic->eLength = ntohs(*(unsigned short *)*pubKeyToken); + *pubKeyToken += 2; + *pubKeyTokenLength -= 2; + if (rsaKeyTokenPublic->eLength > E_SIZE) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error eLength %04hx too large\n", + rsaKeyTokenPublic->eLength); + return -1; + } + /* nBitLength */ + if (*pubKeyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing nBitLength\n"); + return -1; + } + rsaKeyTokenPublic->nBitLength = ntohs(*(unsigned short *)*pubKeyToken); + *pubKeyToken += 2; + *pubKeyTokenLength -= 2; + /* nByteLength */ + if (*pubKeyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing nByteLength\n"); + return -1; + } + nByteLengthPub = ntohs(*(unsigned short *)*pubKeyToken); /* parsed here */ + *pubKeyToken += 2; + *pubKeyTokenLength -= 2; + if (nByteLengthPub > N_SIZE_MAX) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error, nByteLength %04hx too large\n", + rsaKeyTokenPublic->nByteLength); + return -1; + } + /* if the token does not have a private key, use this value */ + if (!hasPrivKey) { + rsaKeyTokenPublic->nByteLength = nByteLengthPub; + } + /* if the token has a private key, its nByteLength is already in the structure. This value + should be 0 */ + else { + if (nByteLengthPub != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: " + "Error nByteLength %04hx should be 0 for private key token\n", + rsaKeyTokenPublic->nByteLength); + return -1; + } + } + /* e */ + if (*pubKeyTokenLength < rsaKeyTokenPublic->eLength) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing e\n"); + return -1; + } + memcpy(rsaKeyTokenPublic->e, *pubKeyToken, rsaKeyTokenPublic->eLength); + *pubKeyToken += rsaKeyTokenPublic->eLength; + *pubKeyTokenLength -= rsaKeyTokenPublic->eLength; + /* n */ + if (!hasPrivKey) { + if (*pubKeyTokenLength < rsaKeyTokenPublic->nByteLength) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPublicKey: Error parsing n\n"); + return -1; + } + memcpy(rsaKeyTokenPublic->n, *pubKeyToken, rsaKeyTokenPublic->nByteLength); + *pubKeyToken += rsaKeyTokenPublic->nByteLength; + *pubKeyTokenLength -= rsaKeyTokenPublic->nByteLength; + } + if (verbose) printPKA96KeyTokenPublicKey(rsaKeyTokenPublic); + return 0; +} + +/* parsePKA96KeyTokenPrivateKey() returns CCA RsaKeyTokenPublic and RsaKeyTokenPrivate structures + with the members filled in from the binary PKA96 key token. + + It puts the public key modulus n into the RsaKeyTokenPublic, which is where upper layers of the + code expect it. + + keyTokenLength is decremented and keyToken is incremented as binary data is consumed. + + If bitSize is not 0, does sanity check against key token. +*/ + +long parsePKA96KeyTokenPrivateKey(RsaKeyTokenPublic *rsaKeyTokenPublic, + RsaKeyTokenPrivate *rsaKeyTokenPrivate, + long *keyTokenLength, + unsigned char **keyToken, + unsigned int bitSize) /* expected RSA modulus size */ +{ + long encLength; /* length of encrypted private area */ + + /* sectionId */ + if (*keyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing sectionId\n"); + return -1; + } + rsaKeyTokenPrivate->sectionId = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (rsaKeyTokenPrivate->sectionId != RSA_PRIVATE_KEY_CRT) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, unknown sectionId %02x\n", + rsaKeyTokenPrivate->sectionId); + return -1; + } + /* version */ + if (*keyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing version\n"); + return -1; + } + rsaKeyTokenPrivate->version = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (rsaKeyTokenPrivate->version != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, unknown version %02x\n", + rsaKeyTokenPrivate->version); + return -1; + } + /* sectionLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing sectionLength\n"); + return -1; + } + rsaKeyTokenPrivate->sectionLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + /* sha1HashPrivKey[SHA1_SIZE] */ + if (*keyTokenLength < SHA1_SIZE) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing sha1HashPrivKey\n"); + return -1; + } + memcpy(rsaKeyTokenPrivate->sha1HashPrivKey, keyToken, SHA1_SIZE); + *keyToken += SHA1_SIZE; + *keyTokenLength -= SHA1_SIZE; + /* reserved0 */ + if (*keyTokenLength < 4) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing reserved0\n"); + return -1; + } + rsaKeyTokenPrivate->reserved0 = ntohl(*(unsigned long *)*keyToken); + *keyToken += 4; + *keyTokenLength -= 4; + if (rsaKeyTokenPrivate->reserved0 != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, reserved %08lx\n", + rsaKeyTokenPrivate->reserved0); + return -1; + } + /* keyFormat */ + if (*keyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing keyFormat\n"); + return -1; + } + rsaKeyTokenPrivate->keyFormat = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (rsaKeyTokenPrivate->keyFormat != RSA_INTERNAL_ENCRYPTED) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, unknown keyFormat %02x\n", + rsaKeyTokenPrivate->keyFormat); + return -1; + } + /* tokenType */ + if (*keyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing tokenType\n"); + return -1; + } + rsaKeyTokenPrivate->tokenType = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (rsaKeyTokenPrivate->tokenType != RSA_TOKEN_INTERNAL_GEN_RANDOM) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, tokenType unknown %02x\n", + rsaKeyTokenPrivate->tokenType); + return -1; + } + /* sha1HashOptional[SHA1_SIZE] */ + if (*keyTokenLength < SHA1_SIZE) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing sha1HashOptional\n"); + return -1; + } + memcpy(rsaKeyTokenPrivate->sha1HashOptional, keyToken, SHA1_SIZE); + *keyToken += SHA1_SIZE; + *keyTokenLength -= SHA1_SIZE; + /* keyUsageFlag */ + if (*keyTokenLength < 1) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing keyUsageFlag\n"); + return -1; + } + rsaKeyTokenPrivate->keyUsageFlag = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if ((rsaKeyTokenPrivate->keyUsageFlag != SIG_ONLY) && + (rsaKeyTokenPrivate->keyUsageFlag != KEY_MGMT)) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, unknown keyUsageFlag %02x\n", + rsaKeyTokenPrivate->keyUsageFlag); + return -1; + } + /* reserved1[3] */ + if (*keyTokenLength < 3) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing reserved1\n"); + return -1; + } + memcpy(rsaKeyTokenPrivate->reserved1, keyToken, 3); + *keyToken += 3; + *keyTokenLength -= 3; +#if 0 /* the documentation says this should be 0, but it's not */ + if ((rsaKeyTokenPrivate->reserved1[0] != 0) || + (rsaKeyTokenPrivate->reserved1[1] != 0) || + (rsaKeyTokenPrivate->reserved1[2] != 0)) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal reserved1 %02x %02x %02x\n", + rsaKeyTokenPrivate->reserved1[0], + rsaKeyTokenPrivate->reserved1[1], + rsaKeyTokenPrivate->reserved1[2]); + return -1; + } +#endif + /* pLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing pLength\n"); + return -1; + } + rsaKeyTokenPrivate->pLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (bitSize != 0) { + if (rsaKeyTokenPrivate->pLength != (bitSize/(2 * 8))) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal pLength %hu for bit size %u\n", + rsaKeyTokenPrivate->pLength, bitSize); + return -1; + } + } + /* qLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing qLength\n"); + return -1; + } + rsaKeyTokenPrivate->qLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (bitSize != 0) { + if (rsaKeyTokenPrivate->qLength != (bitSize/(2 * 8))) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal qLength %hu for bit size %u\n", + rsaKeyTokenPrivate->qLength, bitSize); + return -1; + } + } + /* dpLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing dpLength\n"); + return -1; + } + rsaKeyTokenPrivate->dpLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (bitSize != 0) { + if (rsaKeyTokenPrivate->dpLength != (bitSize/(2 * 8))) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal dpLength %hu for bit size %u\n", + rsaKeyTokenPrivate->dpLength, bitSize); + return -1; + } + } + /* dqLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing dqLength\n"); + return -1; + } + rsaKeyTokenPrivate->dqLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (bitSize != 0) { + if (rsaKeyTokenPrivate->dqLength != (bitSize/(2 * 8))) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal dqLength %hu for bit size %u\n", + rsaKeyTokenPrivate->dqLength, bitSize); + return -1; + } + } + /* uLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing uLength\n"); + return -1; + } + rsaKeyTokenPrivate->uLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (bitSize != 0) { + if (rsaKeyTokenPrivate->uLength != (bitSize/(2 * 8))) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal uLength %hu for bit size %u\n", + rsaKeyTokenPrivate->uLength, bitSize); + return -1; + } + } + /* nLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing nLength\n"); + return -1; + } + rsaKeyTokenPrivate->nLength = ntohs(*(unsigned short *)*keyToken); + /* copy to public key area as a service, since nByteLength in the public token will be 0 */ + rsaKeyTokenPublic->nByteLength = rsaKeyTokenPrivate->nLength; + *keyToken += 2; + *keyTokenLength -= 2; + if (bitSize != 0) { + if (rsaKeyTokenPrivate->nLength != (bitSize/8)) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error illegal nLength %04hx for bit size %u\n", + rsaKeyTokenPrivate->nLength, bitSize); + return -1; + } + } + /* reserved2 */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing reserved2\n"); + return -1; + } + rsaKeyTokenPrivate->reserved2 = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (rsaKeyTokenPrivate->reserved2 != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal reserved2 %04hx\n", + rsaKeyTokenPrivate->reserved2); + return -1; + } + /* reserved3 */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing reserved3\n"); + return -1; + } + rsaKeyTokenPrivate->reserved3 = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (rsaKeyTokenPrivate->reserved3 != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal reserved3 %04hx\n", + rsaKeyTokenPrivate->reserved3); + return -1; + } + /* padLength */ + if (*keyTokenLength < 2) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing padLength\n"); + return -1; + } + rsaKeyTokenPrivate->padLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + /* reserved4 */ + if (*keyTokenLength < 4) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing reserved4\n"); + return -1; + } + rsaKeyTokenPrivate->reserved4 = ntohl(*(unsigned long *)*keyToken); + *keyToken += 4; + *keyTokenLength -= 4; + if (rsaKeyTokenPrivate->reserved4 != 0) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error, illegal reserved4 %08lx\n", + rsaKeyTokenPrivate->reserved4); + return -1; + } + /* reserved5[16] */ + if (*keyTokenLength < 16) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing reserved5\n"); + return -1; + } + memcpy(rsaKeyTokenPrivate->reserved5, keyToken, 16); + *keyToken += 16; + *keyTokenLength -= 16; + /* reserved6[32] */ + if (*keyTokenLength < 32) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing reserved6\n"); + return -1; + } + memcpy(rsaKeyTokenPrivate->reserved6, keyToken, 32); + *keyToken += 32; + *keyTokenLength -= 32; + /* confounder[8] */ + if (*keyTokenLength < 8) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing confounder\n"); + return -1; + } + memcpy(rsaKeyTokenPrivate->confounder, keyToken, 8); + *keyToken += 8; + *keyTokenLength -= 8; + /* p */ + /* q */ + /* dp */ + /* dq */ + /* u */ + /* pad */ + /* skip the encrypted part */ + encLength = rsaKeyTokenPrivate->pLength + + rsaKeyTokenPrivate->qLength + + rsaKeyTokenPrivate->dpLength + + rsaKeyTokenPrivate->dqLength + + rsaKeyTokenPrivate->uLength + + rsaKeyTokenPrivate->padLength; + if (*keyTokenLength < encLength) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing encrypted area\n"); + return -1; + } + *keyToken += encLength; + *keyTokenLength -= encLength; + /* n */ + if (*keyTokenLength < rsaKeyTokenPrivate->nLength) { + if (verbose) fprintf(messageFile, + "parsePKA96KeyTokenPrivateKey: Error parsing n\n"); + return -1; + } + memcpy(rsaKeyTokenPublic->n, *keyToken, rsaKeyTokenPrivate->nLength); + *keyToken += rsaKeyTokenPrivate->nLength; + *keyTokenLength -= rsaKeyTokenPrivate->nLength; + + if (verbose) printPKA96KeyTokenPrivateKey(rsaKeyTokenPrivate); + return 0; +} + +/* + Debug Print Functions +*/ + +/* printPKA96KeyTokenHeader() prints the RsaKeyTokenHeader structure in human readable form + + */ + +void printPKA96KeyTokenHeader(RsaKeyTokenHeader *rsaKeyTokenHeader) +{ + if (verbose) fprintf(messageFile, + "\tToken Header:\n"); + /* tokenId */ + switch (rsaKeyTokenHeader->tokenId) { + case PKA_EXTERNAL_TOKEN: + if (verbose) fprintf(messageFile, + "\t\ttokenId: External token\n"); + break; + case PKA_INTERNAL_TOKEN: + if (verbose) fprintf(messageFile, + "\t\ttokenId: Internal token\n"); + break; + default: + if (verbose) fprintf(messageFile, + "\t\ttokenId: Unknown %02x\n", + rsaKeyTokenHeader->tokenId); + } + /* version */ + if (verbose) fprintf(messageFile, + "\t\tversion: %02x\n", rsaKeyTokenHeader->version); + /* tokenLength */ + if (verbose) fprintf(messageFile, + "\t\ttokenLength: %04hx %hu\n", + rsaKeyTokenHeader->tokenLength, rsaKeyTokenHeader->tokenLength); + /* reserved */ + if (verbose) fprintf(messageFile, + "\t\treserved: %08lx\n", rsaKeyTokenHeader->reserved); + return; +} + +/* printPKA96KeyTokenPublicKey() prints the RsaKeyTokenPublic structure in human readable form + + */ + +void printPKA96KeyTokenPublicKey(RsaKeyTokenPublic *rsaKeyTokenPublic) +{ + if (verbose) fprintf(messageFile, + "\tToken Public Key:\n"); + + /* sectionId */ + printSectionID(rsaKeyTokenPublic->sectionId); + /* version */ + if (verbose) fprintf(messageFile, + "\t\tversion: %02x\n", rsaKeyTokenPublic->version); + /* section_length */ + if (verbose) fprintf(messageFile, + "\t\tsectionLength: %04hx %hu\n", + rsaKeyTokenPublic->sectionLength, rsaKeyTokenPublic->sectionLength); + /* reserved[2] */ + if (verbose) fprintf(messageFile, + "\t\treserved: %02x %02x\n", + rsaKeyTokenPublic->reserved[0], + rsaKeyTokenPublic->reserved[1]); + /* eLength */ + if (verbose) fprintf(messageFile, + "\t\teLength : %04hx %hu\n", + rsaKeyTokenPublic->eLength, rsaKeyTokenPublic->eLength); + /* nBitLength */ + if (verbose) fprintf(messageFile, + "\t\tnBitLength: %04hx %hu\n", + rsaKeyTokenPublic->nBitLength, rsaKeyTokenPublic->nBitLength); + /* nByteLength */ + if (verbose) fprintf(messageFile, + "\t\tnByteLength: %04hx %hu\n", + rsaKeyTokenPublic->nByteLength, rsaKeyTokenPublic->nByteLength); + if (verbose) { + /* e */ + PrintAll(messageFile, + "\t\te", rsaKeyTokenPublic->eLength, rsaKeyTokenPublic->e); + /* n */ + PrintAll(messageFile, + "\t\tn", rsaKeyTokenPublic->nByteLength, rsaKeyTokenPublic->n); + } + return; +} + +/* printPKA96KeyTokenPrivateKey() prints the RsaKeyTokenPrivate structure in human readable form + + */ + +void printPKA96KeyTokenPrivateKey(RsaKeyTokenPrivate *rsaKeyTokenPrivate) +{ + if (verbose) fprintf(messageFile, + "\tToken Private Key:\n"); + + /* sectionId */ + printSectionID(rsaKeyTokenPrivate->sectionId); + /* version */ + if (verbose) fprintf(messageFile, + "\t\tversion: %02x\n", rsaKeyTokenPrivate->version); + /* sectionLength */ + if (verbose) fprintf(messageFile, + "\t\tsectionLength: %04hx %hu\n", + rsaKeyTokenPrivate->sectionLength, rsaKeyTokenPrivate->sectionLength); + /* sha1HashPrivKey[SHA1_SIZE] */ + if (verbose) fprintf(messageFile, + "\t\tsha1HashPrivKey: "); + if (verbose) { + PrintAll(messageFile, + "", SHA1_SIZE, rsaKeyTokenPrivate->sha1HashPrivKey); + } + /* reserved0 */ + if (verbose) fprintf(messageFile, + "\t\treserved0 %08lx\n", rsaKeyTokenPrivate->reserved0); + /* keyFormat */ + printKeyFormat(rsaKeyTokenPrivate->keyFormat); + /* tokenType */ + printTokenType(rsaKeyTokenPrivate->tokenType); + /* sha1HashOptional[SHA1_SIZE] */ + if (verbose) fprintf(messageFile, + "\t\tsha1HashOptional: "); + if (verbose) { + PrintAll(messageFile, + "", SHA1_SIZE, rsaKeyTokenPrivate->sha1HashOptional); + } + /* keyUsageFlag */ + printKeyUsageFlag(rsaKeyTokenPrivate->keyUsageFlag); + /* reserved1[3] */ + if (verbose) fprintf(messageFile, + "\t\treserved1 %02x %02x %02x \n", + rsaKeyTokenPrivate->reserved1[0], + rsaKeyTokenPrivate->reserved1[1], + rsaKeyTokenPrivate->reserved1[2]); + /* pLength */ + if (verbose) fprintf(messageFile, + "\t\tpLength : %04hx %hu\n", + rsaKeyTokenPrivate->pLength, rsaKeyTokenPrivate->pLength); + /* qLength */ + if (verbose) fprintf(messageFile, + "\t\tqLength: %04hx %hu\n", + rsaKeyTokenPrivate->qLength, rsaKeyTokenPrivate->qLength); + /* dpLength */ + if (verbose) fprintf(messageFile, + "\t\tdpLength: %04hx %hu\n", + rsaKeyTokenPrivate->dpLength, rsaKeyTokenPrivate->dpLength); + /* dqLength */ + if (verbose) fprintf(messageFile, + "\t\tdqLength: %04hx %hu\n", + rsaKeyTokenPrivate->dqLength, rsaKeyTokenPrivate->dqLength); + /* uLength */ + if (verbose) fprintf(messageFile, + "\t\tuLength: %04hx %hu\n", + rsaKeyTokenPrivate->uLength, rsaKeyTokenPrivate->uLength); + /* nLength */ + if (verbose) fprintf(messageFile, + "\t\tnLength: %04hx %hu\n", + rsaKeyTokenPrivate->nLength, rsaKeyTokenPrivate->nLength); + /* reserved2 */ + if (verbose) fprintf(messageFile, + "\t\treserved2: %04hx\n", rsaKeyTokenPrivate->reserved2); + /* reserved3 */ + if (verbose) fprintf(messageFile, + "\t\treserved3: %04hx\n", rsaKeyTokenPrivate->reserved3); + /* padLength */ + if (verbose) fprintf(messageFile, + "\t\tpadLength: %04hx %hu\n", + rsaKeyTokenPrivate->padLength, rsaKeyTokenPrivate->padLength); + /* reserved4 */ + if (verbose) fprintf(messageFile, + "\t\treserved4: %08lx\n", rsaKeyTokenPrivate->reserved4); + /* reserved5[16] */ + if (verbose) fprintf(messageFile, + "\t\treserved5: "); + if (verbose) { + PrintAll(messageFile, + "", 16, rsaKeyTokenPrivate->reserved5); + } + /* reserved6[32] */ + if (verbose) fprintf(messageFile, + "\t\treserved6: "); + if (verbose) { + PrintAll(messageFile, + "", 32, rsaKeyTokenPrivate->reserved6); + } + /* confounder[8] */ + if (verbose) fprintf(messageFile, + "\t\tconfounder: %02x%02x%02x%02x %02x%02x%02x%02x\n", + rsaKeyTokenPrivate->confounder[0], rsaKeyTokenPrivate->confounder[1], + rsaKeyTokenPrivate->confounder[2], rsaKeyTokenPrivate->confounder[3], + rsaKeyTokenPrivate->confounder[4], rsaKeyTokenPrivate->confounder[5], + rsaKeyTokenPrivate->confounder[6], rsaKeyTokenPrivate->confounder[7]); + return; +} + +void printSectionID(unsigned char sectionId) +{ + if (verbose) fprintf(messageFile, + "\t\tsectionId: "); + switch (sectionId) { + case RSA_PRIVATE_KEY_1024_EXTERNAL: + if (verbose) fprintf(messageFile, + "RSA_PRIVATE_KEY_1024_EXTERNAL\n"); + break; + case RSA_PUBLIC_SECTION: + if (verbose) fprintf(messageFile, + "RSA_PUBLIC_SECTION\n"); + break; + case RSA_PRIVATE_KEY_2048_CRT_DEP: + if (verbose) fprintf(messageFile, + "RSA_PRIVATE_KEY_2048_CRT_DEP\n"); + break; + case RSA_PRIVATE_KEY_1024_INTERNAL: + if (verbose) fprintf(messageFile, + "RSA_PRIVATE_KEY_1024_INTERNAL\n"); + break; + case RSA_PRIVATE_KEY_CRT: + if (verbose) fprintf(messageFile, + "RSA_PRIVATE_KEY_CRT\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", sectionId); + } + return; +} + +void printKeyFormat(unsigned char keyFormat) +{ + if (verbose) fprintf(messageFile, + "\t\tkeyFormat: "); + switch (keyFormat) { + case RSA_EXTERNAL_UNENCRYPTED: + if (verbose) fprintf(messageFile, + "RSA_EXTERNAL_UNENCRYPTED\n"); + break; + case RSA_EXTERNAL_ENCRYPTED: + if (verbose) fprintf(messageFile, + "RSA_EXTERNAL_ENCRYPTED\n"); + break; + case RSA_INTERNAL_ENCRYPTED: + if (verbose) fprintf(messageFile, + "RSA_INTERNAL_ENCRYPTED\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", keyFormat); + } + return; +} + +void printTokenType (unsigned char tokenType) +{ + if (verbose) fprintf(messageFile, + "\t\ttokenType: "); + switch (tokenType) { + case RSA_TOKEN_EXTERNAL: + if (verbose) fprintf(messageFile, + "RSA_TOKEN_EXTERNAL\n"); + break; + case RSA_TOKEN_INTERNAL_IMPORT_CLEARTEXT: + if (verbose) fprintf(messageFile, + "RSA_TOKEN_INTERNAL_IMPORT_CLEARTEXT\n"); + break; + case RSA_TOKEN_INTERNAL_IMPORT_CIPHERTEXT: + if (verbose) fprintf(messageFile, + "RSA_TOKEN_INTERNAL_IMPORT_CIPHERTEXT\n"); + break; + case RSA_TOKEN_INTERNAL_GEN_REGEN: + if (verbose) fprintf(messageFile, + "RSA_TOKEN_INTERNAL_GEN_REGEN\n"); + break; + case RSA_TOKEN_INTERNAL_GEN_RANDOM: + if (verbose) fprintf(messageFile, + "RSA_TOKEN_INTERNAL_GEN_RANDOM\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", tokenType); + } + return; +} + +void printKeyUsageFlag(unsigned char keyUsageFlag) +{ + if (verbose) fprintf(messageFile, + "\t\tkeyUsageFlag: "); + switch (keyUsageFlag) { + case SIG_ONLY: + if (verbose) fprintf(messageFile, + "SIG_ONLY\n"); + break; + case KM_ONLY: + if (verbose) fprintf(messageFile, + "KM_ONLY\n"); + break; + case KEY_MGMT: + if (verbose) fprintf(messageFile, + "KEY_MGMT\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", keyUsageFlag); + } + return; +} diff --git a/src/signframework/cca_structures.h b/src/signframework/cca_structures.h new file mode 100644 index 0000000..b0dd3cc --- /dev/null +++ b/src/signframework/cca_structures.h @@ -0,0 +1,147 @@ +/* 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. + */ + +#ifndef CCA_STRUCTURES_H +#define CCA_STRUCTURES_H + +/* tokenID */ +#define PKA_EXTERNAL_TOKEN 0x1E +#define PKA_INTERNAL_TOKEN 0x1F + +/* sectionID */ +#define RSA_PRIVATE_KEY_1024_EXTERNAL 0x02 +#define RSA_PUBLIC_SECTION 0x04 +#define RSA_PRIVATE_KEY_2048_CRT_DEP 0x05 /* deprecated */ +#define RSA_PRIVATE_KEY_1024_INTERNAL 0x06 +#define RSA_PRIVATE_KEY_CRT 0x08 + +/* keyFormat */ +#define RSA_EXTERNAL_UNENCRYPTED 0x40 +#define RSA_EXTERNAL_ENCRYPTED 0x42 +#define RSA_INTERNAL_ENCRYPTED 0x08 + +/* tokenType */ +#define RSA_TOKEN_EXTERNAL 0x00 +#define RSA_TOKEN_INTERNAL_IMPORT_CLEARTEXT 0x21 +#define RSA_TOKEN_INTERNAL_IMPORT_CIPHERTEXT 0x22 +#define RSA_TOKEN_INTERNAL_GEN_REGEN 0x23 +#define RSA_TOKEN_INTERNAL_GEN_RANDOM 0x24 + +/* keyUsageFlag */ +#define SIG_ONLY 0x00 +#define KM_ONLY 0xc0 +#define KEY_MGMT 0x80 + +/* Application hard coded sizes + + */ + +#define E_SIZE 4 /* bytes, exponent maximum, typically 010001 */ +#define N_SIZE 256 /* bytes, public modulus for 2048 bit key */ +#define N_SIZE_MAX 512 /* bytes, public modulus for 4096 bit key */ +#define N_BIT_SIZE 2048 /* bits, public modulus for 2048 bit key */ +#define N_BIT_SIZE_MAX 4096 /* bits, public modulus for 4096 bit key */ +#define SHA1_SIZE 20 +#define SHA256_SIZE 32 +#define SHA512_SIZE 64 + +/* PKA96 RSA token header */ + +typedef struct tdRsaKeyTokenHeader +{ + unsigned char tokenId; + unsigned char version; + unsigned short tokenLength; + unsigned long reserved; +} RsaKeyTokenHeader; + +typedef struct tdRsaKeyTokenPublic { + unsigned char sectionId; + unsigned char version; + unsigned short sectionLength; /* Length of the RSA public key section */ + unsigned char reserved[2]; + unsigned short eLength; + unsigned short nBitLength; + unsigned short nByteLength; /* Prime divisor length in bytes */ + unsigned char e[E_SIZE]; + unsigned char n[N_SIZE_MAX]; +} RsaKeyTokenPublic ; + +typedef struct tdRsaKeyTokenPrivate { + unsigned char sectionId; + unsigned char version; + unsigned short sectionLength; + unsigned char sha1HashPrivKey[SHA1_SIZE]; + unsigned long reserved0; + unsigned char keyFormat; + unsigned char tokenType; + unsigned char sha1HashOptional[SHA1_SIZE]; + unsigned char keyUsageFlag; + unsigned char reserved1[3]; + unsigned short pLength; + unsigned short qLength; + unsigned short dpLength; + unsigned short dqLength; + unsigned short uLength; + unsigned short nLength; + unsigned short reserved2; + unsigned short reserved3; + unsigned short padLength; + unsigned long reserved4; + unsigned char reserved5[16]; + unsigned char reserved6[32]; + unsigned char confounder[8]; + /* p */ + /* q */ + /* dp */ + /* dq */ + /* u */ + /* pad */ + /* n */ +} RsaKeyTokenPrivate; + + +long getPKA96PublicKey(RsaKeyTokenPublic *rsaKeyTokenPublic, + long keyTokenLength, + unsigned char *keyToken, + unsigned int bitSize); +long parsePKA96KeyTokenHeader(RsaKeyTokenHeader *rsaKeyTokenHeader, + long *keyTokenLength, + unsigned char **keyToken); +long parsePKA96KeyTokenPublicKey(RsaKeyTokenPublic *rsaKeyTokenPublic, + long *pubKeyTokenLength, + unsigned char **pubKeyToken, + int hasPrivKey); +long parsePKA96KeyTokenPrivateKey(RsaKeyTokenPublic *rsaKeyTokenPublic, + RsaKeyTokenPrivate *rsaKeyTokenPrivate, + long *keyTokenLength, + unsigned char **keyToken, + unsigned int bitSize); + +/* + Debug Print Functions +*/ + +void printPKA96KeyTokenHeader(RsaKeyTokenHeader *rsaKeyTokenHeader); +void printPKA96KeyTokenPublicKey(RsaKeyTokenPublic *rsaKeyTokenPublic); +void printPKA96KeyTokenPrivateKey(RsaKeyTokenPrivate *rsaKeyTokenPrivate); + +void printSectionID(unsigned char sectionId); +void printKeyFormat(unsigned char keyFormat); +void printTokenType (unsigned char tokenType); +void printKeyUsageFlag(unsigned char keyUsageFlag); + +#endif diff --git a/src/signframework/cca_structures_ecc.c b/src/signframework/cca_structures_ecc.c new file mode 100644 index 0000000..cabc784 --- /dev/null +++ b/src/signframework/cca_structures_ecc.c @@ -0,0 +1,866 @@ +/* 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> + +/* Linux specific */ +#include <sys/types.h> +#include <netinet/in.h> + +/* local */ +#include "cca_structures_ecc.h" +#include "utils.h" +#include "debug.h" + +extern FILE* messageFile; +extern int verbose; + +/* local functions */ + +long validatePKA96EccKeyToken(EccKeyTokenPrivate *eccKeyTokenPrivate, + EccKeyTokenPublic *eccKeyTokenPublic); + +/* getPKA96EccPublicKey() returns a CCA EccKeyTokenPublic structure with the members filled in from the + binary PKA96 key token. + +*/ + +long getPKA96EccPublicKey(EccKeyTokenPublic *eccKeyTokenPublic, + long keyTokenLength, + unsigned char *keyToken) +{ + long rc = 0; + EccKeyTokenHeader eccKeyTokenHeader; + EccKeyTokenPrivate eccKeyTokenPrivate; + int foundPrivateSection = FALSE; + + /* parse the PKA96 key token header */ + if (rc == 0) { + rc = parsePKA96EccKeyTokenHeader(&eccKeyTokenHeader, + &keyTokenLength, + &keyToken); + } + /* if there's a private key section, parse it just to bypass it */ + if (rc == 0) { + if ((keyTokenLength > 0) && (*keyToken == ECC_PRIVATE_SECTION)) { + foundPrivateSection = TRUE; + rc = parsePKA96EccKeyTokenPrivateKey(&eccKeyTokenPrivate, + &keyTokenLength, + &keyToken); + } + } + /* parse the public key */ + if (rc == 0) { + rc = parsePKA96EccKeyTokenPublicKey(eccKeyTokenPublic, + &keyTokenLength, + &keyToken); + } + if ((rc == 0) && foundPrivateSection) { + rc = validatePKA96EccKeyToken(&eccKeyTokenPrivate, + eccKeyTokenPublic); + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "getPKA96EccEccPublicKey: public key", + eccKeyTokenPublic->qLen, eccKeyTokenPublic->publicKey); + } + return rc; +} + +/* parsePKA96EccKeyTokenHeader() returns a CCA EccKeyTokenHeader structure with the members filled in + from the binary PKA96 key token. + + keyTokenLength is decremented and keyToken is incremented as binary data is consumed. +*/ + +long parsePKA96EccKeyTokenHeader(EccKeyTokenHeader *eccKeyTokenHeader, + long *keyTokenLength, + unsigned char **keyToken) +{ + /* tokenId */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenHeader: Error parsing tokenId \n"); + return -1; + } + eccKeyTokenHeader->tokenId = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenHeader->tokenId != PKA_INTERNAL_TOKEN) { + fprintf(messageFile, + "parsePKA96EccKeyTokenHeader: Error, unknown tokenId %02x\n", + eccKeyTokenHeader->tokenId); + return -1; + } + /* version */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenHeader: Error parsing version\n"); + return -1; + } + eccKeyTokenHeader->version = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenHeader->version != 0) { + fprintf(messageFile, + "parsePKA96EccKeyTokenHeader: Error, unknown version %02x\n", + eccKeyTokenHeader->version); + return -1; + } + /* tokenLength */ + if (*keyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenHeader: Error parsing tokenLength \n"); + return -1; + } + eccKeyTokenHeader->tokenLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + /* reserved */ + if (*keyTokenLength < 4) { + fprintf(messageFile, + "parsePKA96EccKeyTokenHeader: Error parsing reserved\n"); + return -1; + } + eccKeyTokenHeader->reserved = ntohl(*(unsigned long *)*keyToken); + *keyToken += 4; + *keyTokenLength -= 4; + if (eccKeyTokenHeader->reserved != 0) { + fprintf(messageFile, + "parsePKA96EccKeyTokenHeader: Error, reserved %08x\n", + eccKeyTokenHeader->reserved); + return -1; + } + if (verbose) printPKA96EccKeyTokenHeader(eccKeyTokenHeader); + return 0; +} + +/* parsePKA96EccKeyTokenPublicKey() returns a CCA EccKeyTokenPublic structure + with the members filled in from the binary PKA96 public key token. + + keyTokenLength is decremented and keyToken is incremented as binary data is consumed. +*/ + +long parsePKA96EccKeyTokenPublicKey(EccKeyTokenPublic *eccKeyTokenPublic, + long *pubKeyTokenLength, + unsigned char **pubKeyToken) +{ + /* sectionId */ + if (*pubKeyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing sectionId\n"); + return -1; + } + eccKeyTokenPublic->sectionId = **pubKeyToken; + *pubKeyToken += 1; + *pubKeyTokenLength -= 1; + if (eccKeyTokenPublic->sectionId != ECC_PUBLIC_SECTION) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error, unknown sectionId %02x\n", + eccKeyTokenPublic->sectionId); + return -1; + } + /* version */ + if (*pubKeyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing version\n"); + return -1; + } + eccKeyTokenPublic->version = **pubKeyToken; + *pubKeyToken += 1; + *pubKeyTokenLength -= 1; + if (eccKeyTokenPublic->version != 0) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error, unknown version %02x\n", + eccKeyTokenPublic->version); + return -1; + } + /* sectionLength */ + if (*pubKeyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing sectionLength\n"); + return -1; + } + eccKeyTokenPublic->sectionLength = ntohs(*(unsigned short *)*pubKeyToken); + *pubKeyToken += 2; + *pubKeyTokenLength -= 2; + /* reserved */ + if (*pubKeyTokenLength < 4) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing reserved\n"); + return -1; + } + eccKeyTokenPublic->reserved = ntohl(*(unsigned long *)*pubKeyToken); + *pubKeyToken += 4; + *pubKeyTokenLength -= 4; + if (eccKeyTokenPublic->reserved != 0) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error, reserved %08x\n", + eccKeyTokenPublic->reserved); + return -1; + } + /* curveType */ + if (*pubKeyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing curveType\n"); + return -1; + } + eccKeyTokenPublic->curveType = **pubKeyToken; + *pubKeyToken += 1; + *pubKeyTokenLength -= 1; + if (eccKeyTokenPublic->curveType != ECC_PRIME) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error, curveType unknown %02x\n", + eccKeyTokenPublic->curveType); + return -1; + } + /* reserved2 */ + if (*pubKeyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing reserved2\n"); + return -1; + } + eccKeyTokenPublic->reserved2 = **pubKeyToken; + *pubKeyToken += 1; + *pubKeyTokenLength -= 1; + if (eccKeyTokenPublic->reserved2 != 0) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error, reserved2 unknown %02x\n", + eccKeyTokenPublic->curveType); + return -1; + } + /* pLength */ + if (*pubKeyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing pLength\n"); + return -1; + } + eccKeyTokenPublic->pLength = ntohs(*(unsigned short *)*pubKeyToken); + *pubKeyToken += 2; + *pubKeyTokenLength -= 2; + if (eccKeyTokenPublic->pLength != ECC_PRIME_521) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error, pLength %04x\n", + eccKeyTokenPublic->pLength); + return -1; + } + /* qLen */ + if (*pubKeyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing qLen\n"); + return -1; + } + eccKeyTokenPublic->qLen = ntohs(*(unsigned short *)*pubKeyToken); + *pubKeyToken += 2; + *pubKeyTokenLength -= 2; + if (eccKeyTokenPublic->qLen != MAX_Q_LEN_BYTES) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error, qLen %04x\n", + eccKeyTokenPublic->qLen); + return -1; + } + /* publicKey */ + if (*pubKeyTokenLength < MAX_Q_LEN_BYTES) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPublicKey: Error parsing publicKey\n"); + return -1; + } + memcpy(eccKeyTokenPublic->publicKey, *pubKeyToken, MAX_Q_LEN_BYTES); + *pubKeyToken += MAX_Q_LEN_BYTES; + *pubKeyTokenLength -= MAX_Q_LEN_BYTES; + if (verbose) printPKA96EccKeyTokenPublicKey(eccKeyTokenPublic); + return 0; +} + +/* parsePKA96EccKeyTokenPrivateKey() returns CCA the EccKeyTokenPrivate structure + with the members filled in from the binary PKA96 key token. + + keyTokenLength is decremented and keyToken is incremented as binary data is consumed. +*/ + +long parsePKA96EccKeyTokenPrivateKey(EccKeyTokenPrivate *eccKeyTokenPrivate, + long *keyTokenLength, + unsigned char **keyToken) +{ + /* sectionId */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing sectionId\n"); + return -1; + } + eccKeyTokenPrivate->sectionId = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenPrivate->sectionId != ECC_PRIVATE_SECTION) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, unknown sectionId %02x\n", + eccKeyTokenPrivate->sectionId); + return -1; + } + /* version */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing version\n"); + return -1; + } + eccKeyTokenPrivate->version = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenPrivate->version != ECC_PRIV_VERSION_00) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, unknown version %02x\n", + eccKeyTokenPrivate->version); + return -1; + } + /* sectionLength */ + if (*keyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing sectionLength\n"); + return -1; + } + eccKeyTokenPrivate->sectionLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (eccKeyTokenPrivate->sectionLength > (*keyTokenLength + 4)) { /* char, char, short */ + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, sectionLength %04x too large\n", + eccKeyTokenPrivate->sectionLength ); + return -1; + } + /* wrappingMethod */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing wrappingMethod\n"); + return -1; + } + eccKeyTokenPrivate->wrappingMethod = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenPrivate->wrappingMethod != ECC_WRAP_METH_AESKW) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, unknown wrappingMethod %02x\n", + eccKeyTokenPrivate->wrappingMethod); + return -1; + } + /* hashType */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing hashType\n"); + return -1; + } + eccKeyTokenPrivate->hashType = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if ((eccKeyTokenPrivate->hashType != ECC_HASH_SHA224) && + (eccKeyTokenPrivate->hashType != ECC_HASH_SHA256) && + (eccKeyTokenPrivate->hashType != ECC_HASH_SHA384) && + (eccKeyTokenPrivate->hashType != ECC_HASH_SHA512)) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, unknown hashType %02x\n", + eccKeyTokenPrivate->hashType); + return -1; + } + /* reserved */ + if (*keyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing reserved\n"); + return -1; + } + eccKeyTokenPrivate->reserved = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (eccKeyTokenPrivate->reserved != 0x0000) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, reserved %04x\n", + eccKeyTokenPrivate->reserved); + return -1; + } + /* keyUsage */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing keyUsage\n"); + return -1; + } + eccKeyTokenPrivate->keyUsage = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenPrivate->keyUsage != ECC_SIGNATURE_USE_ONLY) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, unknown keyUsage %02x\n", + eccKeyTokenPrivate->keyUsage); + return -1; + } + /* curveType */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing curveType\n"); + return -1; + } + eccKeyTokenPrivate->curveType = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenPrivate->curveType != ECC_PRIME) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, curveType unknown %02x\n", + eccKeyTokenPrivate->curveType); + return -1; + } + /* keyFormatSecurity */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing keyFormatSecurity\n"); + return -1; + } + eccKeyTokenPrivate->keyFormatSecurity = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenPrivate->keyFormatSecurity != ECC_INTERNAL_ENCRYPTED) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, tokenType unknown %02x\n", + eccKeyTokenPrivate->keyFormatSecurity); + return -1; + } + /* reserved2 */ + if (*keyTokenLength < 1) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing reserved2\n"); + return -1; + } + eccKeyTokenPrivate->reserved2 = **keyToken; + *keyToken += 1; + *keyTokenLength -= 1; + if (eccKeyTokenPrivate->reserved2 != 0x00) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, tokenType unknown %02x\n", + eccKeyTokenPrivate->reserved2); + return -1; + } + /* pLength */ + if (*keyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing pLength\n"); + return -1; + } + eccKeyTokenPrivate->pLength = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (eccKeyTokenPrivate->pLength != ECC_PRIME_521) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, pLength %04x\n", + eccKeyTokenPrivate->pLength); + return -1; + } + /* IBMAssocDataLen */ + if (*keyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing IBMAssocDataLen\n"); + return -1; + } + eccKeyTokenPrivate->IBMAssocDataLen = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (eccKeyTokenPrivate->IBMAssocDataLen < 16) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, IBMAssocDataLen %04x\n", + eccKeyTokenPrivate->IBMAssocDataLen ); + return -1; + } + /* mkvp */ + if (*keyTokenLength < MKVP_LENGTH) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing mkvp\n"); + return -1; + } + memcpy(eccKeyTokenPrivate->mkvp, *keyToken, MKVP_LENGTH); + *keyToken += MKVP_LENGTH; + *keyTokenLength -= MKVP_LENGTH; + /* objProtection */ + if (*keyTokenLength < ECC_OBJ_PROTECTION_LEN) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing \n"); + return -1; + } + memcpy(eccKeyTokenPrivate->objProtection, *keyToken, ECC_OBJ_PROTECTION_LEN); + *keyToken += ECC_OBJ_PROTECTION_LEN; + *keyTokenLength -= ECC_OBJ_PROTECTION_LEN; + /* aDataLen */ + if (*keyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing aDataLen\n"); + return -1; + } + eccKeyTokenPrivate->aDataLen = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + if (eccKeyTokenPrivate->aDataLen > eccKeyTokenPrivate->IBMAssocDataLen) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error, aDataLen %04x > IBMAssocDataLen %04x\n", + eccKeyTokenPrivate->aDataLen, eccKeyTokenPrivate->IBMAssocDataLen ); + return -1; + } + /* formattedDataLen */ + if (*keyTokenLength < 2) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing formattedDataLen\n"); + return -1; + } + eccKeyTokenPrivate->formattedDataLen = ntohs(*(unsigned short *)*keyToken); + *keyToken += 2; + *keyTokenLength -= 2; + /* skip the aData part */ + if (*keyTokenLength < eccKeyTokenPrivate->aDataLen) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing aData\n"); + return -1; + } + *keyToken += eccKeyTokenPrivate->aDataLen; + *keyTokenLength -= eccKeyTokenPrivate->aDataLen; + /* skip the encrypted part */ + if (*keyTokenLength < eccKeyTokenPrivate->formattedDataLen) { + fprintf(messageFile, + "parsePKA96EccKeyTokenPrivateKey: Error parsing formattedDataLen\n"); + return -1; + } + *keyToken += eccKeyTokenPrivate->formattedDataLen; + *keyTokenLength -= eccKeyTokenPrivate->formattedDataLen; + if (verbose) printPKA96EccKeyTokenPrivateKey(eccKeyTokenPrivate); + return 0; +} + +/* validatePKA96EccKeyToken() validates the public section against the private section + */ + +long validatePKA96EccKeyToken(EccKeyTokenPrivate *eccKeyTokenPrivate, + EccKeyTokenPublic *eccKeyTokenPublic) +{ + long rc = 0; + + if (rc == 0) { + if (eccKeyTokenPrivate->curveType != eccKeyTokenPublic->curveType) { + fprintf(messageFile, + "validatePKA96EccKeyToken: Error, curveType inconsistent %02x %02x\n", + eccKeyTokenPrivate->curveType, eccKeyTokenPublic->curveType); + rc = -1; + } + } + if (rc == 0) { + if (eccKeyTokenPrivate->pLength != eccKeyTokenPublic->pLength) { + fprintf(messageFile, + "validatePKA96EccKeyToken: Error, pLength inconsistent %02x %02x\n", + eccKeyTokenPrivate->pLength, eccKeyTokenPublic->pLength); + rc = -1; + } + } + return rc; +} + +/* + Debug Print Functions +*/ + +/* printPKA96EccKeyTokenHeader() prints the EccKeyTokenHeader structure in human readable form + + */ + +void printPKA96EccKeyTokenHeader(EccKeyTokenHeader *eccKeyTokenHeader) +{ + if (verbose) fprintf(messageFile, + "\tToken Header:\n"); + /* tokenId */ + switch (eccKeyTokenHeader->tokenId) { + case PKA_EXTERNAL_TOKEN: + if (verbose) fprintf(messageFile, + "\t\ttokenId: External token\n"); + break; + case PKA_INTERNAL_TOKEN: + if (verbose) fprintf(messageFile, + "\t\ttokenId: Internal token\n"); + break; + default: + if (verbose) fprintf(messageFile, + "\t\ttokenId: Unknown %02x\n", + eccKeyTokenHeader->tokenId); + } + /* version */ + if (verbose) fprintf(messageFile, + "\t\tversion: %02x\n", eccKeyTokenHeader->version); + /* tokenLength */ + if (verbose) fprintf(messageFile, + "\t\ttokenLength: %04hx %hu\n", + eccKeyTokenHeader->tokenLength, eccKeyTokenHeader->tokenLength); + /* reserved */ + if (verbose) fprintf(messageFile, + "\t\treserved: %08x\n", eccKeyTokenHeader->reserved); + return; +} + +/* printPKA96EccKeyTokenPublicKey() prints the EccKeyTokenPublic structure in human readable form + + */ + +void printPKA96EccKeyTokenPublicKey(EccKeyTokenPublic *eccKeyTokenPublic) +{ + if (verbose) fprintf(messageFile, + "\tToken Public Key:\n"); + /* sectionId */ + printEccSectionID(eccKeyTokenPublic->sectionId); + /* version */ + if (verbose) fprintf(messageFile, + "\t\tversion: %02x\n", eccKeyTokenPublic->version); + /* sectionLength */ + if (verbose) fprintf(messageFile, + "\t\tsectionLength: %04hx %hu\n", + eccKeyTokenPublic->sectionLength, eccKeyTokenPublic->sectionLength); + /* reserved */ + if (verbose) fprintf(messageFile, + "\t\treserved: %08x\n", + eccKeyTokenPublic->reserved); + /* curveType */ + printCurveType(eccKeyTokenPublic->curveType); + /* reserved2 */ + if (verbose) fprintf(messageFile, + "\t\treserved: %02x\n", + eccKeyTokenPublic->reserved2); + /* pLength */ + if (verbose) fprintf(messageFile, + "\t\tpLength: %04hx %hu\n", + eccKeyTokenPublic->pLength, eccKeyTokenPublic->pLength); + /* publicKey */ + if (verbose) { + PrintAll(messageFile, + "\t\tpublicKey\n", eccKeyTokenPublic->qLen, eccKeyTokenPublic->publicKey); + } + return; +} + +/* printPKA96EccKeyTokenPrivateKey() prints the EccKeyTokenPrivate structure in human readable form + + */ + +void printPKA96EccKeyTokenPrivateKey(EccKeyTokenPrivate *eccKeyTokenPrivate) +{ + if (verbose) fprintf(messageFile, + "\tECC Token Private Key:\n"); + + /* sectionId */ + printEccSectionID(eccKeyTokenPrivate->sectionId); + /* version */ + if (verbose) fprintf(messageFile, + "\t\tversion: %02x\n", eccKeyTokenPrivate->version); + /* sectionLength */ + if (verbose) fprintf(messageFile, + "\t\tsectionLength: %04hx %hu\n", + eccKeyTokenPrivate->sectionLength, eccKeyTokenPrivate->sectionLength); + /* wrappingMethod */ + printWrappingMethod(eccKeyTokenPrivate->wrappingMethod); + /* hashType */ + printHashType(eccKeyTokenPrivate->hashType); + /* reserved */ + if (verbose) fprintf(messageFile, + "\t\treserved: %04hx %hu\n", + eccKeyTokenPrivate->reserved, eccKeyTokenPrivate->reserved); + /* keyUsage */ + printKeyUsage(eccKeyTokenPrivate->keyUsage); + /* curveType */ + printCurveType(eccKeyTokenPrivate->curveType); + /* keyFormatSecurity */ + printKeyFormatSecurity(eccKeyTokenPrivate->keyFormatSecurity); + /* reserved2 */ + if (verbose) fprintf(messageFile, + "\t\treserved2: %04hx %hu\n", + eccKeyTokenPrivate->reserved2, eccKeyTokenPrivate->reserved2); + /* pLength */ + if (verbose) fprintf(messageFile, + "\t\tpLength: %04hx %hu\n", + eccKeyTokenPrivate->pLength, eccKeyTokenPrivate->pLength); + /* IBMAssocDataLen */ + if (verbose) fprintf(messageFile, + "\t\tIBMAssocDataLength: %04hx %hu\n", + eccKeyTokenPrivate->IBMAssocDataLen, eccKeyTokenPrivate->IBMAssocDataLen); + /* mkvp */ + if (verbose) fprintf(messageFile, + "\t\tmkvp: %02x %02x %02x %02x ... \n", + eccKeyTokenPrivate->mkvp[0], + eccKeyTokenPrivate->mkvp[1], + eccKeyTokenPrivate->mkvp[2], + eccKeyTokenPrivate->mkvp[3]); + /* objProtection */ + if (verbose) fprintf(messageFile, + "\t\tobjProtection: %02x %02x %02x %02x ... \n", + eccKeyTokenPrivate->objProtection[0], + eccKeyTokenPrivate->objProtection[1], + eccKeyTokenPrivate->objProtection[2], + eccKeyTokenPrivate->objProtection[3]); + /* aDataLen */ + if (verbose) fprintf(messageFile, + "\t\taDataLen: %04hx %hu\n", + eccKeyTokenPrivate->aDataLen, eccKeyTokenPrivate->aDataLen); + /* formattedDataLen */ + if (verbose) fprintf(messageFile, + "\t\tformattedDataLen: %04hx %hu\n", + eccKeyTokenPrivate->formattedDataLen, eccKeyTokenPrivate->formattedDataLen); + return; +} + +void printEccSectionID(unsigned char sectionId) +{ + if (verbose) fprintf(messageFile, + "\t\tsectionId: "); + switch (sectionId) { + case ECC_PRIVATE_SECTION: + if (verbose) fprintf(messageFile, + "ECC_PRIVATE_SECTION\n"); + break; + case ECC_PUBLIC_SECTION: + if (verbose) fprintf(messageFile, + "ECC_PUBLIC_SECTION\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", sectionId); + } + return; +} + +void printWrappingMethod(unsigned char wrappingMethod) +{ + if (verbose) fprintf(messageFile, + "\t\twrappingMethod: "); + switch (wrappingMethod) { + case ECC_WRAP_METH_CLEAR: + if (verbose) fprintf(messageFile, + "ECC_WRAP_METH_CLEAR\n"); + break; + case ECC_WRAP_METH_AESKW: + if (verbose) fprintf(messageFile, + "ECC_WRAP_METH_AESKW\n"); + break; + case ECC_WRAP_METH_CBC: + if (verbose) fprintf(messageFile, + "ECC_WRAP_METH_CBC\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", wrappingMethod); + } + return; +} + +void printHashType(unsigned char hashType) +{ + if (verbose) fprintf(messageFile, + "\t\thashType: "); + switch (hashType) { + case ECC_HASH_NONE: + if (verbose) fprintf(messageFile, + "ECC_HASH_NONE\n"); + break; + case ECC_HASH_SHA224: + if (verbose) fprintf(messageFile, + "ECC_HASH_SHA224\n"); + break; + case ECC_HASH_SHA256: + if (verbose) fprintf(messageFile, + "ECC_HASH_SHA256\n"); + break; + case ECC_HASH_SHA384: + if (verbose) fprintf(messageFile, + "ECC_HASH_SHA384\n"); + break; + case ECC_HASH_SHA512: + if (verbose) fprintf(messageFile, + "ECC_HASH_SHA512\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", hashType); + } + return; +} + +void printKeyUsage(unsigned char keyUsage) +{ + if (verbose) fprintf(messageFile, + "\t\tkeyUsage: "); + switch (keyUsage) { + case ECC_KEY_MGMT_ONLY: + if (verbose) fprintf(messageFile, + "ECC_KEY_MGMT_ONLY\n"); + break; + case ECC_KEY_DIST_AND_SIGN: + if (verbose) fprintf(messageFile, + "ECC_KEY_DIST_AND_SIGN\n"); + break; + case ECC_SIGNATURE_USE_ONLY: + if (verbose) fprintf(messageFile, + "ECC_SIGNATURE_USE_ONLY\n"); + break; + case ECC_TRANSLATE: + if (verbose) fprintf(messageFile, + "ECC_TRANSLATE\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", keyUsage); + } + return; +} + +void printCurveType(unsigned char curveType) +{ + if (verbose) fprintf(messageFile, + "\t\tcurveType: "); + switch (curveType) { + case ECC_PRIME: + if (verbose) fprintf(messageFile, + "ECC_PRIME\n"); + break; + case ECC_BRAINPOOL: + if (verbose) fprintf(messageFile, + "ECC_BRAINPOOL\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", curveType); + } + return; +} + +void printKeyFormatSecurity(unsigned char keyFormatSecurity) +{ + if (verbose) fprintf(messageFile, + "\t\tkeyFormatSecurity: "); + switch (keyFormatSecurity) { + case ECC_INTERNAL_ENCRYPTED: + if (verbose) fprintf(messageFile, + "ECC_INTERNAL_ENCRYPTED\n"); + break; + case ECC_EXTERNAL: + if (verbose) fprintf(messageFile, + "ECC_EXTERNAL\n"); + break; + case ECC_EXTERNAL_ENCRYPTED: + if (verbose) fprintf(messageFile, + "ECC_EXTERNAL_ENCRYPTED\n"); + break; + default: + if (verbose) fprintf(messageFile, + "Unknown %02x\n", keyFormatSecurity); + } + return; +} diff --git a/src/signframework/cca_structures_ecc.h b/src/signframework/cca_structures_ecc.h new file mode 100644 index 0000000..0a93d14 --- /dev/null +++ b/src/signframework/cca_structures_ecc.h @@ -0,0 +1,140 @@ +/* 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. + */ + +#ifndef CCA_ECC_STRUCTURES_H +#define CCA_ECC_STRUCTURES_H + +#include <stdint.h> + +/* tokenID */ +#define PKA_EXTERNAL_TOKEN 0x1E +#define PKA_INTERNAL_TOKEN 0x1F + +/* sectionID */ +#define ECC_PRIVATE_SECTION 0x20 +#define ECC_PUBLIC_SECTION 0x21 + +#define ECC_WRAP_METH_CLEAR 0x00 +#define ECC_WRAP_METH_AESKW 0x01 +#define ECC_WRAP_METH_CBC 0x02 + +#define ECC_HASH_NONE 0x00 +#define ECC_HASH_SHA224 0x01 +#define ECC_HASH_SHA256 0x02 +#define ECC_HASH_SHA384 0x04 +#define ECC_HASH_SHA512 0x08 + +#define ECC_KEY_MGMT_ONLY 0xC0 +#define ECC_KEY_DIST_AND_SIGN 0x80 +#define ECC_SIGNATURE_USE_ONLY 0x00 +#define ECC_TRANSLATE 0x02 + +#define ECC_PRIME 0x00 +#define ECC_BRAINPOOL 0x01 + +#define ECC_INTERNAL_ENCRYPTED 0x08 +#define ECC_EXTERNAL 0x40 +#define ECC_EXTERNAL_ENCRYPTED 0x42 + +#define ECC_PRIV_VERSION_00 0x00 + + +#define ECC_PRIME_521 0x0209 +#define MKVP_LENGTH 8 + +#define ECC_OBJ_PROTECTION_LEN 48 +#define MAX_Q_LEN_BYTES 133 /* size of pub key for max p-Len (521) */ + + +/* PKA96 ECC token header */ + +typedef struct tdEccKeyTokenHeader +{ + unsigned char tokenId; /* Token identifier. */ + unsigned char version; + uint16_t tokenLength; + uint32_t reserved; +} EccKeyTokenHeader; + +typedef struct tdEccKeyTokenPublic +{ + unsigned char sectionId; + unsigned char version; + uint16_t sectionLength; /* Length of the RSA public key section */ + uint32_t reserved; + unsigned char curveType; /* curve type: Prime or Brainpool */ + unsigned char reserved2; + uint16_t pLength; /* length of p in bits */ + uint16_t qLen; /* length of public key Q in bytes */ + unsigned char publicKey[MAX_Q_LEN_BYTES]; /* beginning of the public key Q */ +} EccKeyTokenPublic; + +typedef struct tdEccKeyTokenPrivate +{ + unsigned char sectionId; + unsigned char version; + uint16_t sectionLength; /* Length of the ECC private key section */ + unsigned char wrappingMethod; /* Wrapping method: 0 - clear @f3a */ + /* 1 - AESKW @f3a */ + /* 2 - CBC wrap @f3a */ + unsigned char hashType; /* Hash used in wrapping: 1 - SHA224 @f3a */ + /* 2 - SHA256 @f3a */ + /* 4 - SHA384 @f3a */ + /* 8 - SHA512 @f3a */ + uint16_t reserved; + unsigned char keyUsage; /* key usage byte */ + unsigned char curveType; /* curve type: Prime or Brainpool */ + unsigned char keyFormatSecurity; /* key format and security flags */ + unsigned char reserved2; + uint16_t pLength; /* length of p in bits */ + uint16_t IBMAssocDataLen; /* length of IBM Assoc. data in bytes @f1c*/ + unsigned char mkvp[MKVP_LENGTH]; /* master key verification pattern */ + unsigned char objProtection[ECC_OBJ_PROTECTION_LEN]; /* object protection key */ + uint16_t aDataLen; /* associated data length */ + uint16_t formattedDataLen; /* formatted data length */ +} EccKeyTokenPrivate; + + + +long getPKA96EccPublicKey(EccKeyTokenPublic *eccKeyTokenPublic, + long keyTokenLength, + unsigned char *keyToken); +long parsePKA96EccKeyTokenHeader(EccKeyTokenHeader *eccKeyTokenHeader, + long *keyTokenLength, + unsigned char **keyToken); +long parsePKA96EccKeyTokenPublicKey(EccKeyTokenPublic *eccKeyTokenPublic, + long *pubKeyTokenLength, + unsigned char **pubKeyToken); +long parsePKA96EccKeyTokenPrivateKey(EccKeyTokenPrivate *eccKeyTokenPrivate, + long *keyTokenLength, + unsigned char **keyToken); + +/* + Debug Print Functions +*/ + +void printPKA96EccKeyTokenHeader(EccKeyTokenHeader *eccKeyTokenHeader); +void printPKA96EccKeyTokenPublicKey(EccKeyTokenPublic *eccKeyTokenPublic); +void printPKA96EccKeyTokenPrivateKey(EccKeyTokenPrivate *eccKeyTokenPrivate); + +void printEccSectionID(unsigned char sectionId); +void printWrappingMethod(unsigned char wrappingMethod); +void printHashType(unsigned char hashType); +void printKeyUsage(unsigned char keyUsage); +void printCurveType(unsigned char curveType); +void printKeyFormatSecurity(unsigned char keyFormatSecurity); + +#endif diff --git a/src/signframework/cleandropbox b/src/signframework/cleandropbox new file mode 100755 index 0000000..39aabbc --- /dev/null +++ b/src/signframework/cleandropbox @@ -0,0 +1,39 @@ +#!/bin/sh -e +# 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. + +ROOT_DIR=$1 + +ARCHIVE_DAYS=90 + +REQUEST=".request" +REQUEST_GO=".request.go" +RESPONSE=".response" +RESPONSE_GO=".response.go" + +if [ "$ROOT_DIR" = "" ]; then + echo "ERROR: Must provide dropbox root dir" + echo "ERROR: syntax: <dropbox>" + exit -1 +fi + +if [ -d $ROOT_DIR ]; then + find -P $ROOT_DIR -mtime +$ARCHIVE_DAYS \( -name "*$REQUEST" -o -name "*$RESPONSE" -o -name "*$REQUEST_GO" -o -name "*$RESPONSE_GO" \) -exec rm -fv {} \; +else + echo 'ERROR: provided directory does not exist' + exit -1 +fi + +exit 0 diff --git a/src/signframework/debug.c b/src/signframework/debug.c new file mode 100644 index 0000000..1f47700 --- /dev/null +++ b/src/signframework/debug.c @@ -0,0 +1,89 @@ +/* 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 "utils.h" + +#include "debug.h" + +extern FILE* messageFile; + +/* PrintAll() prints 'message', the length, and then the entire byte array 'buff' + */ + +void PrintAll(FILE *file, const char *message, unsigned long length, const unsigned char *buff) +{ + unsigned long i; + + if (buff != NULL) { + fprintf(file, "%s length %lu\n ", message, length); + for (i = 0 ; i < length ; i++) { + if (i && !( i % 16 )) { + fprintf(file, "\n "); + } + fprintf(file, "%.02x ",buff[i]); + } + fprintf(file, "\n"); + } + else { + fprintf(file, "%s null\n", message); + } + return; +} + +/* sprintAll() hex prints the byte array 'buff' to 'string'. 'string' must be long enough to hold + the entire array. length * 4 should be safe, two characters, a space, and some newlines. +*/ + +void sprintAll(char *string, unsigned long length, const unsigned char* buff) +{ + unsigned long i; + + if (buff != NULL) { + for (i = 0 ; i < length ; i++) { + if (i && !( i % 16 )) { + sprintf(string, "\n "); + string += strlen("\n "); + } + sprintf(string, "%.2X ",buff[i]); + string += 3; + } + sprintf(string, "\n"); + string += strlen("\n "); + } + else { + sprintf(string, "null\n"); + } + return; +} + +/* Hook to simulate an error */ +int makeError = FALSE; /* user sets to create an error */ +static int letError = TRUE; /* lets user create only one error */ + +int GetError(int statusIn) +{ + if (makeError && letError) { + letError = FALSE; + return statusIn; + } + else { + return 0; + } +} diff --git a/src/signframework/debug.h b/src/signframework/debug.h new file mode 100644 index 0000000..ac0f3c2 --- /dev/null +++ b/src/signframework/debug.h @@ -0,0 +1,32 @@ +/* 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. + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include <stdio.h> + +extern int verbose; /* verbose debug tracing */ + +void PrintAll(FILE *file, const char *message, unsigned long length, const unsigned char* buff); +void sprintAll(char *string, unsigned long length, const unsigned char* buff); + +/* Hook to simulate Notes API error */ +extern int makeError; + +int GetError(int statusIn); + +#endif diff --git a/src/signframework/dropbox_utils.c b/src/signframework/dropbox_utils.c new file mode 100644 index 0000000..d12ef12 --- /dev/null +++ b/src/signframework/dropbox_utils.c @@ -0,0 +1,663 @@ +/* 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 <sys/inotify.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/inotify.h> +#include <unistd.h> + +#include "dropbox_utils.h" +#include "framework_utils.h" + +extern FILE* messageFile; +extern int verbose; + +// Local functions +DropboxWatchConfig* getConfigByUserid(FrameworkConfig *frameworkConfig, const char* userid); +DropboxWatchConfig* getConfigByWd(FrameworkConfig *frameworkConfig, int wd); +int addDropboxWatcher(FrameworkConfig *frameworkConfig, const char* userid); +/// Display information about the incoming event +void displayInotifyEvent(struct inotify_event *i); +void dropboxRequestInit(DropboxRequest* request, FrameworkConfig *frameworkConfigParm); +void dropboxRequestClear(DropboxRequest* request); +int readRequest(FrameworkConfig *frameworkConfig, DropboxRequest* request); +int JS_ObjectGetString(const char **string, + const char *key, + json_object *object); +/* Array_Scan() converts a string to a binary array */ +int Array_Scan(unsigned char **data, /* output binary, freed by caller */ + size_t *len, + const char *string); /* input string */ +char nibbleToChar(unsigned char nibble); +struct json_object* binaryFileToJsonObject(FILE* fp); + + +void dropboxRequestInit(DropboxRequest* request, FrameworkConfig *frameworkConfigParm) +{ + memset(request, 0, sizeof(DropboxRequest)); + request->frameworkConfig = frameworkConfigParm; +} +void dropboxRequestClear(DropboxRequest* request) +{ + if (request->requestId != NULL) free(request->requestId); + if (request->rJsonO != NULL) json_object_put(request->rJsonO); + if (request->requestJson != NULL) free(request->requestJson); + + if (request->comment != NULL) free(request->comment); + if (request->respJsonO != NULL) json_object_put(request->respJsonO); + +} + + +DropboxWatchConfig* getConfigByUserid(FrameworkConfig *frameworkConfig, const char* userid) +{ + int i; + for (i = 0; i < MAX_WATCHERS; i ++) { + if (frameworkConfig->dropboxWatchers[i].wd != 0 && + !strcmp(frameworkConfig->dropboxWatchers[i].sender, userid)) { + return &(frameworkConfig->dropboxWatchers[i]); + } + } + return NULL; +} + +DropboxWatchConfig* getConfigByWd(FrameworkConfig *frameworkConfig, int wd) +{ + int i; + for (i = 0; i < MAX_WATCHERS; i ++) { + if (frameworkConfig->dropboxWatchers[i].wd == wd) { + return &(frameworkConfig->dropboxWatchers[i]); + } + } + return NULL; +} + +int addDropboxWatcher(FrameworkConfig *frameworkConfig, const char* userid) +{ + int rc = 0; + int idx = 0; + int i; + + if (verbose) fprintf(messageFile, + "addDropboxWatcher: Adding new dropbox watcher for user : %s\n", userid); + + // Find an emtpy spot + idx = MAX_WATCHERS; + for (i = 0; i < MAX_WATCHERS; i ++) { + if (frameworkConfig->dropboxWatchers[i].wd == 0) { + idx = i; + break; + } + } + if (idx == MAX_WATCHERS) { + fprintf(messageFile, + "addDropboxWatcher: Number of signers exceeds max %d\n", MAX_WATCHERS); + rc = 1; + } + if (strlen(userid) > MAX_USERNAME-1) { + fprintf(messageFile, + "addDropboxWatcher: String length of signer userid '%s' exceeds max %d\n", + userid, MAX_USERNAME-1); + rc = 1; + } + + if (rc == 0) { + // Construct the dropbox dir + char* dir = malloc(strlen(frameworkConfig->dropboxDir) + strlen(userid) + 5); + sprintf(dir, "%s/%s", frameworkConfig->dropboxDir, userid); + + if (verbose) fprintf(messageFile, + "addDropboxWatcher: Dropbox location : %s\n", dir); + + strcpy(frameworkConfig->dropboxWatchers[idx].sender, userid); + // Setup the notify watcher to look for files created in the dropbox + frameworkConfig->dropboxWatchers[idx].wd = inotify_add_watch(frameworkConfig->inotifyFd, + dir, IN_CREATE); + if (frameworkConfig->dropboxWatchers[idx].wd < 0) { + fprintf(messageFile, + "addDropboxWatcher: Failed to add inotify watch for : %s\n", userid); + perror("addDropboxWatcher: Failed to add inotify watch"); + // Ignore this failure as there may be signers listed in + // configuration that haven't been setup yet + //rc = 1; + } + free(dir); + } + + return rc; +} + +int dropboxInit(FrameworkConfig *frameworkConfig) +{ + int rc = 0; + size_t p,s; + + frameworkConfig->inotifyFd = inotify_init(); + if (frameworkConfig->inotifyFd < 0) { + fprintf(messageFile, + "dropboxInit: Inotify_init failed\n"); + rc = 1; + } + if (verbose) + fprintf(messageFile, "dropboxInit: inotify FD %d\n", frameworkConfig->inotifyFd); + + // Add control file watcher + if (rc == 0) { + frameworkConfig->controlWd = inotify_add_watch(frameworkConfig->inotifyFd, + frameworkConfig->dropboxDir, IN_CREATE); + if (frameworkConfig->controlWd < 0) { + fprintf(messageFile, + "addDropboxWatcher: Failed to add controlfile inotify watch\n"); + perror("addDropboxWatcher: Failed to add controlfile inotify watch"); + rc = 1; + } + } + + if (rc == 0) { + // Walk through the authorized senders and setup a dropbox + for (p = 0; p < frameworkConfig->projectLength; p ++) { + for (s = 0; s < frameworkConfig->projectConfigFiles[p]->sendersCount; s++) { + // Set one up if there isn't one already + if (NULL == getConfigByUserid(frameworkConfig, frameworkConfig->projectConfigFiles[p]->senders[s])) { + rc = addDropboxWatcher(frameworkConfig, frameworkConfig->projectConfigFiles[p]->senders[s]); + } + if (rc != 0) break; + } + if (rc != 0) break; + } + } + + return rc; +} + +int dropboxShutdown(FrameworkConfig *frameworkConfig) +{ + int rc = 0; + int i; + + if (verbose) { + fprintf(messageFile, "Shutting down dropbox\n"); + } + + if (frameworkConfig->inotifyFd > 0) + { + // Remove the stop/restart watchers + inotify_rm_watch(frameworkConfig->inotifyFd,frameworkConfig->controlWd); + + // Remove the signer dropbox watchers + for (i = 0; i < MAX_WATCHERS; i ++) { + if (frameworkConfig->dropboxWatchers[i].wd != 0) { + inotify_rm_watch(frameworkConfig->inotifyFd, + frameworkConfig->dropboxWatchers[i].wd); + frameworkConfig->dropboxWatchers[i].sender[0] = '\0'; + frameworkConfig->dropboxWatchers[i].wd = 0; + } + } + + close(frameworkConfig->inotifyFd); + frameworkConfig->inotifyFd = 0; + } + return rc; +} + + +void displayInotifyEvent(struct inotify_event *i) +{ + fprintf(messageFile, " wd =%2d; ", i->wd); + if (i->cookie > 0) + fprintf(messageFile, "cookie =%4d; ", i->cookie); + + fprintf(messageFile, "mask = "); + if (i->mask & IN_ACCESS) fprintf(messageFile, "IN_ACCESS "); + if (i->mask & IN_ATTRIB) fprintf(messageFile, "IN_ATTRIB "); + if (i->mask & IN_CLOSE_NOWRITE) fprintf(messageFile, "IN_CLOSE_NOWRITE "); + if (i->mask & IN_CLOSE_WRITE) fprintf(messageFile, "IN_CLOSE_WRITE "); + if (i->mask & IN_CREATE) fprintf(messageFile, "IN_CREATE "); + if (i->mask & IN_DELETE) fprintf(messageFile, "IN_DELETE "); + if (i->mask & IN_DELETE_SELF) fprintf(messageFile, "IN_DELETE_SELF "); + if (i->mask & IN_IGNORED) fprintf(messageFile, "IN_IGNORED "); + if (i->mask & IN_ISDIR) fprintf(messageFile, "IN_ISDIR "); + if (i->mask & IN_MODIFY) fprintf(messageFile, "IN_MODIFY "); + if (i->mask & IN_MOVE_SELF) fprintf(messageFile, "IN_MOVE_SELF "); + if (i->mask & IN_MOVED_FROM) fprintf(messageFile, "IN_MOVED_FROM "); + if (i->mask & IN_MOVED_TO) fprintf(messageFile, "IN_MOVED_TO "); + if (i->mask & IN_OPEN) fprintf(messageFile, "IN_OPEN "); + if (i->mask & IN_Q_OVERFLOW) fprintf(messageFile, "IN_Q_OVERFLOW "); + if (i->mask & IN_UNMOUNT) fprintf(messageFile, "IN_UNMOUNT "); + fprintf(messageFile, "\n"); + + if (i->len > 0) + fprintf(messageFile, " name = %s\n", i->name); +} + +int processEvent(FrameworkConfig *frameworkConfig, struct inotify_event *i, + int* stop, int* restart) +{ + int rc = 0; + char* pos = NULL; + char* filename = NULL; + DropboxRequest request; + + File_OpenMessageFile(frameworkConfig->outputBodyFilename, "w"); + + if (verbose) displayInotifyEvent(i); + + DropboxWatchConfig* db = getConfigByWd(frameworkConfig, i->wd); + + if (strlen(i->name) > 100) { + fprintf(messageFile, "WARNING : Saw large filename dropped in dropbox, len %d\n", + (int)strlen(i->name)); + + // Was the event from a signers dropbox ? + } else if (db != NULL) { + + // Ignore anything but a request.go file + pos = strstr(i->name, ".request.go"); + if (pos != NULL) { + dropboxRequestInit(&request, frameworkConfig); + request.dbConfig = db; + request.event = i; + + rc = readRequest(frameworkConfig, &request); + + // Process the request + if (request.requestId != NULL) { + rc = FrameworkProcess_Process(&request); + } + + dropboxRequestClear(&request); + } + + + } else if (i->wd == frameworkConfig->controlWd) { + // Was a stop signaled ? + if (!strcmp(i->name, frameworkConfig->stopFile)) { + *stop = 1; + rc = Malloc_Safe((unsigned char**)(&filename), strlen(frameworkConfig->dropboxDir) + + strlen(frameworkConfig->stopFile) + 5, + frameworkConfig->lineMax); + if (rc == 0) { + sprintf(filename,"%s/%s", frameworkConfig->dropboxDir, frameworkConfig->stopFile); + remove(filename); + free(filename); + filename=NULL; + } + + // Was a restart signaled + } else if (!strcmp(i->name, frameworkConfig->restartFile)) { + *restart = 1; + rc = Malloc_Safe((unsigned char**)(&filename), strlen(frameworkConfig->dropboxDir) + + strlen(frameworkConfig->restartFile) + 5, + frameworkConfig->lineMax); + if (rc == 0) { + sprintf(filename,"%s/%s", frameworkConfig->dropboxDir, frameworkConfig->restartFile); + remove(filename); + free(filename); + filename=NULL; + } + } + + } + + return rc; +} + +int readRequest(FrameworkConfig *frameworkConfig, DropboxRequest* request) +{ + int rc = 0; + char* filename = NULL; + char* pos = NULL; + size_t fileSize = 0; + + pos = strstr(request->event->name, ".request.go"); + if (pos != NULL) { + // Parse out the request id from the go file + rc = Malloc_Safe((unsigned char**)&request->requestId, + (pos - request->event->name) + 1, (pos - request->event->name) + 1); + if (rc) return rc; + strncpy(request->requestId, request->event->name, (pos - request->event->name)); + request->requestId[(pos - request->event->name)]='\0'; + + if (verbose) { + fprintf(messageFile, "Saw dropbox event for signer : %s Request : '%s'\n", + request->dbConfig->sender, request->requestId); + } + + // Now we need to read in the request file + rc = Malloc_Safe((unsigned char**)(&filename), strlen(frameworkConfig->dropboxDir) + + strlen(request->dbConfig->sender) + strlen(request->requestId) + + strlen(".request") + 5, frameworkConfig->lineMax); /* freed @2 */ + if (rc) return rc; + sprintf(filename,"%s/%s/%s.request", frameworkConfig->dropboxDir, + request->dbConfig->sender, request->requestId); + rc = File_ReadTextFile(&(request->requestJson), /* freed by caller */ + &fileSize, + frameworkConfig->fileMax, + filename); + + +#if 0 + if (rc == 0) { + printf("REQUEST FILE CONTENTS\n========\n%s\n========\n", request->requestJson); + } +#endif + + free(filename); /* @2 */ + + // Parse the json + if (rc == 0) { + request->rJsonO = json_tokener_parse(request->requestJson); + if (NULL == request->rJsonO) { + fprintf(messageFile, "ERROR: Unable to parse JSON from request %s\n", request->requestId); + rc = 1; + } + } + + // Now we pull required fields + if (rc == 0) { + rc = JS_ObjectGetString(&(request->project), "project", request->rJsonO); + if (rc == 0 && strlen(request->project) > frameworkConfig->lineMax) { + rc = 1; + fprintf(messageFile, "ERROR: Project overflow detected\n"); + } + if (rc == 0 && verbose) fprintf(messageFile, "Project : %s\n", request->project); + } + if (rc == 0) { + // Make a copy of the comment as we will be scanning for invalid chars later + const char* cmt = NULL; + rc = JS_ObjectGetString(&cmt, "comment", request->rJsonO); + if (rc == 0 && strlen(cmt) > frameworkConfig->lineMax) { + rc = 1; + fprintf(messageFile, "ERROR: Comment overflow detected\n"); + } else if (rc == 0) { + Malloc_Strcpy(&request->comment, cmt); + } else { + Malloc_Strcpy(&request->comment, ""); + } + if (rc == 0 && verbose) fprintf(messageFile, "Comment : %s\n", request->comment); + } + if (rc == 0) { + rc = JS_ObjectGetString(&(request->user), "user", request->rJsonO); + if (rc == 0 && strlen(request->user) > frameworkConfig->lineMax) { + rc = 1; + fprintf(messageFile, "ERROR: User overflow detected\n"); + } + if (rc == 0 && verbose) fprintf(messageFile, "User : %s\n", request->user); + } + if (rc == 0) { + rc = JS_ObjectGetString(&(request->parameters), "parameters", request->rJsonO); + if (rc == 0 && strlen(request->parameters) > frameworkConfig->lineMax) { + rc = 1; + fprintf(messageFile, "ERROR: Parameters overflow detected\n"); + } + if (rc == 0 && verbose) fprintf(messageFile, "Parameters : %s\n", request->parameters); + } + if (rc == 0) { + rc = JS_ObjectGetString(&(request->epwd), "epwd", request->rJsonO); + if (rc == 0 && strlen(request->epwd) > frameworkConfig->lineMax) { + rc = 1; + fprintf(messageFile, "ERROR: EPWD overflow detected\n"); + } + } + + + // Pull the payload + if (rc == 0) { + const char* payload = NULL; + rc = JS_ObjectGetString(&payload, "payload", request->rJsonO); + if (rc == 0 && payload != NULL && + strlen(payload) > 0) { + // Store the payload into the input file + + uint8_t* data = NULL; + size_t len = 0; + rc = Array_Scan(&data, &len, payload); + + if (rc == 0) { + // Write to the file + rc = File_WriteBinaryFile(data, len, + request->frameworkConfig->inputAttachmentFilename); + } + if (rc == 0) { + request->hasPayload = 1; + if (verbose) fprintf(messageFile, "Payload stored to %s\n", request->frameworkConfig->inputAttachmentFilename); + } + + if (data != NULL) free(data); + } + rc = 0; // payload optional + } + } + request->status = rc; + + return rc; +} + + +int JS_ObjectGetString(const char **string, + const char *key, + json_object *object) +{ + int rc = 0; + json_object *keyJson = NULL; + + if (rc == 0) { + if (!json_object_object_get_ex(object, key, &keyJson)) { + fprintf(messageFile, + "ERROR: JS_ObjectGetString: getting key: %s\n", key); + rc = 1; + } + } + if (rc == 0) { + *string = json_object_get_string(keyJson); + if (verbose) fprintf(messageFile, + "JS_ObjectGetString: key: %s string: %s\n", + key, *string); + } + return rc; +} + +/* Array_Scan() converts a string to a binary array */ +int Array_Scan(unsigned char **data, /* output binary, freed by caller */ + size_t *len, + const char *string) /* input string */ +{ + uint32_t rc = 0; + size_t strLength; + if (rc == 0) { + strLength = strlen(string); + if ((strLength %2) != 0) { + fprintf(messageFile, + "ERROR: Array_Scan: number of bytes %lu is not even\n", + (unsigned long)strLength); + rc = 1; + } + } + if (rc == 0) { + *len = strlen(string) / 2; /* safe because already tested for even number of bytes */ + *data = malloc((*len) + 8); /* add bytes at end because scanf uses int */ + if (*data == NULL) { + fprintf(messageFile, + "ERROR: Array_Scan: could not malloc %u bytes\n", (unsigned int)*len); + rc = 1; + } + } + if (rc == 0) { + unsigned int i; + for (i = 0 ; i < *len ; i++) { + unsigned int tmpint; + int irc = sscanf(string + (2*i), "%2x", &tmpint); + *((*data)+i) = tmpint; + if (irc != 1) { + fprintf(messageFile, + "ERROR: Array_Scan: invalid hexascii\n"); + rc = 1; + } + } + } + return rc; +} + +char nibbleToChar(unsigned char nibble) +{ + if(nibble > 0xF) + { + return 0; + } + else + { + if(nibble < 10) + { + return ('0' + nibble); + } + else + { + return ('A' + nibble - 10); + } + } +} + +struct json_object* binaryFileToJsonObject(FILE* fp) +{ + unsigned char hi = 0; + unsigned char lo = 0; + + fseek(fp, 0, SEEK_END); + size_t fileSize = ftell(fp); + rewind(fp); + + unsigned char* hashFile = malloc((fileSize+1)); + char* hashString = malloc((fileSize*2)+1); + + fread(hashFile, fileSize, 1, fp); + + size_t i = 0; + while(i < fileSize) + { + hi = (hashFile[i] >> 4) & 0xF; + lo = hashFile[i] & 0x0F; + hashString[2*i] = nibbleToChar(hi); + hashString[(2*i)+1] = nibbleToChar(lo); + i++; + } + hashString[2*i] = 0; + + struct json_object* json = json_object_new_string(hashString); + + free(hashFile); + free(hashString); + + return json; +} + + +int sendResult(DropboxRequest* requestParms) +{ + int rc = 0; + const char* outputJson = NULL; + char* output = NULL; + char* filename = NULL; + size_t len = 0; + FILE *file = NULL; + + // Create the result json and place it back in the senders dropbox + + struct json_object* json = json_object_new_object(); + + //fprintf(messageFile, "Sending response\n"); + + File_CloseMessageFile(); + + if (requestParms->hasResult) { + FILE * resFile = fopen(requestParms->frameworkConfig->outputAttachmentFilename, "r"); + if (resFile == NULL && requestParms->status == 0) { + fprintf(requestParms->frameworkConfig->frameworkLogFile, + "ERROR: Unable to open output attachment : %s\n", + requestParms->frameworkConfig->outputAttachmentFilename); + requestParms->status = 1; + } else if (resFile != NULL) { + json_object_object_add(json, "result" , binaryFileToJsonObject(resFile)); + fclose(resFile); + } + } + remove(requestParms->frameworkConfig->outputAttachmentFilename); + + if (rc == 0) { + rc = File_ReadTextFile(&output, &len, requestParms->frameworkConfig->fileMax, + requestParms->frameworkConfig->outputBodyFilename); + if (rc == 0) { + json_object_object_add(json, "stdout", json_object_new_string(output)); + free(output); + } + } + + if (rc == 0) { + json_object_object_add(json, "retval", json_object_new_int(requestParms->status)); + } + + // Get our output json + if (rc == 0) { + outputJson = json_object_get_string(json); + + rc = Malloc_Safe((unsigned char**)&filename, + strlen(requestParms->frameworkConfig->dropboxDir) + + strlen(requestParms->dbConfig->sender) + + strlen(requestParms->requestId) + strlen(".response.go") + 10, + requestParms->frameworkConfig->lineMax); + } + + /// TODO REMOVE +#if 0 + if (verbose) { + File_Printf(requestParms->frameworkConfig->frameworkLogFile, NULL, + "Response: %s\n", outputJson); + fprintf(messageFile, "Response : %s\n", outputJson); + } +#endif + + // Write the response file + if (rc == 0) { + sprintf(filename,"%s/%s/%s.response", requestParms->frameworkConfig->dropboxDir, + requestParms->dbConfig->sender, requestParms->requestId); + + rc = File_Open(&file, filename, "w"); + if (rc == 0) { + fprintf(file, "%s\n", outputJson); + fclose(file); + } + + } + + // Write the go file + if (rc == 0) { + sprintf(filename,"%s/%s/%s.response.go", requestParms->frameworkConfig->dropboxDir, + requestParms->dbConfig->sender, requestParms->requestId); + + rc = File_Open(&file, filename, "w"); + if (rc == 0) { + fprintf(file, "GO\n"); + fclose(file); + } + + } + + + if (filename != NULL) free(filename); + json_object_put(json); + return rc; +} diff --git a/src/signframework/dropbox_utils.h b/src/signframework/dropbox_utils.h new file mode 100644 index 0000000..55f0134 --- /dev/null +++ b/src/signframework/dropbox_utils.h @@ -0,0 +1,35 @@ +/* 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 <sys/inotify.h> + +#include "framework_utils.h" + +#ifndef DROPBOX_UTILS_H +#define DROPBOX_UTILS_H + + +/// Initialize the dropbox +int dropboxInit(FrameworkConfig *frameworkConfig); +/// Close all active dropbox watchers +int dropboxShutdown(FrameworkConfig *frameworkConfig); + +/// Process the inotify event +int processEvent(FrameworkConfig *frameworkConfig, struct inotify_event *i, + int* stop, int* restart); + +int sendResult(DropboxRequest* requestParms); +#endif diff --git a/src/signframework/errorcodes.h b/src/signframework/errorcodes.h new file mode 100644 index 0000000..c1e2f27 --- /dev/null +++ b/src/signframework/errorcodes.h @@ -0,0 +1,25 @@ +/* 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. + */ + +#ifndef ERRORCODES_H +#define ERRORCODES_H + +#define ERR_FATAL 1 +#define ERR_SUBJECT_BAD 2 +#define ERR_BAD_ARG 3 + + +#endif diff --git a/src/signframework/framework.c b/src/signframework/framework.c new file mode 100644 index 0000000..3417bb7 --- /dev/null +++ b/src/signframework/framework.c @@ -0,0 +1,316 @@ +/* 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 <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <unistd.h> +#include <sys/inotify.h> + +#include "utils.h" +#include "debug.h" +#include "framework_utils.h" +#include "dropbox_utils.h" +#include "mail.h" + +/* local prototypes */ + +int GetArgs(int argc, + char **argv); +void PrintUsage(void); +int Main_Loop(void); + +void Framework_GetControlFlags(int *stop, + int *restart, + FrameworkConfig *frameworkConfig); +void Framework_Init(FrameworkConfig *frameworkConfig); +int Framework_Load(FrameworkConfig *frameworkConfig, + int transientError); +void Framework_Delete(FrameworkConfig *frameworkConfig); + +/* global variables */ + +/* At startup, messageFile points to stdout. This allows the person starting the program to see + startup errors. + + In the main loop, messageFile will point to the output body file. It is opened for write on each + new email request. It is closed when the project specific program is called, so it can be opened + for append by that program. That program will then close it, so that the framework can reopen it + for append to add any final text. +*/ + +FILE *messageFile = NULL; +int verbose = FALSE; +int debug = FALSE; + +int main(int argc, char **argv) +{ + int rc = 0; + + messageFile = stdout; + + printf("Starting initialization\n"); + /* get framework command line argument */ + if (rc == 0) { + rc = GetArgs(argc, argv); + } + /* run the framework */ + if (rc == 0) { + rc = Main_Loop(); + } + return rc; +} + +/* framework main loop */ + +int Main_Loop(void) +{ + int rc = 0; + int stop = FALSE; + int restart = FALSE; + int transientError = FALSE; /* boolean */ + FrameworkConfig frameworkConfig; + char eventBuf[1024]; + char* eventPtr = NULL; + struct inotify_event* event = NULL; + + /* outer loop runs until stop or fatal error */ + while ((rc == 0) && !stop) { + + /* outer loop rereads the framework configuration files on each restart */ + Framework_Init(&frameworkConfig); + + /* read the configuration files, start the framework log */ + if (rc == 0) { + rc = Framework_Load(&frameworkConfig, transientError); /* freed @1 */ + } + + /* Setup the signer dropbox watchers */ + if (rc == 0) { + rc = dropboxInit(&frameworkConfig); + } + + if (rc == 0) { + printf("\nStarting Main Loop\n"); + printf("Create file \'%s/%s\' to stop\n", + frameworkConfig.dropboxDir, frameworkConfig.stopFile); + printf("Create file \'%s/%s\' to restart\n\n", + frameworkConfig.dropboxDir, frameworkConfig.restartFile); + } + /* inner loop runs until restart or stop or fatal error */ + while ((rc == 0) && !stop && !restart) { + + + /// Do a blocking read on the dropbox waiting for next event + ssize_t numRead = read(frameworkConfig.inotifyFd, eventBuf, sizeof(eventBuf)); + if (numRead <= 0) { + fprintf(messageFile, "read() from inotify fd returned <= 0 : %d", (int)numRead); + //rc = 1; + continue; + } + + /* Process all of the events in buffer returned by read() */ + /* A single read can return multiple inotify events */ + for (eventPtr = eventBuf; eventPtr < eventBuf + numRead; ) { + event = (struct inotify_event *) eventPtr; + + processEvent(&frameworkConfig, event, &stop, &restart); + + eventPtr += sizeof(struct inotify_event) + event->len; + } + + + } + /* Log the error, stop or restart event to the framework log */ + File_LogTime(frameworkConfig.frameworkLogFile); + if (stop) { + File_Printf(frameworkConfig.frameworkLogFile, stdout, "Stopping\n"); + } + else if (restart) { + File_Printf(frameworkConfig.frameworkLogFile, stdout, "Restarting\n"); + restart = FALSE; + } + else { + File_Printf(frameworkConfig.frameworkLogFile, stdout, "Stopping because of error\n"); + } + /* cleanup */ + dropboxShutdown(&frameworkConfig); + Framework_Delete(&frameworkConfig); /* @1 */ + } + return rc; +} + +/* The framework is stopped by creating stopFile. If the file exists here, set a flag so a loop + exits. + + NOTE: Crtl-C will not terminate the program. It just signals the sleep to return. +*/ + +void Framework_GetControlFlags(int *stop, + int *restart, + FrameworkConfig *frameworkConfig) +{ + int rc; + int verboseSave; + + /* Save and restore 'verbose' so the validate doesn't trace the expected failure. */ + verboseSave = verbose; + verbose=FALSE; + + /* check for a stop file */ + rc = File_ValidateOpen(frameworkConfig->stopFile, "r"); + if (rc == 0) { + remove(frameworkConfig->stopFile); + *stop = 1; + } + /* check for a restart file */ + rc = File_ValidateOpen(frameworkConfig->restartFile, "r"); + if (rc == 0) { + remove(frameworkConfig->restartFile); + *restart = 1; + } + verbose = verboseSave; + return; +} + +/* Framework_Init() does basic program initialization before calling the main loop. + + NOTE: Since this is called at startup, messages should go to stdout. +*/ + +void Framework_Init(FrameworkConfig *frameworkConfig) +{ + FrameworkConfig_Init(frameworkConfig); + return; +} + +/* Framework_Load() + + - loads the FrameworkConfig structure members from the configuration file + - opend the framework audit log + - loads the ProjectConfig structure members from the configuration files + - starts the audit log + - emails the startup message + + transientError indicates the reason for the load: + FALSE: a normal start or restart + TRUE: a restart due to a transient error +*/ + +int Framework_Load(FrameworkConfig *frameworkConfig, + int transientError) +{ + int rc = 0; + size_t i; + + /* read the signer framework configuration file and get basic parameters */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "\nFramework_Load: Loading framework configuration file\n\n"); + rc = FrameworkConfig_Parse(TRUE, /* need master key */ + TRUE, /* validate */ + frameworkConfig); + } + /* open framework audit log file for append */ + if (rc == 0) { + frameworkConfig->frameworkLogFile = fopen(frameworkConfig->frameworkLogFilename, "a"); + if (frameworkConfig->frameworkLogFile == NULL) { + fprintf(messageFile, + "ERROR0003: Opening: %s\n", frameworkConfig->frameworkLogFilename); + frameworkConfig->frameworkLogFile = stdout; + rc = ERROR_CODE; + } + else { + /* no buffering, so log can be monitored while the framework is running */ + setvbuf(frameworkConfig->frameworkLogFile, 0, _IONBF, 0); + } + } + /* Walk the project configuration files and validate them. Cache the results in the + frameworkConfig structure */ + /* for each project */ + for (i = 0 ; (rc == 0) && (i < frameworkConfig->projectLength) ; i++) { + + if (verbose) fprintf(messageFile, + "\nFramework_Load: Loading project configuration file %s\n\n", + frameworkConfig->projectConfigFilenames[i]); + if (rc == 0) { + rc = ProjectConfig_Parse(frameworkConfig->projectConfigFiles[i], + TRUE, /* validate */ + frameworkConfig->projectConfigFilenames[i], + frameworkConfig); + } + } + /* log the framework startup event */ + if (rc == 0) { + FrameworkConfig_LogStart(frameworkConfig); + } + if (rc == 0) { + rc = FrameworkConfig_SendStartupMessage(frameworkConfig, transientError); + } + return rc; +} + +/* Framework_Delete() closes the framework audit log and frees the structure members */ + +void Framework_Delete(FrameworkConfig *frameworkConfig) +{ + /* close the framework audit log */ + if (frameworkConfig->frameworkLogFile != stdout) { + fclose(frameworkConfig->frameworkLogFile); + } + FrameworkConfig_Delete(frameworkConfig); + return; +} + +/* GetArgs() gets the main program command line arguments */ + +int GetArgs(int argc, + char **argv) +{ + int rc = 0; + int i; + + /* command line argument defaults */ + verbose = FALSE; + + /* get the command line arguments */ + for (i=1 ; (i<argc) && (rc == 0) ; i++) { + if (strcmp(argv[i],"-v") == 0) { + verbose = TRUE; + } + else if (strcmp(argv[i],"-h") == 0) { + PrintUsage(); + rc = ERROR_CODE; + } + else { + printf("\nframework: Error, %s is not a valid option\n",argv[i]); + PrintUsage(); + rc = ERROR_CODE; + } + } + return rc; +} + +void PrintUsage() +{ + printf("\n"); + printf("\tframework:\n" + "\t[-v - verbose tracing]\n" + "\t[-h - print usage help]\n"); + printf("\n"); + return; +} diff --git a/src/signframework/framework.cfg.sample b/src/signframework/framework.cfg.sample new file mode 100644 index 0000000..049a83a --- /dev/null +++ b/src/signframework/framework.cfg.sample @@ -0,0 +1,72 @@ +# Notes Signer Framework Configuration File Sample +# +# NOTE: The items must remain in this order! +# The environment variable FRAMEWORK_CONFIG_FILE must point to this file. + +# maximum characters in a line. This is used for email address, +# subject, body, and configuration files + +line_max=4000 + +# maximum dropbox file size +file_max=10000 + +# Name to use as prefix of all email notifications +framework_name=PowerSignFramework + +# CCA user profile password expiration (in months) + +password_expire=12 + +# File name for the signer framework audit log + +log=logs/framework.log + +# Rooted !!! Base directory for dropbox +dropbox=/home/dropbox + +# File name used to stop the framework + +stop_file=stop + +# File name used to restart the framework + +restart_file=restart + +# File names for the output response messages (response and audit messages) + +out_body=obody.txt +notif_log=nlog.txt +full_email=email.txt + +# Rooted !!! file names for the input and output attachments + +in_attachment=/home/framework/signframework/src/signframework/iatt.bin +out_attachment=/home/framework/signframework/src/signframework/oatt.bin + +# File name for the password master AES key token (used to decrypt the +# password string received in a user email) + +key=masterkey.bin + +# LDAP information for sender validation +# URL ldap://ldap.com:389 +ldapurl=unused +# BASEDN ou=company,o=ldap.com +ldapbase=unused + + +# signer framework administrator email address +admins=1 +framework@email.com + +# mapping of project to project configuration file + +# The format is project=configuration_filename + +testproject=testproject.cfg +audit=audit.cfg +#admin=admin.cfg +password_change=password_change.cfg +getpubkey=getpubkey.cfg +getpubkeyecc=getpubkeyecc.cfg diff --git a/src/signframework/framework_test.c b/src/signframework/framework_test.c new file mode 100644 index 0000000..ba065c3 --- /dev/null +++ b/src/signframework/framework_test.c @@ -0,0 +1,1053 @@ +/* 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. + */ + +/* This program is a stub 'signer' application used to test the Notes code signer framework. It + simply prints a 'hello' message and then the command line arguments it was called with. +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> + +#include "openssl/evp.h" + +#include "cca_functions.h" +#include "cca_structures.h" +#include "ossl_functions.h" +#ifdef ADD_ECC +#include "cca_structures_ecc.h" +#include "cca_functions_ecc.h" +#include "ossl_functions_ecc.h" +#endif +#include "utils.h" +#include "debug.h" + +/* local prototypes */ + +int EchoArgs(int argc, char** argv); +int CheckAuxArgs(int argc, char** argv); +int GetArgs(const char **outputBodyFilename, + const char **usr, + const char **password, + const char **projectLogFileName, + const char **projectAuxConfigFileName, + const char **sender, + const char **project, + const char **keyFileName, + const char **inputAttachmentFileName, + const char **outputAttachmentFileName, + int *verbose, + int argc, + char **argv); +int GetAuxArgs(char **signAlgorithm, + const char *projectAuxConfigFileName); +void PrintUsage(void); + +int SignSample(const char *keyFileName, + const char *inputAttachmentFileName, + const char *outputAttachmentFileName, + FILE *projectLogFile, + const char *signAlgorithm); +int SignSampleRSA(unsigned char *keyToken, + size_t keyTokenLength, + unsigned char *testMessage, + size_t testMessageLength, + const char *outputAttachmentFileName, + FILE *projectLogFile); +#ifdef ADD_ECC +int SignSampleECCP521(unsigned char *keyToken, + size_t keyTokenLength, + unsigned char *testMessage, + size_t testMessageLength, + const char *outputAttachmentFileName, + FILE *projectLogFile); + +#endif + +/* This table describes the parameters added by the auxiliary configuration file. They should not + be in the input body, as this might indicate a misguided or malicious attempt to override the + project auxiliary configuration file values. It might also indicate a program design error, + where a framework value clashes with the project auxiliary configuration file values. + + Use this table to screen the user input. +*/ + +static const char *claTable[] = { + "-sign_algorithm", +}; + + +/* messages are traced here */ +FILE *messageFile = NULL; +int verbose = FALSE; +int debug = FALSE; + +int main(int argc, char** argv) +{ + int rc = 0; + const char *usr = NULL; + const char *password = NULL; + const char *projectLogFileName = NULL; + FILE *projectLogFile = NULL; /* closed @1 */ + const char *projectAuxConfigFileName = NULL; + char *signAlgorithm = NULL; /* freed @2 */ + time_t log_time; + const char *sender = NULL; + const char *project = NULL; + const char *keyFileName = NULL; + const char *inputAttachmentFileName = NULL; + const char *outputAttachmentFileName = NULL; + const char *outputBodyFilename = NULL; + + messageFile = stdout; + + /* OpenSSL_add_all_algorithms(); */ + /* get caller's command line arguments */ + if (rc == 0) { + rc = GetArgs(&outputBodyFilename, + &usr, + &password, + &projectLogFileName, + &projectAuxConfigFileName, + &sender, + &project, + &keyFileName, + &inputAttachmentFileName, + &outputAttachmentFileName, + &verbose, + argc, argv); + } + /* check that no command line arguments clash with those in the project auxiliary configuration + file */ + if (rc == 0) { + rc = CheckAuxArgs(argc, argv); + } + /* get additional parameters from the project auxiliary configuration file */ + if (rc == 0) { + rc = GetAuxArgs(&signAlgorithm, /* freed @2 */ + projectAuxConfigFileName); + } + /* sample - log in to CCA */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Hello from framework_test\n"); + if (verbose) fprintf(messageFile, "Logging in with user name %s\n", usr); + rc = Login_Control(TRUE, /* log in */ + usr, /* CCA profile */ + password); /* CCA password */ + } + /* sample audit logging */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Opening audit log %s\n", projectLogFileName); + projectLogFile = fopen(projectLogFileName, "a"); /* closed @1 */ + 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"); + log_time = time(NULL); + fprintf(projectLogFile, "\n%s", ctime(&log_time)); + fprintf(projectLogFile, "\tSender: %s\n", sender); + fprintf(projectLogFile, "\tProject: %s\n", project); + fprintf(projectLogFile, "\tProgram: %s\n", argv[0]); + fprintf(projectLogFile, "\tKey file: %s\n", keyFileName); + fprintf(projectLogFile, "\tProfile %s\n", usr); + } + /* + sample - sign and verify + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Signing\n"); + if (verbose) fprintf(messageFile, " Input attachment %s\n", inputAttachmentFileName); + if (verbose) fprintf(messageFile, " Output attachment %s\n", outputAttachmentFileName); + rc = SignSample(keyFileName, + inputAttachmentFileName, + outputAttachmentFileName, + projectLogFile, + signAlgorithm); + } + /* sample log out of CCA */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Logging out with user name %s\n", usr); + rc = Login_Control(FALSE, /* log out */ + usr, /* CCA profile */ + NULL); /* password */ + } + /* stub processor prints the command line arguments */ + if (rc == 0) { + rc = EchoArgs(argc, argv); + } + fprintf(messageFile, "Return code: %u\n", rc); + /* update audit log */ + if (projectLogFile != NULL) { + fprintf(projectLogFile, "\tReturn code: %d\n", rc); + } + /* clean up */ + if (projectLogFile != NULL) { + fclose(projectLogFile); /* @1 */ + } + free(signAlgorithm); /* @2 */ + if (messageFile != stdout) { + fflush(messageFile); + fclose(messageFile); + } + + return rc; +} + +/* SignSample() is sample code to demonstrate CCA signing and verification + + */ + +int SignSample(const char *keyFileName, + const char *inputAttachmentFileName, + const char *outputAttachmentFileName, + FILE *projectLogFile, + const char *signAlgorithm) +{ + int rc = 0; + /* + signing key + */ + unsigned char *keyToken = NULL; /* CCA key token */ + size_t keyTokenLength; + /* + data to be signed + */ + unsigned char *testMessage = NULL; + size_t testMessageLength; + + /* get the CCA key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignSample: Reading CCA key token file %s\n", + keyFileName); + rc = File_ReadBinaryFile(&keyToken, &keyTokenLength, 2000, keyFileName); /* freed @1 */ + if (rc != 0) { + fprintf(messageFile, "ERROR1026: Could not open key file: %s\n", keyFileName); + } + } + /* get the input message */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignSample: Reading input file %s\n", + inputAttachmentFileName); + rc = File_ReadBinaryFile(&testMessage , &testMessageLength, 10000, + inputAttachmentFileName); /* freed @2 */ + if (rc != 0) { + fprintf(messageFile, "ERROR1027 while opening the attachment, file: %s\n", + inputAttachmentFileName); + } + } + if (rc == 0) { + if (strcmp(signAlgorithm, "rsa") == 0) { + rc = SignSampleRSA(keyToken, + keyTokenLength, + testMessage, + testMessageLength, + outputAttachmentFileName, + projectLogFile); + } +#ifdef ADD_ECC + else if (strcmp(signAlgorithm, "eccp521") == 0) { + rc = SignSampleECCP521(keyToken, + keyTokenLength, + testMessage, + testMessageLength, + outputAttachmentFileName, + projectLogFile); + } +#endif + else { + fprintf(messageFile, + "ERROR1022: Unsupported signature algorithm: %s\n", + signAlgorithm); + rc = ERROR_CODE; + } + } + /* clean up */ + free(keyToken); /* @1 */ + free(testMessage); /* @2 */ + return rc; +} + +int SignSampleRSA(unsigned char *keyToken, + size_t keyTokenLength, + unsigned char *testMessage, + size_t testMessageLength, + const char *outputAttachmentFileName, + FILE *projectLogFile) +{ + int rc = 0; + + int valid; /* true if signature verifies */ + + RsaKeyTokenPublic rsaKeyTokenPublic; /* CCA public key structure */ + /* http://tools.ietf.org/html/draft-ietf-smime-sha2-11 */ + + /* SHA-1 with RSA OID (Object Identifier) */ + static const unsigned char sha1_rsa_oid[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, + 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14}; + /* SHA-256 with RSA OID (Object Identifier) */ + static const unsigned char sha256_rsa_oid[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x04, 0x20}; + + /* SHA-512 with RSA OID (Object Identifier) */ + static const unsigned char sha512_rsa_oid[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, + 0x00, 0x04, 0x40}; + /* + digest to be signed + */ + unsigned char hash1[sizeof(sha1_rsa_oid) + SHA1_SIZE]; /* OID + SHA-1 digest */ + unsigned char hash256[sizeof(sha256_rsa_oid) + SHA256_SIZE]; /* OID + SHA-256 digest */ + unsigned char hash512[sizeof(sha512_rsa_oid) + SHA512_SIZE]; /* OID + SHA-512 digest */ + unsigned long hashLength; + /* + signature + */ + unsigned char signature[N_SIZE]; + unsigned long signatureLength; + unsigned long signatureBitLength; + + /* extract the CCA public key from the CCA key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignSample: key token length %u\n", (uint)keyTokenLength); + if (verbose) + fprintf(messageFile, "SignSample: extract the public key from CCA key token\n"); + rc = getPKA96PublicKey(&rsaKeyTokenPublic, /* output: structure */ + keyTokenLength, + keyToken, /* input: PKA96 key token */ + 2048); + } + unsigned int i; /* loop through digest algorithms */ + const char *hashStr; /* test, for trace */ + const unsigned char *oid; /* object identifier */ + size_t oidSize; /* size of object identifier */ + unsigned char *hash; /* OID + digest */ + unsigned int digestLength; /* digest length */ + long (*verifyFunc)(); /* signature verification function */ + + /* loop for SHA1, SHA256, SHA512 */ + for (i = 0 ; (rc == 0) && (i < 3) ; i++) { + switch (i) { + case 0: + hashStr = "sha1"; + oid = sha1_rsa_oid; + oidSize = sizeof(sha1_rsa_oid); + hash = hash1; + hashLength = sizeof(hash1); + digestLength = SHA1_SIZE; + verifyFunc = osslVerify; + break; + case 1: + hashStr = "sha256"; + oid = sha256_rsa_oid; + oidSize = sizeof(sha256_rsa_oid); + hash = hash256; + hashLength = sizeof(hash256); + digestLength = SHA256_SIZE; + verifyFunc = osslVerify256; + break; + case 2: + hashStr = "sha512"; + oid = sha512_rsa_oid; + oidSize = sizeof(sha512_rsa_oid); + hash = hash512; + hashLength = sizeof(hash512); + digestLength = SHA512_SIZE; + verifyFunc = osslVerify512; + break; + } + /* prepend OID */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignSample: Hashing input data with %s\n", hashStr); + memcpy(hash, oid, oidSize); + } + /* hash the data for signing, append the hash */ + if (rc == 0) { + switch (i) { + case 0: + Ossl_SHA1(hash + oidSize, + testMessageLength, testMessage, + 0L, NULL); + break; + case 1: + Ossl_SHA256(hash + oidSize, + testMessageLength, testMessage, + 0L, NULL); + break; + case 2: + Ossl_SHA512(hash + oidSize, + testMessageLength, testMessage, + 0L, NULL); + break; + } + } + /* sign with the coprocessor. The coprocessor doesn't know the digest algorithm. It just + signs an OID + digest1 */ + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "SignSample: hash to sign", + hashLength, hash); + signatureLength = sizeof(signature); + rc = Digital_Signature_Generate(&signatureLength, /* i/o */ + &signatureBitLength, /* output */ + signature, /* output */ + keyTokenLength, /* input */ + keyToken, /* input */ + hashLength, /* input */ + hash); /* input */ + + } + /* sample - create the audit log entry */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignSample: Updating audit log\n"); + /* binary data as printable */ + char pubkey_string[N_SIZE * 4]; + char digest_string[SHA512_SIZE * 4]; /* use the largest */ + char sig_string[N_SIZE * 4]; + + /* get the user and group structures */ + /* binary to printable */ + sprintAll(pubkey_string, N_SIZE, rsaKeyTokenPublic.n); + sprintAll(digest_string, digestLength, hash + oidSize); + sprintAll(sig_string, N_SIZE, signature); + /* send to audit log */ + fprintf(projectLogFile, "\tPublic Key:\n %s\n", pubkey_string); + fprintf(projectLogFile, "\tDigest:\n %s\n", digest_string); + fprintf(projectLogFile, "\tSignature:\n %s\n", sig_string); + } + /* + The verify functions should never fail. They are just sanity checks on the code. + */ + /* sanity check on the signature length */ + if (rc == 0) { + if (signatureLength != N_SIZE) { + fprintf(messageFile, "ERROR1001: signature invalid length %lu\n", signatureLength); + rc = ERROR_CODE; + } + } + /* verify the signature with the coprocessor key CCA token */ + if (rc == 0) { + if (verbose) + fprintf(messageFile, + "SignSample: verify signature with the coprocessor key token\n"); + rc = Digital_Signature_Verify(N_SIZE, /* input */ + signature, /* input signature */ + keyTokenLength, /* input */ + keyToken, /* input key */ + hashLength, /* input */ + hash); /* input hash */ + } + /* sample code to verify the signature using openssl */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "SignSample: verify signature with OpenSSL and the key token\n"); + rc = verifyFunc(&valid, + hash + oidSize, /* input: digest to be verified */ + rsaKeyTokenPublic.e, /* exponent */ + rsaKeyTokenPublic.eLength, + rsaKeyTokenPublic.n, /* public key */ + rsaKeyTokenPublic.nByteLength, + signature, /* signature */ + signatureLength); + if (!valid) { + fprintf(messageFile, + "ERROR1023: Error verifying signature with OpenSSL and the key token\n"); + rc = ERROR_CODE; + } + } + /* write the SHA-256 signature to the output attachment */ + if ((rc == 0) && (outputAttachmentFileName != NULL) && (i == 1)) { + if (verbose) fprintf(messageFile, "SignSample: Writing output file %s\n", + outputAttachmentFileName); + rc = File_WriteBinaryFile(signature, signatureLength, outputAttachmentFileName); + } + /* write the signature as hex ascii to the output body */ + if (rc == 0) { + char *signatureString = NULL; /* freed @3 */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&signatureString, /* freed @3 */ + (signatureLength * 2) + 1, + (signatureLength * 2) + 1); + } + if (rc == 0) { + Format_ToHexascii(signatureString, signature, signatureLength); + fprintf(messageFile, "Signature with %s digest, length %u\n%s\n", + hashStr, + (uint)strlen(signatureString), + signatureString); + } + free(signatureString); /* @3 */ + } + } + + return rc; +} + +#ifdef ADD_ECC + +int SignSampleECCP521(unsigned char *keyToken, + size_t keyTokenLength, + unsigned char *testMessage, + size_t testMessageLength, + const char *outputAttachmentFileName, + FILE *projectLogFile) +{ + int rc = 0; + int valid; /* true if signature verifies */ + + EccKeyTokenPublic eccKeyTokenPublic; /* CCA public key structure */ + /* + digest to be signed + */ + unsigned char hash1[SHA1_SIZE]; /* SHA-1 digest */ + unsigned char hash256[SHA256_SIZE]; /* SHA-256 digest */ + unsigned char hash512[SHA512_SIZE]; /* SHA-512 digest */ + unsigned long hashLength; + /* + signature + */ + unsigned char signature[132]; /* FIXME 132 according to CCA, openssl produces + 139 */ + unsigned long signatureLength; + unsigned long signatureBitLength; + + /* extract the CCA public key from the CCA key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignSampleECCP521: key token length %u\n", + (uint)keyTokenLength); + if (verbose) + fprintf(messageFile, "SignSampleECCP521: extract the public key from CCA key token\n"); + rc = getPKA96EccPublicKey(&eccKeyTokenPublic, /* output: structure */ + keyTokenLength, + keyToken); /* input: PKA96 key token */ + } + unsigned int i; /* loop through digest algorithms */ + const char *hashStr; /* test, for trace */ + unsigned char *hash; /* digest */ + + /* loop for SHA1, SHA256, SHA512 */ + for (i = 0 ; (rc == 0) && (i < 3) ; i++) { + if (rc == 0) { + switch (i) { + case 0: + hashStr = "sha1"; + hash = hash1; + hashLength = sizeof(hash1); + break; + case 1: + hashStr = "sha256"; + hash = hash256; + hashLength = sizeof(hash256); + break; + case 2: + hashStr = "sha512"; + hash = hash512; + hashLength = sizeof(hash512); + break; + } + /* hash the data for signing */ + if (verbose) fprintf(messageFile, "SignSampleECCP521: Hashing input data with %s\n", + hashStr); + switch (i) { + case 0: + Ossl_SHA1(hash, + testMessageLength, testMessage, + 0L, NULL); + break; + case 1: + Ossl_SHA256(hash, + testMessageLength, testMessage, + 0L, NULL); + break; + case 2: + Ossl_SHA512(hash, + testMessageLength, testMessage, + 0L, NULL); + break; + } + if (verbose) PrintAll(messageFile, + "SignSampleECCP521: hash to sign", + hashLength, hash); + } + /* sign with the coprocessor. The coprocessor doesn't know the digest algorithm. It just + signs a digest */ + if (rc == 0) { + signatureLength = sizeof(signature); + rc = Digital_Signature_Generate_ECC(&signatureLength, /* i/o */ + &signatureBitLength, /* output */ + signature, /* output */ + keyTokenLength, /* input */ + keyToken, /* input */ + hashLength, /* input */ + hash); /* input */ + + } + /* sample - create the audit log entry */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignSampleECCP521: Updating audit log\n"); + /* binary data as printable */ + char pubkey_string[133 * 4]; + char digest_string[SHA512_SIZE * 4]; /* use the largest */ + char sig_string[132 * 4]; + + /* get the user and group structures */ + /* binary to printable */ + sprintAll(pubkey_string, eccKeyTokenPublic.qLen, eccKeyTokenPublic.publicKey); + sprintAll(digest_string, hashLength, hash); + sprintAll(sig_string, signatureLength, signature); + /* send to audit log */ + fprintf(projectLogFile, "\tPublic Key:\n %s\n", pubkey_string); + fprintf(projectLogFile, "\tDigest:\n %s\n", digest_string); + fprintf(projectLogFile, "\tSignature:\n %s\n", sig_string); + } + /* + The verify functions should never fail. They are just sanity checks on the code. + */ + /* sanity check on the signature length */ + if (rc == 0) { + if (signatureLength != sizeof(signature)) { + fprintf(messageFile, "ERROR1001: signature invalid length %lu\n", signatureLength); + rc = ERROR_CODE; + } + } + /* verify the signature with the coprocessor key CCA token */ + if (rc == 0) { + if (verbose) + fprintf(messageFile, + "SignSampleECCP521: verify signature with the coprocessor key token\n"); + rc = Digital_Signature_Verify_ECC(signatureLength, /* input */ + signature, /* input signature */ + keyTokenLength, /* input */ + keyToken, /* input key */ + hashLength, /* input */ + hash); /* input hash */ + } + /* sample code to verify the signature using openssl */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "SignSampleECCP521: " + "verify signature with OpenSSL and the key token\n"); + rc = Ossl_VerifyECC(&valid, + hash, /* input: digest to be verified */ + hashLength, + eccKeyTokenPublic.publicKey, + eccKeyTokenPublic.qLen, + signature, /* input: signature */ + signatureLength); + if (!valid) { + fprintf(messageFile, + "ERROR1024: " + "Error verifying signature with OpenSSL and the key token\n"); + rc = ERROR_CODE; + } + } + /* write one signature to the output attachment */ + if ((rc == 0) && (outputAttachmentFileName != NULL)) { + if (verbose) fprintf(messageFile, "SignSampleECCP521: Writing output file %s\n", + outputAttachmentFileName); + rc = File_WriteBinaryFile(signature, signatureLength, outputAttachmentFileName); + } + /* write the signature as hex ascii to the output body */ + if (rc == 0) { + char *signatureString = NULL; /* freed @3 */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&signatureString, /* freed @3 */ + (signatureLength * 2) + 1, + (signatureLength * 2) + 1); + } + if (rc == 0) { + Format_ToHexascii(signatureString, signature, signatureLength); + fprintf(messageFile, "Signature with %s digest\n%s\n", + hashStr, + signatureString); + } + free(signatureString); /* @3 */ + } + } + /* cleanup */ + return rc; +} + +#endif + +/* EchoArgs() prints all incoming command line arguments, except it does not print the plaintext + password. */ + +int EchoArgs(int argc, char** argv) +{ + int rc = 0; + int irc; + int i; + + fprintf(messageFile, "framework_test argc = %u\n", argc); + + for (i = 0 ; i < argc ; i++) { + fprintf(messageFile, "%u: %s\n", i, argv[i]); + irc = strcmp(argv[i], "-pwd"); /* if the argv is -pwd */ + if (irc == 0) { + i++; /* skip the next argument */ + } + } + return rc; +} + +/* GetArgs() gets the command line arguments from the framework. + */ + +int GetArgs(const char **outputBodyFilename, + const char **usr, + const char **password, + const char **projectLogFileName, + const char **projectAuxConfigFileName, + const char **sender, + const char **project, + const char **keyFileName, + const char **inputAttachmentFileName, + const char **outputAttachmentFileName, + int *verbose, + int argc, + char **argv) +{ + long rc = 0; + int i; + FILE *tmpFile; + + /* command line argument defaults */ + *outputBodyFilename = NULL; + *verbose = FALSE; + + /* 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"); + /* 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],"-usr") == 0) { + i++; + if (i < argc) { + *usr = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1003: -usr option (CCA user ID) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-core") == 0) { + int *p = NULL; + i = *p; + } + else if (strcmp(argv[i],"-pwd") == 0) { + i++; + if (i < argc) { + *password = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1004: -pwd option (CCA password) 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],"-auxcfg") == 0) { + i++; + if (i < argc) { + *projectAuxConfigFileName = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1019: " + "-auxcfg option (auxiliary configuration 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],"-key") == 0) { + i++; + if (i < argc) { + *keyFileName = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1008: -key option needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-di") == 0) { + i++; + if (i < argc) { + *inputAttachmentFileName = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1016: -di 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; + } + } + /* 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 mandatory command line arguments */ + if (rc == 0) { + // If the usr isn't specified just use the sender + if (*usr == NULL) { + *usr = *sender; + } + if (*usr == NULL) { + fprintf(messageFile, + "ERROR1010: -usr option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*password == NULL) { + fprintf(messageFile, + "ERROR1018: -pwd option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*sender== NULL) { + fprintf(messageFile, + "ERROR1011: -sender option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*projectLogFileName == NULL) { + fprintf(messageFile, + "ERROR1012: -log option missing\n"); + rc = ERROR_CODE; + } + } +#if 0 /* A program that needs auxiliary configuration data would use this test. It's commented + out here because the regression test tries both. */ + if (rc == 0) { + if (*projectAuxConfigFileName == NULL) { + fprintf(messageFile, + "ERROR1020: -auxcfg option missing\n"); + rc = ERROR_CODE; + } + } +#endif + if (rc == 0) { + if (*keyFileName == NULL) { + fprintf(messageFile, + "ERROR1014: -key option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*inputAttachmentFileName == NULL) { + fprintf(messageFile, + "ERROR1017: -di option missing\n"); + rc = ERROR_CODE; + } + } + return rc; +} + +/* CheckAuxArgs() checks that no command line arguments clash with those in the project auxiliary + configuration file +*/ + +int CheckAuxArgs(int argc, char** argv) +{ + int rc = 0; + int irc; + size_t i; + int j; + + /* screen out command line arguments that attempt to override the project auxiliary + configuration file values */ + + /* for each value in the project auxiliary configuration file */ + for (i = 0 ; (rc == 0) && (i < (sizeof(claTable)/sizeof(char *))) ; i++) { + + /* for each value from the command line */ + for (j = 0 ; (rc == 0) && (j < argc) ; j++) { + + irc = strcmp(claTable[i], argv[j]); + + if (irc == 0) { + fprintf(messageFile, "ERROR1025: %s illegal in input body\n", argv[j]); + rc = ERROR_CODE; + } + } + } + return rc; +} + +int GetAuxArgs(char **signAlgorithm, /* freed by caller */ + const char *projectAuxConfigFileName) +{ + int rc = 0; + char *lineBuffer = NULL; /* freed @2 */ + size_t lineBufferLength = 4000; /* hard code for the project */ + FILE *projectAuxConfigFile = NULL; /* closed @1 */ + + if (projectAuxConfigFileName != NULL) { + /* open project auxiliary configuration file */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Opening auxiliary configuration file %s\n", + projectAuxConfigFileName); + projectAuxConfigFile = fopen(projectAuxConfigFileName, "r"); /* closed @1 */ + if (projectAuxConfigFile == NULL) { + fprintf(messageFile, + "ERROR1021: Cannot open auxiliary configuration file %s, %s\n", + projectAuxConfigFileName, strerror(errno)); + rc = ERROR_CODE; + } + } + /* allocate a line buffer, used when parsing the configuration file */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&lineBuffer, /* freed @2 */ + lineBufferLength, + lineBufferLength); /* hard code for the project */ + } + if (rc == 0) { + rc = File_MapNameToValue(signAlgorithm, /* freed by caller */ + "sign_algorithm", /* name to search for */ + lineBuffer, /* supplied buffer for lines */ + lineBufferLength, /* size of the line buffer */ + projectAuxConfigFile); /* input file stream */ + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Signature algorithm: %s\n", *signAlgorithm); + } + if (projectAuxConfigFile != NULL) { + fclose(projectAuxConfigFile); /* @1 */ + } + } + else { /* projectAuxConfigFileName == NULL, use default */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)signAlgorithm, /* freed by caller */ + sizeof("rsa"), + sizeof("rsa")); /* hard code the default */ + } + if (rc == 0) { + memcpy(*signAlgorithm, "rsa", sizeof("rsa")); + } + } + free(lineBuffer); /* @2 */ + return rc; +} + +void PrintUsage() +{ + fprintf(messageFile, "\n"); + fprintf(messageFile, + "\tframework_test usage:\n" + "\n" + "Common arguments:\n" + "\n" + "\t-usr - CCA user (profile) ID\n" + "\t[-core - cause the program to core dump]\n" + "\t[-v - verbose logging]\n" + "\t[-h - print usage help]\n" + "\n" + "Email only arguments:\n" + "\n" + "\t-project - project name\n" + "\t-epwd - CCA user password (encrypted)\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-di - input attachment file name\n" + "\t-do - output attachment file name\n" + "\t-log - project audit log file name\n" + "\t-auxcfg - project auxiliary configuration file name\n" + "\t-key - project CCA signing key token\n" + "\t-pwd - CCA user password (plaintext)\n" + ); + fprintf(messageFile, "\n"); + return; +} diff --git a/src/signframework/framework_utils.c b/src/signframework/framework_utils.c new file mode 100644 index 0000000..7c90968 --- /dev/null +++ b/src/signframework/framework_utils.c @@ -0,0 +1,2414 @@ +/* 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "utils.h" +#include "framework_utils.h" +#include "cca_functions.h" +#include "cca_structures.h" +#ifdef ADD_ECC +#include "cca_structures_ecc.h" +#include "cca_functions_ecc.h" +#endif +#include "ossl_functions.h" +#include "debug.h" +#include "mail.h" +#include "errorcodes.h" +#include "dropbox_utils.h" +#ifdef LDAP_VERIFY +#include "ldap_lookup.h" +#endif + +extern FILE* messageFile; +extern int verbose; + +/* This table describes command line arguments added to the Arguments array by the framework. They + cannot be in the input body, as an attacker could then try to override the framework values. + + Use this table to screen the user input. Do not rely on the called program to detect duplicates. +*/ + +static const char *claTable[] = { + "-di", + "-do", + "-key", + "-log", + "-auxcfg", + "-obody", + "-sender", + "-pwd" +}; + +/* This hard coded line size is used for the first few lines of the framework configuration file. + Once the actual maximum line size is read from the configuration file, a buffer is allocated for + the rest of the file. */ + +#define MAX_LINE_SIZE 1024 + +/* local function prototypes */ + +int FrameworkConfig_Validate(FrameworkConfig *frameworkConfig); +int ProjectConfig_Validate(ProjectConfig *projectConfig, + FrameworkConfig *frameworkConfig); +int ProjectConfig_ValidateKey(ProjectConfig *projectConfig); +int ProjectConfig_ValidateKeyRSA(size_t keyTokenLength, + unsigned char *keyToken); +int ProjectConfig_ValidateKeyECC(size_t keyTokenLength, + unsigned char *keyToken); + + +/* FrameworkConfig_Init() initializes the FrameworkConfig object members + */ + +void FrameworkConfig_Init(FrameworkConfig *frameworkConfig) +{ + memset(frameworkConfig, 0, sizeof(FrameworkConfig)); + + /* frameworkLogFile starts as stdout. Once the framework starts, it is switched to the actual + framework audit log. + */ + frameworkConfig->frameworkLogFile = stdout; + return; +} + +/* FrameworkConfig_Delete() frees and reinitializes the FrameworkConfig object members + */ + +void FrameworkConfig_Delete(FrameworkConfig *frameworkConfig) +{ + size_t i; + + /* do not free frameworkConfigFilename, it comes from getenv */ + free(frameworkConfig->frameworkName); + free(frameworkConfig->frameworkLogFilename); + free(frameworkConfig->dropboxDir); + free(frameworkConfig->stopFile); + free(frameworkConfig->restartFile); + free(frameworkConfig->outputBodyFilename); + free(frameworkConfig->notificationFilename); + free(frameworkConfig->emailFilename); + free(frameworkConfig->inputAttachmentFilename); + free(frameworkConfig->outputAttachmentFilename); + free(frameworkConfig->masterAesKeyTokenFilename); + free(frameworkConfig->masterAesKeyToken); + free(frameworkConfig->ldapUrl); + free(frameworkConfig->ldapBase); + for (i = 0 ; i < frameworkConfig->frameworkAdminCount ; i++) { + free(frameworkConfig->frameworkAdmins[i]); + } + free(frameworkConfig->frameworkAdmins); + /* free and reinitialize each ProjectConfig object member, then free the members in the + FrameworkConfig object project arrays */ + for (i = 0 ; i < frameworkConfig->projectLength ; i++) { + free(frameworkConfig->projectNames[i]); + free(frameworkConfig->projectConfigFilenames[i]); + if (frameworkConfig->projectConfigFiles != NULL) { + ProjectConfig_Delete(frameworkConfig->projectConfigFiles[i]); + free(frameworkConfig->projectConfigFiles[i]); + frameworkConfig->projectConfigFiles[i] = NULL; + } + } + /* then free the project arrays themselves */ + free(frameworkConfig->projectNames); + free(frameworkConfig->projectConfigFilenames); + free(frameworkConfig->projectConfigFiles); + /* initialize so the next delete is safe */ + FrameworkConfig_Init(frameworkConfig); + return; +} + +/* FrameworkConfig_Parse() parses the framework configuration file into the FrameworkConfig object. + + NOTE: Since this is called at startup, messages should go to stdout. All calls that set an error + code must print as well. + + needMasterKey flags whether the framework AES master key must exist. It's usually true, but is + false when the calling program is generating that key. + + validate flags whether the validation, which writes files, should be done. The main framework + should perform validation. Ancillary programs should not, so as not to interfere with a running + framework. + +*/ + +int FrameworkConfig_Parse(int needMasterKey, + int validate, + FrameworkConfig *frameworkConfig) +{ + int rc = 0; + size_t i; + FILE *configFile = NULL; /* closed @2 */ + /* temporary, to hold lines when parsing the configuration file */ + char firstLineBuffer[MAX_LINE_SIZE]; + char *lineBuffer = NULL; /* freed @3 */ + size_t masterAesKeyTokenLength; + + /* validate that all required environment variables are set */ + if (rc == 0) { + rc = Env_Validate(); + } + /* framework configuration file name from environment variable */ + if (rc == 0) { + frameworkConfig->frameworkConfigFilename = getenv("FRAMEWORK_CONFIG_FILE"); + if (frameworkConfig->frameworkConfigFilename == NULL) { + fprintf(messageFile, + "ERROR0001: " + "FRAMEWORK_CONFIG_FILE environment variable is not set\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Framework configuration file name: %s\n", + frameworkConfig->frameworkConfigFilename); + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Max args %u, max args length %u\n", + MAX_ARGV_BODY, ARG_MAX); + } + /* save a hash of the framework configuration file for the framework audit log */ + if (rc == 0) { + rc = Ossl_HashBinaryFile(frameworkConfig->digest, + MAX_CONFIG, + frameworkConfig->frameworkConfigFilename); + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "FrameworkConfig_Parse: Framework configuration file digest", + DIGEST_SIZE, + frameworkConfig->digest); + } + /* open framework configuration file */ + if (rc == 0) { + configFile = fopen(frameworkConfig->frameworkConfigFilename , "r"); /* closed @2 */ + if (configFile == NULL) { + fprintf(messageFile, + "ERROR0002: Opening %s\n", + frameworkConfig->frameworkConfigFilename); + rc = ERROR_CODE; + } + } + + /* line_max */ + if (rc == 0) { + frameworkConfig->lineMax = 0; + rc = File_MapNameToUint((unsigned int *)&(frameworkConfig->lineMax), + "line_max", + firstLineBuffer, + MAX_LINE_SIZE, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Line maximum length: %u bytes\n", + (unsigned int)frameworkConfig->lineMax); + } + if (rc == 0) { + if (frameworkConfig->lineMax == 0) { + fprintf(messageFile, "ERROR0008: line max has illegal value\n"); + rc = ERROR_CODE; + } + } + /* allocate a line buffer, used when parsing the rest of the framework configuration file */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&lineBuffer, /* freed @3 */ + frameworkConfig->lineMax, + frameworkConfig->lineMax); /* trust the framework config file */ + } + + /* file_max */ + if (rc == 0) { + rc = File_MapNameToUint((unsigned int *)&(frameworkConfig->fileMax), + "file_max", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: File maximum size: %hu bytes\n", + (int)frameworkConfig->fileMax); + } + if (rc == 0) { + if (frameworkConfig->fileMax == 0) { + fprintf(messageFile, "ERROR0008: file max has illegal value\n"); + rc = ERROR_CODE; + } + } + + /* framework name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->frameworkName), + "framework_name", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Framework name: %s\n", + frameworkConfig->frameworkName); + } + + /* password expiration period */ + if (rc == 0) { + rc = File_MapNameToUint(&(frameworkConfig->passwordExpire), + "password_expire", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Password expiration: %u months\n", + frameworkConfig->passwordExpire); + } + /* framework audit log file name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->frameworkLogFilename), + "log", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Framework log file name: %s\n", + frameworkConfig->frameworkLogFilename); + } + /* dropbox dir */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->dropboxDir), /* freed by + caller */ + "dropbox", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Dropbox root dir: %s\n", + frameworkConfig->dropboxDir); + } + /* stop file name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->stopFile), /* freed by caller */ + "stop_file", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Stop file name: %s\n", + frameworkConfig->stopFile); + } + /* restart file name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->restartFile), /* freed by caller */ + "restart_file", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Restart file name: %s\n", + frameworkConfig->restartFile); + } + /* output body file name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->outputBodyFilename), /* freed by caller */ + "out_body", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Output body file name: %s\n", + frameworkConfig->outputBodyFilename); + } + /* output notification file name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->notificationFilename), /* freed by caller */ + "notif_log", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Notification file name: %s\n", + frameworkConfig->notificationFilename); + } + /* email file name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->emailFilename), /* freed by caller */ + "full_email", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Email file name: %s\n", + frameworkConfig->emailFilename); + } + /* input attachment file name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->inputAttachmentFilename), /* freed by + caller */ + "in_attachment", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Input attachment file name: %s\n", + frameworkConfig->inputAttachmentFilename); + } + /* output attachment file name */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->outputAttachmentFilename), /* freed by + caller */ + "out_attachment", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Output attachment file name: %s\n", + frameworkConfig->outputAttachmentFilename); + } + + /* master AES key token file */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->masterAesKeyTokenFilename), + "key", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: master AES key token file name: %s\n", + frameworkConfig->masterAesKeyTokenFilename); + } + /* The master AES key token was saved on disk as a one time operation during framework + installation. */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Reading master AES key token\n"); + rc = File_ReadBinaryFile(&(frameworkConfig->masterAesKeyToken), /* freed by caller */ + &masterAesKeyTokenLength, + CCA_KEY_IDENTIFIER_LENGTH, + frameworkConfig->masterAesKeyTokenFilename); + /* sanity check, the CCA key token is a fixed length */ + if (rc == 0) { + if (masterAesKeyTokenLength != CCA_KEY_IDENTIFIER_LENGTH) { + fprintf(messageFile, + "ERROR0004: Master AES key token length is invalid\n"); + rc = ERROR_CODE; + } + } + else { + /* at installation, the AES master key does not exist yet */ + if (!needMasterKey) { + rc = 0; + } + } + } + /* LDAP URL */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->ldapUrl), + "ldapurl", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: LDAP URL: %s\n", + frameworkConfig->ldapUrl); + } + /* LDAP BASEDN */ + if (rc == 0) { + rc = File_MapNameToValue(&(frameworkConfig->ldapBase), + "ldapbase", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: LDAP BASEDN: %s\n", + frameworkConfig->ldapBase); + } + + /* read the list of framework admin email addresses */ + if (rc == 0) { + rc = File_GetValueArray(&(frameworkConfig->frameworkAdmins), /* freed by caller */ + &(frameworkConfig->frameworkAdminCount), /* number framework + admins */ + "admins", + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: " + "Framework administrators, %u entries\n", + (unsigned int)frameworkConfig->frameworkAdminCount); + for (i = 0 ; i < frameworkConfig->frameworkAdminCount ; i++) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Framework administrator: %s\n", + frameworkConfig->frameworkAdmins[i]); + } + } + /* read the project to project configuration file mapping */ + if (rc == 0) { + rc = File_GetNameValueArray(&(frameworkConfig->projectNames), /* freed by caller */ + &(frameworkConfig->projectConfigFilenames), /* freed caller */ + &(frameworkConfig->projectLength), + lineBuffer, + frameworkConfig->lineMax, + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: " + "Project name to configuration file map, %u entries\n", + (unsigned int)frameworkConfig->projectLength); + for (i = 0 ; i < frameworkConfig->projectLength ; i++) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Project name: %s Configuration file: %s\n", + frameworkConfig->projectNames[i], + frameworkConfig->projectConfigFilenames[i]); + } + } + /* Once the number of projects is determined, allocate an array for the project configuration + file structures. The max size is the same because we trust the framework administrator not + to attack itself. */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&(frameworkConfig->projectConfigFiles), + frameworkConfig->projectLength * sizeof(unsigned char *), + frameworkConfig->projectLength * sizeof(unsigned char *)); + } + /* NULL the elements immediately so the free() is valid on error */ + for (i = 0 ; (rc == 0) && (i < frameworkConfig->projectLength) ; i++) { + frameworkConfig->projectConfigFiles[i] = NULL; + } + /* next malloc the project configuration file structures */ + for (i = 0 ; (rc == 0) && (i < frameworkConfig->projectLength) ; i++) { + rc = Malloc_Safe((unsigned char **)&(frameworkConfig->projectConfigFiles[i]), + sizeof(ProjectConfig), + sizeof(ProjectConfig)); /* max size, trust the compiler */ + /* if the malloc is successful, _Init each one immediately so the delete is safe */ + if (rc == 0) { + ProjectConfig_Init(frameworkConfig->projectConfigFiles[i]); + } + } + /* validate the signer framework configuration file */ + if ((rc == 0) && validate) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Parse: Validating framework configuration file\n"); + rc = FrameworkConfig_Validate(frameworkConfig); + } + if (rc != 0) { + fprintf(messageFile, + "ERROR0005: processing framework configuration file: %s\n", + frameworkConfig->frameworkConfigFilename); + } + if (configFile != NULL) { + fclose(configFile); /* @2 */ + } + free(lineBuffer); /* @3 */ + return rc; +} + +/* FrameworkConfig_Validate() checks that the object has valid values. + */ + +int FrameworkConfig_Validate(FrameworkConfig *frameworkConfig) +{ + int rc = 0; + unsigned char eku[AES128_SIZE]; /* password encryption key */ + unsigned char aku[AKU_SIZE]; /* password authentication HMAC key */ + + if (rc == 0) { + if (frameworkConfig->passwordExpire == 0) { + fprintf(messageFile, "ERROR0011: password expire has illegal value\n"); + rc = ERROR_CODE; + } + } + /* validate that the output body can be opened */ + if (rc == 0) { + rc = File_ValidateOpen(frameworkConfig->outputBodyFilename, "a"); + } + /* validate that the notification output body can be opened */ + if (rc == 0) { + rc = File_ValidateOpen(frameworkConfig->notificationFilename, "a"); + } + /* validate that the full_email file can be opened */ + if (rc == 0) { + rc = File_ValidateOpen(frameworkConfig->emailFilename, "a"); + } + /* validate that the input attachment file can be written, try to create it */ + if (rc == 0) { + rc = File_ValidateOpen(frameworkConfig->inputAttachmentFilename, "w"); + if (rc != 0) { + fprintf(messageFile, + "ERROR0013: opening: %s for write\n", frameworkConfig->inputAttachmentFilename); + } + } + /* then validate the open for read */ + if (rc == 0) { + rc = File_ValidateOpen(frameworkConfig->inputAttachmentFilename, "r"); + if (rc != 0) { + fprintf(messageFile, + "ERROR0013: opening: %s for read\n", frameworkConfig->inputAttachmentFilename); + } + } + /* validate that the output attachment file can be written and read */ + if (rc == 0) { + rc = File_ValidateOpen(frameworkConfig->outputAttachmentFilename, "w"); + if (rc != 0) { + fprintf(messageFile, + "ERROR0013: opening: %s for write\n", frameworkConfig->outputAttachmentFilename); + } + } + if (rc == 0) { + rc = File_ValidateOpen(frameworkConfig->outputAttachmentFilename, "r"); + if (rc != 0) { + fprintf(messageFile, + "ERROR0013: opening: %s for read\n", frameworkConfig->outputAttachmentFilename); + } + } + /* Validate that the master AES key token can be used. At first start up, the key may not + exist. Otherwise, the error is reported earlier and this code is not executed. */ + if ((rc == 0) && (frameworkConfig->masterAesKeyToken != NULL)) { + if (verbose) fprintf(messageFile, + "FrameworkConfig_Validate: Testing AES master key: %s\n", + frameworkConfig->masterAesKeyTokenFilename); + rc = Password_KDF(eku, /* user encryption key */ + aku, /* user authentication HMAC key */ + frameworkConfig->frameworkAdmins[0], /* dummy sender */ + frameworkConfig->masterAesKeyToken); + } + /* there must be at least one project */ + if (rc == 0) { + if (frameworkConfig->projectNames == 0) { + fprintf(messageFile, "ERROR0014: Framework configuration file has no projects\n"); + rc = ERROR_CODE; + } + } + /* erase the secret keys */ + memset(eku, 0, AES128_SIZE); + memset(aku, 0, AKU_SIZE); + return rc; +} + +/* FrameworkConfig_LogStart() logs the framework startup event */ + +int FrameworkConfig_LogStart(FrameworkConfig *frameworkConfig) +{ + int rc = 0; + size_t i; + + if (rc == 0) { + /* log the startup time */ + File_LogTime(frameworkConfig->frameworkLogFile); + /* log the framework configuration file and its digest */ + fprintf(frameworkConfig->frameworkLogFile, + "\tStartup, framework configuration file: %s\n", + frameworkConfig->frameworkConfigFilename); + PrintAll(frameworkConfig->frameworkLogFile, + "\tFramework configuration file digest", + DIGEST_SIZE, + frameworkConfig->digest); + } + /* log each project configuration file and its digest */ + for (i = 0 ; (rc == 0) && (i < frameworkConfig->projectLength) ; i++) { + fprintf(frameworkConfig->frameworkLogFile, + "\tProject: %s, Configuration file: %s\n", + frameworkConfig->projectNames[i], + frameworkConfig->projectConfigFilenames[i]); + PrintAll(frameworkConfig->frameworkLogFile, + "\tProject configuration file digest", + DIGEST_SIZE, + frameworkConfig->projectConfigFiles[i]->digest); + } + return rc; +} + +/* FrameworkConfig_SendStartupMessage() sends the frameworkConfig->outputBodyFileName to + frameworkConfig->frameworkAdmins as a startup notification. + + This has these useful side effects: + + - it displays the Notes password prompt while the administrator is still at the framework start + up terminal, and validates the password + + - it validates the Notes client install, the Notes API install, and the ability to send email + + - it validates the framework administrator email address + + Returns ERR_FATAL on any error +*/ + +int FrameworkConfig_SendStartupMessage(FrameworkConfig *frameworkConfig, + int transientError) +{ + int rc = 0; + size_t i; + FILE *outputBodyFile = NULL; + + /* do not change the message file here, let trace messages go to stdout */ + if (verbose) fprintf(messageFile, + "FrameworkConfig_SendStartupMessage: Sending framework startup message\n"); + /* open the output email body file */ + if (rc == 0) { + rc = File_Open(&outputBodyFile, frameworkConfig->outputBodyFilename, "w"); + } + /* construct the startup message */ + if (rc == 0) { + fprintf(outputBodyFile, "Starting signer framework\n"); + /* let the administrators know if this was a restart due to a transient error */ + if (transientError) { + fprintf(outputBodyFile, "\n\n"); + fprintf(outputBodyFile, "Restarting after transient error.\n"); + fprintf(outputBodyFile, "Check signer framework log file for details\n"); + } + } + /* close the output email body file */ + if (outputBodyFile != NULL) { + fclose(outputBodyFile); + } + + /* send the startup message to each framework administrator */ + for (i = 0 ; (rc == 0) && (i < frameworkConfig->frameworkAdminCount) ; i++) { + rc = SendMailFile(frameworkConfig, frameworkConfig->frameworkAdmins[i], + "Signer Framework Startup", frameworkConfig->outputBodyFilename); + } + remove(frameworkConfig->outputBodyFilename); + /* If the startup can't be sent, even non-fatal errors terminate the program. */ + if (rc != 0) { + rc = ERR_FATAL; + } + + return rc; +} + + +/* FrameworkProcess_Process() is called to process one received email. + + It does common setup, and then branches based on the received email requestParms->status. + + NOTE: Each error return must log to the messageFile and frameworkLogFile. + + The return codes are: + + non-zero for transient error that will cause a Notes restart + except, ERR_FATAL indicates a fatal error that should terminate the program +*/ + +int FrameworkProcess_Process(DropboxRequest *requestParms) +{ + int rc = requestParms->status; /* for errors that get passed back to the caller */ + int processRc = 0; /* for errors that indicate no response to sender */ + int responseType = 0; /* response type to sender */ + size_t i; + + /* validate the comments, sanitize the input so that use is safe below */ + if ((rc == 0) && (processRc == 0)) { + if (verbose) fprintf(messageFile, + "FrameworkProcess_Process: validate comment\n"); + processRc = Comment_Validate(requestParms); + /* on error, Comment_Validate() writes the framework log */ + if (processRc != 0) { + /* error response back to sender */ + requestParms->status = ERR_SUBJECT_BAD; + processRc = 0; + } + } + /* basic logging and email response, sender and subject */ + if ((rc == 0) && (processRc == 0)) { + File_LogTime(requestParms->frameworkConfig->frameworkLogFile); + File_Printf(requestParms->frameworkConfig->frameworkLogFile, NULL, + "Sender: %s\n", requestParms->dbConfig->sender); + File_Printf(requestParms->frameworkConfig->frameworkLogFile, NULL, + "Request User: %s\n", requestParms->user); + File_Printf(requestParms->frameworkConfig->frameworkLogFile, NULL, + "Comment: %s\n", requestParms->comment); + fprintf(messageFile, "Signer framework administrators are:\n"); + for (i = 0 ; i < requestParms->frameworkConfig->frameworkAdminCount ; i++) { + fprintf(messageFile, "\t%s\n", requestParms->frameworkConfig->frameworkAdmins[i]); + } + } + /* zero the output attachment file, just to prevent a bug from sending a previous attachment */ + if ((rc == 0) && (processRc == 0)) { + rc = File_ValidateOpen(requestParms->frameworkConfig->outputAttachmentFilename, "w"); + /* fatal error */ + if (rc != 0) { + fprintf(requestParms->frameworkConfig->frameworkLogFile, + "\tFatal error opening %s\n", + requestParms->frameworkConfig->outputAttachmentFilename); + rc = ERR_FATAL; + } + } + if (rc == 0) { /* if no error */ + if (processRc == 0) { /* if should respond to sender */ + + responseType = FrameworkProcess_ProcessOK(requestParms); + + /* FrameworkProcess_SendResponse() sends a response to the original email sender. + + It will return 0 unless there is an error that should cause the main loop to + restart or exit. */ + rc = FrameworkProcess_SendResponse(responseType, requestParms); + } + /* processRc != 0 -> no response to sender. Errors must be logged to the framework log */ + } else { + // Error path send a response back + FrameworkProcess_SendResponse(responseType, requestParms); + } + + /* close the messages file here if some error path (e.g., no response to sender ) kept it + open */ + File_CloseMessageFile(); + + return rc; +} + +/* FrameworkProcess_ProcessOK() processes a received email that has passed initial validation tests. + + - the email signature is present and valid + - the sender is validated + - the subject is validated + + It + + - forms the argv array for the signer program from the email header and body + - adds project specific arguments to argv + - validates the sender authority for the project + - decrypts the password + - calls the signer program + - sends notification messages + + NOTE: Each error return must log to the messageFile and frameworkLogFile. + + On error, returns responseType RESPONSE_BODY_ONLY +*/ + +int FrameworkProcess_ProcessOK(DropboxRequest *requestParms) +{ + int rc = 0; + size_t i; + Arguments arguments; + + /* project configuration file */ + const char *projectConfigFilename = NULL; + ProjectConfig *projectConfig = NULL; + + /* create the project command line arguments */ + Arguments_Init(&arguments); /* freed @1 */ + arguments.argc = 1; /* start with 1, the first argument is the program name */ + + /* append output body file name to argv. This should remain the first argument so the project + program can immediately switch from stdout to the output body when tracing. */ + if (rc == 0) { + rc = Arguments_AddPairTo(&arguments, "-obody", + requestParms->frameworkConfig->outputBodyFilename); + } + /* append the sender to argv */ + if (rc == 0) { + rc = Arguments_AddPairTo(&arguments, "-sender", requestParms->dbConfig->sender); + } + // Add the epwd + if (rc == 0 && requestParms->epwd != NULL) { + rc = Arguments_AddPairTo(&arguments, "-epwd", requestParms->epwd); + } + // Add the project + if (rc == 0 && requestParms->project != NULL) { + rc = Arguments_AddPairTo(&arguments, "-project", requestParms->project); + } + /* process the input request, append to argv */ + if (rc == 0) { + rc = ProjectProcess_ProcessInputParameters(&arguments, + requestParms); + if (rc != 0) { + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "ERROR0018: processing input request parameters\n"); + } + } + /* index into the frameworkConfig array and retrieve the ProjectConfig structure */ + if (rc == 0) { + int found = FALSE; + for (i = 0 ; (i < requestParms->frameworkConfig->projectLength) && !found ; i++) { + if (strcmp(requestParms->project, requestParms->frameworkConfig->projectNames[i]) == 0) { + projectConfig = requestParms->frameworkConfig->projectConfigFiles[i]; + projectConfigFilename = requestParms->frameworkConfig->projectConfigFilenames[i]; + found = TRUE; + } + } + if (!found) { + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "ERROR0020: Could not map project: %s\n", + requestParms->project); + fprintf(messageFile, + "Contact framework administrator\n"); + rc = RESPONSE_BODY_ONLY; + } + } + /* add project email address to output body */ + if (rc == 0) { + fprintf(messageFile, "Project administrator is %s\n\n", projectConfig->emailProject); + } + /* process the project specific configuration file, add command line arguments to argv */ + if (rc == 0) { + rc = ProjectConfig_Process(&arguments, + projectConfig, + requestParms); + if (rc != 0) { + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "ERROR0021: Error while processing the project configuration file: %s\n", + projectConfigFilename); + fprintf(messageFile, + "Contact framework administrator\n"); + } + } + /* validate that the sender is authorized for the project, that the sender email address is in + the project file */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "FrameworkProcess_ProcessOK: " + "Validate sender %s authorization for project %s\n", + requestParms->dbConfig->sender, requestParms->project); + rc = ProjectConfig_ValidateSender(requestParms->dbConfig->sender, + projectConfig, + requestParms->frameworkConfig); + if (rc != 0) { + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "ERROR0022: %s is not authorized for project: %s\n", + requestParms->dbConfig->sender, requestParms->project); + fprintf(messageFile, + "Contact framework administrator\n"); + } + } + /* if needed, decrypt and append the password. + + Since there are several errors, Password_Decrypt does the error logging. */ + if ((rc == 0) && projectConfig->needPassword) { + rc = Password_Decrypt(&arguments, + requestParms->dbConfig->sender, + requestParms->frameworkConfig->masterAesKeyToken, + requestParms->frameworkConfig->frameworkLogFile); + } + + /* NOTE Traces the command line arguments. This is a security hole and should not be compiled + into the final product. */ +#if 0 + if (rc == 0) { + printf("\nargc = %u\n", arguments.argc); + + for (i = 0 ; i < (size_t)arguments.argc ; i++) { + printf("%u: %s\n", (unsigned int)i, arguments.argv[i]); + } + } +#endif + + /* call the project specific program */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "FrameworkProcess_ProcessOK: calling program: %s\n", + arguments.argv[0]); + rc = CallSigner(&arguments, + TRUE, /* useOutputBody */ + requestParms->frameworkConfig->outputBodyFilename); + if (rc != 0) { + if (verbose) fprintf(messageFile, + "FrameworkProcess_ProcessOK: Error from %s\n", + arguments.argv[0]); + } + /* update framework audit log */ + fprintf(requestParms->frameworkConfig->frameworkLogFile, "\tProject: %s\n", requestParms->project); + fprintf(requestParms->frameworkConfig->frameworkLogFile, "\tProgram: %s\n", arguments.argv[0]); + fprintf(requestParms->frameworkConfig->frameworkLogFile, "\tReturn code: %d\n", rc); + } + /* notification recipient logging */ + if (rc == 0) { + /* a notification failure does not prevent a response to the sender, although it probably + won't work either */ + FrameworkProcess_SendNotificationMessage(requestParms->project, + projectConfig, + requestParms); + } + requestParms->status = rc; + /* indicates that the body is valid but there is no attachment */ + if (rc != 0) { + rc = RESPONSE_BODY_ONLY; + } + /* cleanup */ + /* remove input attachment, if any. This is likely unnecessary, since it's also removed before + each new email is processed. */ + remove(requestParms->frameworkConfig->inputAttachmentFilename); + Arguments_Delete(&arguments); /* @1 */ + return rc; +} + + +/* FrameworkProcess_SendResponse() sends a response to the original sender. It is called + upon success or non-fatal error conditions. + + responseType + + 0 success - send response and attachment to sender + RESPONSE_BODY_ONLY - send response to sender + RESPONSE_BODY_TO_ADMIN - send response to admin rather than sender + RESPONSE_NO_EMAIL - don't send any response + + Returns: non-zero on an error that should cause the main loop to restart or exit +*/ + +int FrameworkProcess_SendResponse(int responseType, + DropboxRequest* requestParms) +{ + int rc = 0; + int i = 0; + + /* this is the last email to be sent. close the messageFile before sending */ + requestParms->closeMessageFile = TRUE; + + if (rc == 0) { + switch (responseType) { + case 0: + /* send reply to sender: sendto, body, attachment */ + requestParms->hasResult=1; + rc = sendResult(requestParms); + /* send audit message to notification receivers */ + break; + case RESPONSE_BODY_ONLY: + /* send reply to sender: sendto, body */ + rc = sendResult(requestParms); + /* send audit message to notification receivers */ + break; + case RESPONSE_BODY_TO_ADMIN: + /* send reply to admin: sendto, body */ + for (i = 0 ; (rc == 0) && (i < (int)requestParms->frameworkConfig->frameworkAdminCount) ; i++) { + rc = SendMailFile(requestParms->frameworkConfig, + requestParms->frameworkConfig->frameworkAdmins[i], + "Result Response", requestParms->frameworkConfig->outputBodyFilename); + } + break; + case RESPONSE_NO_EMAIL: + /* already logged, just continue in loop */ + break; + default: + fprintf(requestParms->frameworkConfig->frameworkLogFile, + "\tERROR0056: Error, unexpected response type %u\n", + responseType); + rc = ERR_BAD_ARG; + break; + } + } + /* The message file is typically closed by the function that sends the email. This call catches + error cases that abort before sending the email. */ + File_CloseMessageFile(); + return rc; +} + +/* FrameworkProcess_SendNotificationMessage() forms the notification message and sends it to the + notification recipients. +*/ + +int FrameworkProcess_SendNotificationMessage(const char *project, + ProjectConfig *projectConfig, + DropboxRequest* requestParms) +{ + int rc = 0; + size_t i; + char *subjectRe = NULL; /* freed @1 */ + FILE *notificationFile = NULL; /* email body back to notification receivers */ + + /* open the output notification body log file */ + if (rc == 0) { + rc = File_Open(¬ificationFile, + requestParms->frameworkConfig->notificationFilename, + "w"); + if (rc != 0) { + fprintf(requestParms->frameworkConfig->frameworkLogFile, + "\tERROR0057: Error opening notification message file %s\n", + requestParms->frameworkConfig->notificationFilename); + } + } + /* construct the notification message */ + if (rc == 0) { + /* time stamp */ + File_LogTime(notificationFile); + /* project */ + fprintf(notificationFile, + "Project : %s\n", project); + /* sender */ + fprintf(notificationFile, + "Dropbox sender : %s\n", requestParms->dbConfig->sender); + fprintf(notificationFile, + "Request Comments: %s\n", requestParms->comment); + /* people copied */ + fprintf(notificationFile, + "\nNotification recipients:\n"); + for (i = 0 ; i < projectConfig->notificationListCount ; i++) { + fprintf(notificationFile, + " %s\n", + projectConfig->notificationList[i]); + } + fprintf(notificationFile, + "\nSigner framework administrators are:\n"); + for (i = 0 ; i < requestParms->frameworkConfig->frameworkAdminCount ; i++) { + fprintf(notificationFile, + " %s\n", requestParms->frameworkConfig->frameworkAdmins[i]); + } + fprintf(notificationFile, + "\nSigner project administrator is %s\n", + projectConfig->emailProject); + } + /* close early, before sending the emails */ + if (notificationFile != NULL) { + fclose(notificationFile); + notificationFile = NULL; + } + /* form a notification subject, e.g. Re: Subject */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char**)&subjectRe, strlen("Request for : ") + strlen(project) + 5, + requestParms->frameworkConfig->lineMax); /* freed @1 */ + if (rc != 0) { + fprintf(requestParms->frameworkConfig->frameworkLogFile, + "\tERROR0058: Error forming response subject\n"); + } else { + sprintf(subjectRe, "Request for : %s", project); + } + } + if (rc == 0) { + /* iterate through the notification recipients, sending the notification message. Don't + check the return code here. Try to send as many notifications as will succeed */ + for (i = 0 ; i < projectConfig->notificationListCount ; i++) { + + rc = SendMailFile(requestParms->frameworkConfig, projectConfig->notificationList[i], + subjectRe, requestParms->frameworkConfig->notificationFilename); + if (rc == 0) { + fprintf(requestParms->frameworkConfig->frameworkLogFile, + "\tSent notification to %s\n", projectConfig->notificationList[i]); + } + /* log notification failures in the framework audit log, but keep trying the other + notification recipients */ + else { + fprintf(requestParms->frameworkConfig->frameworkLogFile, + "\tERROR0059 sending notification to %s\n", + projectConfig->notificationList[i]); + } + } + } + remove(requestParms->frameworkConfig->notificationFilename); + /* cleanup */ + free(subjectRe); /* @1 */ + return rc; +} + +/* ProjectConfig_Init() initializes the ProjectConfig object members + */ + +void ProjectConfig_Init(ProjectConfig *projectConfig) +{ + memset(projectConfig, 0, sizeof(ProjectConfig)); + return; +} + +/* ProjectConfig_Delete() frees and reinitializes the ProjectConfig object members + */ + +void ProjectConfig_Delete(ProjectConfig *projectConfig) +{ + size_t i; + + if (projectConfig != NULL) { + free(projectConfig->program); + free(projectConfig->projectLogFilename); + free(projectConfig->keyFilename); + free(projectConfig->auxCfgFilename); + free(projectConfig->emailProject); + /* free array members before freeing the array */ + for (i = 0 ; i < projectConfig->sendersCount ; i++) { + free(projectConfig->senders[i]); + free(projectConfig->senderemails[i]); + } + free(projectConfig->senders); + free(projectConfig->senderemails); + /* free array members before freeing the array */ + for (i = 0 ; i < projectConfig->notificationListCount ; i++) { + free(projectConfig->notificationList[i]); + } + free(projectConfig->notificationList); + /* initialize so the next delete is safe */ + ProjectConfig_Init(projectConfig); + } + return; +} + +/* ProjectConfig_Parse() parses a project configuration file into the ProjectConfig object. + + NOTE: Since this is called at startup, messages should go to stdout. All calls that set an error + code must print as well. + + validate flags whether the validation, which writes files, should be done. The main framework + should perform validation. Ancillary programs should not, so as not to interfere with a running + framework. +*/ + +int ProjectConfig_Parse(ProjectConfig *projectConfig, + int validate, + const char *projectConfigFilename, + FrameworkConfig *frameworkConfig) +{ + int rc = 0; + size_t i; + FILE *projectConfigFile = NULL; /* closed @1 */ + char *lineBuffer = NULL; /* freed @2 */ + + /* allocate a line buffer, used when parsing the configuration file */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&lineBuffer, + frameworkConfig->lineMax, + frameworkConfig->lineMax); /* trust the framework config file */ + } + /* save a hash of the project configuration file for the framework audit log */ + if (rc == 0) { + rc = Ossl_HashBinaryFile(projectConfig->digest, + MAX_CONFIG, + projectConfigFilename); + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "ProjectConfig_Parse: Project configuration file digest", + DIGEST_SIZE, + projectConfig->digest); + } + /* open project configuration file */ + if (rc == 0) { + projectConfigFile = fopen(projectConfigFilename, "r"); /* closed @1 */ + if (projectConfigFile == NULL) { + rc = ERROR_CODE; + } + } + /* program */ + if (rc == 0) { + rc = File_MapNameToValue(&(projectConfig->program), /* freed by caller */ + "program", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: Program filename: %s\n", + projectConfig->program); + } + /* projectLogFilename */ + if (rc == 0) { + rc = File_MapNameToValue(&(projectConfig->projectLogFilename), /* freed by caller */ + "log", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: Project log file name: %s\n", + projectConfig->projectLogFilename); + } + /* determine whether a signing key is needed */ + if (rc == 0) { + rc = File_MapNameToBool(&(projectConfig->needKey), + "needkey", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "ProjectConfig_Parse: project needs key: %d\n", + projectConfig->needKey); + } + /* keyFilename */ + if ((rc == 0) && projectConfig->needKey) { + rc = File_MapNameToValue(&(projectConfig->keyFilename), /* freed by caller */ + "key", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if ((rc == 0) && projectConfig->needKey) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: Project key file name: %s\n", + projectConfig->keyFilename ); + } + /* determine whether an auxiliary project configuration file is needed */ + if (rc == 0) { + rc = File_MapNameToBool(&(projectConfig->needAuxCfg), + "needauxcfg", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: " + "project needs auxiliary project configuration file: %d\n", + projectConfig->needAuxCfg); + } + /* auxCfgFilename */ + if ((rc == 0) && projectConfig->needAuxCfg) { + rc = File_MapNameToValue(&(projectConfig->auxCfgFilename), /* freed by caller */ + "auxcfg", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if ((rc == 0) && projectConfig->needAuxCfg) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: " + "Project auxiliary project configuration file name: %s\n", + projectConfig->auxCfgFilename); + } + /* determine whether an input attachment is needed */ + if (rc == 0) { + rc = File_MapNameToBool(&(projectConfig->needInputAttachment), + "neediatt", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: project needs input attachment: %d\n", + projectConfig->needInputAttachment); + } + /* determine whether an output attachment is needed */ + if (rc == 0) { + rc = File_MapNameToBool(&(projectConfig->needOutputAttachment), + "needoatt", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: project needs output attachment: %d\n", + projectConfig->needOutputAttachment); + } + /* determine whether a CCA password is needed */ + if (rc == 0) { + rc = File_MapNameToBool(&(projectConfig->needPassword), + "needpwd", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: project needs password: %d\n", + projectConfig->needPassword); + } + /* emailProject */ + if (rc == 0) { + rc = File_MapNameToValue(&(projectConfig->emailProject), /* freed by caller */ + "email", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: Project administrator email: %s\n", + projectConfig->emailProject); + } + /* read the list of notification receivers */ + if (rc == 0) { + rc = File_GetValueArray(&(projectConfig->notificationList), /* freed by caller */ + &(projectConfig->notificationListCount), + "notifs", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + /* determine whether senders are needed */ + if (rc == 0) { + rc = File_MapNameToBool(&(projectConfig->needSenders), + "needsenders", + lineBuffer, + frameworkConfig->lineMax, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: 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, + frameworkConfig->lineMax, + projectConfigFile); + } + /* validate the project configuration file */ + if ((rc == 0) && validate) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: Validating project configuration file\n"); + rc = ProjectConfig_Validate(projectConfig, + frameworkConfig); + } + if (rc != 0) { + fprintf(messageFile, + "ERROR0023: processing project configuration file: %s\n", + projectConfigFilename); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: " + "Authorized senders, %u entries\n", + (unsigned int)projectConfig->sendersCount); + for (i = 0 ; i < projectConfig->sendersCount ; i++) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: Authorized sender: %s\n", + projectConfig->senders[i]); + } + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: " + "Notification receivers, %u entries\n", + (unsigned int)projectConfig->notificationListCount); + for (i = 0 ; i < projectConfig->notificationListCount; i++) { + if (verbose) fprintf(messageFile, + "ProjectConfig_Parse: Notification receiver: %s\n", + projectConfig->notificationList[i]); + } + } + if (projectConfigFile != NULL) { + fclose(projectConfigFile); /* @1 */ + } + free(lineBuffer); /* @2 */ + return rc; +} + +/* ProjectConfig_Validate() checks that the project configuration file object has valid values. + */ + +int ProjectConfig_Validate(ProjectConfig *projectConfig, + FrameworkConfig *frameworkConfig) +{ + int rc = 0; + Arguments arguments; + + /* create the project command line arguments */ + Arguments_Init(&arguments); /* freed @1 */ + arguments.argc = 1; /* start with 1, the first argument is the program name */ + + /* validate that the project log file can be opened for append */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "ProjectConfig_Validate: " + "Validating project log file %s\n", + projectConfig->projectLogFilename); + rc = File_ValidateOpen(projectConfig->projectLogFilename, "a"); + } + /* validate that the project key file can be opened for read */ + if ((rc == 0) && projectConfig->needKey) { + rc = File_ValidateOpen(projectConfig->keyFilename, "r"); + } + /* validate that the project key can be used to sign */ + if ((rc == 0) && projectConfig->needKey) { + rc = ProjectConfig_ValidateKey(projectConfig); + } + /* validate that the auxiliary project configuration file can be opened for read */ + if ((rc == 0) && projectConfig->needAuxCfg) { + rc = File_ValidateOpen(projectConfig->auxCfgFilename, "r"); + } + /* validate the sender count */ + if (rc == 0) { + if (projectConfig->needSenders) { + if (projectConfig->sendersCount == 0) { + fprintf(messageFile, + "ERROR0024: Project configuration file has no authorized senders\n"); + rc = ERROR_CODE; + } + } + else { + if (projectConfig->sendersCount != 0) { + fprintf(messageFile, + "ERROR0024: Project configuration file has authorized senders\n"); + rc = ERROR_CODE; + } + } + } + /* + validate that the project file can be called, use -h + */ + /* add the program name for the project */ + if (rc == 0) { + /* create the project command line arguments */ + rc = Arguments_AddTo(&arguments, + projectConfig->program, + TRUE); /* add at index 0 */ + } + /* append output body file name to argv so the -h usage text goes to a file rather than to + stdout */ + if (rc == 0) { + rc = Arguments_AddPairTo(&arguments, "-obody", frameworkConfig->outputBodyFilename); + } + /* add -h flag, trick to probe if the program can be called */ + if (rc == 0) { + rc = Arguments_AddTo(&arguments, "-h", FALSE); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "ProjectConfig_Validate: calling program: %s\n", + arguments.argv[0]); + rc = CallSigner(&arguments, + FALSE, /* useOutputBody */ + frameworkConfig->outputBodyFilename); + } + /* cleanup */ + Arguments_Delete(&arguments); /* @1 */ + return rc; +} + +int ProjectConfig_ValidateKey(ProjectConfig *projectConfig) +{ + int rc = 0; + + int typeFound = FALSE; + unsigned char *keyToken = NULL; /* CCA signing key token, freed @1 */ + size_t keyTokenLength; + RsaKeyTokenPublic rsaKeyTokenPublic; /* signing key CCA public key structure */ +#ifdef ADD_ECC + EccKeyTokenPublic eccKeyTokenPublic; /* CCA public key structure */ +#endif + + /* get the CCA signing key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_ValidateKey: Reading CCA key token file %s\n", + projectConfig->keyFilename); + rc = File_ReadBinaryFile(&keyToken, &keyTokenLength, 2000, + projectConfig->keyFilename); /* freed @1 */ + if (rc != 0) { + fprintf(messageFile, + "ERROR0012: Could not open key file: %s\n", projectConfig->keyFilename); + } + } + /* determine if the key is RSA or ECC */ + if ((rc == 0) && !typeFound) { + rc = getPKA96PublicKey(&rsaKeyTokenPublic, /* output: structure */ + keyTokenLength, + keyToken, + 0); /* input: PKA96 key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_ValidateKey: CCA key token is RSA\n"); + typeFound = TRUE; +#if 0 /* NOTE Not run, since it requires a CCA login. This can be added in the future */ + rc = ProjectConfig_ValidateKeyRSA(keyTokenLength, + keyToken); +#endif + } + else { + rc = 0; /* try next type */ + } + } +#ifdef ADD_ECC + if ((rc == 0) && !typeFound) { + rc = getPKA96EccPublicKey(&eccKeyTokenPublic, /* output: structure */ + keyTokenLength, + keyToken); /* input: PKA96 key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_ValidateKey: CCA key token is ECC\n"); + typeFound = TRUE; +#if 0 /* NOTE Not run, since it requires a CCA login. This can be added in the future */ + rc = ProjectConfig_ValidateKeyECC(keyTokenLength, + keyToken); +#endif + } + else { + rc = 0; /* try next type */ + } + } +#endif + if ((rc == 0) && !typeFound) { + fprintf(messageFile, + "ERROR0015: CCA key token is unknown type %s\n", projectConfig->keyFilename); + rc = ERROR_CODE; + } + /* clean up */ + free(keyToken); /* @1 */ + + return rc; +} + +/* ProjectConfig_ValidateKeyRSA() validates that a null digest can be signed with the CCA RSA key + token + + NOTE Not tested +*/ + +int ProjectConfig_ValidateKeyRSA(size_t keyTokenLength, + unsigned char *keyToken) +{ + int rc = 0; + + unsigned char signature[N_SIZE]; + unsigned long signatureLength = sizeof(signature); + unsigned long signatureBitLength; /* unused */ + unsigned char digest[SHA1_SIZE]; + + memset(digest, 0, sizeof(digest)); + if (rc == 0) { + if (verbose) fprintf (messageFile, + "ProjectConfig_ValidateKeyRSA: Signing"); + rc = Digital_Signature_Generate(&signatureLength, /* i/o */ + &signatureBitLength, /* output */ + signature, /* output */ + keyTokenLength, /* input */ + keyToken, /* input */ + sizeof(digest), /* input */ + digest); /* input */ + } + /* clean up */ + + return rc; +} + +#ifdef ADD_ECC + +/* ProjectConfig_ValidateKeyECC() validates that a null digest can be signed with the CCA ECC key + token + + NOTE Not tested +*/ + +int ProjectConfig_ValidateKeyECC(size_t keyTokenLength, + unsigned char *keyToken) +{ + int rc = 0; + + unsigned char signature[132]; /* NOTE 132 according to CCA, openssl produces + 139 */ + unsigned long signatureLength = sizeof(signature); + unsigned long signatureBitLength; + unsigned char digest[SHA1_SIZE]; + + memset(digest, 0, sizeof(digest)); + if (rc == 0) { + if (verbose) fprintf (messageFile, + "ProjectConfig_ValidateKeyECC: Signing"); + rc = Digital_Signature_Generate_ECC(&signatureLength, /* i/o */ + &signatureBitLength, /* output */ + signature, /* output */ + keyTokenLength, /* input */ + keyToken, /* input */ + sizeof(digest), /* input */ + digest); /* input */ + + } + /* clean up */ + + return rc; +} +#endif + + +/* ProjectProcess_ProcessInputParameters() takes each line of the input body and assigns it to an argv + element. + + It rejects certain standard framework command line argument, so that the request cannot + override them. +*/ + +int ProjectProcess_ProcessInputParameters(Arguments *arguments, + DropboxRequest *requestParms) +{ + int rc = 0; + int i = 0; + int irc = 0; + char buf[requestParms->frameworkConfig->lineMax]; + const char* curpos = requestParms->parameters; + const char* nextpos = NULL; + + if (requestParms->parameters != NULL && + strlen(requestParms->parameters) != 0) { + + do { + while (*curpos == ' ') curpos++; + + // Break the parms into words and add as args + nextpos = strchr(curpos, ' '); + if (nextpos == NULL) { + // end of the line + strcpy(buf, curpos); + curpos = NULL; + } else if (nextpos-curpos > (int)requestParms->frameworkConfig->lineMax) { + rc = 1; + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "Parameter overflow in input parameters\n"); + } else { + strncpy(buf, curpos, nextpos-curpos); + buf[(nextpos-curpos)] = '\0'; + curpos = nextpos; + if (*curpos == '\0') curpos = NULL; + } + + /* screen out command line arguments that attempt to override the framework values */ + for (i = 0 ; (rc == 0) && (i < (int)(sizeof(claTable)/sizeof(char *))) ; i++) { + irc = strcmp(claTable[i], buf); + if (irc == 0) { + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "%s illegal in input parameters\n", buf); + if (verbose) fprintf(messageFile, + "ProjectProcess_ProcessInputBody: %s illegal in input parameters\n", + buf); + rc = ERROR_CODE; + } + } + /* add line to argv */ + if (rc == 0) { + rc = Arguments_AddTo(arguments, buf, FALSE); /* add to end */ + } + } + while (rc == 0 && curpos != NULL); + } + return rc; +} + +/* ProjectConfig_Process() uses the project configuration file to add command line arguments + + - project specific signer program (put in arguments->argv[0]) + - audit log file name + - signing key (optional) + - input attachment file (optional) + - output attachment file (optional) +*/ + +int ProjectConfig_Process(Arguments *arguments, + ProjectConfig *projectConfig, + DropboxRequest *requestParms) +{ + int rc = 0; + + /* add the program name for the project */ + if (rc == 0) { + rc = Arguments_AddTo(arguments, + projectConfig->program, + TRUE); /* add at index 0 */ + } + /* add the project audit log filename */ + if (rc == 0) { + rc = Arguments_AddPairTo(arguments, + "-log", projectConfig->projectLogFilename); + } + /* add the signing key if needed */ + if (rc == 0) { + if (projectConfig->needKey) { + rc = Arguments_AddPairTo(arguments, + "-key", projectConfig->keyFilename); + } + } + /* add the auxiliary project configuration file if needed */ + if (rc == 0) { + if (projectConfig->needAuxCfg) { + rc = Arguments_AddPairTo(arguments, + "-auxcfg", projectConfig->auxCfgFilename); + } + } + /* add the input attachment if needed */ + if ((rc == 0) && projectConfig->needInputAttachment) { + if (requestParms->hasPayload) { + rc = Arguments_AddPairTo(arguments, + "-di", requestParms->frameworkConfig->inputAttachmentFilename); + } + else { + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "ERROR0055: Project requires an attachment"); + rc = ERROR_CODE; + } + } + /* add the output attachment if needed */ + if (rc == 0) { + if (projectConfig->needOutputAttachment) { + rc = Arguments_AddPairTo(arguments, + "-do", requestParms->frameworkConfig->outputAttachmentFilename); + } + } + return rc; +} + +/* ProjectConfig_ValidateSender() validates that the sender is authorized via the project + configuration file. +*/ + +int ProjectConfig_ValidateSender(const char *sender, + ProjectConfig *projectConfig, + FrameworkConfig *configParm) +{ + int rc = 0; + int irc; + int ldaprc = 0; + size_t i; + int authorized = FALSE; + + /* if the project doesn't have a senders list, no authorization is required */ + if (!projectConfig->needSenders) + { + if (verbose) fprintf(messageFile, + "ProjectConfig_ValidateSender: " + "Project does not use sender authorization\n"); + authorized = TRUE; + } + else + { + for (i = 0 ; (i < projectConfig->sendersCount) && !authorized ; i++) { + irc = strcmp(projectConfig->senders[i], sender); +#ifdef LDAP_VERIFY + if (NULL != configParm && irc == 0) { /* found the sender in the list of authorized senders */ + ldaprc = ldapLookupByEmail(configParm, projectConfig->senderemails[i], + NULL, (bool)verbose); + // Make sure we found at least one matching entry + if (ldaprc == 0) { + irc = 1; + } + } +#endif + if (irc == 0) { + if (verbose) fprintf(messageFile, + "ProjectConfig_ValidateSender: " + "Sender %s is authorized\n", sender); + authorized = TRUE; + } + } + } + if (!authorized) { + if (verbose) fprintf(messageFile, + "ProjectConfig_ValidateSender: " + "%u senders, sender %s is not authorized\n", + (unsigned int)projectConfig->sendersCount, sender); + rc = ERROR_CODE; + } + return rc; +} + +/* Comment_Validate() validates and possibly sanitizes the request comment + + Legal characters are printable + + Illegal characters are replaced with ? + + Returns ERROR_CODE on error and logs to the framework log +*/ + +int Comment_Validate(DropboxRequest *requestParms) +{ + int rc = 0; + size_t i; + int foundError = FALSE; + + /* Do this even if there were previous errors. Screen the comment for illegal characters, + sanitize the output */ + for (i = 0 ; i < strlen(requestParms->comment) ; i++) { + if (!isprint(requestParms->comment[i])) { /* non-printable characters */ + requestParms->comment[i] = '?'; /* sanitize with ? character */ + if (!foundError) { /* log the first error */ + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "ERROR0029: comment has invalid character at index %u\n", + i); + foundError = TRUE; + rc = ERROR_CODE; + } + } + } + /* on error log the sanitized subject */ + if (rc != 0) { + File_Printf(requestParms->frameworkConfig->frameworkLogFile, messageFile, + "ERROR0030: Sanitized subject is %s\n", + requestParms->comment); + } + + return rc; +} + +/* Env_Validate() validates that all required environment variables are set + + */ + +int Env_Validate() +{ + int rc = 0; + + return rc; +} + +/* + Password Crypto +*/ + +/* Password_KDF() uses the master AES key and the user email address to derive the password + encryption key and authentication key. + + The caller should erase the keys when done. + + The algorithm is: + + encryption key EKU = AES with master key MKC (sender email address || 0) + encryption key AKU msb = AES with master key MKC (sender email address || 1) + encryption key AKU lsb = AES with master key MKC (sender email address || 2) + + In each case, take the last 128 bits/ 16 bytes. That's because sender email addresses could be + common in the first bytes. Since the IV is constant, the first bytes of the keys could be the + same. +*/ + +int Password_KDF(unsigned char *eku, /* preallocated 16 bytes, 128 bits */ + unsigned char *aku, /* preallocated 32 bytes, 256 bits */ + const char *sender, + const unsigned char *masterAesKeyToken) +{ + int rc = 0; + + unsigned char initialization_vector[IV_SIZE]; + unsigned char *cleartext = NULL; /* freed @1 */ + unsigned char *ciphertext = NULL; /* freed @2 */ + long cleartext_length; + long ciphertext_length; + + /* cannot modify 'sender', so make a copy. The NUL terminator will be replaced by the 0,1,2 */ + if (rc == 0) { + memset(initialization_vector, 0, IV_SIZE); + cleartext_length = strlen(sender) + 1; + rc = Malloc_Safe(&cleartext, cleartext_length, cleartext_length); /* freed @1 */ + } + if (rc == 0) { + strcpy((char *)cleartext, sender); + } + /* EKU */ + if (rc == 0) { + cleartext[cleartext_length-1] = 0; + rc = Symmetric_Algorithm_Encipher(&ciphertext_length, + &ciphertext, /* freed @2 */ + cleartext_length, + cleartext, + initialization_vector, + masterAesKeyToken); + } + if (rc == 0) { + memcpy(eku, ciphertext + ciphertext_length - AES128_SIZE, AES128_SIZE); + /* ciphertext is part of a secret key, clear before free */ + memset(ciphertext, 0, ciphertext_length); + free(ciphertext); /* @2 */ + ciphertext = NULL; + } + /* AKU msb */ + if (rc == 0) { + cleartext[cleartext_length-1] = 1; + rc = Symmetric_Algorithm_Encipher(&ciphertext_length, + &ciphertext, /* freed @2 */ + cleartext_length, + cleartext, + initialization_vector, + masterAesKeyToken); + } + if (rc == 0) { + memcpy(aku, ciphertext + ciphertext_length - AES128_SIZE, AES128_SIZE); + /* ciphertext is part of a secret key, clear before free */ + memset(ciphertext, 0, ciphertext_length); + free(ciphertext); /* @2 */ + ciphertext = NULL; + } + /* AKU lsb */ + if (rc == 0) { + cleartext[cleartext_length-1] = 2; + rc = Symmetric_Algorithm_Encipher(&ciphertext_length, + &ciphertext, /* freed @2 */ + cleartext_length, + cleartext, + initialization_vector, + masterAesKeyToken); + } + if (rc == 0) { + memcpy(aku + AES128_SIZE, ciphertext + ciphertext_length - AES128_SIZE, AES128_SIZE); + /* ciphertext is part of a secret key, clear before free */ + memset(ciphertext, 0, ciphertext_length); + free(ciphertext); /* @2 */ + ciphertext = NULL; + } + /* cleartext is just the sender, not a secret, so it does not have to be cleared */ + free(cleartext); /* @1 */ + free(ciphertext); /* @2 */ + + return rc; +} + +#define RANDOM_CHUNK 8 /* get 8 bytes from rng at a time */ + +/* Password_Generate() generates a strong password. The password is graphic characters with a NUL + terminator +*/ + +int Password_Generate(char *password, + size_t password_length) /* including space for NUL terminator */ +{ + int rc = 0; + + size_t i = 0; /* password character iterator */ + size_t j; /* get 8 bytes from rng at a time */ + unsigned char random_number[RANDOM_CHUNK ]; + int rand_int; + + password[password_length-1] = '\0'; + while ((rc == 0) && (i < (password_length-1))) { /* -1 for NUL terminator */ + + /* get a new chunk of random numbers */ + rc = Random_Number_Generate_Long(random_number, RANDOM_CHUNK); + + /* process the chunk */ + for (j = 0 ; (rc == 0) && (j < RANDOM_CHUNK) ; j++) { + + rand_int = random_number[j]; + if (isgraph(rand_int)) { /* use only graphic characters, no white space */ + password[i] = random_number[j]; + i++; + if (i == (password_length-1)) { /* done, terminate j loop */ + break; + } + } + } + } + + return rc; +} + +/* Password_ToString() creates the actual password string that the sender puts in the email body. + It consists of the concatenation of: + + Initialization vector[16] || HMAC[32] || Encrypted Password +*/ + +int Password_ToString(char **passwordString, /* freed by caller */ + size_t *passwordStringLength, /* not including NUL terminator */ + unsigned char *initialization_vector, + unsigned char *hmac, + unsigned char *passwordCiphertext, + size_t passwordCiphertextLength) +{ + int rc = 0; + + /* allocate the entire string length */ + if (rc == 0) { + *passwordStringLength = ((IV_SIZE + HMAC_SIZE + passwordCiphertextLength) * 2); + rc = Malloc_Safe((unsigned char **)passwordString, + (*passwordStringLength) + 1, + (*passwordStringLength) + 1); + } + if (rc == 0) { + /* Format_ToHexascii() converts binary to hex ascii and appends a NUL terminator. All but + the last NUL is overwritten */ + /* Initialization vector */ + Format_ToHexascii(*passwordString, + initialization_vector, IV_SIZE); + /* HMAC */ + Format_ToHexascii((*passwordString) + (IV_SIZE * 2), + hmac, HMAC_SIZE); + /* Encrypted Password */ + Format_ToHexascii((*passwordString) + ((IV_SIZE + HMAC_SIZE) * 2), + passwordCiphertext, passwordCiphertextLength); + } + return rc; +} + +/* Password_FromString() cracks the passwordString of length passwordStringLength (not including NUL + terminator) and returns + + Initialization vector[16] + HMAC[32] + Encrypted Password +*/ + +int Password_FromString(unsigned char *initialization_vector, + unsigned char *hmac, + unsigned char **passwordCiphertext, /* freed by caller */ + size_t *passwordCiphertextLength, + const char *passwordString, + size_t passwordStringLength, + FILE *logFile) +{ + int rc = 0; + + /* there must be at least enough characters for the IV and HMAC */ + if (rc == 0) { + if ((passwordStringLength/2) < (size_t)(IV_SIZE + HMAC_SIZE)) { + File_Printf(logFile, messageFile, + "ERROR0032: Encrypted password is too small\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + /* after the IV and HMAC, the rest are the password characters */ + *passwordCiphertextLength = ((passwordStringLength) / 2) - IV_SIZE - HMAC_SIZE; + rc = Malloc_Safe(passwordCiphertext, *passwordCiphertextLength, *passwordCiphertextLength); + } + /* Initialization vector */ + if (rc == 0) { + rc = Format_FromHexascii(initialization_vector, + passwordString, IV_SIZE); + } + /* HMAC */ + if (rc == 0) { + rc = Format_FromHexascii(hmac, + passwordString + (IV_SIZE * 2), HMAC_SIZE); + } + /* Encrypted Password */ + if (rc == 0) { + rc = Format_FromHexascii(*passwordCiphertext, + passwordString + ((IV_SIZE + HMAC_SIZE) * 2), + *passwordCiphertextLength); + } + return rc; +} + +/* Password_Decrypt() adds the decrypted password to argv + + The encrypted password is pulled from the argv. + + The master AES key token was saved on disk as a one time operation during framework installation. +*/ + +int Password_Decrypt(Arguments *arguments, + const char *sender, + const unsigned char *masterAesKeyToken, + FILE *logFile) +{ + int rc = 0; + const char *passwordString = NULL; /* combined IV, HMAC, encrypted pwd */ + size_t passwordStringLength; + unsigned char initialization_vector[IV_SIZE]; + unsigned char hmac[HMAC_SIZE]; + unsigned char *ciphertext = NULL; /* in binary, freed @1 */ + long ciphertext_length; + size_t cleartext_length; + unsigned char *cleartext = NULL; /* freed @2 */ + int hmac_valid; + unsigned char eku[AES128_SIZE]; /* password encryption key */ + unsigned char aku[AKU_SIZE]; /* password authentication HMAC key */ + + /* pull the encrypted password from argv, in hex ASCII */ + if (rc == 0) { + rc = Arguments_GetFrom(&passwordString, "-epwd", + arguments); + if (rc != 0) { + File_Printf(logFile, messageFile, + "ERROR0031: Email missing -epwd\n"); + } + } + if (rc == 0) { + passwordStringLength = strlen(passwordString); + if (verbose) fprintf(messageFile, "Password_Decrypt: encrypted password length %u\n", + (unsigned int)passwordStringLength); + if (verbose) fprintf(messageFile, "Password_Decrypt: encrypted password %s\n", + passwordString); + } + /* convert the hex ASCII back to IV, HMAC, and encrypted password in binary */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password_Decrypt: Convert back to binary\n"); + rc = Password_FromString(initialization_vector, + hmac, + &ciphertext, /* freed @1 */ + (size_t *)&ciphertext_length, + passwordString, + passwordStringLength, + logFile); + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "initialization_vector", + IV_SIZE, initialization_vector); + if (verbose) PrintAll(messageFile, + "HMAC", HMAC_SIZE, hmac); + if (verbose) PrintAll(messageFile, + "Encrypted password", ciphertext_length, ciphertext); + } + else { + File_Printf(logFile, messageFile, + "ERROR0032: Encrypted password has an illegal format\n"); + } + } + /* derive the encryption key (eku) and authentication HMAC key (aku) from the sender and the + master AES key */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password_Decrypt: Deriving decryption and HMAC keys\n"); + rc = Password_KDF(eku, /* user encryption key */ + aku, /* user authentication HMAC key */ + sender, /* user */ + masterAesKeyToken); + if (rc != 0) { + File_Printf(logFile, messageFile, + "ERROR0033: deriving the password decryption and HMAC keys\n"); + fprintf(messageFile, + "Contact framework administrator\n"); + } + } + /* validate the converted authentication HMAC */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password_Decrypt: Checking HMAC\n"); + Ossl_HMAC_Check(&hmac_valid, + hmac, + aku, /* HMAC key */ + ciphertext_length, + ciphertext, + 0, NULL); + if (!hmac_valid) { + File_Printf(logFile, messageFile, + "ERROR0034: Password HMAC failed, the password is corrupt\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password_Decrypt: HMAC is valid\n"); + } + /* decrypt the password - use the recovered IV and the derived encryption key (eku) */ + if (rc == 0) { + rc = Ossl_AES_Decrypt(&cleartext, /* freed @2 */ + &cleartext_length, + ciphertext, + ciphertext_length, + initialization_vector, + eku); + if (rc != 0) { + File_Printf(logFile, messageFile, + "ERROR0035: decrypting the encrypted password\n"); + fprintf(messageFile, + "Contact framework administrator\n"); + } + } + /* validate the password */ + if (rc == 0) { +#if 0 /* NOTE: This trace cannot be in the final product, since it leaks the user password */ + /* trace as text */ + if (verbose) fprintf(messageFile, "Password_Decrypt: Password %.*s\n", + cleartext_length, cleartext); + /* trace as binary */ + if (verbose) PrintAll(messageFile, + "Password_Decrypt: Decrypted password", + cleartext_length, cleartext); +#endif + if (strlen((char *)cleartext) != (cleartext_length - 1)) { + if (verbose) fprintf(messageFile, "strlen %u\n", (unsigned int)strlen((char *)cleartext)); + if (verbose) fprintf(messageFile, "cleartext_length %u\n", (unsigned int)cleartext_length); + File_Printf(logFile, messageFile, + "ERROR0037: Password decrypt failed, length error\n"); + fprintf(messageFile, + "Contact framework administrator\n"); + rc = ERROR_CODE; + } + } + /* add the CCA password to the argv */ + if (rc == 0) { + rc = Arguments_AddPairTo(arguments, + "-pwd", + (char *)cleartext); + if (rc != 0) { + File_Printf(logFile, messageFile, + "ERROR0038: adding -pwd to command line arguments\n"); + } + } + /* this is encrypted, no need to erase */ + free(ciphertext); /* @1 */ + /* erase the cleartext password */ + if (cleartext != NULL) { + memset(cleartext, 0, cleartext_length); + } + free(cleartext); /* @2 */ + /* erase the secret keys */ + memset(eku, 0, AES128_SIZE); + memset(aku, 0, AKU_SIZE); + return rc; +} + +/* CallSigner() calls the signer program. + + If 'useOutputBody' is TRUE (the normal case), the output body is used as the messageFile once the + child process returns. If it is FALSE (the start up probe case), messageFile not changed, and + will remain pointing to stdout. This permits start up errors to be displayed to the user. + + The standard command line arguments are: + + 0: Name of the program being invoked + + 1: -obody + 2: output email body file name + + ...This pair is intentionally the first parameter set. The called program should open this file + for append ASAP, so that all errors will be put in the file. This includes errors in other + command line arguments. + + 3: -sender + 4: email sender + + 5-n: These arguments are copied from the input email body + + -log filename: The name of the file used for a program specific audit log. The parameter pair + will always be present but may be ignored by the program. + + -key filename: The name of the file containing the project singing key. This parameter pair will + not be present if the project configuration file indicates that no key is needed. + + -di filename: The name of the file containing the input attachment. This parameter pair + will not be present if the project configuration file indicates that no input attachment is + needed. + + -do filename: The name of the file to place the output attachment. This parameter pair + will not be present if the project configuration file indicates that no output attachment is + needed. + + -pwd password: The plaintext password as decrypted by the framework. This parameter pair will + not be present if the project configuration file indicates that no password is needed. +*/ + +int CallSigner(Arguments *arguments, + int useOutputBody, + const char *outputBodyFilename) +{ + int rc = 0; + pid_t childPid; /* pid of signer program */ + pid_t wrc; + int childRc; /* return code from signer program */ + + /* Sanity checks on arguments. There are most likely fatal errors. */ + if (arguments->argv[0] == NULL) { + if (verbose) fprintf(messageFile, "CallSigner: Error, calling NULL program\n"); + rc = RESPONSE_BODY_ONLY; + } + if (arguments->argv[arguments->argc] != NULL) { + if (verbose) fprintf(messageFile, "CallSigner: Error, argv and argc inconsistent\n"); + rc = RESPONSE_BODY_ONLY; + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "CallSigner: Calling: %s\n", arguments->argv[0]); + } + /* create a child process that will exec() to the signer program */ + /* must fflush the stream before the fork, else get duplicate streams to the output */ + fflush(messageFile); + childPid = fork(); + /* check that the child process started */ + if (childPid < 0) { + if (verbose) fprintf(messageFile, "CallSigner: Error, fork failed\n"); + if (verbose) fprintf(messageFile, "CallSigner: Error, %s\n", strerror(errno)); + } + /* + child - fork() returns 0 for the child process + */ + else if (childPid == 0) { + if (verbose) fprintf(messageFile, "CallSigner: Child\n"); + /* close the message file since the signer specific program may use it */ + File_CloseMessageFile(); + /* NOTE: From here down, no printing to messageFile until unless the exec returns with + an error. The child will reopen the file later. */ + /* start the specific signer program for the project */ + /* int execv(const char *path, char *const argv[]); */ + execv(arguments->argv[0], arguments->argv); + /* The code only gets here if the program cannot be called. Any return from exec() is an + error */ + + /* reopen the messages file and append child errors */ + if ((rc == 0) && useOutputBody) { + rc = File_OpenMessageFile(outputBodyFilename, "a"); + } + if (rc == 0) { + fprintf(messageFile, "ERROR0039: child process could not run program: %s\n", + arguments->argv[0]); + fprintf(messageFile, "ERROR0039: %s\n", strerror(errno)); + /* close the message file since the parent may use it */ + File_CloseMessageFile(); + } + /* if the child exec's, it will not return. If the exec fails, returning here would cause + two process to continue. Therefore, must exit. This lets the parent (and only the + parent) continue. */ + exit(1); + } + /* + parent + */ + else { + /* NOTE: From here down, no printing to messageFile until child returns. */ + /* parent waits for the child signer program to complete. The call to wait() gets the + return code for the exec'ed child process */ + wrc = wait(&childRc); + /* once the child is complete, reopen the messages file and append */ + if ((rc == 0) && useOutputBody) { + rc = File_OpenMessageFile(outputBodyFilename, "a"); + } + if (verbose) fprintf(messageFile, "CallSigner: Parent, child is pid %d\n", childPid); + if (verbose) fprintf(messageFile, "CallSigner: wrc %d childRc %d\n", wrc, childRc); + if (rc == 0) { + if (wrc == -1) { + fprintf(messageFile, "ERROR0041: parent error waiting for child process %s\n", + strerror(errno)); + rc = RESPONSE_BODY_ONLY; + } + } + if (rc == 0) { + /* WIFEXITED returns true if the child terminated normally */ + if (WIFEXITED(childRc)) { + /* WEXITSTATUS returns the exit status of the child. This consists of the least + significant 16-8 bits of the status argument that the child specified in a call + to exit() or _exit() or as the argument for a return statement in main(). This + macro should only be employed if WIFEXITED returned true. */ + rc = WEXITSTATUS(childRc); + if (verbose) fprintf(messageFile, "CallSigner: program return code %d\n", rc); + } + else if (WIFSIGNALED(childRc)) { + fprintf(messageFile, "ERROR0042: Child process %s exited with signal = %d\n", + arguments->argv[0], WTERMSIG(childRc)); +#ifdef WCOREDUMP + if (WCOREDUMP(childRc)) { + fprintf(messageFile, "CallSigner: ERROR0043: %s produced core dump\n", + arguments->argv[0]); + } +#endif + rc = RESPONSE_BODY_ONLY; + } + else { + fprintf(messageFile, + "ERROR0044: Child process %s exited abnormally\n", arguments->argv[0]); + rc = RESPONSE_BODY_ONLY; + } + } + } + return rc; +} diff --git a/src/signframework/framework_utils.h b/src/signframework/framework_utils.h new file mode 100644 index 0000000..53be77e --- /dev/null +++ b/src/signframework/framework_utils.h @@ -0,0 +1,219 @@ +/* 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. + */ + +#ifndef FRAMEWORK_UTILS_H +#define FRAMEWORK_UTILS_H +#include <json/json.h> + +#include "utils.h" + + +/* maximum size of a framework configuration file, for the malloc sanity check */ +#define MAX_CONFIG 64000 +/// Maximum number of dropbox directories to watch (max num of unique signers) +#define MAX_WATCHERS 30 +/// Maximum char length of signers userid +#define MAX_USERNAME 30 + +/* Crypto constants */ + +/* password encryption key, encryption blocks, uses AES 128 */ +#define AES128_SIZE 16 +/* password HMAC key, uses SHA-256 */ +#define AKU_SIZE 32 +/* password HMAC is the same size as the HMAC key, SHA-256 */ +#define HMAC_SIZE 32 +/* encryption and KDF initialization vector, uses AES 128 */ +#define IV_SIZE 16 +/* digest for logging, uses SHA-256 */ +#define DIGEST_SIZE 32 + +/* + ProjectConfig represents a project configuration file. +*/ + +typedef struct tdProjectConfig { + unsigned char digest[DIGEST_SIZE]; + char *program; + char *projectLogFilename; + int needKey; + char *keyFilename; + int needAuxCfg; + char *auxCfgFilename; + int needInputAttachment; + int needOutputAttachment; + int needPassword; + char *emailProject; + int needSenders; /* project uses sender authorization */ + size_t sendersCount; /* number of authorized senders */ + char **senders; /* array of authorized sender dropbox ids */ + char **senderemails; /* array of associated sender email addresses */ + size_t notificationListCount; /* number of notification receivers */ + char **notificationList; /* array of notification names */ +} ProjectConfig; + +/// Status/configuration information for a dropbox +typedef struct tdDropboxWatchConfig +{ + char sender[MAX_USERNAME]; ///< Sender id + int wd; ///< Watch descriptor +} DropboxWatchConfig; + +/* + FrameworkConfig represents the entire framework, the parsed and validated framework and project + configuration files. +*/ + +typedef struct tdFrameworkConfig { + const char *frameworkConfigFilename; + char *frameworkName; /* Name to refer to framework */ + char *frameworkLogFilename; /* framework audit log */ + FILE *frameworkLogFile; /* framework audit log */ + unsigned char digest[DIGEST_SIZE]; + size_t lineMax; /* maximum line length in text parsing */ + size_t fileMax; ///< Maximum upload file size + unsigned int passwordExpire; /* CCA password expiration duration in months */ + char *dropboxDir; /* Base directory of signer dropboxes */ + char *stopFile; /* file used to stop the framework */ + char *restartFile; /* file used to restart the framework */ + char *outputBodyFilename; /* output body, response to sender */ + char *notificationFilename; /* output notification response */ + char *emailFilename; /* file to write email to send */ + char *inputAttachmentFilename; /* fixed names for the input and output + attachments */ + char *outputAttachmentFilename; + char *masterAesKeyTokenFilename; + unsigned char *masterAesKeyToken; /* the CCA AES master key token used to derive the + password encryption and HMAC keys (not the + plaintext AES key)*/ + char *ldapUrl; /* LDAP url for lookup */ + char *ldapBase; /* LDAP BASEDN for search */ + size_t frameworkAdminCount; /* number framework administrators */ + char **frameworkAdmins; /* array of framework administrators */ + size_t projectLength; /* number of project to config file mappings */ + char **projectNames; /* array of project names */ + char **projectConfigFilenames; /* array of project config file names */ + ProjectConfig **projectConfigFiles; /* array of project config files */ + int inotifyFd; ///< inotify file descriptor + int controlWd; ///< Stop/restart file watcher + DropboxWatchConfig dropboxWatchers[MAX_WATCHERS]; ///< array of dropbox watchers +} FrameworkConfig; + +/// Store all relevant info for a unique dropbox request +typedef struct tdDropboxRequest { + FrameworkConfig* frameworkConfig; + int status; ///< Request status + + struct inotify_event* event; ///< Inotify event + DropboxWatchConfig* dbConfig; ///< Signers dropbox configuration + char* requestId; ///< Unique identifier for request + char* requestJson; ///< Json request from sender + json_object* rJsonO; + + // Request fields + const char* project; + char* comment; + const char* user; + const char* epwd; + int hasPayload; + const char* parameters; + + json_object* respJsonO; ///< Response JSON + int hasResult; + + int closeMessageFile; /* hack to keep the messageFile (output body) open when + sending notifications. If closed, trace messages would + start to go to stdout */ + +} DropboxRequest; + +void FrameworkConfig_Init(FrameworkConfig *frameworkConfig); +void FrameworkConfig_Delete(FrameworkConfig *frameworkConfig); +int FrameworkConfig_Parse(int needMasterKey, + int validate, + FrameworkConfig *frameworkConfig); +int FrameworkConfig_LogStart(FrameworkConfig *frameworkConfig); +int FrameworkConfig_SendStartupMessage(FrameworkConfig *frameworkConfig, + int transientError); + +/* + Framework Processing +*/ + +int FrameworkProcess_Process(DropboxRequest *requestParms); +int FrameworkProcess_ProcessOK(DropboxRequest *requestParms); + +int FrameworkProcess_SendResponse(int responseType, + DropboxRequest *requestParms); +int FrameworkProcess_SendNotificationMessage(const char *project, + ProjectConfig *projectConfig, + DropboxRequest *mailParms); + + +/* + Project Configuration +*/ + +void ProjectConfig_Init(ProjectConfig *projectConfig); +void ProjectConfig_Delete(ProjectConfig *projectConfig); +int ProjectConfig_Parse(ProjectConfig *projectConfig, + int validate, + const char *projectConfigFilename, + FrameworkConfig *frameworkConfig); +int ProjectConfig_Process(Arguments *arguments, + ProjectConfig *projectConfig, + DropboxRequest *requestParms); +int ProjectConfig_ValidateSender(const char *sender, + ProjectConfig *projectConfig, + FrameworkConfig *frameworkConfig); +int ProjectProcess_ProcessInputParameters(Arguments *arguments, + DropboxRequest *requestParms); + +int Comment_Validate(DropboxRequest *requestParms); +int Env_Validate(void); + + +int Password_KDF(unsigned char *eku, + unsigned char *aku, + const char *sender, + const unsigned char *masterAesKeyToken); +int Password_Generate(char *password, + size_t password_length); +int Password_ToString(char **passwordString, + size_t *passwordStringLength, + unsigned char *initialization_vector, + unsigned char *hmac, + unsigned char *passwordCiphertext, + size_t passwordCiphertextLength); +int Password_FromString(unsigned char *initialization_vector, + unsigned char *hmac, + unsigned char **passwordCiphertext, + size_t *passwordCiphertextLength, + const char *passwordString, + size_t passwordStringLength, + FILE *logFile); +int Password_Decrypt(Arguments *arguments, + const char *sender, + const unsigned char *masterAesKeyToken, + FILE *logFile); + +int CallSigner(Arguments *arguments, + int useOutputBody, + const char *outputBodyFilename); + + + +#endif diff --git a/src/signframework/frameworkkey_generate.c b/src/signframework/frameworkkey_generate.c new file mode 100644 index 0000000..497cceb --- /dev/null +++ b/src/signframework/frameworkkey_generate.c @@ -0,0 +1,171 @@ +/* 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include <sys/stat.h> + +#include "framework_utils.h" +#include "utils.h" +#include "cca_functions.h" + +/* local prototypes */ + +int GetArgs(int argc, + char **argv); +void PrintUsage(void); + +/* global variables */ + +FILE *messageFile = NULL; +int verbose = TRUE; +int debug = TRUE; + +int main(int argc, char** argv) +{ + int rc = 0; + FrameworkConfig frameworkConfig; + unsigned char masterAesKeyToken[CCA_KEY_IDENTIFIER_LENGTH]; + unsigned char *masterAesKeyTokenOut = NULL; /* CCA key token (not the plaintext AES + key) */ + size_t masterAesKeyTokenLengthOut; + unsigned char eku[AES128_SIZE]; /* password encryption key */ + unsigned char aku[AKU_SIZE]; /* password authentication HMAC key */ + + messageFile = stdout; /* trace always goes to stdout */ + FrameworkConfig_Init(&frameworkConfig); /* freed @1 */ + + /* get command line arguments */ + if (rc == 0) { + rc = GetArgs(argc, argv); + } + /* get the framework configuration file object */ + if (rc == 0) { + rc = FrameworkConfig_Parse(FALSE, /* do not need master key */ + TRUE, /* validate */ + &frameworkConfig); + } + /* verify that the file does not exist */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Testing for key token file %s\n", + frameworkConfig.masterAesKeyTokenFilename); + if (frameworkConfig.masterAesKeyToken != NULL) { + fprintf(messageFile, "Error, File %s already exists\n", + frameworkConfig.masterAesKeyTokenFilename); + rc = ERROR_CODE; + } + } + /* generate a master key */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Generating key token\n"); + rc = Key_Generate(masterAesKeyToken); + } + /* write the AES key token to a file */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Writing key token to %s\n", + frameworkConfig.masterAesKeyTokenFilename); + rc = File_WriteBinaryFile(masterAesKeyToken, + sizeof(masterAesKeyToken), + frameworkConfig.masterAesKeyTokenFilename); + } + /* validate that the master AES key token file can be read */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Reading back key token\n"); + rc = File_ReadBinaryFile(&masterAesKeyTokenOut, /* freed @5 */ + &masterAesKeyTokenLengthOut, + CCA_KEY_IDENTIFIER_LENGTH, + frameworkConfig.masterAesKeyTokenFilename); + } + /* sanity check the length */ + if (rc == 0) { + if (masterAesKeyTokenLengthOut != sizeof(masterAesKeyToken)) { + fprintf(messageFile, "Error reading %s - length mismatch\n", + frameworkConfig.masterAesKeyTokenFilename); + rc = ERROR_CODE; + } + } + /* sanity check the contents */ + if (rc == 0) { + rc = memcmp(masterAesKeyToken, masterAesKeyTokenOut, masterAesKeyTokenLengthOut); + if (rc != 0) { + fprintf(messageFile, "Error reading %s - data mismatch\n", + frameworkConfig.masterAesKeyTokenFilename); + rc = ERROR_CODE; + } + } + /* validate that the master AES key token can be used */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Using key token\n"); + rc = Password_KDF(eku, /* user encryption key */ + aku, /* user authentication HMAC key */ + frameworkConfig.frameworkAdmins[0], /* dummy sender */ + masterAesKeyToken); + } + /* cleanup */ + FrameworkConfig_Delete(&frameworkConfig); /* @1 */ + free(masterAesKeyTokenOut); /* @5 */ + /* erase the secret keys before exit */ + memset(eku, 0, AES128_SIZE); + memset(aku, 0, AKU_SIZE); + fprintf(messageFile, "\nframeworkkey_generate rc %d\n\n", rc); + return rc; +} + +/* GetArgs() gets the command line arguments + + Returns ERROR_CODE on error. +*/ + +int GetArgs(int argc, + char **argv) +{ + int rc = 0; + int i; + + /* command line argument defaults */ + verbose = FALSE; + + /* get the command line arguments */ + for (i=1 ; (i<argc) && (rc == 0) ; i++) { + if (strcmp(argv[i],"-h") == 0) { + PrintUsage(); + rc = ERROR_CODE; + } + else if (strcmp(argv[i],"-v") == 0) { + verbose = TRUE; + } + else { + printf("frameworkkey_generate: Error, %s is not a valid option\n", argv[i]); + PrintUsage(); + rc = ERROR_CODE; + } + } + return rc; +} + +void PrintUsage() +{ + printf("\n"); + printf("frameworkkey_generate:\n" + "\t[-v - verbose tracing]\n" + "\t[-h - print usage help]\n"); + printf("\n"); + printf("\n"); + return; +} 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; +} diff --git a/src/signframework/getpubkey.cfg.sample b/src/signframework/getpubkey.cfg.sample new file mode 100644 index 0000000..868552a --- /dev/null +++ b/src/signframework/getpubkey.cfg.sample @@ -0,0 +1,37 @@ +# getpubkey project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=getpubkey + +# File name for the project audit log + +log=logs/getpubkey.log + +# project signing key file name + +needkey=false + +# auxiliary project configuration file + +needauxcfg=false + +neediatt=false +needoatt=true +needpwd=false + +# Project administrator email address + +email=framework@email.com + + +# Notification receivers + +notifs=1 +framework@email.com + +# Authorized senders (signers) (userid=email) + +needsenders=false diff --git a/src/signframework/getpubkey.log b/src/signframework/getpubkey.log new file mode 100644 index 0000000..3a61a7e --- /dev/null +++ b/src/signframework/getpubkey.log @@ -0,0 +1,25 @@ + +Wed Jul 12 14:55:59 2017 + Sender: cengel2 + Project: getpubkey + Program: getpubkey + Signing Project: testproject + Signing Key file: testprojectkey.tok + Public Key length 256 + a7 5c 56 ee c1 74 e3 ad 0f d0 74 13 11 a2 3f ed + 63 e0 b8 6a d5 79 50 3f 08 3b f6 76 7a d2 a5 65 + ea 99 b1 e7 82 0d 58 e5 61 3d 76 34 a7 ac f2 a0 + 40 ab ae d3 ec a4 f2 59 9d 22 f6 19 10 59 ed a5 + d4 00 7a d4 2a 3d ae 57 31 ba 7e 79 d6 82 ff ee + ba 11 8f 77 27 6a c2 41 6d 9e 03 9b 9a 20 9f 3d + 46 e1 dc 92 a4 95 ff 31 23 f2 b4 36 a6 4d 48 b3 + c3 4d 3d b7 52 e6 27 65 3b 04 fc 82 48 b3 55 22 + 27 ee ae 13 6d 63 4c 85 3d 9c 58 6e 31 a1 37 42 + 49 d9 84 3c 5b 82 0a fd 43 33 62 0b ae 3a 56 f6 + 00 11 77 c2 f6 90 67 90 86 67 5d 7b 11 c7 e3 71 + 69 94 c2 3d 06 74 28 ca b9 bd 71 c2 e6 f1 74 38 + 80 f3 cd 9c 5b 32 18 c3 ee 89 3b 57 83 fd b0 86 + 3f b9 c8 22 53 a4 6b 85 82 06 ec cd a5 6c 3a ba + 4f ac c3 47 33 45 c0 a3 eb 2a 4d e9 0b 4a 63 13 + 90 57 be 73 3c 22 83 41 ad 89 0e 89 60 bc f9 a1 + Return code: 0 diff --git a/src/signframework/getpubkeyecc.c b/src/signframework/getpubkeyecc.c new file mode 100644 index 0000000..8e12d6c --- /dev/null +++ b/src/signframework/getpubkeyecc.c @@ -0,0 +1,438 @@ +/* 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> + +/* local */ +#include "framework_utils.h" +#include "cca_structures_ecc.h" +#include "utils.h" +#include "debug.h" + +/* global variables */ + +FILE *messageFile = NULL; /* needed for utilities */ +int verbose = FALSE; +int debug = FALSE; + +/* local prototypes */ + +int GetArgs(const char **outputBodyFilename, + const char **outputAttachmentFileName, + const char **projectLogFileName, + const char **sender, + const char **project, + const char **signproject, + 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 argument defaults */ + const char *outputBodyFilename = NULL; + const char *outputAttachmentFileName = NULL; + const char *projectLogFileName = NULL; /* project audit log */ + FILE *projectLogFile = NULL; /* closed @1 */ + const char *sender = NULL; + const char *project = NULL; + const char *signproject = NULL; + + unsigned char *keyToken = NULL; /* CCA key token, freed @2 */ + size_t keyTokenLength; + EccKeyTokenPublic eccKeyTokenPublic; /* CCA public key structure */ + + const char *frameworkConfigFileName = NULL; + FILE *frameworkConfigFile = NULL; /* freed @3 */ + char *projectConfigFilename = NULL; /* freed @4 */ + FILE *projectConfigFile = NULL; /* closed @5 */ + ProjectConfig projectConfig; + char *keyFileName = NULL; /* signing CCA key token file, freed @6 */ + + char lineBuffer[MAX_LINE_SIZE]; + + messageFile = stdout; + ProjectConfig_Init(&projectConfig); /* freed @7 */ + + /* command line argument defaults */ + verbose = FALSE; + debug = FALSE; + + /* get command line arguments */ + if (rc == 0) { + rc = GetArgs(&outputBodyFilename, + &outputAttachmentFileName, + &projectLogFileName, + &sender, + &project, + &signproject, + &verbose, + argc, argv); + } + /* project audit log */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Opening audit log %s\n", projectLogFileName); + projectLogFile = fopen(projectLogFileName, "a"); /* closed @1 */ + 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"); /* closed @3 */ + 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 @4 */ + 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"); /* closed @5 */ + 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 @6 */ + "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 @2 */ + } + 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 = getPKA96EccPublicKey(&eccKeyTokenPublic, /* output: CCA structure */ + keyTokenLength, + keyToken); /* input: PKA96 key token */ + } + /* write the public key binary to the output attachment */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Writing binary to output file %s\n", + outputAttachmentFileName); + rc = File_WriteBinaryFile(eccKeyTokenPublic.publicKey, eccKeyTokenPublic.qLen, + outputAttachmentFileName); + } + if (rc == 0) { + PrintAll(projectLogFile, "\tPublic Key", eccKeyTokenPublic.qLen, eccKeyTokenPublic.publicKey); + } + /* cleanup */ + File_Printf(projectLogFile, messageFile, "Return code: %u\n", rc); + /* cleanup */ + /* close the framework configuration file */ + if (frameworkConfigFile != NULL) { + fclose(frameworkConfigFile); /* @3 */ + } + free(projectConfigFilename); /* @4 */ + /* close the project configuration file */ + if (projectConfigFile != NULL) { + fclose(projectConfigFile); /* @5 */ + } + free(keyFileName); /* @6 */ + free(keyToken); /* @2 */ + ProjectConfig_Delete(&projectConfig); /* @7 */ + if (projectLogFile != NULL) { + fclose(projectLogFile); /* @1 */ + } + if (messageFile != stdout) { + fflush(messageFile); + fclose(messageFile); /* @8 */ + messageFile = stdout; + } + return rc; +} + +int GetArgs(const char **outputBodyFilename, + const char **outputAttachmentFileName, + const char **projectLogFileName, + const char **sender, + const char **project, + const char **signproject, + 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; + } + } + /* 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, + "\tgetpubkeyecc usage:\n" + "\n" + "Common arguments:\n" + "\n" + "\t-signproject - signing project name\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 getpubkeyecc -signproject athena\n" + "\n" + "Extracts the public key from the CCA ECC key token in the key file\n" + "The resulting public key is returned as binary\n" + ); + fprintf(messageFile, "\n"); + return; +} diff --git a/src/signframework/getpubkeyecc.cfg.sample b/src/signframework/getpubkeyecc.cfg.sample new file mode 100644 index 0000000..c477323 --- /dev/null +++ b/src/signframework/getpubkeyecc.cfg.sample @@ -0,0 +1,36 @@ +# getpubkeyecc project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=getpubkeyecc + +# File name for the project audit log + +log=logs/getpubkeyecc.log + +# project signing key file name + +needkey=false + +# auxiliary project configuration file + +needauxcfg=false + +neediatt=false +needoatt=true +needpwd=false + +# Project administrator email address + +email=framework@email.com + +# Notification receivers + +notifs=1 +framework@email.com + +# Authorized senders (signers) (userid=email) + +needsenders=false diff --git a/src/signframework/json_regression_test/json_test.c b/src/signframework/json_regression_test/json_test.c new file mode 100644 index 0000000..221d05b --- /dev/null +++ b/src/signframework/json_regression_test/json_test.c @@ -0,0 +1,750 @@ +/* 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 <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <unistd.h> +#include <limits.h> +#include <time.h> + +#include <json-c/json.h> + +struct sign_request +{ + char* comment; + char* epwd; + char* parameters; + char* payload; + char* project; + char* user; +}; + +struct sign_response +{ + char* output; + char* payload; + int retval; +}; + +struct test_case +{ + struct sign_request request; + struct sign_response response; + int expectedRc; + bool resultExpected; + char* name; +}; + +bool GetArgs(int argc, char **argv); +void PrintUsage(); +bool RunTestCase(struct test_case* test_case, char* dropbox, int* num_failures); +bool GetRequestStructFromJson(struct sign_request* request, const char* json_string); +bool GetResponseStructFromJson(struct sign_response* request, const char* json_string); +int FileSize(FILE* fp); + +char* dropbox_path = NULL; +char* valid_json = NULL; +bool verbose = false; +bool debug = false; +char* STR_OVERFLOW = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + +int main(int argc, char** argv) +{ + { + bool success = GetArgs(argc, argv); + if(!success) + { + return -1; + } + } + + struct test_case valid_case; + int failures = 0; + + { + bool success = true; + valid_case.expectedRc = 0; + valid_case.resultExpected = true; + valid_case.name = "(Valid) Rerun of provided request"; + FILE* fp = fopen(valid_json, "r"); + if(fp) + { + int len = FileSize(fp); + char* str = calloc(len+1, 1); + if(str) + { + fread(str, 1, len, fp); + success = GetRequestStructFromJson(&(valid_case.request), str); + if(success) + { + success = RunTestCase(&valid_case, dropbox_path, &failures); + } + else + { + printf("Error in parsing the json file\n"); + success = false; + } + } + else + { + printf("Error in memory allocation\n"); + success = false; + } + fclose(fp); + } + else + { + printf("Unable to open file %s\n", valid_json); + success = false; + } + if(!success) + { + printf("Provided \"good\" json failed. Aborting testing.\n"); + return -1; + } + } + + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.comment = STR_OVERFLOW; + test.request.epwd = STR_OVERFLOW; + test.request.parameters = STR_OVERFLOW; + test.request.payload = STR_OVERFLOW; + test.request.project = STR_OVERFLOW; + test.request.user = STR_OVERFLOW; + test.name = "Dropbox File Overflow"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.user = STR_OVERFLOW; + test.name = "User Overflow"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.parameters = STR_OVERFLOW; + test.name = "Parameters Overflow"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.epwd = STR_OVERFLOW; + test.name = "EPWD Overflow"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.comment = STR_OVERFLOW; + test.name = "Comment Overflow"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.project = STR_OVERFLOW; + test.name = "Project Overflow"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.payload = NULL; + test.name = "Null payload"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.project = NULL; + test.name = "Null project"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 0; + test.resultExpected = true; + test.request.parameters = "garbage"; + test.name = "Garbage parameters"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.comment = NULL; + test.name = "Null Comment"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.comment = NULL; + test.request.epwd = NULL; + test.request.parameters = NULL; + test.request.payload = NULL; + test.request.project = NULL; + test.request.user = NULL; + test.name = "Empty JSON"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 2; + test.resultExpected = false; + test.request.comment = ""; + test.request.epwd = ""; + test.request.parameters = ""; + test.request.payload = ""; + test.request.project = ""; + test.request.user = ""; + test.name = "Empty Values"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.epwd = "bad"; + test.name = "Invalid EPWD (3 nibbles)"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.epwd = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + test.name = "Invalid EPWD (correct length)"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 0; + test.resultExpected = true; + test.request.comment = ""; + test.name = "Empty Comment"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.epwd = ""; + test.name = "Empty epwd"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 0; + test.resultExpected = true; + test.request.parameters = ""; + test.name = "Empty parameters"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.payload = ""; + test.name = "Empty payload"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 2; + test.resultExpected = false; + test.request.project = ""; + test.name = "Empty project"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.resultExpected = true; + test.request.user = ""; + test.name = "Empty user"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.resultExpected = false; + test.expectedRc = 1; + test.request.epwd = NULL; + test.name = "Null epwd"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.resultExpected = false; + test.expectedRc = 1; + test.request.parameters = NULL; + test.name = "Null parameters"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 1; + test.resultExpected = false; + test.request.user = NULL; + test.name = "Null user"; + RunTestCase(&test, dropbox_path, &failures); + } + { + struct test_case test = valid_case; + test.expectedRc = 2; + test.resultExpected = false; + test.request.project = "Garbage"; + test.name = "Garbage project"; + RunTestCase(&test, dropbox_path, &failures); + } + + printf("Number of failures: %d\n", failures); + + return failures; +} + +bool GetArgs(int argc, char **argv) +{ + bool success = true; + for(int i = 1; (i < argc) && success; i++) + { + if (strcmp(argv[i],"-dropbox") == 0) { + i++; + dropbox_path = argv[i]; + } + else if (strcmp(argv[i],"-json") == 0) { + i++; + valid_json = argv[i]; + } + else if (strcmp(argv[i],"-v") == 0) { + verbose = true; + } + else if (strcmp(argv[i],"-d") == 0) { + debug = true; + } + else + { + PrintUsage(); + success = false; + } + } + // Check for required arguments + if(!dropbox_path || !valid_json) + { + printf("Not all required argments found\n"); + success = false; + } + return success; +} + +void PrintUsage() +{ + printf("\t-json 'path to good json'\n" + "\t -dropbox 'path to run in'\n"); + return; +} + +bool AddStringToJson(struct json_object* json, const char* key, const char* value) +{ + bool success = false; + if(json && key && value) + { + struct json_object* obj = json_object_new_string(value); + if(obj) + { + json_object_object_add(json, key, obj); + success = true; + } + else + { + printf("ERROR in object creation\n"); + } + } + else + { + printf(" error in addstirngtojson params\n"); + } + return success; +} + +// Will create a json from the struct. If a value is NULL, no entry will be made. +bool CreateJsonString(char** json_string, const struct sign_request request) +{ + bool success = true; + struct json_object* json = NULL; + + if(json_string) + { + } + else + { + printf("Error in parameters\n"); + success = false; + } + + json = json_object_new_object(); + if(json == NULL) + { + success = false; + } + + if(success && request.project) + { + success = AddStringToJson(json, "project", request.project); + } + if(success && request.parameters) + { + success = AddStringToJson(json, "parameters", request.parameters); + } + if(success && request.user) + { + success = AddStringToJson(json, "user", request.user); + } + if(success && request.epwd) + { + success = AddStringToJson(json, "epwd", request.epwd); + } + if(success && request.payload) + { + success = AddStringToJson(json, "payload", request.payload); + } + if(success && request.comment) + { + success = AddStringToJson(json, "comment", request.comment); + } + + if(success) + { + const char* str = json_object_to_json_string(json); + if(json_string) + { + *json_string = strdup(str); + } + else + { + printf("ERROR in convert\n"); + success = false; + } + } + + if(json) + { + json_object_put(json); + } + + return success; +} + +int FileSize(FILE* fp) +{ + int len = -1; + if(fp) + { + size_t pos = ftell(fp); + fseek(fp, 0, SEEK_END); + len = ftell(fp); + fseek(fp, pos, SEEK_SET); + } + return len; +} + +bool RunTestCase(struct test_case* test_case, char* dropbox, int* num_failures) +{ + char filename[NAME_MAX]; + char workingDir[PATH_MAX]; + char* json_string = NULL; + bool success = true; + time_t timestamp =time(NULL); + + // Change working directory to inside the dropbox + getcwd(workingDir, PATH_MAX); + chdir(dropbox); + + // generate json + success = CreateJsonString(&json_string, test_case->request); + + // create request file + if(success) + { + snprintf(filename, NAME_MAX, "test_%lx.request", timestamp); + FILE* request_file = fopen(filename, "w"); + if(request_file) + { + fwrite(json_string, 1, strlen(json_string), request_file); + fclose(request_file); + if (verbose) printf("===== REQUEST =====\n%s\n===== REQUEST =====\n", json_string); + } + else + { + printf("Unable to open .request file\n"); + success = false; + } + } + + // create request go file + if(success) + { + snprintf(filename, NAME_MAX, "test_%lx.request.go", timestamp); + FILE* request_go_file = fopen(filename, "w"); + if(request_go_file) + { + fclose(request_go_file); + } + else + { + printf("Unable to open .request.go file\n"); + success = false; + } + } + + // wait for response + if(success) + { + success = false; + for( int i = 0; i < 5; i++) + { + snprintf(filename, NAME_MAX, "test_%lx.response.go", timestamp); + FILE* response_go_file = fopen(filename, "r"); + if(response_go_file) + { + fclose(response_go_file); + success = true; + break; + } + else + { + if(errno == ENOENT) + { + sleep(5); + } + else + { + success = false; + break; + } + } + } + if(!success) + { + printf("Did not find .response.go file\n"); + } + } + + // parse response, populate struct + if(success) + { + snprintf(filename, NAME_MAX, "test_%lx.response", timestamp); + FILE* response_file = fopen(filename, "r"); + if(response_file) + { + int file_size = FileSize(response_file); + char* response_string = calloc(file_size + 1, 1); + fread(response_string, 1, file_size, response_file); + if (verbose) printf("===== RESPONSE =====\n%s\n===== RESPONSE =====\n", response_string); + success = GetResponseStructFromJson(&test_case->response, response_string); + if(!success) + { + printf("Unable to populate response struct\n"); + } + } + else + { + printf("Unable to open .response file\n"); + success = false; + } + } + // compare expected & actual rc, return success + if(success && test_case->response.retval != test_case->expectedRc) + { + printf("Unexpected RC (%d) from server. Expected %d\n", test_case->response.retval, test_case->expectedRc); + success = false; + } else if (success && test_case->resultExpected && test_case->response.payload == NULL) { + printf("Payload missing from server.\n"); + success = false; + } else if (success && !test_case->resultExpected && test_case->response.payload != NULL) { + printf("Unexpected payload from server.\n"); + success = false; + } + + if(success) + { + printf("%s: PASS\n", test_case->name); + } + else + { + printf("%s: FAIL\n", test_case->name); + (*num_failures)++; + exit(1); + } + + if (verbose) printf("\n\n"); + + // Restore the working directory + chdir(workingDir); + + return success; +} + +bool GetStringFromJson(char** retval, struct json_object* json, const char* key) +{ + bool success = false; + if(retval && json && key) + { + struct json_object* tmp = NULL; + json_object_object_get_ex(json, key, &tmp); + if(tmp) + { + const char* json_string = json_object_get_string(tmp); + if(json_string) + { + *retval = strdup(json_string); + success = true; + } + } + } + return success; +} + +bool GetIntFromJson(int* retval, struct json_object* json, const char* key) +{ + bool success = false; + if(retval && json && key) + { + struct json_object* tmp = NULL; + json_object_object_get_ex(json, key, &tmp); + if(tmp) + { + *retval = json_object_get_int(tmp); + success = true; + } + } + return success; +} + +bool GetRequestStructFromJson(struct sign_request* request, const char* json_string) +{ + bool success = true; + if(request && json_string) + { + struct json_object* json = json_tokener_parse(json_string); + if(json) + { + // parse each field + + char* project = NULL; + char* parameters = NULL; + char* user = NULL; + char* epwd = NULL; + char* payload = NULL; + char* comment = NULL; + success &= GetStringFromJson(&project, json, "project"); + success &= GetStringFromJson(¶meters, json, "parameters"); + success &= GetStringFromJson(&user, json, "user"); + success &= GetStringFromJson(&epwd, json, "epwd"); + success &= GetStringFromJson(&payload, json, "payload"); + success &= GetStringFromJson(&comment, json, "comment"); + + if(project && parameters && user && epwd && payload && comment) + { + request->comment = comment; + request->epwd = epwd; + request->parameters = parameters; + request->payload = payload; + request->project = project; + request->user = user; + } + else + { + success = false; + } + json_object_put(json); // Frees the json_object's memory + } + else + { + success = false; + } + } + else + { + success = false; + } + + return success; +} + +bool GetResponseStructFromJson(struct sign_response* response, const char* json_string) +{ + bool success = true; + if(response && json_string) + { + struct json_object* json = json_tokener_parse(json_string); + if(json) + { + // parse each field + char* out = NULL; + char* payload = NULL; + int retval; + success &= GetStringFromJson(&out, json, "stdout"); + bool has_payload = GetStringFromJson(&payload, json, "result"); + success &= GetIntFromJson(&retval, json, "retval"); + + if(has_payload) + { + response->payload = payload; + } + else + { + response->payload = NULL; + } + if(success) + { + response->output = out; + response->retval = retval; + } + else + { + printf("Could not extract values from parsed json\n"); + } + json_object_put(json); // Frees the json_object's memory + } + else + { + printf("GetResponseStructFromJson: Unable to parse string\n"); + success = false; + } + } + else + { + printf("GetResponseStructFromJson: Null arguments\n"); + success = false; + } + return success; +} diff --git a/src/signframework/json_regression_test/makefile b/src/signframework/json_regression_test/makefile new file mode 100644 index 0000000..bd859ec --- /dev/null +++ b/src/signframework/json_regression_test/makefile @@ -0,0 +1,34 @@ +# 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. + +CC=gcc +CFLAGS=-g -std=gnu99 -Wall -pedantic -I. +CLIB=-ljson-c +DEPS = +OBJ = json_test.o +CEXEC=json_test + +%.o: %.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +json_test: $(OBJ) + gcc -o $@ $^ $(CFLAGS) $(CLIB) + +default: json_test + +all: $(CEXEC) + +clean: + rm $(CEXEC) $(OBJ) diff --git a/src/signframework/keygen.c b/src/signframework/keygen.c new file mode 100644 index 0000000..2a158a6 --- /dev/null +++ b/src/signframework/keygen.c @@ -0,0 +1,289 @@ +/* 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 <time.h> + +/* local */ +#include "debug.h" +#include "utils.h" +#include "cca_functions.h" +#include "cca_structures.h" + +/* local prototypes */ + +long getArgs(const char **keyFilename, + const char **pubKeyFilename, + const char **userName, + const char **password, + unsigned int *bitSize, + int *encrypt, + int *verbose, + int argc, + char ** argv); +void printUsage(void); + +/* global variables */ +FILE *messageFile = NULL; +int verbose = FALSE; + +/* see printUsage() for the program description */ + +int main(int argc, char** argv) +{ + long rc = 0; /* general return code */ + time_t start_time; + int loggedIn = FALSE; /* Boolean */ + + messageFile = stdout; + + /* command line arguments */ + const char *keyFilename; /* destination file for the generated key pair */ + const char *pubKeyFilename; /* destination file for the generated public key */ + const char *userName; /* optional user name */ + const char *password; /* optional user password */ + unsigned int bitSize = 2048; /* default RSA key size */ + int encrypt = FALSE; /* default is signing key */ + + /* skeleton CCA key token, template parameters for the eventual generated key pair */ + long skeleton_key_length; + unsigned char skeleton_key[2500]; /* CCA key template, maximum length */ + + /* generated CCA key pair token */ + long generated_key_length; + unsigned char generated_key[2500]; /* CCA PKA96 key token stream, maximum length */ + + /* CCA public key token */ + RsaKeyTokenPublic rsaKeyTokenPublic; /* CCA public key structure */ + + setvbuf(stdout, 0, _IONBF, 0); /* output may be going through pipe */ + start_time = time(NULL); + printf("keygen: Key generation at %s", ctime(&start_time)); + + /* get command line arguments */ + if (rc == 0) { + rc = getArgs(&keyFilename, &pubKeyFilename, + &userName, &password, &bitSize, &encrypt, + &verbose, argc, argv); + } + /* + Log in + */ + if ((rc == 0) && (userName != NULL)) { + if (verbose) printf("keygen: logging in to coprocessor as %s\n", userName); + rc = Login_Control(TRUE, /* logIn */ + userName, password); + } + if ((rc == 0) && (userName != NULL)) { + loggedIn = TRUE; + } + /* build a skeleton key token */ + if (rc == 0) { + skeleton_key_length = sizeof(skeleton_key); + rc = PKA_Key_Token_Build(&skeleton_key_length, skeleton_key, bitSize, encrypt); + } + /* generate an RSA key pair using the skeleton key token */ + if (rc == 0) { + generated_key_length = sizeof(generated_key); + rc = PKA_Key_Generate(&generated_key_length, generated_key, + skeleton_key_length, skeleton_key); + } + /* store the key token in a file */ + if (rc == 0) { + rc = File_WriteBinaryFile(generated_key, generated_key_length, keyFilename); + } + /* extract the public key from the key token */ + if (rc == 0) { + if (verbose) printf("keygen: extract the public key from the key token\n"); + rc = getPKA96PublicKey(&rsaKeyTokenPublic, /* output: CCA structure */ + generated_key_length, + generated_key, + bitSize); /* input: PKA96 key token */ + } + /* verify the public key length */ + if (rc == 0) { + if (verbose) printf("keygen: public key length %u\n", rsaKeyTokenPublic.nByteLength); + if (rsaKeyTokenPublic.nByteLength != (bitSize/8)) { + printf("keygen: Error public key length invalid\n"); + rc = ERROR_CODE; + } + } + /* store the public key token in a file */ + if (rc == 0) { + rc = File_WriteBinaryFile(rsaKeyTokenPublic.n, rsaKeyTokenPublic.nByteLength, + pubKeyFilename); + } + /* since the program exits, this is probably not necessary */ + if (loggedIn) { + rc = Login_Control(FALSE, /* logIn */ + userName, password); + } + /* exit handling */ + if (rc == 0) { + printf("keygen: Success\n"); + return EXIT_SUCCESS; + } + else { + printf("keygen: Failure\n"); + return EXIT_FAILURE; + } +} + +/* getArgs() parses the command line arguments into the supplied parameters + + */ + +long getArgs(const char **keyFilename, + const char **pubKeyFilename, + const char **userName, + const char **password, + unsigned int *bitSize, + int *encrypt, + int *verbose, + int argc, + char **argv) +{ + long rc = 0; + int irc = 0; + int i; + char dummy; /* extra characters */ + + /* command line argument defaults */ + *keyFilename = NULL; + *pubKeyFilename = NULL; + *userName = NULL; + *password = NULL; + *verbose = FALSE; + + /* get the command line arguments */ + for (i=1 ; (i<argc) && (rc == 0) ; i++) { + if (strcmp(argv[i],"-k") == 0) { + i++; + if (i < argc) { + *keyFilename = argv[i]; + } + else { + printf("-k option (key file name) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-p") == 0) { + i++; + if (i < argc) { + *pubKeyFilename = argv[i]; + } + else { + printf("-p option (public key file name) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-u") == 0) { + i++; + if (i < argc) { + *userName = argv[i]; + } + else { + printf("-u (user name and password needs a user name\n"); + rc = ERROR_CODE; + } + i++; + if (i < argc) { + *password = argv[i]; + } + else { + printf("-u (user name and password needs a password\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-sz") == 0) { + i++; + irc = sscanf(argv[i], "%u%c", bitSize, &dummy); + if (irc != 1) { + printf("-sz illegal\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-enc") == 0) { + *encrypt = TRUE; + } + else if (strcmp(argv[i],"-h") == 0) { + printUsage(); + rc = ERROR_CODE; + } + else if (strcmp(argv[i],"-v") == 0) { + *verbose = TRUE; + } + else { + printf("\n%s is not a valid option\n",argv[i]); + printUsage(); + rc = ERROR_CODE; + } + } + /* verify command line arguments */ + if (rc == 0) { + if (*keyFilename == NULL) { + printf("-k option (key file name) must be specified\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*pubKeyFilename == NULL) { + printf("-p option (public key file name) must be specified\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*userName != NULL) { + if (strlen(*userName) > 8) { + printf("-u option user name must be <= 8 bytes\n"); + rc = ERROR_CODE; + } + } + } + return rc; +} + +void printUsage() +{ + printf("\n"); + printf("keygen -k key_token_file_name -p public_key_file_name\n" + "\t[-u user password]\n" + "\t[-v]\n"); + printf("\n"); + printf("Example: keygen -k key.tok -p pub.bin -u user password \n"); + printf("\n"); + printf("Generates an RSA 2048-bit signing key token using CCA, and stores the\n" + "resulting key pair token in the key token file. The public key modulus\n" + "n is stored in binary in the public key token file.\n" + "\n" + "If -u is specified, the program logs on to the coprocessor using the specified user\n" + "name and password. If -u is not specified, the DEFAULT profile is used, which will\n" + "fail in production\n"); + printf("\n"); + printf("\t-k key token file name\n"); + printf("\t-p public key modulus file name\n"); + printf("\t-u user name and password\n"); + printf("\t-sz bit size (default 2048)\n"); + printf("\t-enc (can be used as a encryption key, default signing only)\n"); + printf("\t-h help\n"); + printf("\t-v enable debug tracing\n"); + printf("\n"); + return; +} + diff --git a/src/signframework/keygeneccp521.c b/src/signframework/keygeneccp521.c new file mode 100644 index 0000000..e10607f --- /dev/null +++ b/src/signframework/keygeneccp521.c @@ -0,0 +1,266 @@ +/* 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 <time.h> + +/* local */ +#include "utils.h" +#include "cca_functions.h" +#include "cca_functions_ecc.h" +#include "cca_structures_ecc.h" + +/* local prototypes */ + +long getArgs(const char **keyFilename, + const char **pubKeyFilename, + const char **userName, + const char **password, + int *verbose, + int argc, + char ** argv); +void printUsage(void); + +/* global variables */ +FILE *messageFile = NULL; +int verbose = FALSE; + +/* see printUsage() for the program description */ + +int main(int argc, char** argv) +{ + long rc = 0; /* general return code */ + time_t start_time; + int loggedIn = FALSE; /* Boolean */ + + /* command line arguments */ + const char *keyFilename; /* destination file for the generated key pair */ + const char *pubKeyFilename; /* destination file for the generated public key */ + const char *userName; /* optional user name */ + const char *password; /* optional user password */ + + messageFile = stdout; + + /* skeleton CCA key token, template parameters for the eventual generated key pair */ + long skeleton_key_length; + unsigned char skeleton_key[3500]; /* CCA key template, maximum length */ + + /* generated CCA key pair token */ + long generated_key_length; + unsigned char generated_key[3500]; /* CCA ECC key token stream, maximum length */ + + EccKeyTokenPublic eccKeyTokenPublic; + + /* initialization */ + setvbuf(stdout, 0, _IONBF, 0); /* output may be going through pipe */ + start_time = time(NULL); + printf("keygeneccp521: Key generation at %s", ctime(&start_time)); + + /* get command line arguments */ + if (rc == 0) { + rc = getArgs(&keyFilename, &pubKeyFilename, + &userName, &password, + &verbose, argc, argv); + } + /* + Log in + */ + if ((rc == 0) && (userName != NULL)) { + if (verbose) printf("keygeneccp521: logging in to coprocessor as %s\n", userName); + rc = Login_Control(TRUE, /* logIn */ + userName, password); + } + if ((rc == 0) && (userName != NULL)) { + loggedIn = TRUE; + } + /* build a skeleton key token */ + if (rc == 0) { + skeleton_key_length = sizeof(skeleton_key); + rc = PKA_Key_Token_Build_ECCP521(&skeleton_key_length, skeleton_key); + } + /* generate an ECC key pair using the skeleton key token */ + if (rc == 0) { + generated_key_length = sizeof(generated_key); + rc = PKA_Key_Generate(&generated_key_length, generated_key, + skeleton_key_length, skeleton_key); + } + /* store the key token in a file */ + if (rc == 0) { + rc = File_WriteBinaryFile(generated_key, generated_key_length, keyFilename); + } + /* extract the public key from the key token */ + if (rc == 0) { + if (verbose) printf("keygeneccp521: extract the public key from the key token\n"); + rc = getPKA96EccPublicKey(&eccKeyTokenPublic, /* output: CCA structure */ + generated_key_length, + generated_key); /* input: PKA96 key token */ + } + /* verify the public key length */ + if (rc == 0) { + if (verbose) printf("keygeneccp521: public key length %u\n", eccKeyTokenPublic.pLength); + if (eccKeyTokenPublic.pLength != ECC_PRIME_521) { + printf("keygeneccp521: Error public key length invalid\n"); + rc = ERROR_CODE; + } + } + /* store the public key token in a file */ + if (rc == 0) { + rc = File_WriteBinaryFile(eccKeyTokenPublic.publicKey, eccKeyTokenPublic.qLen, + pubKeyFilename); + } + /* since the program exits, this is probably not necessary */ + if (loggedIn) { + rc = Login_Control(FALSE, /* logIn */ + userName, password); + } + /* exit handling */ + if (rc == 0) { + printf("keygeneccp521: Success\n"); + return EXIT_SUCCESS; + } + else { + printf("keygeneccp521: Failure\n"); + return EXIT_FAILURE; + } +} + +/* getArgs() parses the command line arguments into the supplied parameters + + */ + +long getArgs(const char **keyFilename, + const char **pubKeyFilename, + const char **userName, + const char **password, + int *verbose, + int argc, + char **argv) +{ + long rc = 0; + int i; + + /* command line argument defaults */ + *keyFilename = NULL; + *pubKeyFilename = NULL; + *userName = NULL; + *password = NULL; + *verbose = FALSE; + + /* get the command line arguments */ + for (i=1 ; (i<argc) && (rc == 0) ; i++) { + if (strcmp(argv[i],"-k") == 0) { + i++; + if (i < argc) { + *keyFilename = argv[i]; + } + else { + printf("-k option (key file name) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-p") == 0) { + i++; + if (i < argc) { + *pubKeyFilename = argv[i]; + } + else { + printf("-p option (public key file name) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-u") == 0) { + i++; + if (i < argc) { + *userName = argv[i]; + } + else { + printf("-u (user name and password needs a user name\n"); + rc = ERROR_CODE; + } + i++; + if (i < argc) { + *password = argv[i]; + } + else { + printf("-u (user name and password needs a password\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-h") == 0) { + printUsage(); + rc = ERROR_CODE; + } + else if (strcmp(argv[i],"-v") == 0) { + *verbose = TRUE; + } + else { + printf("\n%s is not a valid option\n",argv[i]); + printUsage(); + rc = ERROR_CODE; + } + } + /* verify command line arguments */ + if (rc == 0) { + if (*keyFilename == NULL) { + printf("-k option (key file name) must be specified\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*pubKeyFilename == NULL) { + printf("-p option (public key file name) must be specified\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*userName != NULL) { + if (strlen(*userName) > 8) { + printf("-u option user name must be <= 8 bytes\n"); + rc = ERROR_CODE; + } + } + } + return rc; +} + +void printUsage() +{ + printf("\n"); + printf("keygeneccp521 -k key_token_file_name -p public_key_file_name\n" + "\t[-u user password]\n" + "\t[-v] [-s]\n"); + printf("\n"); + printf("Example: keygeneccp521 -k key.tok -p pub.bin\n"); + printf("\n"); + printf("Generates an ECC P-521 signing key token using CCA, and stores the\n" + "resulting key pair token in the key token file. The public key\n" + "is stored in binary in the public key token file.\n" + "\n" + "If -u is specified, the program logs on to the coprocessor using the specified user\n" + "name and password. If -u is not specified, the DEFAULT profile is used\n"); + printf("\n"); + printf("\t-k key token file name\n"); + printf("\t-p public key modulus file name\n"); + printf("\t-u user name and password\n"); + printf("\t-h help\n"); + printf("\t-v enable debug tracing\n"); + printf("\n"); + return; +} + diff --git a/src/signframework/ldap_lookup.c b/src/signframework/ldap_lookup.c new file mode 100644 index 0000000..9b7cc83 --- /dev/null +++ b/src/signframework/ldap_lookup.c @@ -0,0 +1,177 @@ +/* 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 <stdbool.h> + +// Need to link to ldap libraries -lldap +#include <ldap.h> + +#include "ldap_lookup.h" +#include "framework_utils.h" + +#define FILTER_BUFFER_SIZE 128 +extern FILE* messageFile; + +// Returns the number of results, sets *employeeId to the serial number of the first result +int ldapLookupByEmail(FrameworkConfig* configParm, const char* email, char** employeeId, bool verbose) +{ + const int scope = LDAP_SCOPE_SUBTREE; + const char* rtnAttr = "uid"; + + char filter[FILTER_BUFFER_SIZE]; + LDAP* ldap = NULL; + LDAPMessage* result = NULL; + + int status = 0; + + if(!email) + { + status = -1; + } + + if(status == 0) + { + int len = snprintf(filter, FILTER_BUFFER_SIZE, "(mail=%s)", email); + if(len == -1 || len > FILTER_BUFFER_SIZE) + { + status = -1; + } + } + + if(status == 0) + { + if(verbose) fprintf(messageFile, "Ldap will connect to %s\n", configParm->ldapUrl); + ldap = NULL; + int rc = ldap_initialize(&ldap, configParm->ldapUrl); + if(rc != 0) + { + if(verbose) fprintf(messageFile, "ERROR: ldap_init failed\n"); + status = -1; + } + } + + if(status == 0) + { + int version = LDAP_VERSION3; + + if(verbose) fprintf(messageFile, "Setting ldap to version 3\n"); + + int rc = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version); + if(rc != LDAP_OPT_SUCCESS) + { + if(verbose) fprintf(messageFile, "ERROR: unable to set ldap to version 3\n"); + status = -1; + } + } + + if(status == 0) + { + // Anonomous authentication + const char* who = NULL; + const char* cred = NULL; + if(verbose) fprintf(messageFile, "Attempting to bind:\n\twho: %s\n\tcredentials: %s\n", who, cred); + int rc = ldap_sasl_bind_s(ldap, NULL, NULL, NULL, NULL, NULL, NULL); + + + if(rc == -1) + { + if(verbose) fprintf(messageFile, "ERROR: unable to bind\n"); + status = -1; + } + } + + if(status == 0) + { + char** attrs = NULL; + int attrsonly = 0; + LDAPControl** serverctrls = NULL; + LDAPControl** clientctrls = NULL; + struct timeval* timeout = NULL; + int sizelimit = 0; + int rc = ldap_search_ext_s(ldap, configParm->ldapBase, scope, filter, + attrs, attrsonly, serverctrls, clientctrls, timeout, sizelimit, &result); + if(verbose) + { + fprintf(messageFile, "Attempting Ldap Search:\n" + "\tBase: \t\t %s\n" + "\tScope: \t\t %d\n" + "\tFilter: \t %s\n", configParm->ldapBase, scope, filter); + } + if(rc != 0) + { + if(verbose) fprintf(messageFile, "ERROR: ldap search failed %s\n", ldap_err2string(rc)); + status = -1; + } + } + + if(status == 0) + { + status = ldap_count_entries( ldap, result); + } + + if(employeeId) + { + if(status > 0) + { + struct berval** values = ldap_get_values_len(ldap, result, rtnAttr); + + if(values && values[0]) + { + *employeeId = strndup(values[0]->bv_val, values[0]->bv_len); + } + else + { + if(verbose) fprintf(messageFile, "ERROR: unable to get serial number\n"); + status = -1; + } + + ldap_value_free_len(values); + } + } + + if(result) + { + ldap_msgfree(result); + } + if(ldap) + { + ldap_unbind_ext_s(ldap, NULL, NULL); + } + return status; +} + +// Testing code +//int main() +//{ +// { +// char* id = NULL; +// int nEntries = ldapLookupByEmail("mtvaught@us.ibm.com", &id, true); +// printf("Good:\n\t%d entries found.\n\tID: %s\n", nEntries, id); +// free(id); +// } +// { +// int nEntries = ldapLookupByEmail("mtvaught@us.ibm.com", NULL, true); +// printf("Good:\n\t%d entries found.\n", nEntries); +// } +// { +// char* id = NULL; +// int nEntries = ldapLookupByEmail("invalidemail", &id, true); +// printf("Bad:\n\t%d entries found.\n\tID: %s\n", nEntries, id); +// } +// return 0; +//} diff --git a/src/signframework/ldap_lookup.h b/src/signframework/ldap_lookup.h new file mode 100644 index 0000000..5fa7aff --- /dev/null +++ b/src/signframework/ldap_lookup.h @@ -0,0 +1,27 @@ +/* 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. + */ + +#ifndef LDAP_LOOKUP_H +#define LDAP_LOOKUP_H + +#include <stdbool.h> +#include "framework_utils.h" + +// Returns number of LDAP entries matching the specified email +int ldapLookupByEmail(FrameworkConfig* configParm, + const char* email, char** employeeId, bool verbose); + +#endif diff --git a/src/signframework/logs/.empty b/src/signframework/logs/.empty new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/signframework/logs/.empty diff --git a/src/signframework/mail.c b/src/signframework/mail.c new file mode 100644 index 0000000..b1fb755 --- /dev/null +++ b/src/signframework/mail.c @@ -0,0 +1,122 @@ +/* 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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "mail.h" + +extern FILE* messageFile; +extern int verbose; + +int SendMailFile(FrameworkConfig* configParm, const char* sendto, const char* subject, const char* mailbodyfile) +{ + int rc = 0; + char* text = NULL; + size_t len = 0; + + rc = File_ReadTextFile(&text, &len, configParm->fileMax, mailbodyfile); + + if (rc == 0) { + rc = SendMail(configParm, sendto, subject, text); + } else { + rc = SendMail(configParm, sendto, subject, "INVALID BODY FILE"); + } + free(text); + return rc; +} + +int SendMail(FrameworkConfig* configParm, const char* sendto, const char* subject, const char* mailbody) +{ + int rc = 0; + char* cmd = NULL; + FILE* ofile = NULL; + + if (verbose) + fprintf(messageFile, "Sending email to %s : %s\n", sendto, subject); + + remove(configParm->emailFilename); + + ofile = fopen(configParm->emailFilename, "w"); + if (ofile != NULL) { + fprintf(ofile, "Subject: %s: %s\n", configParm->frameworkName, subject); + fprintf(ofile, "\n%s\n", mailbody); + fclose(ofile); + } else { + fprintf(messageFile, "Unable to open email body file : %s\n", configParm->emailFilename); + rc = 1; + } + + if (rc == 0) { + rc = Malloc_Safe((unsigned char**)&cmd, + strlen(configParm->emailFilename)+ strlen(sendto) + + strlen("cat | sendmail ") + 5, + (size_t)configParm->lineMax); + } + if (rc == 0) { + sprintf(cmd, "cat %s | sendmail %s", configParm->emailFilename, sendto); + } + + if (rc == 0) { + rc = system(cmd); + } + + remove(configParm->emailFilename); + free(cmd); + return rc; +} + + +int SendMailFileWithAttachment(FrameworkConfig* configParm, + const char* sendto, const char* subject, const char* mailbodyfile, + const char* attachmentfile) +{ + + int rc = 0; + char* cmd = NULL; + char* newSubject = NULL; + + if (rc == 0) { + rc = Malloc_Safe((unsigned char**)&newSubject, + strlen(configParm->frameworkName) + + strlen(subject) + 5, + (size_t)configParm->lineMax); + } + if (rc == 0) { + sprintf(newSubject, "%s: %s", configParm->frameworkName, subject); + } + if (rc == 0) { + rc = Malloc_Safe((unsigned char**)&cmd, + strlen(mailbodyfile)+ strlen(sendto) + + strlen(attachmentfile) + + strlen(newSubject) + + strlen("cat | mailx -s "" -a ") + 5, + (size_t)configParm->lineMax); + } + if (rc == 0) { + sprintf(cmd, "cat %s | mailx -s \"%s\" -a %s %s", mailbodyfile, newSubject, attachmentfile, sendto); + } + + if (rc == 0) { + rc = system(cmd); + } + + + free(cmd); + free(newSubject); + return rc; +} diff --git a/src/signframework/mail.h b/src/signframework/mail.h new file mode 100644 index 0000000..8612e9a --- /dev/null +++ b/src/signframework/mail.h @@ -0,0 +1,32 @@ +/* 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. + */ + +#ifndef MAIL_H +#define MAIL_H + +#include "framework_utils.h" + +int SendMailFile(FrameworkConfig* configParm, + const char* sendto, const char* subject, const char* mailbodyfile); + +int SendMail(FrameworkConfig* configParm, + const char* sendto, const char* subject, const char* mailbody); + +int SendMailFileWithAttachment(FrameworkConfig* configParm, + const char* sendto, const char* subject, const char* mailbodyfile, + const char* attachmentfile); + +#endif diff --git a/src/signframework/makefile b/src/signframework/makefile new file mode 100644 index 0000000..aab1308 --- /dev/null +++ b/src/signframework/makefile @@ -0,0 +1,159 @@ +# 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. + +ARCH = $(shell uname) + +# HSM tools install path +HSMPATH = /opt/ibm/4767 + +CC = /usr/bin/gcc +CCFLAGS = -Wall -W -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -c -ggdb -D$(ARCH) -DUNIX \ + -I$(HSMPATH)/include -DADD_ECC +CCFLAGS += $(EXTRA_CCFLAGS) +# -DDEBUG + + +# for all programs +LNFLAGS = -ggdb -lcrypto -lcsulcca -ljson-c +#-lcsulsapi -lcsulsecy -lds30 -lcsulcall + +all: framework framework_test frameworkkey_generate keygen password_generate password_change \ + keygeneccp521 audit sender_validate audit_archive getpubkey getpubkeyecc setclock +#all: ccatest +# signature_verify \ +# audit_archive t + +OBJECTS = framework.o utils.o framework_utils.o debug.o ossl_functions.o ossl_functions_ecc.o \ + mail.o dropbox_utils.o cca_functions.o cca_structures.o cca_structures_ecc.o \ + cca_functions_ecc.o framework_test.o frameworkkey_generate.o keygen.o \ + password_generate.o password_change.o audit.o keygeneccp521.o sender_validate.o \ + audit_archive.o getpubkey.o getpubkeyecc.o setclock.o + +HEADERS = \ + cca_functions.h \ + cca_structures.h \ + cca_structures_ecc.h \ + cca_functions_ecc.h \ + debug.h \ + framework_utils.h \ + ossl_functions.h \ + dropbox_utils.h \ + utils.h + + +LDAPOBJ = +# Use the following to include ldap lookups, comment out to remove +LNFLAGS += -lldap +LDAPOBJ = ldap_lookup.o +OBJECTS += ldap_lookup.o +HEADER += ldap_lookup.h +CCFLAGS += -DLDAP_VERIFY + +# the main program +framework: framework.o utils.o framework_utils.o debug.o ossl_functions.o \ + mail.o dropbox_utils.o cca_functions.o cca_structures.o cca_structures_ecc.o \ + cca_functions_ecc.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $^ -o framework + + +# regression test driver + +t: t.o \ + utils.o debug.o + $(CC) $(LNFLAGS) $(LNNFLAGS) t.o \ + utils.o debug.o \ + -o t + +# unit test program + +ccatest: ccatest.o \ + framework_utils.o cca_functions.o cca_structures.o ossl_functions.o \ + utils.o debug.o notesmail.o + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o ccatest + +# stub signer program + +framework_test: framework_test.o utils.o debug.o ossl_functions.o ossl_functions_ecc.o \ + cca_functions_ecc.o cca_structures_ecc.o cca_functions.o cca_structures.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $^ -o framework_test + +# auditistrator programs + +audit: audit.o utils.o + $(CC) $(LNFLAGS) $^ -o audit + +frameworkkey_generate: frameworkkey_generate.o \ + framework_utils.o cca_functions.o cca_structures.o ossl_functions.o \ + utils.o debug.o mail.o dropbox_utils.o cca_structures_ecc.o cca_functions_ecc.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o frameworkkey_generate + +password_generate: password_generate.o \ + framework_utils.o cca_functions.o cca_structures.o ossl_functions.o \ + utils.o debug.o mail.o dropbox_utils.o cca_structures_ecc.o cca_functions_ecc.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o password_generate + +password_change: password_change.o \ + framework_utils.o cca_functions.o cca_structures.o ossl_functions.o \ + utils.o debug.o mail.o dropbox_utils.o cca_structures_ecc.o cca_functions_ecc.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o password_change + +sender_validate: sender_validate.o \ + framework_utils.o cca_functions.o cca_structures.o ossl_functions.o \ + utils.o debug.o mail.o dropbox_utils.o cca_structures_ecc.o cca_functions_ecc.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o sender_validate + +getpubkey: getpubkey.o \ + framework_utils.o cca_structures.o cca_functions.o ossl_functions.o mail.o \ + utils.o debug.o dropbox_utils.o cca_structures_ecc.o cca_functions_ecc.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o getpubkey + +signature_verify: signature_verify.o \ + ossl_functions.o \ + utils.o debug.o + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o signature_verify + +keygen: keygen.o cca_functions.o cca_structures.o \ + ossl_functions.o utils.o debug.o + $(CC) $(LNFLAGS) $^ -o keygen + +setclock: setclock.o \ + cca_functions.o ossl_functions.o utils.o debug.o + $(CC) $(LNFLAGS) $^ -o setclock + +audit_archive: audit_archive.o \ + framework_utils.o cca_structures.o cca_functions.o ossl_functions.o mail.o \ + utils.o debug.o dropbox_utils.o cca_structures_ecc.o cca_functions_ecc.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o audit_archive + +getpubkeyecc: getpubkeyecc.o \ + framework_utils.o cca_structures_ecc.o cca_functions.o ossl_functions.o mail.o \ + utils.o debug.o dropbox_utils.o cca_functions_ecc.o cca_structures.o $(LDAPOBJ) + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o getpubkeyecc + +keygeneccp521: keygeneccp521.o \ + cca_functions.o cca_structures_ecc.o cca_functions_ecc.o \ + ossl_functions.o utils.o debug.o + $(CC) $(LNFLAGS) $(LNNFLAGS) $^ -o keygeneccp521 + +$(OBJECTS): %.o: %.c $(HEADERS) + $(CC) $(CCFLAGS) $< -o $@ + +clean: + rm -f *.o framework ccatest framework_test audit \ + frameworkkey_generate password_generate password_change \ + signature_verify keygen keygeneccp521 setclock sender_validate \ + getpubkeyecc getpubkey audit_archive + +# geteccpubkey keygeneccp521 diff --git a/src/signframework/ossl_functions.c b/src/signframework/ossl_functions.c new file mode 100644 index 0000000..e3e2f98 --- /dev/null +++ b/src/signframework/ossl_functions.c @@ -0,0 +1,890 @@ +/* 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 <string.h> + +#include <openssl/sha.h> +#include <openssl/aes.h> +#include <openssl/rsa.h> +#include <openssl/engine.h> + +#include "debug.h" +#include "utils.h" + +#define SHA1_SIZE 20 +#define SHA256_SIZE 32 +#define SHA512_SIZE 64 + +#include "ossl_functions.h" + +/* messages are traced here + + All messages, even error messages, are traced only if verbose is set. There messages are + 'techie' and should not be returned unless the user asks for them. +*/ + +extern FILE* messageFile; +extern int verbose; + +/* AES requires data lengths that are a multiple of the block size */ +#define AES_BITS 128 +/* The AES block size is always 16 bytes */ +#define AES_BLOCK_SIZE 16 + +/* + SHA-1 functions +*/ + +/* Ossl_SHA1() can be called directly to hash a list of streams. + + The ... arguments to be hashed are a list of the form + size_t length, unsigned char *buffer + terminated by a 0 length +*/ + +void Ossl_SHA1(unsigned char *md, ...) +{ + va_list ap; + + va_start(ap, md); + Ossl_SHA1_valist(md, 0, NULL, ap); + va_end(ap); + return; +} + +/* Ossl_SHA1_valist() is the internal function, called with the va_list already created. + + It is called from Ossl_SHA1() to do a simple hash. Typically length0==0 and buffer0==NULL. + + It can also be called from the HMAC function to hash the variable number of input parameters. In + that case, the va_list for the text is already formed. length0 and buffer0 are used to input the + padded key. +*/ + +void Ossl_SHA1_valist(unsigned char *md, + size_t length0, unsigned char *buffer0, + va_list ap) +{ + uint32_t length; + unsigned char *buffer; + int done = FALSE; + SHA_CTX context; + + SHA1_Init(&context); + if (length0 !=0) { /* optional first text block */ + SHA1_Update(&context, buffer0, length0); /* hash the buffer */ + } + while (!done) { + length = va_arg(ap, size_t); /* first vararg is the length */ + if (length != 0) { /* loop until a zero length argument terminates */ + buffer = va_arg(ap, unsigned char *); /* second vararg is the array */ + SHA1_Update(&context, buffer, length); /* hash the buffer */ + } + else { + done = TRUE; + } + } + SHA1_Final(md, &context); + return; +} + +/* + SHA-256 functions +*/ + +/* Ossl_SHA256() can be called directly to hash a list of streams. + + The ... arguments to be hashed are a list of the form + size_t length, unsigned char *buffer + terminated by a 0 length +*/ + +void Ossl_SHA256(unsigned char *md, ...) +{ + va_list ap; + + va_start(ap, md); + Ossl_SHA256_valist(md, 0, NULL, ap); + va_end(ap); + return; +} + +/* Ossl_SHA256_valist() is the internal function, called with the va_list already created. + + It is called from Ossl_SHA256() to do a simple hash. Typically length0==0 and buffer0==NULL. + + It can also be called from the HMAC function to hash the variable number of input parameters. In + that case, the va_list for the text is already formed. length0 and buffer0 are used to input the + padded key. +*/ + +void Ossl_SHA256_valist(unsigned char *md, + size_t length0, unsigned char *buffer0, + va_list ap) +{ + uint32_t length; + unsigned char *buffer; + int done = FALSE; + SHA256_CTX context; + + SHA256_Init(&context); + if (length0 !=0) { /* optional first text block */ + SHA256_Update(&context, buffer0, length0); /* hash the buffer */ + } + while (!done) { + length = va_arg(ap, size_t); /* first vararg is the length */ + if (length != 0) { /* loop until a zero length argument terminates */ + buffer = va_arg(ap, unsigned char *); /* second vararg is the array */ + SHA256_Update(&context, buffer, length); /* hash the buffer */ + } + else { + done = TRUE; + } + } + SHA256_Final(md, &context); + return; +} + +/* + SHA-512 functions +*/ + +/* Ossl_SHA512() can be called directly to hash a list of streams. + + The ... arguments to be hashed are a list of the form + size_t length, unsigned char *buffer + terminated by a 0 length +*/ + +void Ossl_SHA512(unsigned char *md, ...) +{ + va_list ap; + + va_start(ap, md); + Ossl_SHA512_valist(md, 0, NULL, ap); + va_end(ap); + return; +} + +/* Ossl_SHA512_valist() is the internal function, called with the va_list already created. + + It is called from Ossl_SHA512() to do a simple hash. Typically length0==0 and buffer0==NULL. + + It can also be called from the HMAC function to hash the variable number of input parameters. In + that case, the va_list for the text is already formed. length0 and buffer0 are used to input the + padded key. +*/ + +void Ossl_SHA512_valist(unsigned char *md, + size_t length0, unsigned char *buffer0, + va_list ap) +{ + uint32_t length; + unsigned char *buffer; + int done = FALSE; + SHA512_CTX context; + + SHA512_Init(&context); + if (length0 !=0) { /* optional first text block */ + SHA512_Update(&context, buffer0, length0); /* hash the buffer */ + } + while (!done) { + length = va_arg(ap, size_t); /* first vararg is the length */ + if (length != 0) { /* loop until a zero length argument terminates */ + buffer = va_arg(ap, unsigned char *); /* second vararg is the array */ + SHA512_Update(&context, buffer, length); /* hash the buffer */ + } + else { + done = TRUE; + } + } + SHA512_Final(md, &context); + return; +} + +/* File_HashBinaryFile() reads filename, storing a SHA-256 hash in digest + */ + +int Ossl_HashBinaryFile(unsigned char *digest, + size_t length_max, + const char *filename) +{ + int rc = 0; + unsigned char *data = NULL; /* freed @1 */ + size_t length; + + + if (rc == 0) { + rc = File_ReadBinaryFile(&data, /* freed @1 */ + &length, + length_max, + filename); + } + if (rc == 0) { + Ossl_SHA256(digest, + length, data, + 0, NULL); + } + free(data); /* @1 */ + return rc; +} + +/* + HMAC functions based on SHA-256 +*/ + +/* Ossl_HMAC_Generate() can be called directly to HMAC a list of streams. + + The ... arguments are a message list of the form + size_t length, unsigned char *buffer + terminated by a 0 length +*/ + +void Ossl_HMAC_Generate(unsigned char *hmac, + const unsigned char *hmac_key, + ...) +{ + va_list ap; + + va_start(ap, hmac_key); + Ossl_HMAC_Generatevalist(hmac, hmac_key, ap); + va_end(ap); + return; +} + +/* Ossl_HMAC_Generatevalist() is the internal function, called with the va_list already created. + + It is called from Ossl_HMAC_Generate() and Ossl_HMAC_Check() with the va_list for the text + already formed. +*/ + +#define Ossl_HMAC_BLOCK_SIZE 64 +#define Ossl_HMAC_KEY_SIZE 32 +#define Ossl_SHA256_BLOCK_SIZE 32 + +void Ossl_HMAC_Generatevalist(unsigned char *hmac, + const unsigned char *hmac_key, + va_list ap) +{ + unsigned char ipad[Ossl_HMAC_BLOCK_SIZE]; + unsigned char opad[Ossl_HMAC_BLOCK_SIZE]; + size_t i; + unsigned char inner_hash[Ossl_SHA256_BLOCK_SIZE]; + + /* calculate key XOR ipad and key XOR opad */ + /* first part, key XOR pad */ + for (i = 0 ; i < Ossl_HMAC_KEY_SIZE ; i++) { + ipad[i] = hmac_key[i] ^ 0x36; /* magic numbers from RFC 2104 */ + opad[i] = hmac_key[i] ^ 0x5c; + } + /* second part, 0x00 XOR pad */ + memset(ipad + Ossl_HMAC_KEY_SIZE , 0x36, Ossl_HMAC_BLOCK_SIZE - Ossl_HMAC_KEY_SIZE); + memset(opad + Ossl_HMAC_KEY_SIZE , 0x5c, Ossl_HMAC_BLOCK_SIZE - Ossl_HMAC_KEY_SIZE); + /* calculate the inner hash, hash the key XOR ipad and the text */ + Ossl_SHA256_valist(inner_hash, + Ossl_HMAC_BLOCK_SIZE, ipad, ap); + /* hash the key XOR opad and the previous hash */ + Ossl_SHA256(hmac, + Ossl_HMAC_BLOCK_SIZE, opad, + Ossl_SHA256_BLOCK_SIZE, inner_hash, + 0, NULL); + return; +} + +/* Ossl_HMAC_Check() can be called directly to check the HMAC of a list of streams. + + The ... arguments are a list of the form + size_t length, unsigned char *buffer + terminated by a 0 length +*/ + +void Ossl_HMAC_Check(int *valid, + unsigned char *expect, + const unsigned char *hmac_key, + ...) +{ + int result; + va_list ap; + unsigned char actual[Ossl_SHA256_BLOCK_SIZE]; + + va_start(ap, hmac_key); + Ossl_HMAC_Generatevalist(actual, hmac_key, ap); + result = memcmp(expect, actual, Ossl_SHA256_BLOCK_SIZE); + if (result == 0) { + *valid = TRUE; + } + else { + *valid = FALSE; + } + va_end(ap); + return; +} + + +/* + AES Functions +*/ + +/* Ossl_AES_Encrypt() is AES non-portable code to encrypt 'decrypt_data' to 'encrypt_data' + + The stream is padded as per PKCS#7 / RFC2630 + + 'encrypt_data' must be free by the caller +*/ + +int Ossl_AES_Encrypt(unsigned char **encrypt_data, /* output, caller frees */ + size_t *encrypt_length, /* output */ + const unsigned char *decrypt_data, /* input */ + size_t decrypt_length, /* input */ + const unsigned char *initialization_vector, /* input */ + const unsigned char *aes_key) /* input */ +{ + int rc = 0; + size_t pad_length; + unsigned char *decrypt_data_pad = NULL; /* freed @1 */ + AES_KEY aes_enc_key; + unsigned char ivec[AES_BLOCK_SIZE]; /* initial chaining vector */ + + if (rc == 0) { + rc = AES_set_encrypt_key(aes_key, + AES_BITS, + &aes_enc_key); + if (rc != 0) { + rc = ERROR_CODE; + } + } + if (rc == 0) { + /* calculate the pad length and padded data length */ + pad_length = AES_BLOCK_SIZE - (decrypt_length % AES_BLOCK_SIZE); + *encrypt_length = decrypt_length + pad_length; + /* allocate memory for the encrypted response */ + rc = Malloc_Safe(encrypt_data, *encrypt_length, *encrypt_length); + } + /* allocate memory for the padded decrypted data */ + if (rc == 0) { + rc = Malloc_Safe(&decrypt_data_pad, *encrypt_length, *encrypt_length); + } + /* pad the decrypted clear text data */ + if (rc == 0) { + /* unpadded original data */ + memcpy(decrypt_data_pad, decrypt_data, decrypt_length); + /* last gets pad = pad length */ + memset(decrypt_data_pad + decrypt_length, pad_length, pad_length); + /* make a copy of the initialization vector */ + memcpy(ivec, initialization_vector, sizeof(ivec)); + /* encrypt the padded input to the output */ + AES_cbc_encrypt(decrypt_data_pad, + *encrypt_data, + *encrypt_length, + &(aes_enc_key), + ivec, + AES_ENCRYPT); + } + free(decrypt_data_pad); /* @1 */ + return rc; +} + +/* Ossl_AES_Decrypt() is AES non-portable code to decrypt 'encrypt_data' to + 'decrypt_data' + + The stream must be padded as per PKCS#7 / RFC2630 + + decrypt_data must be free by the caller +*/ + +int Ossl_AES_Decrypt(unsigned char **decrypt_data, /* output, caller frees */ + size_t *decrypt_length, /* output */ + const unsigned char *encrypt_data, /* input */ + size_t encrypt_length, /* input */ + const unsigned char *initialization_vector, /* input */ + const unsigned char *aes_key) /* input */ +{ + int rc = 0; + size_t pad_length; + size_t i; + unsigned char *pad_data; + AES_KEY aes_dec_key; + unsigned char ivec[AES_BLOCK_SIZE]; /* initial chaining vector */ + + if (rc == 0) { + rc = AES_set_decrypt_key(aes_key, + AES_BITS, + &aes_dec_key); + if (rc != 0) { + rc = ERROR_CODE; + } + } + /* sanity check encrypted length */ + if (rc == 0) { + if (encrypt_length < AES_BLOCK_SIZE) { + if (verbose) fprintf(messageFile, "Ossl_AES_Decrypt: Error, bad length\n"); + rc = ERROR_CODE; + } + } + /* allocate memory for the padded decrypted data */ + if (rc == 0) { + rc = Malloc_Safe(decrypt_data, encrypt_length, encrypt_length); + } + /* decrypt the input to the padded output */ + if (rc == 0) { + /* make a copy of the initialization vector */ + memcpy(ivec, initialization_vector, sizeof(ivec)); + /* decrypt the padded input to the output */ + AES_cbc_encrypt(encrypt_data, + *decrypt_data, + encrypt_length, + &(aes_dec_key), + ivec, + AES_DECRYPT); + } + /* get the pad length */ + if (rc == 0) { + /* get the pad length from the last byte */ + pad_length = (size_t)*(*decrypt_data + encrypt_length - 1); + /* sanity check the pad length */ + if ((pad_length == 0) || + (pad_length > AES_BLOCK_SIZE)) { + if (verbose) fprintf(messageFile, "Ossl_AES_Decrypt: Error, illegal pad length\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + /* get the unpadded length */ + *decrypt_length = encrypt_length - pad_length; + /* pad starting point */ + pad_data = *decrypt_data + *decrypt_length; + /* sanity check the pad */ + for (i = 0 ; i < pad_length ; i++, pad_data++) { + if (*pad_data != pad_length) { + if (verbose) fprintf(messageFile, + "Ossl_AES_Decrypt: Error, bad pad %02x at index %u\n", + *pad_data, (unsigned int)i); + rc = ERROR_CODE; + } + } + } + return rc; +} + +/* + RSA functions +*/ + +long osslBinToRSA(RSA **rsaPubKey, /* freed by caller */ + unsigned char *eArray, + unsigned long eLength, + unsigned char *nArray, + unsigned long nLength) +{ + long rc = 0; /* function return code */ + BIGNUM * n; /* n in BIGNUM format */ + BIGNUM * e; /* e in BIGNUM format */ + + *rsaPubKey = NULL; /* freed by caller */ + n = NULL; /* freed in RSA structure */ + e = NULL; /* freed in RSA structure */ + + /* construct the openSSL public key object from n and e */ + if (rc == 0) { + *rsaPubKey = RSA_new(); /* freed @1 */ + if (*rsaPubKey == NULL) { + fprintf(messageFile, "osslBinToRSA: Error in RSA_new()\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + /* convert nArray to BIGNUM */ + n = BN_bin2bn(nArray, nLength, n); + if (n == NULL) { + fprintf(messageFile, "osslBinToRSA: Error in BN_bin2bn\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + (*rsaPubKey)->n = n; /* store n in the RSA structure */ + /* convert eArray to BIGNUM */ + e = BN_bin2bn(eArray, eLength, e); + if (e == NULL) { + fprintf(messageFile, "osslBinToRSA: Error in BN_bin2bn\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + (*rsaPubKey)->e = e; /* store e in the RSA structure */ + } + return rc; +} + +/* osslVerify() verifies the digital 'signature' over 'digest' using the public key modulus + 'nArray' and exponent 'eArray'. 'digest' is the SHA-1 digest of the data. + + The modulus and exponent are pure binary streams, with no formatting envelope. +*/ + +long osslVerify(int *valid, /* output boolean */ + unsigned char *digest, + unsigned char *eArray, + unsigned long eLength, + unsigned char *nArray, + unsigned long nLength, + unsigned char *signature, + unsigned long signature_size) +{ + long rc = 0; /* function return code */ + RSA * rsaPubKey; /* public key in OpenSSL structure format */ + BIGNUM * n; /* n in BIGNUM format */ + BIGNUM * e; /* e in BIGNUM format */ + + rsaPubKey = NULL; /* freed @1 */ + n = NULL; /* freed in RSA structure */ + e = NULL; /* freed in RSA structure */ + + if (verbose) fprintf(messageFile, "osslVerify: Verifying using key parts\n"); + if (verbose) PrintAll(messageFile, "osslVerify: public key", nLength, nArray); + /* construct the openSSL public key object from n and e */ + if (rc == 0) { + rsaPubKey = RSA_new(); /* freed @1 */ + if (rsaPubKey == NULL) { + fprintf(messageFile, "osslVerify: Error in RSA_new()\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + /* convert nArray to BIGNUM */ + n = BN_bin2bn(nArray, nLength, n); + if (n == NULL) { + fprintf(messageFile, "osslVerify: Error in BN_bin2bn\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rsaPubKey->n = n; /* store n in the RSA structure */ + /* convert eArray to BIGNUM */ + e = BN_bin2bn(eArray, eLength, e); + if (e == NULL) { + fprintf(messageFile, "osslVerify: Error in BN_bin2bn\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rsaPubKey->e = e; /* store e in the RSA structure */ + } + if (rc == 0) { + rc = osslVerifyRSA(valid, /* output boolean */ + digest, /* input digest */ + rsaPubKey, /* OpenSSL RSA key token */ + signature, /* input signature */ + signature_size); + } + if (rsaPubKey != NULL) { + RSA_free(rsaPubKey); /* @1 */ + } + return rc; +} + +/* osslVerifyRSA() verifies the digital 'signature' over 'digest' using the OpenSSL RSA key token. + 'digest' is the SHA-1 digest of the data. +*/ + +long osslVerifyRSA(int *valid, /* output boolean */ + unsigned char *digest, + RSA *rsaPubKey, /* OpenSSL RSA key token */ + unsigned char *signature, + unsigned long signature_size) +{ + long rc = 0; /* function return code */ + int irc; /* OpenSSL return code */ + unsigned char rawDecrypt[signature_size]; /* for debug */ + + if (verbose) fprintf(messageFile, "osslVerifyRSA: Verifying using key token\n"); + if (verbose) PrintAll(messageFile, "osslVerifyRSA: digest", SHA1_SIZE, digest); + if (rc == 0) { + /* RSA_verify() returns 1 on successful verification, 0 otherwise. */ + *valid = RSA_verify(NID_sha1, + digest, SHA1_SIZE, + signature, signature_size, rsaPubKey); + if (verbose) fprintf(messageFile, "\tosslVerifyRSA: RSA_verify valid %d (should be 1)\n", + *valid); + } + /* + for debug, do a raw decrypt and print the result + + The result should be: + + PKCS#1 padding 00 01 FF ... FF 00 + SHA1 with RSA OID 15 bytes + SHA-1 hash 20 bytes + */ + if (rc == 0) { + /* int RSA_public_decrypt(int flen, unsigned char *from, + unsigned char *to, RSA *rsa, int padding); + */ + irc = RSA_public_decrypt(signature_size, signature, + rawDecrypt, + rsaPubKey, + RSA_NO_PADDING); + if (verbose) fprintf(messageFile, + "\tosslVerifyRSA: raw decrypt irc %d (should be key length)\n", irc); + if (irc == -1) { + fprintf(messageFile, "osslVerifyRSA: Error in RSA_public_decrypt\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, "osslVerifyRSA: Raw decrypt", irc, rawDecrypt); + } + return rc; +} + +/* osslVerify256() verifies the digital 'signature' over 'digest' using the public key modulus + 'nArray' and exponent 'eArray'. 'digest' the SHA-256 digest of the data. + + The modulus and exponent are pure binary streams, with no formatting envelope. +*/ + +long osslVerify256(int *valid, /* output boolean */ + unsigned char *digest, + unsigned char *eArray, + unsigned long eLength, + unsigned char *nArray, + unsigned long nLength, + unsigned char *signature, + unsigned long signature_size) +{ + long rc = 0; /* function return code */ + RSA * rsaPubKey; /* public key in OpenSSL structure format */ + BIGNUM * n; /* n in BIGNUM format */ + BIGNUM * e; /* e in BIGNUM format */ + + rsaPubKey = NULL; /* freed @1 */ + n = NULL; /* freed in RSA structure */ + e = NULL; /* freed in RSA structure */ + + if (verbose) fprintf(messageFile, "osslVerify256: Verifying using key parts\n"); + if (verbose) PrintAll(messageFile, "osslVerify256: public key", nLength, nArray); + /* construct the openSSL public key object from n and e */ + if (rc == 0) { + rsaPubKey = RSA_new(); /* freed @1 */ + if (rsaPubKey == NULL) { + fprintf(messageFile, "osslVerify256: Error in RSA_new()\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + /* convert nArray to BIGNUM */ + n = BN_bin2bn(nArray, nLength, n); + if (n == NULL) { + fprintf(messageFile, "osslVerify256: Error in BN_bin2bn\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rsaPubKey->n = n; /* store n in the RSA structure */ + /* convert eArray to BIGNUM */ + e = BN_bin2bn(eArray, eLength, e); + if (e == NULL) { + fprintf(messageFile, "osslVerify256: Error in BN_bin2bn\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rsaPubKey->e = e; /* store e in the RSA structure */ + } + if (rc == 0) { + rc = osslVerifyRSA256(valid, /* output boolean */ + digest, /* input digest */ + rsaPubKey, /* OpenSSL RSA key token */ + signature, /* input signature */ + signature_size); + } + if (rsaPubKey != NULL) { + RSA_free(rsaPubKey); /* @1 */ + } + return rc; +} + +/* osslVerifyRSA256() verifies the digital 'signature' over 'digest' using the OpenSSL RSA key + token. 'digest' is the SHA-256 digest of the data. */ + +long osslVerifyRSA256(int *valid, /* output boolean */ + unsigned char *digest, + RSA *rsaPubKey, /* OpenSSL RSA key token */ + unsigned char *signature, + unsigned long signature_size) +{ + long rc = 0; /* function return code */ + int irc; /* OpenSSL return code */ + unsigned char rawDecrypt[signature_size]; /* for debug */ + + if (verbose) fprintf(messageFile, "osslVerifyRSA256: Verifying using key token\n"); + if (verbose) PrintAll(messageFile, "osslVerifyRSA256: digest", SHA256_SIZE, digest); + if (rc == 0) { + /* RSA_verify() returns 1 on successful verification, 0 otherwise. */ + *valid = RSA_verify(NID_sha256, + digest, SHA256_SIZE, + signature, signature_size, rsaPubKey); + if (verbose) fprintf(messageFile, "\tosslVerifyRSA256: RSA_verify valid %d (should be 1)\n", + *valid); + } + /* + for debug, do a raw decrypt and print the result + + The result should be: + + PKCS#1 padding 00 01 FF ... FF 00 + SHA256 with RSA OID 19 bytes + SHA-256 hash 32 bytes + */ + if (rc == 0) { + /* int RSA_public_decrypt(int flen, unsigned char *from, + unsigned char *to, RSA *rsa, int padding); + */ + irc = RSA_public_decrypt(signature_size, signature, + rawDecrypt, + rsaPubKey, + RSA_NO_PADDING); + if (verbose) fprintf(messageFile, + "\tosslVerifyRSA256: raw decrypt irc %d (should be key length)\n", irc); + if (irc == -1) { + fprintf(messageFile, "tosslVerifyRSA256: Error in RSA_public_decrypt\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, "osslVerifyRSA256: Raw decrypt", irc, rawDecrypt); + } + return rc; +} + +/* osslVerify512() verifies the digital 'signature' over 'digest' using the public key modulus + 'nArray' and exponent 'eArray'. 'digest' the SHA-512 digest of the data. + + The modulus and exponent are pure binary streams, with no formatting envelope. +*/ + +long osslVerify512(int *valid, /* output boolean */ + unsigned char *digest, + unsigned char *eArray, + unsigned long eLength, + unsigned char *nArray, + unsigned long nLength, + unsigned char *signature, + unsigned long signature_size) +{ + long rc = 0; /* function return code */ + RSA * rsaPubKey; /* public key in OpenSSL structure format */ + BIGNUM * n; /* n in BIGNUM format */ + BIGNUM * e; /* e in BIGNUM format */ + + rsaPubKey = NULL; /* freed @1 */ + n = NULL; /* freed in RSA structure */ + e = NULL; /* freed in RSA structure */ + + if (verbose) fprintf(messageFile, "osslVerify512: Verifying using key parts\n"); + if (verbose) PrintAll(messageFile, "osslVerify512: public key", nLength, nArray); + /* construct the openSSL public key object from n and e */ + if (rc == 0) { + rsaPubKey = RSA_new(); /* freed @1 */ + if (rsaPubKey == NULL) { + fprintf(messageFile, "osslVerify512: Error in RSA_new()\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + /* convert nArray to BIGNUM */ + n = BN_bin2bn(nArray, nLength, n); + if (n == NULL) { + fprintf(messageFile, "osslVerify512: Error in BN_bin2bn\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rsaPubKey->n = n; /* store n in the RSA structure */ + /* convert eArray to BIGNUM */ + e = BN_bin2bn(eArray, eLength, e); + if (e == NULL) { + fprintf(messageFile, "osslVerify512: Error in BN_bin2bn\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rsaPubKey->e = e; /* store e in the RSA structure */ + } + if (rc == 0) { + rc = osslVerifyRSA512(valid, /* output boolean */ + digest, /* input digest */ + rsaPubKey, /* OpenSSL RSA key token */ + signature, /* input signature */ + signature_size); + } + if (rsaPubKey != NULL) { + RSA_free(rsaPubKey); /* @1 */ + } + return rc; +} + +/* osslVerifyRSA512() verifies the digital 'signature' over 'digest' using the OpenSSL RSA key + token. 'digest' is the SHA-512 digest of the data. */ + +long osslVerifyRSA512(int *valid, /* output boolean */ + unsigned char *digest, + RSA *rsaPubKey, /* OpenSSL RSA key token */ + unsigned char *signature, + unsigned long signature_size) +{ + long rc = 0; /* function return code */ + int irc; /* OpenSSL return code */ + unsigned char rawDecrypt[signature_size]; /* for debug */ + + if (verbose) fprintf(messageFile, "osslVerifyRSA512: Verifying using key token\n"); + if (verbose) PrintAll(messageFile, "osslVerifyRSA512: digest", SHA512_SIZE, digest); + if (rc == 0) { + /* RSA_verify() returns 1 on successful verification, 0 otherwise. */ + *valid = RSA_verify(NID_sha512, + digest, SHA512_SIZE, + signature, signature_size, rsaPubKey); + if (verbose) fprintf(messageFile, "\tosslVerifyRSA512: RSA_verify valid %d (should be 1)\n", + *valid); + } + /* + for debug, do a raw decrypt and print the result + + The result should be: + + PKCS#1 padding 00 01 FF ... FF 00 + SHA512 with RSA OID 19 bytes + SHA-512 hash 64 bytes + */ + if (rc == 0) { + /* int RSA_public_decrypt(int flen, unsigned char *from, + unsigned char *to, RSA *rsa, int padding); + */ + irc = RSA_public_decrypt(signature_size, signature, + rawDecrypt, + rsaPubKey, + RSA_NO_PADDING); + if (verbose) fprintf(messageFile, + "\tosslVerifyRSA512: raw decrypt irc %d (should be key length)\n", irc); + if (irc == -1) { + fprintf(messageFile, "tosslVerifyRSA512: Error in RSA_public_decrypt\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, "osslVerifyRSA512: Raw decrypt", irc, rawDecrypt); + } + return rc; +} diff --git a/src/signframework/ossl_functions.h b/src/signframework/ossl_functions.h new file mode 100644 index 0000000..725981e --- /dev/null +++ b/src/signframework/ossl_functions.h @@ -0,0 +1,114 @@ +/* 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. + */ + +#ifndef OSSL_FUNCTIONS_H +#define OSSL_FUNCTIONS_H + +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> + +#include <openssl/crypto.h> + +void Ossl_SHA1(unsigned char *md, ...); +void Ossl_SHA1_valist(unsigned char *md, + size_t length0, unsigned char *buffer0, + va_list ap); + +void Ossl_SHA256(unsigned char *md, ...); +void Ossl_SHA256_valist(unsigned char *md, + size_t length0, unsigned char *buffer0, + va_list ap); + +void Ossl_SHA512(unsigned char *md, ...); +void Ossl_SHA512_valist(unsigned char *md, + size_t length0, unsigned char *buffer0, + va_list ap); + +int Ossl_HashBinaryFile(unsigned char *digest, + size_t length_max, + const char *filename); + +void Ossl_HMAC_Generate(unsigned char *hmac, + const unsigned char *hmac_key, + ...); +void Ossl_HMAC_Generatevalist(unsigned char *hmac, + const unsigned char *hmac_key, + va_list ap); +void Ossl_HMAC_Check(int *valid, + unsigned char *expect, + const unsigned char *hmac_key, + ...); + +int Ossl_AES_Encrypt(unsigned char **encrypt_data, + size_t *encrypt_length, + const unsigned char *decrypt_data, + size_t decrypt_length, + const unsigned char *initialization_vector, + const unsigned char *aes_key); +int Ossl_AES_Decrypt(unsigned char **decrypt_data, + size_t *decrypt_length, + const unsigned char *encrypt_data, + size_t encrypt_length, + const unsigned char *initialization_vector, + const unsigned char *aes_key); + +long osslBinToRSA(RSA **rsaPubKey, + unsigned char *eArray, + unsigned long eLength, + unsigned char *nArray, + unsigned long nLength); + +long osslVerify(int *valid, + unsigned char *digest, + unsigned char *eArray, + unsigned long eLength, + unsigned char *nArray, + unsigned long nLength, + unsigned char *signature, + unsigned long signature_size); +long osslVerifyRSA(int *valid, + unsigned char *digest, + RSA *rsaPubKey, + unsigned char *signature, + unsigned long signature_size); +long osslVerify256(int *valid, + unsigned char *digest, + unsigned char *eArray, + unsigned long eLength, + unsigned char *nArray, + unsigned long nLength, + unsigned char *signature, + unsigned long signature_size); +long osslVerifyRSA256(int *valid, + unsigned char *digest, + RSA *rsaPubKey, + unsigned char *signature, + unsigned long signature_size); +long osslVerify512(int *valid, + unsigned char *digest, + unsigned char *eArray, + unsigned long eLength, + unsigned char *nArray, + unsigned long nLength, + unsigned char *signature, + unsigned long signature_size); +long osslVerifyRSA512(int *valid, + unsigned char *digest, + RSA *rsaPubKey, + unsigned char *signature, + unsigned long signature_size); +#endif diff --git a/src/signframework/ossl_functions_ecc.c b/src/signframework/ossl_functions_ecc.c new file mode 100644 index 0000000..58fb3ba --- /dev/null +++ b/src/signframework/ossl_functions_ecc.c @@ -0,0 +1,187 @@ +/* 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 <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <openssl/crypto.h> +#include <openssl/ecdsa.h> +#include <openssl/obj_mac.h> +#include <openssl/objects.h> + +#include "debug.h" +#include "utils.h" +#include "ossl_functions_ecc.h" + +/* messages are traced here + + All messages, even error messages, are traced only if verbose is set. There messages are + 'techie' and should not be returned unless the user asks for them. +*/ + +extern FILE* messageFile; +extern int verbose; + +long Ossl_VerifyECC(int *valid, /* output boolean */ + const unsigned char *digest, + size_t digestLength, + const unsigned char *publicKey, + size_t publicKeyLength, + const unsigned char *signature, + unsigned long signatureLength) +{ + long rc = 0; /* function return code */ + int irc; + EC_KEY *ecPubKey = NULL; /* freed @1 */ + + if (verbose) fprintf(messageFile, "Ossl_VerifyECC: Verifying using OpenSSL\n"); + if (verbose) PrintAll(messageFile, "Ossl_VerifyECC: public key", publicKeyLength, publicKey); + if (verbose) PrintAll(messageFile, "Ossl_VerifyECC: digest", digestLength, digest); + if (verbose) PrintAll(messageFile, "Ossl_VerifyECC: signature", signatureLength, signature); + /* create an EC public key token */ + if (rc == 0) { + rc = Ossl_SetPubKey_ECC(&ecPubKey, /* freed @1 */ + publicKey, + publicKeyLength); + } + /* verify signature */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Ossl_VerifyECC: signature length out %lu\n", + signatureLength); + irc = ECDSA_verify(0, /* type ignored */ + digest, /* digest to be verified*/ + digestLength, + signature, /* DER encoded signature */ + signatureLength, /* length of signature */ + ecPubKey); /* public key */ + if (irc == 0) { + if (verbose) fprintf(messageFile, + "Ossl_VerifyECC: ECDSA_verify failed\n"); + *valid = FALSE; + } + else { + if (verbose) fprintf(messageFile, + "Ossl_VerifyECC: ECDSA_verify success\n"); + *valid = TRUE; + } + } + /* cleanup */ + if (ecPubKey != NULL) { /* @1 */ + EC_KEY_free(ecPubKey); + } + return rc; +} + +long Ossl_SetPubKey_ECC(EC_KEY **ecPubKey, /* freed by caller */ + const unsigned char *publicKey, + size_t publicKeyLength) +{ + long rc = 0; + int irc; + int nid; + EC_GROUP *group = NULL; /* freed @1 */ + EC_POINT *ec_point = NULL; /* freed @2 */ + + if (verbose) fprintf(messageFile, "Ossl_SetPubKey_ECC: Creating public key token\n"); + /* create an EC_KEY */ + if (rc == 0) { + *ecPubKey = EC_KEY_new(); /* freed @5 */ + if (*ecPubKey == NULL) { + fprintf(messageFile, "Ossl_SetPubKey_ECC: unable to EC_KEY_new\n"); + rc = ERROR_CODE; + } + } + /* Creates a EC_GROUP object with a curve specified by a NID */ + if (rc == 0) { + nid = NID_secp521r1; /* P-521 */ + if (verbose) fprintf(messageFile, "Ossl_SetPubKey_ECC: nid %d %s\n", nid, OBJ_nid2sn(nid)); + group = EC_GROUP_new_by_curve_name(nid); /* freed @1 */ + if (group == NULL) { + fprintf(messageFile, "Ossl_SetPubKey_ECC: unable to EC_GROUP_new_by_curve_name\n"); + rc = ERROR_CODE; + } + } + /* create a new EC_POINT */ + if (rc == 0) { + ec_point = EC_POINT_new(group); /* freed @2 */ + if (ec_point == NULL) { + fprintf(messageFile, "Ossl_SetPubKey_ECC: unable to EC_POINT_new\n"); + rc = ERROR_CODE; + } + } + /* Sets the EC_GROUP P-521 of an EC_KEY object */ + if (rc == 0) { + irc = EC_KEY_set_group(*ecPubKey, group); + if (irc == 0) { + fprintf(messageFile, "Ossl_SetPubKey_ECC: unable to EC_KEY_set_group\n"); + rc = ERROR_CODE; + } + } + /* assign the public key to the EC_POINT */ + if (rc == 0) { + /** Decodes a EC_POINT from a octet string + * \param group underlying EC_GROUP object + * \param p EC_POINT object + * \param buf memory buffer with the encoded ec point + * \param len length of the encoded ec point + * \param ctx BN_CTX object (optional) + * \return 1 on success and 0 if an error occured + */ + irc = EC_POINT_oct2point(group, + ec_point, + publicKey, + publicKeyLength, + NULL); + if (irc == 0) { + fprintf(messageFile, "Ossl_SetPubKey_ECC: unable to EC_POINT_oct2point\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + /** Sets the public key of a EC_KEY object. + * \param key EC_KEY object + * \param pub EC_POINT object with the public key (note: the EC_KEY object + * will use an own copy of the EC_POINT object). + * \return 1 on success and 0 if an error occurred. + */ + irc = EC_KEY_set_public_key(*ecPubKey, ec_point); + if (irc == 0) { + fprintf(messageFile, "Ossl_SetPubKey_ECC: unable to EC_KEY_set_public_key\n"); + rc = ERROR_CODE; + } + } + /* sanity check */ + if (rc == 0) { + /** Verifies that a private and/or public key is valid. + * \param key the EC_KEY object + * \return 1 on success and 0 otherwise. + */ + irc = EC_KEY_check_key(*ecPubKey); + if (irc == 0) { + fprintf(messageFile, "Ossl_SetPubKey_ECC: unable to EC_KEY_check_key\n"); + rc = ERROR_CODE; + } + } + /* cleanup */ + if (group != NULL) { + EC_GROUP_free(group); /* @1 */ + } + if (ec_point != NULL) { + EC_POINT_free(ec_point); /* @2 */ + } + return rc; +} diff --git a/src/signframework/ossl_functions_ecc.h b/src/signframework/ossl_functions_ecc.h new file mode 100644 index 0000000..4538364 --- /dev/null +++ b/src/signframework/ossl_functions_ecc.h @@ -0,0 +1,37 @@ +/* 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. + */ + +#ifndef OSSL_FUNCTIONS_ECC_H +#define OSSL_FUNCTIONS_ECC_H + +#include <stdlib.h> + +#include <openssl/crypto.h> +#include <openssl/ecdsa.h> + +long Ossl_VerifyECC(int *valid, + const unsigned char *digest, + size_t digestLength, + const unsigned char *publicKey, + size_t publicKeyLength, + const unsigned char *signature, + unsigned long signatureLength); + +long Ossl_SetPubKey_ECC(EC_KEY **ecPubKey, + const unsigned char *publicKey, + size_t publicKeyLength); + +#endif diff --git a/src/signframework/password_change.c b/src/signframework/password_change.c new file mode 100644 index 0000000..a0df68d --- /dev/null +++ b/src/signframework/password_change.c @@ -0,0 +1,568 @@ +/* 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <time.h> + +#include "cca_functions.h" +#include "ossl_functions.h" +#include "framework_utils.h" +#include "mail.h" +#include "utils.h" +#include "debug.h" + +/* local prototypes */ + +int GetArgs(const char **outputBodyFilename, + const char **profileId, + const char **epassword, + const char **password, + const char **projectLogFileName, + const char **sender, + const char **project, + int argc, + char **argv); +void PrintUsage(void); + +/* global variables */ + +FILE *messageFile = NULL; +int verbose = TRUE; +int debug = FALSE; + +int main(int argc, char** argv) +{ + int rc = 0; + size_t i; + + /* program command line arguments */ + const char *outputBodyFilename = NULL; + const char *profileId = NULL; /* CCA profile ID for the sender */ + const char *epassword = NULL; /* old encrypted password */ + const char *password = NULL; /* old plaintext password */ + const char *projectLogFileName = NULL; /* program audit log */ + FILE *projectLogFile = NULL; + time_t log_time; + const char *sender = NULL; /* sender email address */ + const char *project = NULL; + + FrameworkConfig frameworkConfig; /* framework configuration file object */ + unsigned char eku[AES128_SIZE]; /* password encryption key */ + unsigned char aku[AKU_SIZE]; /* password authentication HMAC key */ + char passwordText[64 + 1]; /* new generated password plaintext */ + unsigned char initialization_vector[IV_SIZE]; /* needed for AES encryption */ + unsigned char *encryptedPassword = NULL; /* new, in binary, freed @3 */ + long encryptedPasswordLength; + unsigned char hmac[HMAC_SIZE]; /* new password authentication HMAC */ + char *passwordString = NULL; /* new hex ASCII combined IV, HMAC, + encrypted pwd *//* freed @4 */ + size_t passwordStringLength; + + messageFile = stdout; + FrameworkConfig_Init(&frameworkConfig); /* freed @1 */ + /* + get the command line arguments + */ + if (rc == 0) { + rc = GetArgs(&outputBodyFilename, + &profileId, + &epassword, + &password, + &projectLogFileName, + &sender, + &project, + argc, argv); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Sender: %s\n", sender); + if (verbose) fprintf(messageFile, "CCA Profile ID: %s\n", profileId); + } + /* project audit log */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Opening audit log %s\n", projectLogFileName); + projectLogFile = fopen(projectLogFileName, "a"); + if (projectLogFile == NULL) { + fprintf(messageFile, "ERROR1015: Cannot open audit log %s, %s\n", + projectLogFileName, strerror(errno)); + rc = ERROR_CODE; + } + } + /* update framework audit log, begin this entry */ + if (projectLogFile != NULL) { + if (verbose) fprintf(messageFile, "Updating audit log\n"); + log_time = time(NULL); + fprintf(projectLogFile, "\n%s", ctime(&log_time)); + fprintf(projectLogFile, "\tSender: %s\n", sender); + fprintf(projectLogFile, "\tProject: %s\n", project); + fprintf(projectLogFile, "\tProgram: %s\n", argv[0]); + fprintf(projectLogFile, "\tCCA Profile ID: %s\n", profileId); + fprintf(projectLogFile, "\tInput password:\n\t%s\n", epassword); + } + /* + get parameters from the framework configuration file + */ + if (rc == 0) { + rc = FrameworkConfig_Parse(TRUE, /* need master key */ + TRUE, /* validate */ + &frameworkConfig); + } + /* + generate the encryption and HMAC keys from the master AES key mixed with the sender email + address + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Deriving password HMAC and encryption keys\n"); + rc = Password_KDF(eku, /* user encryption key */ + aku, /* user authentication HMAC key */ + sender, /* authenticated email sender */ + frameworkConfig.masterAesKeyToken); + } + /* + generate a new strong password + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Generating a password\n"); + memset(passwordText, '\0', sizeof(passwordText)); /* NUL terminator */ + rc = Password_Generate(passwordText, sizeof(passwordText)); + } + +#if 0 /* For tracing. Must be removed from the final product. */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password %s\n", passwordText); + } +#endif + /* + encrypt, HMAC, and convert the new password + */ + /* generate a random IV for the new password */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Generating AES initialization vector\n"); + rc = Random_Number_Generate_Long(initialization_vector, IV_SIZE); + if (verbose) PrintAll(messageFile, + "IV", IV_SIZE, initialization_vector); + } + /* encrypt the new password */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Encrypt the Password\n"); + rc = Ossl_AES_Encrypt(&encryptedPassword, /* freed @3 */ + (size_t *)&encryptedPasswordLength, + (unsigned char *)passwordText, + sizeof(passwordText), + initialization_vector, + eku); /* derived encryption key */ + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "Encrypted password\n", encryptedPasswordLength, encryptedPassword); + } + /* HMAC the encrypted new password */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "HMAC the Password\n"); + Ossl_HMAC_Generate(hmac, /* HMAC result */ + aku, /* derived HMAC key */ + encryptedPasswordLength, + encryptedPassword, + 0, NULL); + if (verbose) PrintAll(messageFile, + "HMAC", HMAC_SIZE, hmac); + } + /* concatenate the IV, HMAC, and encrypted password, convert to hex ASCII */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Concatenate IV, HMAC, Encrypted Password and convert to string\n"); + rc = Password_ToString(&passwordString, /* freed @4 */ + &passwordStringLength, + initialization_vector, + hmac, + encryptedPassword, /* encrypted password */ + encryptedPasswordLength); /* password */ + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password String length %d\n", (int)passwordStringLength); + if (verbose) fprintf(messageFile, "Password String:\n%s\n", passwordString); + } + /* + sanity check - convert back and compare + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password encryption validation\n"); + + unsigned char initialization_vector_out[IV_SIZE]; + unsigned char hmac_out[HMAC_SIZE]; + unsigned char *ciphertext_out = NULL; /* freed @1 */ + long ciphertext_length_out; + long cleartext_length_out; + unsigned char *cleartext_out = NULL; /* freed @2 */ + int hmac_valid; + + /* convert the hex ASCII back to IV, HMAC, and encrypted password */ + if (rc == 0) { + rc = Password_FromString(initialization_vector_out, + hmac_out, + &ciphertext_out, /* freed @4 */ + (size_t *)&ciphertext_length_out, + passwordString, + passwordStringLength, + projectLogFile); + } + /* validate the converted IV */ + if (rc == 0) { + rc = memcmp(initialization_vector, initialization_vector_out, IV_SIZE); + if (rc != 0) { + fprintf(messageFile, "Error, IV conversion mismatch\n"); + rc = ERROR_CODE; + } + } + /* validate the converted HMAC */ + if (rc == 0) { + rc = memcmp(hmac, hmac_out, HMAC_SIZE); + if (rc != 0) { + fprintf(messageFile, "Error, HMAC conversion mismatch\n"); + rc = ERROR_CODE; + } + } + /* validate the converted encrypted password length */ + if (rc == 0) { + if (encryptedPasswordLength != ciphertext_length_out) { + fprintf(messageFile, "Error, Password ciphertext length mismatch\n"); + rc = ERROR_CODE; + } + } + /* validate the converted encrypted password */ + if (rc == 0) { + rc = memcmp(encryptedPassword, ciphertext_out, encryptedPasswordLength ); + if (rc != 0) { + fprintf(messageFile, "Error, Ciphertext conversion mismatch\n"); + rc = ERROR_CODE; + } + } + /* validate the HMAC */ + if (rc == 0) { + Ossl_HMAC_Check(&hmac_valid, + hmac, + aku, + encryptedPasswordLength, + encryptedPassword, + 0, NULL); + } + if (rc == 0) { + if (!hmac_valid) { + fprintf(messageFile, "Error, Password HMAC check failed\n\n"); + rc = ERROR_CODE; + } + } + /* decrypt the password */ + if (rc == 0) { + rc = Ossl_AES_Decrypt(&cleartext_out, /* freed @2 */ + (size_t *)&cleartext_length_out, + encryptedPassword, + encryptedPasswordLength, + initialization_vector, + eku); + } + /* validate the decryption */ + if (rc == 0) { + rc = memcmp((unsigned char *)passwordText, cleartext_out, cleartext_length_out); + if (rc != 0) { + fprintf(messageFile, "Error, Decrypt error\n"); + fprintf(messageFile, "Cleartext in %s\n", passwordText); + fprintf(messageFile, "Cleartext out %s\n", cleartext_out); + fprintf(messageFile, "Cleartext length out %lu\n", cleartext_length_out); + rc = ERROR_CODE; + } + } + free(ciphertext_out); /* @1 */ + if (cleartext_out != NULL) { + memset(cleartext_out, 0, cleartext_length_out); + } + free(cleartext_out); /* @2 */ + } + /* + Change the password in CCA on the coprocessor card + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Login with CCA Profile: %s\n", profileId); + rc = Login_Control(TRUE, + profileId, + password); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR1001, Cannot log in CCA Profile: %s\n", profileId); + } + } + /* send password change to card */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Change password\n"); + rc = Access_Control_Initialization(profileId, + frameworkConfig.passwordExpire, /* password + expiration */ + passwordText); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR1008, Cannot change password for CCA Profile: %s\n", profileId); + } + } + /* logout */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Logout: Profile: %s\n", profileId); + rc = Login_Control(FALSE, + profileId, + NULL); /* password */ + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR1001, Cannot log out CCA Profile: %s\n", profileId); + } + } + /* + sanity check - try to log in with the new password + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Login with CCA Profile: %s, and changed password\n", + profileId); + rc = Login_Control(TRUE, + profileId, + passwordText); + } + /* logout */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Logout: Profile: %s\n", profileId); + rc = Login_Control(FALSE, + profileId, + NULL); /* password */ + + } + /* update audit log */ + if (rc == 0) { + fprintf(projectLogFile, "\tOutput password:\n\t%s\n", passwordString); + } + /* construct the email response */ + if (rc == 0) { + fprintf(messageFile, "A password has been changed for: %s\n", sender); + fprintf(messageFile, "It is valid for %u months.\n", + frameworkConfig.passwordExpire); + fprintf(messageFile, "\n"); + fprintf(messageFile, "You must retain the following encrypted password to perform signing requests.\n"); + fprintf(messageFile, "This password should be placed in a file with permissions set appropriately\n"); + fprintf(messageFile, "to block access to it by anyone other than yourself.\n\n"); + fprintf(messageFile, "Encrypted Password : %s\n", passwordString); + fprintf(messageFile, "\n"); + fprintf(messageFile, "This password will grant you access to the signing server but you will also need\n"); + fprintf(messageFile, "access to a specific project.\n"); + fprintf(messageFile, "\n"); + fprintf(messageFile, "For project names, contact an administrator at:\n"); + for (i = 0 ; i < frameworkConfig.frameworkAdminCount ; i++) { + fprintf(messageFile, + "\t%s\n", frameworkConfig.frameworkAdmins[i]); + } + fprintf(messageFile, "\n"); + /* close the email response file */ + fclose(messageFile); + messageFile = NULL; + } + /* cleanup */ + FrameworkConfig_Delete(&frameworkConfig); /* @1 */ + free(encryptedPassword); /* @3 */ + free(passwordString); /* @4 */ + /* erase the secret keys before exit */ + memset(eku, 0, AES128_SIZE); + memset(aku, 0, AKU_SIZE); + memset(passwordText, '\0', sizeof(passwordText)); + return rc; +} + +/* GetArgs() gets the command line arguments + + Returns ERROR_CODE on error. +*/ + +int GetArgs(const char **outputBodyFilename, + const char **profileId, + const char **epassword, + const char **password, + const char **projectLogFileName, + const char **sender, + const char **project, + int argc, + char **argv) +{ + int rc = 0; + int i; + FILE *tmpFile; + + /* command line argument defaults */ + *outputBodyFilename = NULL; + *profileId = NULL; + *epassword = NULL; + *password = NULL; + *projectLogFileName = NULL, + *sender = NULL; + *project = NULL; + verbose = FALSE; + + /* 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"); + /* 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],"-usr") == 0) { + i++; + if (i < argc) { + *profileId = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1003: -usr option (CCA user ID) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-epwd") == 0) { + i++; + if (i < argc) { + *epassword = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1004: -epwd option (CCA password) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-pwd") == 0) { + i++; + if (i < argc) { + *password = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1004: -pwd option (CCA password) 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; + } + } + /* 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 (*profileId == NULL) { + fprintf(messageFile, + "ERROR1010: -usr option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*projectLogFileName == NULL) { + fprintf(messageFile, + "ERROR1012: -log option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*sender == NULL) { + fprintf(messageFile, + "ERROR1013: -sender option missing\n"); + rc = ERROR_CODE; + } + } + return rc; +} + +void PrintUsage() +{ + fprintf(messageFile, "\n"); + fprintf(messageFile, "password_change:\n" + "\n" + "Common arguments:\n" + "\n" + "\t-usr - CCA user (profile) ID\n" + "\t[-v - verbose logging]\n" + "\t[-h - print usage help]\n" + "\n" + "Email only arguments:\n" + "\n" + "\t-project - project name\n" + "\t-epwd - CCA user password (encrypted)\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-log - project audit log file name\n" + "\t-pwd - CCA user password (plaintext)\n" + ); + fprintf(messageFile, "\n"); + fprintf(messageFile, "Changes the strong CCA profile password, installs the password, and\n" + "returns the password encrypted and mixed with the sender email address\n"); + fprintf(messageFile, "\n"); + return; +} diff --git a/src/signframework/password_change.cfg.sample b/src/signframework/password_change.cfg.sample new file mode 100644 index 0000000..30a6fc8 --- /dev/null +++ b/src/signframework/password_change.cfg.sample @@ -0,0 +1,32 @@ +# Password change configuration file + +# The parameters must be in this order +# +# program=program executable filename + +program=password_change + +# File name for the administrator audit log + +log=logs/password_change.log + +# files to be added to the program + +needkey=false +needauxcfg=false +neediatt=false +needoatt=true +needpwd=true + +# Project administrator + +email=framework@email.com + +# Notification receivers + +notifs=0 + +# Authorized senders (all) (userid=email) + +needsenders=false + diff --git a/src/signframework/password_generate.c b/src/signframework/password_generate.c new file mode 100644 index 0000000..08f72cb --- /dev/null +++ b/src/signframework/password_generate.c @@ -0,0 +1,500 @@ +/* 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include "cca_functions.h" +#include "ossl_functions.h" +#include "framework_utils.h" +#include "mail.h" +#include "utils.h" +#include "debug.h" + +/* local prototypes */ + +int GetArgs(const char **profileId, + const char **sender, + int argc, + char **argv); +void PrintUsage(void); + +/* global variables */ + +FILE *messageFile = NULL; +int verbose = TRUE; +int debug = FALSE; + +int main(int argc, char** argv) +{ + int rc = 0; + size_t i; + + /* program command line arguments */ + const char *sender = NULL; /* sender email address */ + const char *profileId= NULL; /* CCA profile ID for the sender */ + + FrameworkConfig frameworkConfig; + unsigned char eku[AES128_SIZE]; /* password encryption key */ + unsigned char aku[AKU_SIZE]; /* password authentication HMAC key */ + unsigned char initialization_vector[IV_SIZE]; /* needed for AES encryption */ + char passwordText[64 + 1]; /* generated password plaintext */ + + unsigned char *encryptedPassword = NULL; /* in binary, freed @2 */ + long encryptedPasswordLength; + unsigned char hmac[HMAC_SIZE]; /* encrypted password authentication HMAC */ + char *passwordString = NULL; /* hex ASCII combined IV, HMAC, encrypted + pwd *//* freed @3 */ + size_t passwordStringLength; + + FILE *outputBodyFile = NULL; /* closed @4 */ + + /* this is a stand alone program, so trace always goes to stdout */ + messageFile = stdout; + + FrameworkConfig_Init(&frameworkConfig); /* freed @1 */ + /* + get the command line arguments + */ + if (rc == 0) { + rc = GetArgs(&profileId, + &sender, + argc, argv); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Sender: %s\n", sender); + if (verbose) fprintf(messageFile, "CCA Profile ID: %s\n", profileId); + } + /* + get parameters from the framework configuration file + */ + if (rc == 0) { + rc = FrameworkConfig_Parse(TRUE, /* need master key */ + FALSE, /* do not validate */ + &frameworkConfig); + } + /* open framework audit log file for append */ + /* NOTE Is it legitimate to use the framework audit log? There can be a race condition if an + email is being processed at the same time. On the other hand, this is low probability, and + it's probably not worth yet another audit log file. */ + if (rc == 0) { + frameworkConfig.frameworkLogFile = + fopen(frameworkConfig.frameworkLogFilename, "a"); /* closed @6 */ + if (frameworkConfig.frameworkLogFile == NULL) { + fprintf(messageFile, + "Error opening: %s\n", frameworkConfig.frameworkLogFilename); + frameworkConfig.frameworkLogFile = stdout; + rc = ERROR_CODE; + } + else { + /* no buffering, so log can be monitored while the framework is running */ + setvbuf(frameworkConfig.frameworkLogFile, 0, _IONBF, 0); + } + } + /* begin the audit log entry */ + if (rc == 0) { + File_LogTime(frameworkConfig.frameworkLogFile); + fprintf(frameworkConfig.frameworkLogFile, "\tPassword generation\n"); + fprintf(frameworkConfig.frameworkLogFile, "\tSender: %s\n", sender); + fprintf(frameworkConfig.frameworkLogFile, "\tCCA Profile ID: %s\n", profileId); + } + /* + generate the encryption and HMAC keys from the master AES key mixed with the sender email + address + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Deriving password HMAC and encryption keys\n"); + rc = Password_KDF(eku, /* user encryption key */ + aku, /* user authentication HMAC key */ + profileId, /* authenticated email sender */ + frameworkConfig.masterAesKeyToken); + } + /* + generate a strong password + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Generating a password\n"); + memset(passwordText, '\0', sizeof(passwordText)); /* NUL terminator */ + rc = Password_Generate(passwordText, sizeof(passwordText)); + } + +#if 0 /* For tracing. Must be removed from the final product. */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password %s\n", passwordText); + } +#endif + /* + encrypt, HMAC, and convert the password + */ + /* generate a random IV for the password */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Generating AES initialization vector\n"); + rc = Random_Number_Generate_Long(initialization_vector, IV_SIZE); + if (verbose) PrintAll(messageFile, + "IV", IV_SIZE, initialization_vector); + } + /* encrypt the password */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Encrypt the Password\n"); + rc = Ossl_AES_Encrypt(&encryptedPassword, /* freed @2 */ + (size_t *)&encryptedPasswordLength, + (unsigned char *)passwordText, + sizeof(passwordText), + initialization_vector, + eku); /* derived encryption key */ + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "Encrypted password\n", encryptedPasswordLength, encryptedPassword); + } + /* HMAC the encrypted password */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "HMAC the Password\n"); + Ossl_HMAC_Generate(hmac, /* HMAC result */ + aku, /* derived HMAC key */ + encryptedPasswordLength, + encryptedPassword, + 0, NULL); + if (verbose) PrintAll(messageFile, + "HMAC", HMAC_SIZE, hmac); + } + /* concatenate the IV, HMAC, and encrypted password, convert to hex ASCII */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Concatenate IV, HMAC, Encrypted Password and convert to string\n"); + rc = Password_ToString(&passwordString, /* freed @3 */ + &passwordStringLength, + initialization_vector, + hmac, + encryptedPassword, /* encrypted password */ + encryptedPasswordLength); /* password */ + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password String length %d\n", (int)passwordStringLength); + if (verbose) fprintf(messageFile, "Password String:\n%s\n", passwordString); + } + /* + sanity check - convert back and compare + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Password encryption validation\n"); + + unsigned char initialization_vector_out[IV_SIZE]; + unsigned char hmac_out[HMAC_SIZE]; + unsigned char *ciphertext_out = NULL; /* freed @1 */ + long ciphertext_length_out; + long cleartext_length_out; + unsigned char *cleartext_out = NULL; /* freed @2 */ + int hmac_valid; + + /* convert the hex ASCII back to IV, HMAC, and encrypted password */ + if (rc == 0) { + rc = Password_FromString(initialization_vector_out, + hmac_out, + &ciphertext_out, /* freed @4 */ + (size_t *)&ciphertext_length_out, + passwordString, + passwordStringLength, + frameworkConfig.frameworkLogFile); + } + /* validate the converted IV */ + if (rc == 0) { + rc = memcmp(initialization_vector, initialization_vector_out, IV_SIZE); + if (rc != 0) { + fprintf(messageFile, "IV conversion mismatch\n"); + rc = ERROR_CODE; + } + } + /* validate the converted HMAC */ + if (rc == 0) { + rc = memcmp(hmac, hmac_out, HMAC_SIZE); + if (rc != 0) { + fprintf(messageFile, "HMAC conversion mismatch\n"); + rc = ERROR_CODE; + } + } + /* validate the converted encrypted password */ + if (rc == 0) { + if (encryptedPasswordLength != ciphertext_length_out) { + fprintf(messageFile, "Password ciphertext length mismatch\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rc = memcmp(encryptedPassword, ciphertext_out, encryptedPasswordLength ); + if (rc != 0) { + fprintf(messageFile, "ciphertext conversion mismatch\n"); + rc = ERROR_CODE; + } + } + /* validate the HMAC */ + if (rc == 0) { + Ossl_HMAC_Check(&hmac_valid, + hmac, + aku, + encryptedPasswordLength, + encryptedPassword, + 0, NULL); + } + if (rc == 0) { + if (!hmac_valid) { + fprintf(messageFile, "Password HMAC check failed\n\n"); + rc = ERROR_CODE; + } + } + /* decrypt the password */ + if (rc == 0) { + rc = Ossl_AES_Decrypt(&cleartext_out, /* freed @2 */ + (size_t *)&cleartext_length_out, + encryptedPassword, + encryptedPasswordLength, + initialization_vector, + eku); + } + /* validate the decryption */ + if (rc == 0) { + rc = memcmp((unsigned char *)passwordText, cleartext_out, cleartext_length_out); + if (rc != 0) { + fprintf(messageFile, "Decrypt error\n"); + fprintf(messageFile, "Cleartext in %s\n", passwordText); + fprintf(messageFile, "Cleartext out %s\n", cleartext_out); + fprintf(messageFile, "Cleartext length out %lu\n", cleartext_length_out); + rc = ERROR_CODE; + } + } + free(ciphertext_out); /* @1 */ + if (cleartext_out != NULL) { + memset(cleartext_out, 0, cleartext_length_out); + } + free(cleartext_out); /* @2 */ + } + /* + Change the password in CCA on the coprocessor card + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Login with CCA Profile: %s, default password: aaaaaa\n", + profileId); + rc = Login_Control(TRUE, + profileId, + "aaaaaa"); + if (rc != 0) { + File_Printf(frameworkConfig.frameworkLogFile, messageFile, + "ERROR1001, Cannot log in CCA Profile: %s with password aaaaaa\n", + profileId); + } + } + /* send password change to card */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Change password\n"); + rc = Access_Control_Initialization(profileId, + frameworkConfig.passwordExpire, /* password + expiration */ + passwordText); + if (rc != 0) { + File_Printf(frameworkConfig.frameworkLogFile, messageFile, + "ERROR1002, Cannot change password for CCA Profile: %s\n", profileId); + } + } + /* logout */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Logout: Profile: %s\n", profileId); + rc = Login_Control(FALSE, + profileId, + NULL); /* password */ + if (rc != 0) { + File_Printf(frameworkConfig.frameworkLogFile, messageFile, + "ERROR1003, Cannot log out CCA Profile: %s\n", profileId); + } + } + /* + sanity check - try to log in with the new password + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Login with CCA Profile: %s, and changed password\n", + profileId); + rc = Login_Control(TRUE, + profileId, + passwordText); + } + /* logout */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Logout: Profile: %s\n", profileId); + rc = Login_Control(FALSE, + profileId, + NULL); /* password */ + + } + + /* open the email response file */ + if (rc == 0) { + outputBodyFile = fopen(frameworkConfig.outputBodyFilename, "w"); + if (outputBodyFile == NULL) { + fprintf(messageFile, "Error opening %s, %s\n", + frameworkConfig.outputBodyFilename, strerror(errno)); + rc = ERROR_CODE; + } + } + + /* construct the email response */ + if (rc == 0) { + fprintf(outputBodyFile, "A password has been generated for: %s\n", profileId); + fprintf(outputBodyFile, "It is valid for %u months.\n", + frameworkConfig.passwordExpire); + fprintf(outputBodyFile, "\n"); + fprintf(outputBodyFile, "You must retain the following encrypted password to perform signing requests.\n"); + fprintf(outputBodyFile, "This password should be placed in a file with permissions set appropriately\n"); + fprintf(outputBodyFile, "to block access to it by anyone other than yourself.\n\n"); + fprintf(outputBodyFile, "Encrypted Password : %s\n", passwordString); + fprintf(outputBodyFile, "\n"); + fprintf(outputBodyFile, "This password will grant you access to the signing server but you will also need\n"); + fprintf(outputBodyFile, "access to a specific project.\n"); + fprintf(outputBodyFile, "\n"); + fprintf(outputBodyFile, "For project names, contact the administrator at:\n"); + for (i = 0 ; i < frameworkConfig.frameworkAdminCount ; i++) { + fprintf(outputBodyFile, + "\t%s\n", frameworkConfig.frameworkAdmins[i]); + } + fprintf(outputBodyFile, "\n"); + /* close the email response file */ + fclose(outputBodyFile); + outputBodyFile = NULL; + } + + /* send the email response */ + if (rc == 0) { + /* send the enrollment message to the signer */ + rc = SendMailFile(&frameworkConfig, sender, "Signer Framework Enrollment", frameworkConfig.outputBodyFilename); + if (rc != 0) { + fprintf(messageFile, "SendMail failed, status %u\n", rc); + rc = ERROR_CODE; + } + remove(frameworkConfig.outputBodyFilename); + } + + /* log the results to the framework audit log */ + if (rc == 0) { + fprintf(frameworkConfig.frameworkLogFile, "\tEncrypted password:\n\t%s\n", passwordString); + } + if (rc == 0) { + fprintf(messageFile, "\npassword_generate succeeded\n\n"); + } + else { + fprintf(messageFile, "\npassword_generate failed\n\n"); + } + /* cleanup */ + if (frameworkConfig.frameworkLogFile != stdout) { + fclose(frameworkConfig.frameworkLogFile); /* @6 */ + } + FrameworkConfig_Delete(&frameworkConfig); /* @1 */ + free(encryptedPassword); /* @2 */ + free(passwordString); /* @3 */ + if (outputBodyFile != NULL) { + fclose(outputBodyFile); /* @4 */ + } + /* erase the secret keys before exit */ + memset(eku, 0, AES128_SIZE); + memset(aku, 0, AKU_SIZE); + memset(passwordText, '\0', sizeof(passwordText)); + return rc; +} + +/* GetArgs() gets the command line arguments + + Returns ERROR_CODE on error. +*/ + +int GetArgs(const char **profileId, + const char **sender, + int argc, + char **argv) +{ + int rc = 0; + int i; + + /* command line argument defaults */ + *profileId = NULL; + *sender = NULL; + verbose = FALSE; + + /* get the command line arguments */ + for (i=1 ; (i<argc) && (rc == 0) ; i++) { + if (strcmp(argv[i],"-sender") == 0) { + i++; + if (i < argc) { + *sender = argv[i]; + } + else { + fprintf(messageFile, "password_generate: Error, -sender option needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-profile") == 0) { + i++; + if (i < argc) { + *profileId = argv[i]; + } + else { + fprintf(messageFile, "password_generate: Error, -profile option needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-h") == 0) { + PrintUsage(); + rc = ERROR_CODE; + } + else if (strcmp(argv[i],"-v") == 0) { + verbose = TRUE; + } + else { + fprintf(messageFile, "password_generate: Error, %s is not a valid option\n", argv[i]); + PrintUsage(); + rc = ERROR_CODE; + } + } + /* verify command line arguments */ + if (rc == 0) { + if (*sender == NULL) { + fprintf(messageFile, "password_generate: Error, -sender option must be specified\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*profileId == NULL) { + fprintf(messageFile, "password_generate: Error, -profile option must be specified\n"); + rc = ERROR_CODE; + } + } + return rc; +} + +void PrintUsage() +{ + fprintf(messageFile, "\n"); + fprintf(messageFile, "password_generate:\n" + "\t-profile - CCA profile ID\n" + "\t-sender - email sender\n" + "\t[-v - verbose tracing]\n" + "\t[-h - print usage help]\n"); + fprintf(messageFile, "\n"); + fprintf(messageFile, "Creates a strong CCA profile password, installs the password, and\n" + "returns the password encrypted and mixed with the sender email address\n"); + fprintf(messageFile, "\n"); + fprintf(messageFile, "It assumes that the original password is 'aaaaaa'\n"); + fprintf(messageFile, "\n"); + return; +} diff --git a/src/signframework/pullconfig b/src/signframework/pullconfig new file mode 100755 index 0000000..75699b9 --- /dev/null +++ b/src/signframework/pullconfig @@ -0,0 +1,34 @@ +#!/bin/sh +# 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. + +if [ "$1" = "" ]; then + echo "Syntax: pullconfig <signframework-cfg base dir>" + exit 1; +fi + +BASEDIR=$1 + +if [ ! -e "$BASEDIR/cfgs/framework.cfg" ]; then + echo "ERROR : $BASEDIR doesn't appear to have framework configuration files" + exit 1; +fi + +if [ ! -e "$PWD/framework.c" ]; then + echo "ERROR : pullconfig must be run from framework directory" + exit 1; +fi + +cp -r $BASEDIR/cfgs/* . diff --git a/src/signframework/regress2.sh b/src/signframework/regress2.sh new file mode 100755 index 0000000..cb7bad4 --- /dev/null +++ b/src/signframework/regress2.sh @@ -0,0 +1,552 @@ +#!/bin/sh +# 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. + +# +# signer framework regression test +# +# Run this script from the framework server +# Do not start the framework +# + +#set -x + +removeFiles() +{ + rm -f oatt.bin + rm -f iatt.bin + rm -f /home/dropbox/stop + rm -f run.out + rm -f test.cfg +} + +# searches $1 for "Return codeErrornnnn" and returns the value */ + +getErrorCode() +{ + sleep 1 + errorcode=`grep -m 1 "ERROR" $1 | gawk '{print $1}' | sed s/ERROR// | sed s/:// ` +# echo "getErrorCode: $errorcode" + return $errorcode +} + +# searches $1 for "Return codeErrornnnn" and returns the value */ + +getReasonCode() +{ + reasoncode=`grep -m 1 "reason_code" $1 | gawk '{print $6}' ` + return $reasoncode +} + + +export FRAMEWORK_CONFIG_FILE=test.cfg + +echo "" +echo "Errors in framework configuration file" +echo "" + +echo "Missing line_max" +removeFiles +sed s/line_max/line_maxa/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Illegal value in line_max" +removeFiles +sed s/line_max=4000/line_max=0/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "8" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Illegal character in line_max" +removeFiles +sed s/line_max=4000/line_max=4000a/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing file_max" +removeFiles +sed s/file_max/file_maxa/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Illegal value in file_max" +removeFiles +sed s/file_max=10000/file_max=0/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "8" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Illegal character in file_max" +removeFiles +sed s/file_max=10000/file_max=10000a/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing framework_name" +removeFiles +sed s/framework_name/framework_namea/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + + + + + +echo "Missing admins" +removeFiles +sed s/admins/adminsa/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Illegal character in admins" +removeFiles +sed s/admins=1/admins=1a/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Insufficient senders" +removeFiles +sed s/admins=1/admins=300/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + + +### + +echo "Missing dropbox" +removeFiles +sed s/dropbox/dropboxa/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +### + +echo "Missing password_expire" +removeFiles +sed s/password_expire/password_expirea/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Illegal value in password_expire" +removeFiles +sed s/password_expire=12/password_expire=0/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "11" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Illegal character in password_expire" +removeFiles +sed s/password_expire=12/password_expire=2a/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +### + +echo "Missing log" +removeFiles +sed s/log/loga/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible log" +removeFiles +cp framework.cfg test.cfg +chmod u-w logs/framework.log +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod u+w logs/framework.log +if [ "$errorcode" -eq "3" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing stop_file" +removeFiles +sed s/stop_file/stop_filea/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing restart_file" +removeFiles +sed s/restart_file/restart_filea/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing out_body" +removeFiles +sed s/out_body/out_bodya/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible out_body" +removeFiles +cp framework.cfg test.cfg +chmod u-w obody.txt +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod u+w obody.txt +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing notif_log" +removeFiles +sed s/notif_log/notif_loga/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible notif_log" +removeFiles +cp framework.cfg test.cfg +chmod u-w nlog.txt +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod u+w nlog.txt +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing full_email" +removeFiles +sed s/full_email/full_emaila/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible full_email" +removeFiles +cp framework.cfg test.cfg +touch email.txt +chmod u-w email.txt +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod u+w email.txt +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + cat run.out + exit $errorcode +fi + +echo "Missing in_attachment" +removeFiles +sed s/in_attachment/in_attachmenta/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing out_attachment" +removeFiles +sed s/out_attachment/out_attachmenta/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + + +echo "Missing key" +removeFiles +sed s/key/keya/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing master key file" +removeFiles +sed s/key=masterkey.bin/key=masterkey.bina/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible master key file" +removeFiles +cp framework.cfg test.cfg +chmod a-r masterkey.bin +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod a+r masterkey.bin +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Too large master key file" +removeFiles +sed s/key=masterkey.bin/key=athenakey.tok/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Too small master key file" +removeFiles +sed s/key=masterkey.bin/key=email.txt/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "4" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + cat run.out + exit $errorcode +fi + +#echo "Invalid master key file" +#removeFiles +#sed s/key=masterkey.bin/key=badmasterkey.bin/ framework.cfg > test.cfg +#(./framework > run.out)& +#touch /home/dropbox/stop +#getReasonCode run.out +#if [ "$reasoncode" -eq "20" ]; then +# echo " INFO: Error case OK" +#else +# echo " ERROR: Error case failed" +# cat run.out +# exit $errorcode +#fi + + + +echo "Bad project format" +removeFiles +sed s/athena=/athena/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing project config file" +removeFiles +sed s/athena=athena.cfg/athenaathena.cfga/ framework.cfg > test.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "5" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible project config file" +removeFiles +cp framework.cfg test.cfg +chmod a-r athena.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod a+r athena.cfg +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "" +echo "Tests Passed" +echo "" + +removeFiles + +exit 0 + diff --git a/src/signframework/regress3.sh b/src/signframework/regress3.sh new file mode 100755 index 0000000..cb410b1 --- /dev/null +++ b/src/signframework/regress3.sh @@ -0,0 +1,527 @@ +#!/bin/sh +# 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. + +# +# signer framework regression test - project configuration file +# +# Run this script from the framework server +# Do not start the framework +# + +#set -x + +removeFiles() +{ + rm -f oatt.bin + rm -f iatt.bin + rm -f /home/dropbox/stop + rm -f run.out +} + +# searches $1 for "Return codeErrornnnn" and returns the value */ + +getErrorCode() +{ + sleep 1 + errorcode=`grep -m 1 "ERROR" $1 | gawk '{print $1}' | sed s/ERROR// | sed s/:// ` +# echo "getErrorCode: $errorcode" + return $errorcode +} + +# searches $1 for "Return codeErrornnnn" and returns the value */ + +getReasonCode() +{ + reasoncode=`grep -m 1 "reason_code" $1 | gawk '{print $6}' ` + return $reasoncode +} + + +export FRAMEWORK_CONFIG_FILE=test.cfg + +sed s/athena\.cfg/athenatest\.cfg/ framework.cfg > test.cfg + + +echo "" +echo "Errors in project configuration file" +echo "" + +# program + +echo "Missing program" +removeFiles +sed s/program/programa/ athena.cfg > athenatest.cfg +(./framework -v > run.out) & +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Bad program" +removeFiles +sed s/program=framework_test/program=framework_testa/ athena.cfg > athenatest.cfg +./framework -v > run.out +touch /home/dropbox/stop +getErrorCode run.out +echo $errorcode +if [ "$errorcode" -eq "39" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible program" +removeFiles +chmod u-x framework_test +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod u+x framework_test +if [ "$errorcode" -eq "39" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +# log + +echo "Missing log" +removeFiles +sed s/log/loga/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible log" +removeFiles +cp athena.cfg athenatest.cfg +chmod u-w logs/athena.log +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod u+w logs/athena.log +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +# key + +echo "Missing needkey" +removeFiles +sed s/needkey/needkeya/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Bad value needkey" +removeFiles +sed s/needkey=true/needkey=truea/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing key" +removeFiles +sed s/key/keya/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing key file" +removeFiles +sed s/key=athenakey.tok/key=athenakey.toka/ athena.cfg > athenatest.cfg +(./framework -v > run.out) & +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible athena key file" +removeFiles +cp athena.cfg athenatest.cfg +chmod a-r athenakey.tok +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod a+r athenakey.tok +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Bad athena key file" +removeFiles +sed s/key=athenakey.tok/key=athena.cfg/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "15" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +# auxiliary project configuration file + +echo "Missing needauxcfg" +removeFiles +sed s/needauxcfg/needauxcfga/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Bad value needauxcfg" +removeFiles +sed s/needauxcfg=true/needauxcfg=truea/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing auxcfg" +removeFiles +sed s/auxcfg/auxcfga/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing auxcfg file" +removeFiles +sed s/auxcfg=athenaaux.cfg/auxcfg=athenaaux.bina/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Unaccessible athena auxcfg file" +removeFiles +cp athena.cfg athenatest.cfg +chmod a-r athenaaux.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +chmod a+r athenaaux.cfg +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +# input attachment + +echo "Missing neediatt" +removeFiles +sed s/neediatt/neediatta/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Bad value neediatt" +removeFiles +sed s/neediatt=true/neediatt=truea/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +# output attachment + +echo "Missing needoatt" +removeFiles +sed s/needoatt/needoatta/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Bad value needoatt" +removeFiles +sed s/needoatt=true/needoatt=truea/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +# need pwd + +echo "Missing needpwd" +removeFiles +sed s/needpwd/needpwda/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Bad value needpwd" +removeFiles +sed s/needpwd=true/needpwd=truea/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +# email + +echo "Missing email" +removeFiles +sed s/email/emaila/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +# senders + +echo "Missing needsenders" +removeFiles +sed s/needsenders/needsendersa/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Missing needsenders" +removeFiles +sed s/needsenders/needsendersa/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Bad value needsenders" +removeFiles +sed s/needsenders=true/needsenders=truea/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +#echo "needsenders but zero senders" +#removeFiles +#sed s/senders=3/senders=0/ athena.cfg > athenatest.cfg +#(./framework > run.out)& +#touch /home/dropbox/stop +#getErrorCode run.out +#if [ "$errorcode" -eq "24" ]; then +# echo " INFO: Error case OK" +#else +# echo " ERROR: Error case failed" +# exit $errorcode +#fi + +echo "senders but no needsenders" +removeFiles +sed s/needsenders=true/needsenders=false/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "24" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +#echo "Illegal character in senders" +#removeFiles +#sed s/senders=3/senders=3a/ athena.cfg > athenatest.cfg +#(./framework > run.out)& +#touch /home/dropbox/stop +#getErrorCode run.out +#if [ "$errorcode" -eq "23" ]; then +# echo " INFO: Error case OK" +#else +# echo " ERROR: Error case failed" +# exit $errorcode +#fi + +#echo "Insufficient senders" +#removeFiles +#sed s/senders=3/senders=300/ athena.cfg > athenatest.cfg +#(./framework > run.out)& +#touch /home/dropbox/stop +#getErrorCode run.out +#if [ "$errorcode" -eq "23" ]; then +# echo " INFO: Error case OK" +#else +# echo " ERROR: Error case failed" +# exit $errorcode +#fi + +# notification + +echo "Missing notifs" +removeFiles +sed s/notifs/notifsa/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Illegal character in notifs" +removeFiles +sed s/notifs=/notifs=a/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "Insufficient notifs" +removeFiles +sed s/notifs=/notifs=400/ athena.cfg > athenatest.cfg +(./framework > run.out)& +touch /home/dropbox/stop +getErrorCode run.out +if [ "$errorcode" -eq "23" ]; then + echo " INFO: Error case OK" +else + echo " ERROR: Error case failed" + exit $errorcode +fi + +echo "" +echo "Tests Passed" +echo "" + +removeFiles +rm -f test.cfg +rm -f athenatest.cfg + +exit 0 diff --git a/src/signframework/sender_validate.c b/src/signframework/sender_validate.c new file mode 100644 index 0000000..6722994 --- /dev/null +++ b/src/signframework/sender_validate.c @@ -0,0 +1,287 @@ +/* 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. + */ + +/* This program iterates through all the project configuration files, getting the project + administrator and all authorized users. It sends a validation email the the administrator with + the user list. + + The intent is to run this as a periodic cron job, authough it can be run at any time from the + command line. +*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include "framework_utils.h" +#include "mail.h" +#include "utils.h" +#include "debug.h" + +/* local prototypes */ + +int SenderValidate_Parse(char **outputBodyFilename, + char *configFilename); +int GetArgs(int argc, + char **argv, + char **configFilename); +void PrintUsage(void); + +/* global variables */ + +FILE *messageFile = NULL; /* needed for utilities */ +int verbose = TRUE; +int debug = FALSE; + +int main(int argc, char** argv) +{ + int rc = 0; + size_t i, j; + char *subject = NULL; /* freed @2 */ + char *configFilename = NULL; + FrameworkConfig frameworkConfig; + char *outputBodyFilename = NULL; /* freed @5 */ + FILE *outputBodyFile = NULL; /* closed @4 */ + const char projectSubject1[] = "Signing server "; + const char projectSubject2[] = " periodic validation for project: "; + const char *hostname = NULL; + + /* this is a stand alone program, so trace always goes to stdout */ + messageFile = stdout; + + FrameworkConfig_Init(&frameworkConfig); /* freed @1 */ + /* + get the command line arguments + */ + if (rc == 0) { + rc = GetArgs(argc, argv, + &configFilename); + } + if (rc == 0) { + hostname = getenv("HOSTNAME"); + if (hostname == NULL) { + fprintf(messageFile, "Error getting environment variable HOSTNAME\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "\nsender_validate: hostname %s\n", hostname); + } + /* + get parameters from the framework configuration file + */ + if (rc == 0) { + rc = FrameworkConfig_Parse(TRUE, /* need master key */ + FALSE, /* do not validate */ + &frameworkConfig); + } + /* get parameters from the audit archive configuration file */ + if (rc == 0) { + rc = SenderValidate_Parse(&outputBodyFilename, /* freed @5 */ + configFilename); + } + /* for each project */ + for (i = 0 ; (rc == 0) && (i < frameworkConfig.projectLength) ; i++) { + + if (verbose) fprintf(messageFile, + "\nsender_validate: Loading project configuration file %s\n\n", + frameworkConfig.projectConfigFilenames[i]); + /* get parameters from the project configuration file */ + if (rc == 0) { + rc = ProjectConfig_Parse(frameworkConfig.projectConfigFiles[i], + FALSE, /* do not validate */ + frameworkConfig.projectConfigFilenames[i], + &frameworkConfig); + } + + /* only send the email if the project requires authorization */ + if (frameworkConfig.projectConfigFiles[i]->needSenders) { + + /* open the email response file */ + if (rc == 0) { + outputBodyFile = fopen(outputBodyFilename, "w"); + if (outputBodyFile == NULL) { + fprintf(messageFile, "Error opening %s, %s\n", + outputBodyFilename, strerror(errno)); + rc = ERROR_CODE; + } + } + /* construct the email body */ + if (rc == 0) { + fprintf(outputBodyFile, + "This is a validation message from the signing server.\n\n"); + fprintf(outputBodyFile, + "You are the project administrator for project: %s\n\n", + frameworkConfig.projectNames[i]); + + fprintf(outputBodyFile, + "Please validate that the following %d users should be authorized " + "for this project.\n", + (int)frameworkConfig.projectConfigFiles[i]->sendersCount); + fprintf(outputBodyFile, "\n"); + for (j = 0 ; j < frameworkConfig.projectConfigFiles[i]->sendersCount ; j++) { + fprintf(outputBodyFile, + "\t%s\n", + frameworkConfig.projectConfigFiles[i]->senders[j]); + } + fprintf(outputBodyFile, "\n"); + + fprintf(outputBodyFile, + "If a sender should not be authorized, contact a signing " + "server administrator at:\n"); + for (j = 0 ; j < frameworkConfig.frameworkAdminCount ; j++) { + fprintf(outputBodyFile, + "\t%s\n", + frameworkConfig.frameworkAdmins[j]); + } + fprintf(outputBodyFile, "\n"); + } + /* close the email response file */ + if (outputBodyFile != NULL) { + fclose(outputBodyFile); + outputBodyFile = NULL; + } + /* construct the subject */ + if (rc == 0) { + subject = realloc(subject, + sizeof(projectSubject1) + + strlen(hostname) + + sizeof(projectSubject2) + + strlen(frameworkConfig.projectNames[i])); + strcpy(subject, projectSubject1); + strcat(subject, hostname); + strcat(subject, projectSubject2); + strcat(subject, frameworkConfig.projectNames[i]); + } + /* send the email response */ + if (rc == 0) { + /* send the message to the project administrator */ + rc = SendMailFile(&frameworkConfig, + frameworkConfig.projectConfigFiles[i]->emailProject, + subject, outputBodyFilename); + if (rc != 0) { + fprintf(messageFile, "SendMail failed, status %u\n", rc); + rc = ERROR_CODE; + } + } + } + } + /* cleanup */ + FrameworkConfig_Delete(&frameworkConfig); /* @1 */ + free(subject); /* @2 */ + free(outputBodyFilename); /* @5 */ + return rc; +} + +int SenderValidate_Parse(char **outputBodyFilename, /* freed by caller */ + char *configFilename) +{ + int rc = 0; + FILE *configFile = NULL; + char lineBuffer[4000]; + + if (rc == 0) { + rc = File_Open(&configFile, configFilename, "r"); /* closed @1 */ + } + /* output body file name */ + if (rc == 0) { + rc = File_MapNameToValue(outputBodyFilename, /* freed by caller */ + "out_body", + lineBuffer, + sizeof(lineBuffer), + configFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "sender_validate: Output body file name: %s\n", + *outputBodyFilename); + } + if (configFile != NULL) { + fclose(configFile); /* @1 */ + } + if (rc != 0) { + fprintf(messageFile, + "sender_validate: Error, rc %d\n", rc); + } + return rc; +} + +/* GetArgs() gets the command line arguments + + Returns ERROR_CODE on error. +*/ + +int GetArgs(int argc, + char **argv, + char **configFilename) +{ + int rc = 0; + int i; + + /* command line argument defaults */ + + verbose = FALSE; + *configFilename = NULL; + + /* get the command line arguments */ + for (i=1 ; (i<argc) && (rc == 0) ; i++) { + if (strcmp(argv[i],"-cfg") == 0) { + i++; + if (i < argc) { + *configFilename = argv[i]; + } + else { + fprintf(messageFile, + "sender_validate: Error, -cfg option needs a value\n"); + rc = ERROR_CODE; + } + + } + else if (strcmp(argv[i],"-h") == 0) { + PrintUsage(); + rc = ERROR_CODE; + } + else if (strcmp(argv[i],"-v") == 0) { + verbose = TRUE; + } + else { + fprintf(messageFile, "sender_validate: Error, %s is not a valid option\n", argv[i]); + PrintUsage(); + rc = ERROR_CODE; + } + } + /* check for missing connand line parameters */ + if (*configFilename == NULL) { + fprintf(messageFile, "sender_validate: Error, missing -cfg option\n"); + } + return rc; +} + +void PrintUsage() +{ + fprintf(messageFile, "\n"); + fprintf(messageFile, "sender_validate:\n" + "\t-cfg - configuration file\n" + "\t[-v - verbose tracing]\n" + "\t[-h - print usage help]\n"); + fprintf(messageFile, "\n"); + fprintf(messageFile, "Sends a validation message for each project\n" + "to confirm that a sender should be authorized for the project\n"); + fprintf(messageFile, "\n"); + return; +} diff --git a/src/signframework/sender_validate.cfg.sample b/src/signframework/sender_validate.cfg.sample new file mode 100644 index 0000000..cbee0e1 --- /dev/null +++ b/src/signframework/sender_validate.cfg.sample @@ -0,0 +1,6 @@ +# Sender Validate program configuration file + +# File names for the output email body + +out_body=sender_validate.txt + diff --git a/src/signframework/sendkeys b/src/signframework/sendkeys new file mode 100755 index 0000000..7bef88a --- /dev/null +++ b/src/signframework/sendkeys @@ -0,0 +1,39 @@ +#!/bin/sh +# 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. + +if [ "$1" = "" -o "$2" = "" -o "$3" = "" -o "$4" = "" ]; then + echo "syntax: sendkeys <projectname> <binfile> <tokfile> <email>" + exit 1 +fi + +MSGFILE=./sendkeys.msg +if [ -e ../sendkeys.msg ]; then + MSGFILE=../sendkeys.msg +fi + +if [ ! -e $2 ]; then + echo "ERROR: binfile '$2' doesn't appear to exist" + exit 1 +fi + +if [ ! -e $3 ]; then + echo "ERROR: tokfile '$3' doesn't appear to exist" + exit 1 +fi + +echo "Sending email to $4" +cat $MSGFILE | mailx -s "$1 signing project keys" -a $2 -a $3 $4 + diff --git a/src/signframework/sendkeys.msg b/src/signframework/sendkeys.msg new file mode 100644 index 0000000..4a96094 --- /dev/null +++ b/src/signframework/sendkeys.msg @@ -0,0 +1,8 @@ +The attached files have been created for your new signing project. + +These keys should be kept in a safe place to ensure proper recovery +if there is a HW failure with the signing server. + +The files contain: + *.tok : Encrypted private key + *.bin : Public key diff --git a/src/signframework/setclock.c b/src/signframework/setclock.c new file mode 100644 index 0000000..5f4d4e9 --- /dev/null +++ b/src/signframework/setclock.c @@ -0,0 +1,96 @@ +/* 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include "cca_functions.h" +#include "utils.h" + +/* local prototypes */ + +int GetArgs(int argc, + char **argv); +void PrintUsage(void); + +/* global variables */ + +FILE *messageFile = NULL; +int verbose = TRUE; +int debug = TRUE; + +int main(int argc, char** argv) +{ + int rc = 0; + + messageFile = stdout; /* trace always goes to stdout */ + + if (rc == 0) { + rc = GetArgs(argc, argv); + } + /* generate a master key */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Setting the clock\n"); + rc = Crypto_Facility_SetClock(); + } + fprintf(messageFile, "\tsetclock rc %d\n\n", rc); + return rc; +} + +/* GetArgs() gets the command line arguments + + Returns ERROR_CODE on error. +*/ + +int GetArgs(int argc, + char **argv) +{ + int rc = 0; + int i; + + /* command line argument defaults */ + verbose = FALSE; + + /* get the command line arguments */ + for (i=1 ; (i<argc) && (rc == 0) ; i++) { + if (strcmp(argv[i],"-h") == 0) { + PrintUsage(); + rc = ERROR_CODE; + } + else if (strcmp(argv[i],"-v") == 0) { + verbose = TRUE; + } + else { + printf("setclock: Error, %s is not a valid option\n", argv[i]); + PrintUsage(); + rc = ERROR_CODE; + } + } + return rc; +} + +void PrintUsage() +{ + printf("\n"); + printf("setclock:\n" + "\t[-v - verbose tracing]\n" + "\t[-h - print usage help]\n"); + printf("\n"); + printf("\n"); + return; +} diff --git a/src/signframework/signecc/Container.h b/src/signframework/signecc/Container.h new file mode 100644 index 0000000..52cdef9 --- /dev/null +++ b/src/signframework/signecc/Container.h @@ -0,0 +1,120 @@ +/* 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 <stdint.h> + +#include "hw_utils.h" +#include "sha512.h" +#include "ecdsa521.h" + + +#define CONTAINER_VERSION 1 +#define HEADER_VERSION 1 +#define HASH_ALG_SHA512 1 +#define SIG_ALG_ECDSA521 1 + +#define HBI_BASE_SIGNING_KEY 0x80000000 + +#define ROM_MAGIC_NUMBER 0x17082011 + + +typedef struct { + uint32_t m_magicNumber; // (17082011) + uint16_t m_version; // (1: see versions above) + uint8_t m_containerSize[8]; // filled by caller + uint8_t m_targetHrmor[8]; // filled by caller + uint8_t m_stackPointer[8]; // filled by caller + uint8_t m_hwPkeyA[ECDSA521_KEY_SIZE]; + uint8_t m_hwPkeyB[ECDSA521_KEY_SIZE]; + uint8_t m_hwPkeyC[ECDSA521_KEY_SIZE]; +} ContainerHdr; + + +typedef struct { + uint16_t m_version; // (1: see versions above) + uint8_t m_hashAlg; // (1: SHA-512) + uint8_t m_sigAlg; // (1: SHA-512/ECDSA-521) + uint8_t m_codeStartOffset[8]; + uint8_t m_reserved[8]; + uint32_t m_flags; + uint8_t m_swKeyCount; + uint8_t m_payloadSize[8]; + uint8_t m_payloadHash[SHA512_DIGEST_SIZE]; + uint8_t m_ecidCount; + uint8_t m_ecid[ECID_SIZE]; // optional ecid place + // holder ecid_count * szeof(ecids) +} PrefixHdr; + + +typedef struct { + uint8_t m_hwSigA[ECDSA521_SIG_SIZE]; + uint8_t m_hwSigB[ECDSA521_SIG_SIZE]; + uint8_t m_hwSigC[ECDSA521_SIG_SIZE]; + uint8_t m_swPkeyP[ECDSA521_KEY_SIZE]; + uint8_t m_swPkeyQ[ECDSA521_KEY_SIZE]; + uint8_t m_swPkeyR[ECDSA521_KEY_SIZE]; +} PrefixData; + + +typedef struct { + uint16_t m_version; // (1: see versions above) + uint8_t m_hashAlg; // (1: SHA-512) + uint8_t m_unused; + uint8_t m_codeStartOffset[8]; + uint8_t m_reserved[8]; + uint32_t m_flags; + uint8_t m_reserved0; + uint8_t m_payloadSize[8]; + uint8_t m_payloadHash[SHA512_DIGEST_SIZE]; + uint8_t m_ecidCount; + uint8_t m_ecid[ECID_SIZE]; // optional ecid place + // holder ecid_count * szeof(ecids) +} SoftwareHdr; + + +typedef struct { + uint8_t m_swSigP[ECDSA521_SIG_SIZE]; + uint8_t m_swSigQ[ECDSA521_SIG_SIZE]; + uint8_t m_swSigR[ECDSA521_SIG_SIZE]; +} SoftwareSig; + + + + +/* The Container Layout consists of the following 5 blocks + * ContainerHdr + * PrefixHdr + * PrefixData + * SoftwareHdr + * SoftwareSig + */ +typedef struct { + ContainerHdr m_containerHdr; + PrefixHdr m_prefixHdr; + PrefixData m_prefixData; + SoftwareHdr m_softwareHdr; + SoftwareSig m_softwareSig; +} Container; + + +// Print out all the fields of the container +void PrintContainer( const Container *p_container ); + +// Validate the container +int ValidateContainer( const Container *p_container ); + +// Given a stream of bytes, parse the data and constuct the container +Container* ParseContainer( uint8_t *p_rawData, int p_endianess ); diff --git a/src/signframework/signecc/cca_functions.c b/src/signframework/signecc/cca_functions.c new file mode 120000 index 0000000..df99f3c --- /dev/null +++ b/src/signframework/signecc/cca_functions.c @@ -0,0 +1 @@ +../cca_functions.c
\ No newline at end of file diff --git a/src/signframework/signecc/cca_functions.h b/src/signframework/signecc/cca_functions.h new file mode 120000 index 0000000..b4ca110 --- /dev/null +++ b/src/signframework/signecc/cca_functions.h @@ -0,0 +1 @@ +../cca_functions.h
\ No newline at end of file diff --git a/src/signframework/signecc/cca_functions_ecc.c b/src/signframework/signecc/cca_functions_ecc.c new file mode 120000 index 0000000..8acc0be --- /dev/null +++ b/src/signframework/signecc/cca_functions_ecc.c @@ -0,0 +1 @@ +../cca_functions_ecc.c
\ No newline at end of file diff --git a/src/signframework/signecc/cca_functions_ecc.h b/src/signframework/signecc/cca_functions_ecc.h new file mode 120000 index 0000000..9128029 --- /dev/null +++ b/src/signframework/signecc/cca_functions_ecc.h @@ -0,0 +1 @@ +../cca_functions_ecc.h
\ No newline at end of file diff --git a/src/signframework/signecc/cca_structures.h b/src/signframework/signecc/cca_structures.h new file mode 120000 index 0000000..b974b92 --- /dev/null +++ b/src/signframework/signecc/cca_structures.h @@ -0,0 +1 @@ +../cca_structures.h
\ No newline at end of file diff --git a/src/signframework/signecc/cca_structures_ecc.c b/src/signframework/signecc/cca_structures_ecc.c new file mode 120000 index 0000000..1ed801f --- /dev/null +++ b/src/signframework/signecc/cca_structures_ecc.c @@ -0,0 +1 @@ +../cca_structures_ecc.c
\ No newline at end of file diff --git a/src/signframework/signecc/cca_structures_ecc.h b/src/signframework/signecc/cca_structures_ecc.h new file mode 120000 index 0000000..eb19d39 --- /dev/null +++ b/src/signframework/signecc/cca_structures_ecc.h @@ -0,0 +1 @@ +../cca_structures_ecc.h
\ No newline at end of file diff --git a/src/signframework/signecc/debug.c b/src/signframework/signecc/debug.c new file mode 120000 index 0000000..3943a0d --- /dev/null +++ b/src/signframework/signecc/debug.c @@ -0,0 +1 @@ +../debug.c
\ No newline at end of file diff --git a/src/signframework/signecc/debug.h b/src/signframework/signecc/debug.h new file mode 120000 index 0000000..845c7cc --- /dev/null +++ b/src/signframework/signecc/debug.h @@ -0,0 +1 @@ +../debug.h
\ No newline at end of file diff --git a/src/signframework/signecc/eccutils.c b/src/signframework/signecc/eccutils.c new file mode 100644 index 0000000..b56d4bb --- /dev/null +++ b/src/signframework/signecc/eccutils.c @@ -0,0 +1,393 @@ +/* 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 "debug.h" +#include "utils.h" + +#include "eccutils.h" + +extern FILE* messageFile; +extern int verbose; + +#define CFG_FILES_MAX 100 /* a sufficiently large number of project configuration files */ + +/* GetAuxArgs() parses an ECC auxiliary configuration file of the form: + + sign_algorithm= + digest_algorithm= + check_unique= + raw_header_input= + cfg_files= + project= + ... +*/ + +int GetAuxArgs(char **signAlgorithm, /* freed by caller */ + char **digestAlgorithm, /* freed by caller */ + int *checkUnique, + int *rawHeaderInput, + unsigned int *numberOfProjectFiles, + char ***projectConfigFilenames, /* array freed by caller */ + const char *projectAuxConfigFileName) +{ + int rc = 0; /* general return code */ + size_t i; + char *lineBuffer = NULL; /* freed @2 */ + size_t lineBufferLength = 4000; /* hard code for the project */ + FILE *projectAuxConfigFile = NULL; /* closed @1 */ + + + /* open project auxiliary configuration file */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Opening auxiliary configuration file %s\n", + projectAuxConfigFileName); + projectAuxConfigFile = fopen(projectAuxConfigFileName, "r"); /* closed @1 */ + if (projectAuxConfigFile == NULL) { + fprintf(messageFile, + "ERROR2001: Cannot open auxiliary configuration file %s, %s\n", + projectAuxConfigFileName, strerror(errno)); + rc = ERROR_CODE; + } + } + /* allocate a line buffer, used when parsing the configuration file */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&lineBuffer, /* freed @2 */ + lineBufferLength, + lineBufferLength); /* hard code for the project */ + } + /* signing algorithm */ + if (rc == 0) { + rc = File_MapNameToValue(signAlgorithm, /* freed by caller */ + "sign_algorithm", + lineBuffer, + lineBufferLength, + projectAuxConfigFile); + if (rc != 0) { + fprintf(messageFile, + "ERROR2002: Signing algorithm is not specified in " + "auxiliary configuration file\n"); + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Signing algorithm %s\n", + *signAlgorithm); + } + /* digest algorithm */ + if (rc == 0) { + rc = File_MapNameToValue(digestAlgorithm, /* freed by caller */ + "digest_algorithm", + lineBuffer, + lineBufferLength, + projectAuxConfigFile); + if (rc != 0) { + fprintf(messageFile, + "ERROR2004: Digest algorithm is not " + "specified in auxiliary configuration file\n"); + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Digest algorithm %s\n", + *digestAlgorithm); + } + /* checkUnique boolean */ + if (rc == 0) { + rc = File_MapNameToBool(checkUnique, + "check_unique", + lineBuffer, + lineBufferLength, + projectAuxConfigFile); + if (rc != 0) { + fprintf(messageFile, + "ERROR2005: Check_unique has an illegal value\n"); + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "ProjectConfig_Parse: Check unique: %d\n", + *checkUnique); + } + /* rawHeaderInput boolean */ + if (rc == 0) { + rc = File_MapNameToBool(rawHeaderInput, + "raw_header_input", + lineBuffer, + lineBufferLength, + projectAuxConfigFile); + if (rc != 0) { + fprintf(messageFile, + "ERROR2005a: raw_header_input has an illegal value\n"); + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "ProjectConfig_Parse: Raw Header Input: %d\n", + *rawHeaderInput); + } + /* get the number of project configuration files */ + if (rc == 0) { + rc = File_MapNameToUint(numberOfProjectFiles, + "cfg_files", + lineBuffer, + lineBufferLength, + projectAuxConfigFile); + if (rc != 0) { + fprintf(messageFile, + "ERROR2006: Number of project configuration files " + "is not specified in auxiliary configuration file\n"); + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Number of project configuration files: %u\n", + *numberOfProjectFiles); + } + if (rc == 0) { + if (*numberOfProjectFiles > CFG_FILES_MAX) { + fprintf(messageFile, + "ERROR2007: Number of project configuration files %u is > %u\n", + *numberOfProjectFiles, CFG_FILES_MAX); + rc = ERROR_CODE; + } + if (*numberOfProjectFiles == 0) { + fprintf(messageFile, + "ERROR2008: Number of project configuration files is zero\n"); + rc = ERROR_CODE; + } + } + /* allocate the array of project file names */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)projectConfigFilenames, + sizeof(char *) * (*numberOfProjectFiles), /* freed by caller */ + CFG_FILES_MAX * sizeof(char *)); /* hard code for the project */ + } + /* immediately NULL so that free is safe */ + for (i = 0 ; (rc == 0) && (i < *numberOfProjectFiles) ; i++) { + (*projectConfigFilenames)[i] = NULL; + } + /* get the project file names */ + for (i = 0 ; (rc == 0) && (i < *numberOfProjectFiles) ; i++) { + if (rc == 0) { + rc = File_MapNameToValue(&((*projectConfigFilenames)[i]), /* freed by caller */ + "project", + lineBuffer, + lineBufferLength, + projectAuxConfigFile); + if (rc != 0) { + fprintf(messageFile, + "ERROR2009: Insufficient number of project configuration files\n"); + } + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Project config file name %d: %s\n", + (int)i+1, (*projectConfigFilenames)[i]); + } + } + if (projectAuxConfigFile != NULL) { + fclose(projectAuxConfigFile); /* @1 */ + } + free(lineBuffer); /* @2 */ + return rc; +} + +/* GetSendersArray() gets an array of senders per project. It allocates the ***senders array of + projects. + + Pointers: + + senders - pointer to array of projects + + (*senders) - array of project arrays, allocated here + + (*senders)[i] - project array, allocated by each File_GetValueArray() + + (*senders)[i][j] - project array entry, pointer to sender string, allocated within + File_GetValueArray() + +*/ + +int GetSendersArray(char ****senders, /* array of senders per project, + freed by caller */ + unsigned int **numberOfSenders, /* array of number of senders per + project, freed by caller */ + unsigned int numberOfProjectFiles, + char **projectConfigFilenames) /* array of file names, freed @4 */ +{ + int rc = 0; /* general return code */ + FILE *projectConfigFile = NULL; /* closed @1 */ + + size_t i; /* iterate through project configuration files */ + size_t j; /* interate through senders in a file */ + char *lineBuffer = NULL; /* freed @1 */ + size_t lineBufferLength = 4000; /* hard code for the project */ + + /* allocate arrays for the number of senders and senders list in each */ + /* allocate a line buffer, used when parsing the configuration file */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&lineBuffer, /* freed @1 */ + lineBufferLength, + lineBufferLength); /* hard code for the project */ + } + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)numberOfSenders, /* freed by caller */ + numberOfProjectFiles * sizeof(size_t), + MAX_PROJECT_FILES * sizeof(size_t)); /* hard code for the project */ + + } + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)senders, /* freed by caller */ + numberOfProjectFiles * sizeof(char *), + MAX_PROJECT_FILES * sizeof(char *)); /* hard code for the project */ + + } + /* immediately NULL the array so it can be freed */ + for (i = 0 ; (rc == 0) && (i < numberOfProjectFiles) ; i++) { + (*senders)[i] = NULL; + } + /* iterate through the project configuration files, building arrays of authorized senders */ + for (i = 0 ; (rc == 0) && (i < numberOfProjectFiles) ; i++) { + /* open project configuration file */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "GetSendersArray: Processing project configuration file %s\n", + projectConfigFilenames[i]); + projectConfigFile = fopen(projectConfigFilenames[i], "r"); /* closed @1 */ + if (projectConfigFile == NULL) { + fprintf(messageFile, + "ERROR2010: Could not open project configuration file: %s\n", + projectConfigFilenames[i]); + rc = ERROR_CODE; + } + } + /* determine whether senders are needed */ + int needSenders = 0; + if (rc == 0) { + rc = File_MapNameToBool(&needSenders, + "needsenders", + lineBuffer, + lineBufferLength, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Signing project needs senders: %d\n", needSenders); + } + /* read the list of authorized senders */ + if (rc == 0) { + char ** emails = NULL; + rc = File_GetNameValueArray(&((*senders)[i]), /* freed by caller */ + &emails, /* freed below */ + (size_t*)&((*numberOfSenders)[i]), /* number of authorized senders */ + lineBuffer, + lineBufferLength, + projectConfigFile); + // We don't use the emails so delete right away + for (j = 0; j < (*numberOfSenders)[i]; j ++) { + free(emails[j]); + } + free(emails); + emails = NULL; + } + if (rc == 0) { + rc = File_GetValueArray(&((*senders)[i]), + (size_t*)&((*numberOfSenders)[i]), + "senders", + lineBuffer, + lineBufferLength, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "GetSendersArray: Found %u authorized senders\n", + (*numberOfSenders)[i]); + for (j = 0 ; j < (*numberOfSenders)[i] ; j++) { + if (verbose) fprintf(messageFile, + "GetSendersArray: Sender %d: %s\n", + (int)j+1, (*senders)[i][j]); + } + } + if (projectConfigFile != NULL) { + fclose(projectConfigFile); /* @7 */ + projectConfigFile = NULL; + } + } + free(lineBuffer); /* @1 */ + return rc; +} + +/* CheckSenders() verifies that all senders in the arrays are unique. */ + +int CheckSenders(unsigned int numberOfProjectFiles, + char **projectConfigFilenames, + unsigned int *numberOfSenders, + char ***senders) /* array of senders per project */ +{ + int rc = 0; + size_t i; /* iterate through project configuration files */ + size_t j; /* interate through senders in a file */ + size_t k; /* interate through senders in a file */ + size_t l; /* interate through senders in a file */ + char *senderToCheck; + + /* iterate through all files */ + for (i = 0 ; (rc == 0) && (i < numberOfProjectFiles) ; i++) { + /* iterate through all senders in the file */ + for (j = 0 ; (rc == 0) && (j < numberOfSenders[i]) ; j++) { + senderToCheck = senders[i][j]; + + if (verbose) fprintf(messageFile, + "CheckSenders: Check sender %d %d\n", + (int)i, (int)j); + + + /* first check that the sender is not duplicated in this file */ + for (k = j+1 ; (rc == 0) && (k < numberOfSenders[i]) ; k++) { + if (verbose) fprintf(messageFile, + "CheckSenders: Check against %d %d\n", + (int)i, (int)k); + if (strcmp(senderToCheck, senders[i][k]) == 0) { + fprintf(messageFile, + "ERROR2003: Duplicate sender %s in file %s\n", + senderToCheck, projectConfigFilenames[i]); + rc = ERROR_CODE; + } + } + /* then check that it's not duplicated in any other file */ + for (k = i+1 ; (rc == 0) && (k < numberOfProjectFiles) ; k++) { + for (l = 0 ; (rc == 0) && (l < numberOfSenders[k]) ; l++) { + if (verbose) fprintf(messageFile, + "CheckSenders: Check against %d %d\n", + (int)k, (int)l); + if (strcmp(senderToCheck, senders[k][l]) == 0) { + fprintf(messageFile, + "ERROR2004: Duplicate sender %s in files %s and %s\n", + senderToCheck, + projectConfigFilenames[i], + projectConfigFilenames[k]); + rc = ERROR_CODE; + } + } + } + } + } + return rc; +} diff --git a/src/signframework/signecc/eccutils.h b/src/signframework/signecc/eccutils.h new file mode 100644 index 0000000..fb40bca --- /dev/null +++ b/src/signframework/signecc/eccutils.h @@ -0,0 +1,41 @@ +/* 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. + */ + + +#ifndef ECCUTILS_H +#define ECCUTILS_H + +/* some very high maximums, just to prevent resource exhaustion in case of a configuration error */ +#define MAX_PROJECT_FILES 100 +#define MAX_SENDERS_PER_PROJECT 4000 + +int GetAuxArgs(char **signAlgorithm, + char **digestAlgorithm, + int *checkUnique, + int *rawHeaderInput, + unsigned int *numberOfProjectFiles, + char ***projectConfigFilenames, + const char *projectAuxConfigFileName); +int GetSendersArray(char ****senders, + unsigned int **numberOfSenders, + unsigned int numberOfProjectFiles, + char **projectConfigFilenames); +int CheckSenders(unsigned int numberOfProjectFiles, + char **projectConfigFilenames, + unsigned int *numberOfSenders, + char ***senders); + +#endif diff --git a/src/signframework/signecc/ecdsa521.h b/src/signframework/signecc/ecdsa521.h new file mode 100644 index 0000000..49744ad --- /dev/null +++ b/src/signframework/signecc/ecdsa521.h @@ -0,0 +1,24 @@ +/* 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. + */ +#ifndef ECDSA521_H +#define ECDSA521_H + +#include "types_t.h" + +#define ECDSA521_KEY_SIZE 132 +#define ECDSA521_SIG_SIZE 132 + +#endif diff --git a/src/signframework/signecc/getkeysecc.c b/src/signframework/signecc/getkeysecc.c new file mode 100644 index 0000000..c5defbd --- /dev/null +++ b/src/signframework/signecc/getkeysecc.c @@ -0,0 +1,498 @@ +/* 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> + +/* local */ +#include "ossl_functions.h" +#include "cca_structures.h" +#include "cca_structures_ecc.h" +#include "utils.h" +#include "debug.h" + +#include "eccutils.h" + +/* global variables */ + +FILE *messageFile = NULL; /* needed for utilities */ +int verbose = FALSE; +int debug = FALSE; + +/* local prototypes */ + +int GetArgs(const char **outputBodyFilename, + const char **outputAttachmentFileName, + const char **projectLogFileName, + const char **sender, + const char **project, + const char **type, + const char **auxcfgFilename, + int *verbose, + int argc, + char **argv); +int GetKeyFilename(char **keyFileName, + char *projectConfigFilename); + +void PrintUsage(void); + +/* see printUsage() for the program description */ + +int main(int argc, char** argv) +{ + int rc = 0; /* general return code */ + size_t i; + + /* command line argument defaults */ + const char *outputBodyFilename = NULL; + const char *outputAttachmentFileName = NULL; + const char *projectLogFileName = NULL; /* project audit log */ + FILE *projectLogFile = NULL; /* closed @1 */ + const char *sender = NULL; + const char *project = NULL; + const char *type = NULL; + const char *auxcfgFilename = NULL; /* project auxiliary configuration file name */ + + /* parameters from project auxiliary configuration file */ + char *signAlgorithm = NULL; /* freed @2 */ + char *digestAlgorithm = NULL; /* freed @3 */ + int checkUnique; + int rawHeaderInput; + unsigned int numberOfProjectFiles = 0; + char **projectConfigFilenames = NULL; /* freed @4 */ + + char *keyFileName = NULL; /* signing CCA key token file, freed @5 */ + unsigned char *keyToken = NULL; /* CCA key token, freed @6 */ + size_t keyTokenLength; + EccKeyTokenPublic eccKeyTokenPublic; /* CCA public key structure */ + unsigned char *publicKeyArray = NULL; /* freed @7 */ + size_t publicKeyArrayLength = 0; /* is the currently used area */ + messageFile = stdout; + + /* command line argument defaults */ + verbose = FALSE; + debug = FALSE; + + /* get command line arguments */ + if (rc == 0) { + rc = GetArgs(&outputBodyFilename, /* closed @1 */ + &outputAttachmentFileName, + &projectLogFileName, + &sender, + &project, + &type, + &auxcfgFilename, + &verbose, + argc, argv); + } + /* project audit log */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Opening audit log %s\n", projectLogFileName); + projectLogFile = fopen(projectLogFileName, "a"); /* closed @1 */ + 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]); + } + /* get additional parameters from the project auxiliary configuration file */ + if (rc == 0) { + rc = GetAuxArgs(&signAlgorithm, /* freed @2 */ + &digestAlgorithm, /* freed @3 */ + &checkUnique, + &rawHeaderInput, /* Raw Prefix Header or hash provided? */ + &numberOfProjectFiles, + &projectConfigFilenames, /* array of file names, freed @4 */ + auxcfgFilename); + } + if (rc == 0) { + fprintf(projectLogFile, "\tType: %s\n", type); + } + /* for each signing project file */ + for (i = 0 ; (rc == 0) && (i < numberOfProjectFiles) ; i++) { + + /* get the signing key file name from the project configuration file */ + keyFileName = NULL; + if (rc == 0) { + rc = GetKeyFilename(&keyFileName, /* freed @5 */ + projectConfigFilenames[i]); + } + /* audit logging */ + if (rc == 0) { + fprintf(projectLogFile, "\tProject configuration file name: %s\n", + projectConfigFilenames[i]); + fprintf(projectLogFile, "\tSigning key file name: %s\n", keyFileName); + } + /* read the key token file */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Key token %u at %s\n", + (uint)i+1, keyFileName); + rc = File_ReadBinaryFile(&keyToken, &keyTokenLength, 4000, keyFileName); /* freed @6 */ + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Key token %u length %lu\n", + (uint)i+1, (unsigned long)keyTokenLength); + } + /* extract the public key from the key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Extracting the public key from the key token\n"); + rc = getPKA96EccPublicKey(&eccKeyTokenPublic, /* output: CCA structure */ + keyTokenLength, + keyToken); /* input: PKA96 key token */ + } + if (rc == 0) { + if (verbose) PrintAll(messageFile, + "Public key", + eccKeyTokenPublic.qLen, eccKeyTokenPublic.publicKey); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Growing the array for the public key to %u\n", + (uint)publicKeyArrayLength + eccKeyTokenPublic.qLen); + rc = Realloc_Safe(&publicKeyArray, /* freed @7 */ + publicKeyArrayLength + eccKeyTokenPublic.qLen); + } + /* append the public key to the public keys array */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Appending the public key to the array\n"); + memcpy(publicKeyArray + publicKeyArrayLength, + eccKeyTokenPublic.publicKey, + eccKeyTokenPublic.qLen); + publicKeyArrayLength += eccKeyTokenPublic.qLen; + } + + /* close stuff */ + free(keyFileName); /* @5 */ + keyFileName = NULL; /* for next time through the loop */ + free(keyToken); /* @6 */ + keyToken = NULL; /* for next time through the loop */ + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "Type of response: %s\n", type); + unsigned char digest[SHA512_SIZE]; + /* if a digest is to be returned, today only supports SHA-512, in the future, strcmp() on + digestAlgorithm here */ + if (strcmp(type, "digest") == 0) { + if (verbose) fprintf(messageFile, "Digesting the public key array\n"); + Ossl_SHA512(digest, + publicKeyArrayLength, publicKeyArray, + 0L, NULL); + if (verbose) PrintAll(messageFile, + "Digest of public key array", + SHA512_SIZE, digest); + /* write the public keys digest to the output attachment */ + if (verbose) fprintf(messageFile, "Writing binary to output file %s\n", + outputAttachmentFileName); + rc = File_WriteBinaryFile(digest, SHA512_SIZE, + outputAttachmentFileName); + } + /* if the array of public keys is to be returned */ + else { + /* write the public keys array to the output attachment */ + if (verbose) fprintf(messageFile, "Writing binary to output file %s\n", + outputAttachmentFileName); + rc = File_WriteBinaryFile(publicKeyArray, publicKeyArrayLength, + outputAttachmentFileName); + } + } + /* cleanup */ + File_Printf(projectLogFile, messageFile, "Return code: %u\n", rc); + + if (projectLogFile != NULL) { + fclose(projectLogFile); /* @1 */ + } + free(signAlgorithm); /* @2 */ + free(digestAlgorithm); /* @3 */ + if (projectConfigFilenames != NULL) { + for (i = 0 ; i < numberOfProjectFiles ; i++) { + free(projectConfigFilenames[i]); + } + free(projectConfigFilenames); /* @4 */ + } + free(keyFileName); /* @5 */ + free(keyToken); /* @6 */ + free(publicKeyArray); /* @7 */ + if (messageFile != stdout) { + fflush(messageFile); + fclose(messageFile); + messageFile = stdout; + } + return rc; +} + +int GetKeyFilename(char **keyFileName, /* freed by caller */ + char *projectConfigFilename) +{ + int rc = 0; /* general return code */ + char *lineBuffer = NULL; /* freed @2 */ + size_t lineBufferLength = 4000; /* hard code for the project */ + FILE *projectConfigFile = NULL; /* closed @1 */ + + /* open project configuration file */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Opening project configuration file %s\n", + projectConfigFilename); + projectConfigFile = fopen(projectConfigFilename, "r"); /* closed @1 */ + if (projectConfigFile == NULL) { + fprintf(messageFile, + "ERROR1016: Cannot open project configuration file %s, %s\n", + projectConfigFilename, strerror(errno)); + rc = ERROR_CODE; + } + } + /* allocate a line buffer, used when parsing the configuration file */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)&lineBuffer, /* freed @2 */ + lineBufferLength, + lineBufferLength); /* hard code for the project */ + } + /* digest algorithm */ + if (rc == 0) { + rc = File_MapNameToValue(keyFileName, /* freed by caller */ + "key", + lineBuffer, + lineBufferLength, + projectConfigFile); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, + "Key file name %s\n", + *keyFileName); + } + if (projectConfigFile != NULL) { + fclose(projectConfigFile); /* @1 */ + } + free(lineBuffer); /* @2 */ + return rc; +} + +int GetArgs(const char **outputBodyFilename, /* close as messageFile */ + const char **outputAttachmentFileName, + const char **projectLogFileName, + const char **sender, + const char **project, + const char **type, + const char **auxcfgFilename, + int *verbose, + int argc, + char **argv) +{ + int 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, + "ERROR1001: -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, + "ERROR1002: -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, + "ERROR1003: -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, + "ERROR1004: -project option needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-type") == 0) { + i++; + if (i < argc) { + *type = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1005: -type option needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-auxcfg") == 0) { + i++; + if (i < argc) { + *auxcfgFilename = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1006: -auxcfg 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, + "ERROR1007: -do option needs a value\n"); + 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, + "ERROR1008: -do option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*projectLogFileName == NULL) { + fprintf(messageFile, + "ERROR1009: -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, + "ERROR1011: -project option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*type == NULL) { + fprintf(messageFile, + "ERROR1012: -type option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if ((strcmp(*type, "keys") != 0) && + (strcmp(*type, "digest") != 0)) { + fprintf(messageFile, + "ERROR1013: -type %s illegal value\n", *type); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*auxcfgFilename == NULL) { + fprintf(messageFile, + "ERROR1014: -auxcfg option missing\n"); + rc = ERROR_CODE; + } + } + return rc; +} + +void PrintUsage() +{ + fprintf(messageFile, "\n"); + fprintf(messageFile, + "\tgekeysecc usage:\n" + "\n" + "Common arguments:\n" + "\n" + "\t[-v - verbose logging]\n" + "\t[-h - print usage help]\n" + "\t-type - response type\n" + "\t\tkeys - return an array of public keys\n" + "\t\tdigest - return a digest of an array of public keys\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" + "\t-auxcfg - project auxiliary configuration file name\n" + + "\n" + "Email example: -project getkeysp8hw\n" + "\n" + "Extracts the public keys from the CCA ECC key tokens in the key files\n" + "Returns either an array of keys or a digest of the array\n" + ); + fprintf(messageFile, "\n"); + return; +} diff --git a/src/signframework/signecc/hw_utils.h b/src/signframework/signecc/hw_utils.h new file mode 100644 index 0000000..a833fc6 --- /dev/null +++ b/src/signframework/signecc/hw_utils.h @@ -0,0 +1,96 @@ +/* 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. + */ +#ifndef HW_UTILS_H +#define HW_UTILS_H + +#include "types_t.h" +#include "sha512.h" + +#define ECID_SIZE 16 + +#define HOST_RESET_VECTOR 0x100 + +#define TEST_SYSTEM_MEMORY (64*1024*1024) +#define TRUSTED_MEMORY_BASE (8*1024*1024) // should be 256MB +#define TOTAL_TEST_MEMORY (TEST_SYSTEM_MEMORY+4*1024) +#define CACHE_LINE 128 +#define MEMORY_MASK 0x0fffffffffffffffull +#define CACHE_MASK 0x0fffffffffffff80ull // 128B cache line aligned +#define STACK_MASK 0x0ffffffffffff000ull // 4KB page aligned +#define HRMOR_MASK 0x0ffffffffff00000ull // 1MB aligned +#define HRMOR_IGNORE 0x8000000000000000ull +#define XSCOM_MASK 0x0ffffff800000000ull // 32GB aligned + +#define HRMOR_RELATIVE(_a) ((_a)&~HRMOR_IGNORE) +#define ABSOLUTE_ADDR(_a) ((_a)|HRMOR_IGNORE) +#define PHYSICAL(_a) ((_a)&MEMORY_MASK) + +typedef struct { + uint64_t GPR[32]; + uint64_t SPRG7; + uint64_t HRMOR; + uint64_t SCRATCH_0; + struct { + uint64_t value; + uint64_t mask; + } FSP_BAR; + uint8_t ECID[ECID_SIZE]; + uint8_t OTP_HW_KEY_HASH[SHA512_DIGEST_SIZE]; + uint8_t* data; // 64M+4K malloc/mmap + uint8_t* memory; // 64M (4K aligned) + int mfd; +} hw_settings; + +extern hw_settings HW; + +#define r1 HW.GPR[1] +#define r2 HW.GPR[2] +#define r30 HW.GPR[30] + +extern void HW_Init (void); +extern void HW_Free (void); + +extern void Log (uint64_t code); +extern void Error (uint64_t code); +extern void Check_Stop (char* msg); +extern void Error_Stop (uint64_t code, char* msg); + +extern void assem_DCBI (uint64_t addr); +extern void assem_DCBZ (uint64_t addr); +extern void assem_DCBST (uint8_t* addr); +extern void assem_ICBI (uint8_t* addr); +extern void assem_SYNC (void); +extern void assem_ISYNC (void); + +extern void mtspr_SPRG7 (uint64_t addr); +extern uint64_t mfspr_SPRG7 (void); + +extern void mtspr_HRMOR (uint64_t addr); +extern uint64_t mfspr_HRMOR (void); + +extern void mtspr_SCRATCH_0 (uint64_t addr); +extern uint64_t mfspr_SCRATCH_0 (void); + +extern uint64_t getscom_FSP_BAR_value (uint64_t base); +extern uint64_t getscom_FSP_BAR_mask (uint64_t base); +extern void getscom_HW_ECID (uint64_t base, uint8_t* buf); +extern void getscom_OTP_HW_Key_Hash (uint64_t base, uint8_t* buf); + +extern uint64_t physical_addr (uint64_t addr); +extern uint8_t* Convert_Mem_Addr (uint64_t); +extern uint64_t Convert_Mem_Offset (uint8_t*); + +#endif diff --git a/src/signframework/signecc/makefile b/src/signframework/signecc/makefile new file mode 100644 index 0000000..50980b1 --- /dev/null +++ b/src/signframework/signecc/makefile @@ -0,0 +1,111 @@ +# 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. + +ARCH = $(shell uname) + +CC = /usr/bin/gcc +CCFLAGS = -Wall -W -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -c -ggdb \ + -D$(ARCH) -DUNIX \ + -I/opt/ibm/4767/include \ + -I/usr/local/ssl/include -DADD_ECC + +#OPENSSLDIR = /usr/local/ssl/lib + +# for all programs +LNFLAGS = -ggdb -Wl,-rpath-link -lssl -lcrypto -lcsulcca + +all: signecc getkeysecc + +HEADERS = \ + debug.h \ + cca_functions.h \ + cca_functions_ecc.h \ + cca_structures.h \ + cca_structures_ecc.h \ + eccutils.h \ + ossl_functions.h \ + ossl_functions_ecc.h \ + utils.h + +# signer program + +signecc: signecc.o \ + cca_functions.o cca_functions_ecc.o cca_structures_ecc.o \ + ossl_functions.o ossl_functions_ecc.o \ + debug.o utils.o eccutils.o + $(CC) $(LNFLAGS) signecc.o \ + cca_functions.o cca_functions_ecc.o cca_structures_ecc.o \ + ossl_functions.o ossl_functions_ecc.o \ + debug.o utils.o eccutils.o \ + -o signecc + +getkeysecc: getkeysecc.o \ + ossl_functions.o cca_structures_ecc.o \ + debug.o utils.o eccutils.o + $(CC) $(LNFLAGS) getkeysecc.o \ + ossl_functions.o cca_structures_ecc.o \ + debug.o utils.o eccutils.o \ + -o getkeysecc + +# create links from common programs in FRAMEWORK_DIR + +FRAMEWORK_DIR = .. + +cca_functions.c: + ln -s $(FRAMEWORK_DIR)/cca_functions.c . +cca_functions.h: + ln -s $(FRAMEWORK_DIR)/cca_functions.h . +cca_functions_ecc.c: + ln -s $(FRAMEWORK_DIR)/cca_functions_ecc.c . +cca_functions_ecc.h: + ln -s $(FRAMEWORK_DIR)/cca_functions_ecc.h . +cca_structures.h: + ln -s $(FRAMEWORK_DIR)/cca_structures.h . +cca_structures_ecc.c: + ln -s $(FRAMEWORK_DIR)/cca_structures_ecc.c . +cca_structures_ecc.h: + ln -s $(FRAMEWORK_DIR)/cca_structures_ecc.h . +debug.c: + ln -s $(FRAMEWORK_DIR)/debug.c . +debug.h: + ln -s $(FRAMEWORK_DIR)/debug.h . +ossl_functions.c: + ln -s $(FRAMEWORK_DIR)/ossl_functions.c . +ossl_functions.h: + ln -s $(FRAMEWORK_DIR)/ossl_functions.h . +ossl_functions_ecc.c: + ln -s $(FRAMEWORK_DIR)/ossl_functions_ecc.c . +ossl_functions_ecc.h: + ln -s $(FRAMEWORK_DIR)/ossl_functions_ecc.h . +utils.c: + ln -s $(FRAMEWORK_DIR)/utils.c . +utils.h: + ln -s $(FRAMEWORK_DIR)/utils.h . + +cca_functions.o: $(HEADERS) +cca_functions_ecc.o: $(HEADERS) +cca_structures_ecc.o: $(HEADERS) +debug.o: $(HEADERS) +signecc.o: $(HEADERS) +ossl_functions.o: $(HEADERS) +ossl_functions_ecc.o: $(HEADERS) +utils.o: $(HEADERS) +eccutils.o: $(HEADERS) + +.c.o: + $(CC) $(CCFLAGS) $< + +clean: + rm -f *.o signecc getkeysecc diff --git a/src/signframework/signecc/ossl_functions.c b/src/signframework/signecc/ossl_functions.c new file mode 120000 index 0000000..144a869 --- /dev/null +++ b/src/signframework/signecc/ossl_functions.c @@ -0,0 +1 @@ +../ossl_functions.c
\ No newline at end of file diff --git a/src/signframework/signecc/ossl_functions.h b/src/signframework/signecc/ossl_functions.h new file mode 120000 index 0000000..216efd6 --- /dev/null +++ b/src/signframework/signecc/ossl_functions.h @@ -0,0 +1 @@ +../ossl_functions.h
\ No newline at end of file diff --git a/src/signframework/signecc/ossl_functions_ecc.c b/src/signframework/signecc/ossl_functions_ecc.c new file mode 120000 index 0000000..071c23e --- /dev/null +++ b/src/signframework/signecc/ossl_functions_ecc.c @@ -0,0 +1 @@ +../ossl_functions_ecc.c
\ No newline at end of file diff --git a/src/signframework/signecc/ossl_functions_ecc.h b/src/signframework/signecc/ossl_functions_ecc.h new file mode 120000 index 0000000..58aaa87 --- /dev/null +++ b/src/signframework/signecc/ossl_functions_ecc.h @@ -0,0 +1 @@ +../ossl_functions_ecc.h
\ No newline at end of file diff --git a/src/signframework/signecc/regress-ecc.sh b/src/signframework/signecc/regress-ecc.sh new file mode 100755 index 0000000..959b90b --- /dev/null +++ b/src/signframework/signecc/regress-ecc.sh @@ -0,0 +1,613 @@ +#!/bin/sh +# 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. + +# +# signer framework regression test - cosigner tests +# +# Run this script locally +# +# Prerequisites: +# The coprocessor should have a profile (user) $USER with password $PWD. See below. +# + +#set -x + +ECCKEY_A_TOK=tmpkeya.tok +ECCKEY_B_TOK=tmpkeyb.tok +ECCKEY_C_TOK=tmpkeyc.tok +ECCKEY_P_TOK=tmpkeyp.tok +ECCKEY_Q_TOK=tmpkeyq.tok +ECCKEY_R_TOK=tmpkeyr.tok + +ECCKEY_A_BIN=tmpkeya.bin +ECCKEY_B_BIN=tmpkeyb.bin +ECCKEY_C_BIN=tmpkeyc.bin +ECCKEY_P_BIN=tmpkeyp.bin +ECCKEY_Q_BIN=tmpkeyq.bin +ECCKEY_R_BIN=tmpkeyr.bin + +# CCA test user and password +USER=test +PWD=qazTGB + +# output email body, for error messages +OBODY=tmpobody.txt + +# aux config file +AUXCONFIG=tmpaux.cfg +AUXCONFIGA=tmpauxa.cfg + +# audit log +AUDITLOG=tmp.log + +# signature +SIGFILE=tmpsig.bin + +removeFiles() +{ + rm -f $AUXCONFIG + rm -f $AUXCONFIGA + rm -f $OBODY +} + +# searches $1 for "Return code:" and returns the value */ + +getReturnCode() +{ + returncode=`grep -m 1 "Return code:" $1 | gawk '{print $3 }'` + return $returncode +} + +# searches $1 for "ERRORnnnn" and returns the value */ + +getErrorCode() +{ + errorcode=`grep -m 1 "ERROR" $1 | gawk '{print $1}' | sed s/ERROR// | sed s/:// ` + return $errorcode +} + +# searches $1 for "ERRORnnnn" at the end of the file and returns the value */ + +getLastErrorCode() +{ + errorcode=`tail -1 $1 | grep -m 1 "ERROR" | gawk '{print $1}' | sed s/ERROR// | sed s/:// ` + return $errorcode +} + +# searches $1 for "reason_code" and returns the value */ + +getReasonCode() +{ + reasoncode=`grep -m 1 "reason_code" $1 | gawk '{print $6}' ` + return $reasoncode +} + +sendFile() +{ +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg "$1" -di b64.bin -do $SIGFILE -project RegressionTest "$2" + +} + +# +# Regression Tests Start Here ... +# + +echo "Create six test keys" +../keygeneccp521 -k $ECCKEY_A_TOK -p $ECCKEY_A_BIN -u $USER $PWD +../keygeneccp521 -k $ECCKEY_B_TOK -p $ECCKEY_B_BIN -u $USER $PWD +../keygeneccp521 -k $ECCKEY_C_TOK -p $ECCKEY_C_BIN -u $USER $PWD +../keygeneccp521 -k $ECCKEY_P_TOK -p $ECCKEY_P_BIN -u $USER $PWD +../keygeneccp521 -k $ECCKEY_Q_TOK -p $ECCKEY_Q_BIN -u $USER $PWD +../keygeneccp521 -k $ECCKEY_R_TOK -p $ECCKEY_R_BIN -u $USER $PWD + + +# check_unique false + +echo "Testing Basic ECC hwa success case" +removeFiles +sendFile signeccsample-hwaux.cfg +getReturnCode $OBODY +if [ "$returncode" -eq "0" ]; then + echo " INFO: Basic ECC hwa success case OK" +else + echo " ERROR: Basic ECC hwa success case failed" + exit $returncode +fi + +# auxiliary cfg tests + +echo "Testing Missing aux config file - ERROR2001" +removeFiles +sendFile signeccsample-hwaux.cfgxxx +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2001" ]; then + echo " INFO: Missing aux config file OK" +else + echo " ERROR: Missing aux config file failed" + exit $returncode +fi + +echo "Testing Missing Sign Algorithm - ERROR2002" +removeFiles +sed /sign_algorithm/d signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2002" ]; then + echo " INFO: Missing Sign Algorithm OK" +else + echo " ERROR: Missing Sign Algorithm failed" + exit $returncode +fi + +echo "Testing Bad Sign Algorithm - ERROR1027" +removeFiles +sed s/eccp521/rsa/ signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1027" ]; then + echo " INFO: Bad Sign Algorithm - OK" +else + echo " ERROR: Bad Sign Algorithm - failed" + exit $returncode +fi + +echo "Testing Missing digest algorithm - ERROR2004" +removeFiles +sed /digest_algorithm/d signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2004" ]; then + echo " INFO: Missing digest algorithm OK" +else + echo " ERROR: Missing digest algorithm failed" + exit $returncode +fi + +echo "Testing Bad digest algorithm - ERROR1028" +removeFiles +sed s/SHA-512/SHA-256/ signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1028" ]; then + echo " INFO: Bad digest algorithm OK" +else + echo " ERROR: Bad digest algorithm failed" + exit $returncode +fi + +echo "Testing Bad check unique value - ERROR2005" +removeFiles +sed s/check_unique=false/check_unique=falsexxx/ signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2005" ]; then + echo " INFO: Bad check unique value OK" +else + echo " ERROR: Bad check unique value failed" + exit $returncode +fi + +echo "Testing cfg file count illegal - ERROR2006" +removeFiles +sed s/cfg_files=3/cfg_files=3a/ signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG -v +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2006" ]; then + echo " INFO: cfg file count illegal OK" +else + echo " ERROR: cfg file count illegal failed" + exit $returncode +fi + +echo "Testing cfg file count too large >100 - ERROR2007" +removeFiles +sed s/cfg_files=3/cfg_files=101/ signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG -v +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2007" ]; then + echo " INFO: cfg file count too largeOK" +else + echo " ERROR: cfg file count too largefailed" + exit $returncode +fi + +echo "Testing cfg file count 0 - ERROR2008 " +removeFiles +sed s/cfg_files=3/cfg_files=0/ signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2008" ]; then + echo " INFO: cfg file count 0 OK" +else + echo " ERROR: cfg file count 0 failed" + exit $returncode +fi + +echo "Testing cfg files too small - ERROR2009" +removeFiles +sed s/cfg_files=3/cfg_files=4/ signeccsample-hwaux.cfg > $AUXCONFIG +sendFile $AUXCONFIG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2009" ]; then + echo " INFO: cfg files too small OK" +else + echo " ERROR: cfg files too small failed" + exit $returncode +fi + +echo "Testing cfg file missing - ERROR2010" +removeFiles +sed s/signeccsample-hwa.cfg/signeccsample-hwaxxx.cfg/ signeccsample-hwaux.cfg > $AUXCONFIGA +sed s/check_unique=false/check_unique=true/ $AUXCONFIGA > $AUXCONFIG +sendFile $AUXCONFIG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "2010" ]; then + echo " INFO: cfg file missing OK" +else + echo " ERROR: cfg file missing failed" + exit $returncode +fi + + +# input files + +echo "Testing read only audit log - ERROR1018" +removeFiles +touch $AUDITLOG +chmod a-w $AUDITLOG +sendFile signeccsample-hwaux.cfg +chmod u+w $AUDITLOG +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1018" ]; then + echo " INFO: read only audit log OK" +else + echo " ERROR: read only audit log failed" + exit $returncode +fi + +echo "Testing missing key file - ERROR1019" +removeFiles +mv $ECCKEY_A_TOK ${ECCKEY_A_TOK}.save +sendFile signeccsample-hwaux.cfg +cp ${ECCKEY_A_TOK}.save ${ECCKEY_A_TOK} +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1019" ]; then + echo " INFO: missing key file OK" +else + echo " ERROR: missing key file failed" + exit $returncode +fi + +echo "Testing not readable key file - ERROR1019" +removeFiles +chmod a-r ${ECCKEY_A_TOK} +sendFile signeccsample-hwaux.cfg +chmod a+r ${ECCKEY_A_TOK} +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1019" ]; then + echo " INFO: not readable key file OK" +else + echo " ERROR: not readable key file failed" + exit $returncode +fi + +echo "Testing read only attachment - ERROR1020" +removeFiles +chmod a-r b64.bin +sendFile signeccsample-hwaux.cfg +chmod a+r b64.bin +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1020" ]; then + echo " INFO: read only attachment OK" +else + echo " ERROR: read only attachment failed" + exit $returncode +fi + +echo "Testing read only output file - ERROR1023" +removeFiles +chmod a-w $SIGFILE +sendFile signeccsample-hwaux.cfg +chmod u+w $SIGFILE +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1023" ]; then + echo " INFO: read only attachment OK" +else + echo " ERROR: read only attachment failed" + exit $returncode +fi + +# +# command line arguments +# + +echo "Testing missing value for -usr - ERROR1002" +removeFiles +./signecc -obody $OBODY -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest -usr +sendFile +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1002" ]; then + echo " INFO: missing value for -usr OK" +else + echo " ERROR: missing value for -usr failed" + exit $returncode +fi + +echo "Testing missing value for -pwd - ERROR1003" +removeFiles +./signecc -obody $OBODY -usr $USER -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest -pwd +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1003" ]; then + echo " INFO: missing value for -pwd OK" +else + echo " ERROR: missing value for -pwd failed" + exit $returncode +fi + +echo "Testing missing value for -log - ERROR1004 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest -log +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1004" ]; then + echo " INFO: missing value for -log OK" +else + echo " ERROR: missing value for -log failed" + exit $returncode +fi + +echo "Testing missing value for -sender - ERROR1005" +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest -sender +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1005" ]; then + echo " INFO: missing value for -sender OK" +else + echo " ERROR: missing value for -sender failed" + exit $returncode +fi + +echo "Testing missing value for -project - ERROR1006" +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1006" ]; then + echo " INFO: missing value for -project OK" +else + echo " ERROR: missing value for -project failed" + exit $returncode +fi + +echo "Testing missing value for -key - ERROR1007 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest -key +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1007" ]; then + echo " INFO: missing value for -key OK" +else + echo " ERROR: missing value for -key failed" + exit $returncode +fi + +echo "Testing missing value for -auxcfg - ERROR1008" +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -di b64.bin -do $SIGFILE -project RegressionTest -auxcfg +sendFile signeccsample-hwaux.cfg +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1008" ]; then + echo " INFO: missing value for -auxcfg OK" +else + echo " ERROR: missing value for -auxcfg failed" + exit $returncode +fi + +echo "Testing missing value for -di - ERROR1009" +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -do $SIGFILE -project RegressionTest -di +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1009" ]; then + echo " INFO: missing value for -di OK" +else + echo " ERROR: missing value for -di failed" + exit $returncode +fi + +echo "Testing missing value for -do - ERROR1010" +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -project RegressionTest -do +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1010" ]; then + echo " INFO: missing value for -do OK" +else + echo " ERROR: missing value for -do failed" + exit $returncode +fi + +echo "Testing missing -usr - ERROR1011 " +removeFiles +./signecc -obody $OBODY -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1011" ]; then + echo " INFO: missing -usr OK" +else + echo " ERROR: missing -usr failed" + exit $returncode +fi + +echo "Testing bad usr - ERROR1024 " +removeFiles +./signecc -obody $OBODY -usr ${USER}x -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1024" ]; then + echo " INFO: bad usr OK" +else + echo " ERROR: bad usr failed" + exit $returncode +fi + +echo "Testing missing -pwd - ERROR1012 " +removeFiles +./signecc -obody $OBODY -usr $USER -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1012" ]; then + echo " INFO: missing -pwd OK" +else + echo " ERROR: missing -pwd failed" + exit $returncode +fi + +echo "Testing bad -pwd - ERROR1024 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd aaaaa -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1024" ]; then + echo " INFO: bad -pwd OK" +else + echo " ERROR: bad -pwd failed" + exit $returncode +fi + +echo "Testing missing -sender - ERROR1013 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest +sendFile signeccsample-hwaux.cfg +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1013" ]; then + echo " INFO: OK" +else + echo " ERROR: failed" + exit $returncode +fi + +echo "Testing missing -log - ERROR1014 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest +sendFile signeccsample-hwaux.cfg +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1014" ]; then + echo " INFO: OK" +else + echo " ERROR: failed" + exit $returncode +fi + +echo "Testing missing -key - ERROR1015 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -auxcfg signeccsample-hwaux.cfg -di b64.bin -do $SIGFILE -project RegressionTest +sendFile signeccsample-hwaux.cfg +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1015" ]; then + echo " INFO: OK" +else + echo " ERROR: failed" + exit $returncode +fi + +echo "Testing missing -auxcfg - ERROR1026 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -di b64.bin -do $SIGFILE -project RegressionTest +sendFile signeccsample-hwaux.cfg +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1026" ]; then + echo " INFO: OK" +else + echo " ERROR: failed" + exit $returncode +fi + +echo "Testing missing -di - ERROR1016 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -do $SIGFILE -project RegressionTest +sendFile signeccsample-hwaux.cfg +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1016" ]; then + echo " INFO: OK" +else + echo " ERROR: failed" + exit $returncode +fi + +echo "Testing missing -do - ERROR1017 " +removeFiles +./signecc -obody $OBODY -usr $USER -pwd $PWD -sender "test@email.com" -log $AUDITLOG -key $ECCKEY_A_TOK -auxcfg signeccsample-hwaux.cfg -di b64.bin -project RegressionTest +sendFile signeccsample-hwaux.cfg +getReturnCode $OBODY +getErrorCode $OBODY +if [ "$returncode" -eq "1" ] && [ "$errorcode" -eq "1017" ]; then + echo " INFO: OK" +else + echo " ERROR: failed" + exit $returncode +fi + +echo "" +echo "Tests Passed" +echo "" + +removeFiles +rm $SIGFILE +rm $AUDITLOG +rm ${ECCKEY_A_TOK}.save + +rm -f $ECCKEY_A_TOK +rm -f $ECCKEY_B_TOK +rm -f $ECCKEY_C_TOK +rm -f $ECCKEY_P_TOK +rm -f $ECCKEY_Q_TOK +rm -f $ECCKEY_R_TOK + +rm -f $ECCKEY_A_BIN +rm -f $ECCKEY_B_BIN +rm -f $ECCKEY_C_BIN +rm -f $ECCKEY_P_BIN +rm -f $ECCKEY_Q_BIN +rm -f $ECCKEY_R_BIN + +exit 0 + diff --git a/src/signframework/signecc/regress-eccloop.sh b/src/signframework/signecc/regress-eccloop.sh new file mode 100755 index 0000000..5e6feb0 --- /dev/null +++ b/src/signframework/signecc/regress-eccloop.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# 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. + +#set -x + +i=0 + +#while [ $i -le 10000 ]; +while [ $i -le 10 ]; + +do + + i=`expr ${i} + 1` + + rm obody.txt + + echo "Starting loop $i" + + openssl rand 64 -out regression-hash.bin + + ../keygeneccp521 -k regression-key.bin -p regression-pubkey.bin + + if [ $? -ne 0 ]; then + echo " ERROR: Could not create key" + exit 1 + fi + + ./signecc -obody obody.txt -usr test -pwd qazTGB -sender "test@email.com" -log regression.log -key regression-key.bin -auxcfg signecc-hwaux.cfg -di regression-hash.bin -do regression.sig -project RegressionTest + + if [ $? -ne 0 ]; then + echo " ERROR: Could not sign" + exit 1 + fi + +done + +rm obody.txt +rm regression.log +rm regression-key.bin +rm regression-pubkey.bin +rm regression.sig +rm regression-hash.bin diff --git a/src/signframework/signecc/sha512.h b/src/signframework/signecc/sha512.h new file mode 100644 index 0000000..e1ab1f2 --- /dev/null +++ b/src/signframework/signecc/sha512.h @@ -0,0 +1,36 @@ +/* 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. + */ + +#ifndef SHA512_H +#define SHA512_H + +#include "types_t.h" + +#define SHA512_DIGEST_SIZE 64 + +typedef struct { + uint64_t M[16]; + uint64_t H[8]; + uint64_t Q; + uint64_t L; +} SHA512; + +extern void SHA512_Init (SHA512* sha); +extern void SHA512_Update (SHA512* sha, uint8_t* M, uint64_t len); +extern void SHA512_Finish (SHA512* sha, uint8_t* H); +extern void SHA512_Hash (SHA512* sha, uint8_t* M, uint64_t len, uint8_t* H); + +#endif diff --git a/src/signframework/signecc/signecc-hwa.cfg.sample b/src/signframework/signecc/signecc-hwa.cfg.sample new file mode 100644 index 0000000..8bdd66a --- /dev/null +++ b/src/signframework/signecc/signecc-hwa.cfg.sample @@ -0,0 +1,40 @@ +# HWA project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=signecc/signecc + +# File name for the project audit log + +log=signecc/signecc.log + +# project signing key file name + +needkey=true +key=signecc/ecckeya.tok + +# auxiliary project configuration file + +needauxcfg=true +auxcfg=signecc/signecc-hwaux.cfg + +neediatt=true +needoatt=true +needpwd=true + +# Project administrator email address + +email=joe@email.com + +# Notification receivers (emails) + +notifs=1 +joe@email.com + +# Authorized senders (signers) (userid=email) + +needsenders=true +fred=fred@email.com + diff --git a/src/signframework/signecc/signecc-hwaux.cfg.sample b/src/signframework/signecc/signecc-hwaux.cfg.sample new file mode 100644 index 0000000..2a29224 --- /dev/null +++ b/src/signframework/signecc/signecc-hwaux.cfg.sample @@ -0,0 +1,12 @@ +# P8 project auxiliary configuration file + +# NOTE: The parameters must be in this order! + +sign_algorithm=eccp521 +digest_algorithm=SHA-512 +check_unique=true +raw_header_input=true +cfg_files=3 +project=signecc/signecc-hwa.cfg +project=signecc/signecc-hwb.cfg +project=signecc/signecc-hwc.cfg diff --git a/src/signframework/signecc/signecc-hwb.cfg.sample b/src/signframework/signecc/signecc-hwb.cfg.sample new file mode 100644 index 0000000..300c47c --- /dev/null +++ b/src/signframework/signecc/signecc-hwb.cfg.sample @@ -0,0 +1,40 @@ +# HWB project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=signecc/signecc + +# File name for the project audit log + +log=signecc/signecc.log + +# project signing key file name + +needkey=true +key=signecc/ecckeyb.tok + +# auxiliary project configuration file + +needauxcfg=true +auxcfg=signecc/signecc-hwaux.cfg + +neediatt=true +needoatt=true +needpwd=true + +# Project administrator email address + +email=joe@email.com + +# Notification receivers (emails) + +notifs=1 +joe@email.com + + +# Authorized senders (signers) (userid=email) + +needsenders=true +fred=fred@email.com diff --git a/src/signframework/signecc/signecc-hwc.cfg.sample b/src/signframework/signecc/signecc-hwc.cfg.sample new file mode 100644 index 0000000..b31166a --- /dev/null +++ b/src/signframework/signecc/signecc-hwc.cfg.sample @@ -0,0 +1,41 @@ +# HWC project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=signecc/signecc + +# File name for the project audit log + +log=signecc/signecc.log + +# project signing key file name + +needkey=true +key=signecc/ecckeyc.tok + +# auxiliary project configuration file + +needauxcfg=true +auxcfg=signecc/signecc-hwaux.cfg + +neediatt=true +needoatt=true +needpwd=true + +# Project administrator email address + +email=joe@email.com + +# Notification receivers (emails) + +notifs=1 +joe@email.com + + +# Authorized senders (signers) (userid=email) + +needsenders=true +fred=fred@email.com + diff --git a/src/signframework/signecc/signecc-swaux.cfg.sample b/src/signframework/signecc/signecc-swaux.cfg.sample new file mode 100644 index 0000000..68176f6 --- /dev/null +++ b/src/signframework/signecc/signecc-swaux.cfg.sample @@ -0,0 +1,12 @@ +# P8 project auxiliary configuration file + +# NOTE: The parameters must be in this order! + +sign_algorithm=eccp521 +digest_algorithm=SHA-512 +check_unique=false +raw_header_input=false +cfg_files=3 +project=signecc/signecc-swp.cfg +project=signecc/signecc-swq.cfg +project=signecc/signecc-swr.cfg diff --git a/src/signframework/signecc/signecc-swp.cfg.sample b/src/signframework/signecc/signecc-swp.cfg.sample new file mode 100644 index 0000000..b771f16 --- /dev/null +++ b/src/signframework/signecc/signecc-swp.cfg.sample @@ -0,0 +1,39 @@ +# SWP project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=signecc/signecc + +# File name for the project audit log + +log=signecc/signecc.log + +# project signing key file name + +needkey=true +key=signecc/ecckeyp.tok + +# auxiliary project configuration file + +needauxcfg=true +auxcfg=signecc/signecc-swaux.cfg + +neediatt=true +needoatt=true +needpwd=true + +# Project administrator email address + +email=joe@email.com + +# Notification receivers (emails) + +notifs=3 +joe@email.com + +# Authorized senders (signers) (userid=email) + +needsenders=true +fred=fred@email.com diff --git a/src/signframework/signecc/signecc-swq.cfg.sample b/src/signframework/signecc/signecc-swq.cfg.sample new file mode 100644 index 0000000..1e8e258 --- /dev/null +++ b/src/signframework/signecc/signecc-swq.cfg.sample @@ -0,0 +1,40 @@ +# SWQ project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=signecc/signecc + +# File name for the project audit log + +log=signecc/signecc.log + +# project signing key file name + +needkey=true +key=signecc/ecckeyq.tok + +# auxiliary project configuration file + +needauxcfg=true +auxcfg=signecc/signecc-swaux.cfg + +neediatt=true +needoatt=true +needpwd=true + +# Project administrator email address + +email=joe@email.com + +# Notification receivers (emails) + +notifs=1 +joe@email.com + +# Authorized senders (signers) (userid=email) + +needsenders=true +fred=fred@email.com + diff --git a/src/signframework/signecc/signecc-swr.cfg.sample b/src/signframework/signecc/signecc-swr.cfg.sample new file mode 100644 index 0000000..e8ebcac --- /dev/null +++ b/src/signframework/signecc/signecc-swr.cfg.sample @@ -0,0 +1,44 @@ +# SWR project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=signecc/signecc + +# File name for the project audit log + +log=signecc/signecc.log + +# project signing key file name + +needkey=true +key=signecc/ecckeyr.tok + +# auxiliary project configuration file + +needauxcfg=true +auxcfg=signecc/signecc-swaux.cfg +# auxiliary project configuration file + +needauxcfg=false + +neediatt=true +needoatt=true +needpwd=true + +# Project administrator email address + +email=joe@email.com + +# Notification receivers (emails) + +notifs=1 +joe@email.com + + +# Authorized senders (signers) (userid=email) + +needsenders=true +fred=fred@email.com + diff --git a/src/signframework/signecc/signecc.c b/src/signframework/signecc/signecc.c new file mode 100644 index 0000000..f9f6971 --- /dev/null +++ b/src/signframework/signecc/signecc.c @@ -0,0 +1,954 @@ +/* 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. + */ + +/* This program signs with an ECC P521 key. Since ECC doesn't add an object identifier (OID), this + one program will sign a hash of any length that will fit. */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> + +#include "openssl/evp.h" + +#include "cca_structures.h" +#include "cca_structures_ecc.h" +#include "cca_functions.h" +#include "cca_functions_ecc.h" +#include "ossl_functions_ecc.h" +#include "ossl_functions.h" +#include "utils.h" +#include "debug.h" + +#include "eccutils.h" + +#include "Container.h" + +/* local prototypes */ + +int GetArgs(const char **outputBodyFilename, + const char **usr, + const char **password, + const char **projectLogFileName, + const char **sender, + const char **project, + const char **auxcfgFilename, + const char **keyFileName, + const char **inputAttachmentFileName, + const char **outputAttachmentFileName, + int *verbose, + int argc, + char **argv); +int CheckAlgorithms(const char *signAlgorithm, + const char *digestAlgorithm); + +void PrintUsage(void); + +int CheckPrefix(const char *inputAttachmentFileName, /* file holding prefix binary, also the output file */ + FILE *projectLogFile); /* audit log file */ +uint16_t getUint16( uint8_t *data, int p_endianess ); +uint32_t getUint32( uint8_t *data, int p_endianess ); +void putUint32( uint32_t *value, uint8_t *data, int p_endianess ); + +int Sign(const char *keyFileName, + const char *inputAttachmentFileName, + const char *outputAttachmentFileName, + FILE *projectLogFile); +int SignECCP521(unsigned char *keyToken, + size_t keyTokenLength, + unsigned char *testMessage, + size_t testMessageLength, + const char *outputAttachmentFileName, + FILE *projectLogFile); + +/* messages are traced here */ +FILE *messageFile = NULL; +int verbose = FALSE; +int debug = FALSE; + +int main(int argc, char** argv) +{ + int rc = 0; + int rc1 = 0; + size_t i; /* iterate through project configuration files */ + size_t j; /* interate through senders in a file */ + time_t log_time; + int loggedIn = FALSE; + + /* command line parameters */ + const char *usr = NULL; + const char *password = NULL; + const char *projectLogFileName = NULL; + FILE *projectLogFile = NULL; /* closed @1 */ + const char *sender = NULL; + const char *project = NULL; + const char *auxcfgFilename = NULL; /* project auxiliary configuration file name */ + const char *keyFileName = NULL; + const char *inputAttachmentFileName = NULL; + const char *outputAttachmentFileName = NULL; + const char *outputBodyFilename = NULL; + + /* parameters from project auxiliary configuration file */ + char *signAlgorithm = NULL; /* freed @2 */ + char *digestAlgorithm = NULL; /* freed @3 */ + int checkUnique; + int rawHeaderInput; + unsigned int numberOfProjectFiles = 0; + char **projectConfigFilenames = NULL; /* freed @4 */ + unsigned int *numberOfSenders = NULL; /* array of number of senders per + project, freed @5 */ + char ***senders = NULL; /* array of senders per project, + freed @6 */ + messageFile = stdout; + + /* get caller's command line arguments */ + if (rc == 0) { + rc = GetArgs(&outputBodyFilename, + &usr, + &password, + &projectLogFileName, + &sender, + &project, + &auxcfgFilename, + &keyFileName, + &inputAttachmentFileName, + &outputAttachmentFileName, + &verbose, + argc, argv); + } + /* get auxiliary arguments from auxiliary configuration file */ + if (rc == 0) { + rc = GetAuxArgs(&signAlgorithm, /* freed @2 */ + &digestAlgorithm, /* freed @3 */ + &checkUnique, + &rawHeaderInput, /* Raw Prefix Header or hash provided? */ + &numberOfProjectFiles, + &projectConfigFilenames, /* array of file names, freed @4 */ + auxcfgFilename); + } + /* verify that the specified crypto algorithms are supported */ + if (rc == 0) { + rc = CheckAlgorithms(signAlgorithm, + digestAlgorithm); + } + /* The auxiliary configuration file links multiple signing projects */ + if ((rc == 0) && checkUnique) { + rc = GetSendersArray(&senders, + &numberOfSenders, /* freed @5 */ + numberOfProjectFiles, + projectConfigFilenames); /* array of file names */ + } + /* if required, check that all co-signer senders in the project configuration files are unique. + This ensures that no one co-signer is authorized for more than one signing key. */ + if ((rc == 0) && checkUnique) { + rc = CheckSenders(numberOfProjectFiles, + projectConfigFilenames, + numberOfSenders, + senders); + } + /* audit logging */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Opening audit log %s\n", projectLogFileName); + projectLogFile = fopen(projectLogFileName, "a"); /* closed @1 */ + if (projectLogFile == NULL) { + fprintf(messageFile, "ERROR1018: 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"); + log_time = time(NULL); + fprintf(projectLogFile, "\n%s", ctime(&log_time)); + fprintf(projectLogFile, "\tSender : %s\n", sender); + fprintf(projectLogFile, "\tProject : %s\n", project); + fprintf(projectLogFile, "\tProgram : %s\n", argv[0]); + fprintf(projectLogFile, "\tKey file: %s\n", keyFileName); + fprintf(projectLogFile, "\tProfile : %s\n", usr); + } + /* log in to CCA */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Logging in with user name %s\n", usr); + rc = Login_Control(TRUE, /* log in */ + usr, /* CCA profile */ + password); /* CCA password */ + if (rc == 0) { + loggedIn = TRUE; + } + else { + File_Printf(projectLogFile, messageFile, + "ERROR1024 : Login failed, Bad user name %s or bad password\n", usr); + } + } + /* If raw prefix provided as input, apply required container policies by checking flag field bits */ + if ((rc == 0) && rawHeaderInput) { + if (verbose) fprintf(messageFile, "Checking Prefix Header\n"); + if (verbose) fprintf(messageFile, " Input attachment %s\n", inputAttachmentFileName); + rc = CheckPrefix(inputAttachmentFileName, + projectLogFile); + } + /* sign and verify */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Signing\n"); + if (verbose) fprintf(messageFile, " Input attachment %s\n", inputAttachmentFileName); + if (verbose) fprintf(messageFile, " Output attachment %s\n", outputAttachmentFileName); + rc = Sign(keyFileName, + inputAttachmentFileName, + outputAttachmentFileName, + projectLogFile); + } + /* log out of CCA */ + if (loggedIn) { + if (verbose) fprintf(messageFile, "Logging out with user name %s\n", usr); + rc1 = Login_Control(FALSE, /* log out */ + usr, /* CCA profile */ + NULL); /* password */ + if (rc == 0) { + rc = rc1; + } + } + File_Printf(projectLogFile, messageFile, + "Return code: %u\n", rc); + /* clean up */ + if (projectLogFile != NULL) { + fclose(projectLogFile); /* @1 */ + } + free(signAlgorithm); /* @2 */ + free(digestAlgorithm); /* @3 */ + if (projectConfigFilenames != NULL) { + for (i = 0 ; i < numberOfProjectFiles ; i++) { + free(projectConfigFilenames[i]); + } + free(projectConfigFilenames); /* @4 */ + } + if (senders != NULL) { + for (i = 0 ; i < numberOfProjectFiles ; i++) { + if (senders[i] != NULL) { + for (j = 0 ; j < numberOfSenders[i] ; j++) { + free(senders[i][j]); + } + } + free(senders[i]); + } + free(senders); /* @6 */ + } + free(numberOfSenders); /* @5 */ + if (messageFile != stdout) { + fflush(messageFile); + fclose(messageFile); + } + return rc; +} + +/* CheckAlgorithms() verifies that the crypto algorithms are valid for the project. + + Currently, only eccp521 and SHA-512 are supported. +*/ + +int CheckAlgorithms(const char *signAlgorithm, + const char *digestAlgorithm) +{ + int rc = 0; + + if (rc == 0) { + if (strcmp(signAlgorithm, "eccp521") != 0) { + fprintf(messageFile, + "ERROR1027: Signing algorithm %s not supported\n", + signAlgorithm); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (strcmp(digestAlgorithm, "SHA-512") != 0) { + fprintf(messageFile, + "ERROR1028: Digest algorithm %s not supported\n", + digestAlgorithm); + rc = ERROR_CODE; + } + } + return rc; +} +/* CheckPrefix() receives the rawcontainer header as input and executes policy checks + * against raw before hashing into inputfile to be used for rest of signing flow + * + * 1. Never sign a HW Prefix Hdr w/ .new key. bit on + * 2. Policy change later to allow Prod key 1 to prod key 2 if/as required + * 3. Imprint to prod key 1 header can be signed w/o sign server (imprint only) + * 4. NEVER sign a HW Prefix Hdr w/ any Attributes flags set (bits 12-23) + * + * HW Prefix Header flags field + * Key sets: bits 0-11 + * Attributes: bits 12-23 + * Special Containers: bits 24-31 + * flags (FW key indicator) + * 1000 0000 0000 0000 0000 0000 0000 0000 Images signed by KeySet 1 (op-build) + * 0100 0000 0000 0000 0000 0000 0000 0000 Images signed by KeySet 2 (fips-build) + * 0010 0000 0000 0000 0000 0000 0000 0000 Images signed by KeySet 3 (ODM e.g., IBM AIX Kernel) + * --- for attributes + * xxxx xxxx xxxx 1000 0000 0000 xxxx xxxx Enable SBE checking of mailbox scratch reg for secure boot disable req + * --- for .new key. container + * 0000 0000 0000 0000 0000 0000 0000 0001 New Key Container which is signed by the current key + * (Nested Payload Image signed by new key) + */ +int CheckPrefix(const char *inputAttachmentFileName, /* file holding prefix binary, also the output file */ + FILE *projectLogFile) /* audit log file */ +{ + int rc = 0; + unsigned char *p_raw_prefix = NULL; /* prefix header to be analyzed */ + unsigned char *p_hdr = NULL; + unsigned char *p_flags = NULL; + PrefixHdr *hwPrefixHdr = NULL; /* prefix header overlay */ + size_t prefixSize; + unsigned char digest[SHA512_SIZE]; /* digest to be generated */ + const uint32_t OP_BLD_CONTAINER = 0x80000000; + const uint32_t FIPS_BLD_CONTAINER = 0x40000000; + const uint32_t NEW_KEY_CONTAINER = 0x00000001; + const uint32_t ATTR_FLAG_MASK = 0x000FFF00; + const uint16_t HDR_LEN = 98; + const uint8_t HDR_VER = 1; + const uint8_t HDR_HASH = 1; + const uint8_t HDR_SIG = 1; + + if (verbose) fprintf(messageFile, "CheckPrefix: Reading input file %s\n", + inputAttachmentFileName); + rc = File_ReadBinaryFile(&p_raw_prefix , &prefixSize, sizeof(PrefixHdr), + inputAttachmentFileName); /* freed @22 */ + + PrintAll(messageFile, + "Input file data", prefixSize, p_raw_prefix); /* test code */ + fprintf(messageFile, "Prefix Size: %d\n", (int)prefixSize); + + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR1020 while opening the attachment, file: %s\n", + inputAttachmentFileName); + } + + if (rc == 0 && prefixSize != (sizeof(PrefixHdr)-ECID_SIZE-2) ) { /* TODO: find the majic number 2 */ + File_Printf(projectLogFile, messageFile, + "SIZE ERROR on input file: %s\n", + inputAttachmentFileName); + rc = ERROR_CODE; + fprintf(messageFile, "Calculated Prefix Size: %d\n", (int)sizeof(PrefixHdr)-ECID_SIZE); + fprintf(messageFile, "sizeof PrefixHdr: %d\n", (int)sizeof(PrefixHdr)); + } + + /* Overlay PrefixHdr struct and perform checks + * Confirm version, algos, key count for this proj, ecidCnt + * then check flag bits + */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "CheckPrefix: Perform checks on header %s\n", + inputAttachmentFileName); + + /* Construct and init a HwPrefixHdr */ + hwPrefixHdr = (PrefixHdr *) malloc(sizeof(PrefixHdr)); + if (hwPrefixHdr == NULL) { + File_Printf(projectLogFile, messageFile, + "Malloc ERROR on PrefixHdr: %s\n", + inputAttachmentFileName); + rc = ERROR_CODE; + } + if (rc == 0) { + /* Build the PrefixHdr */ + p_hdr = p_raw_prefix; + hwPrefixHdr->m_version = getUint16(p_hdr,1); + p_hdr += 2; + hwPrefixHdr->m_hashAlg = *p_hdr++; + hwPrefixHdr->m_sigAlg = *p_hdr++; + memcpy( hwPrefixHdr->m_codeStartOffset, p_hdr, 8 ); + p_hdr += 8; + memcpy( hwPrefixHdr->m_reserved, p_hdr, 8 ); + p_hdr += 8; + hwPrefixHdr->m_flags = getUint32( p_hdr,1 ); + p_flags = p_hdr; // use to update original header later + p_hdr += 4; + hwPrefixHdr->m_swKeyCount = *p_hdr++; + memcpy( hwPrefixHdr->m_payloadSize, p_hdr, 8 ); + p_hdr += 8; + memcpy( hwPrefixHdr->m_payloadHash, p_hdr, SHA512_DIGEST_SIZE ); + p_hdr += SHA512_DIGEST_SIZE; + + fprintf(messageFile, "Prefix Version: %u\n", hwPrefixHdr->m_version); + fprintf(messageFile, "Prefix Hash Alg: %u\n", hwPrefixHdr->m_hashAlg); + fprintf(messageFile, "Prefix Signature Alg: %u\n", hwPrefixHdr->m_sigAlg); + fprintf(messageFile, "Prefix flags: %08X\n", hwPrefixHdr->m_flags); + fprintf(messageFile, "FW Key Count: %u\n", hwPrefixHdr->m_swKeyCount); + + /* Check Valid Prefix Hdr here */ + if (verbose) fprintf(messageFile, "CheckPrefix: Checking Valid Hdr %s\n", + inputAttachmentFileName); + if (prefixSize != HDR_LEN || hwPrefixHdr->m_version != HDR_VER || hwPrefixHdr->m_hashAlg != HDR_HASH || hwPrefixHdr->m_sigAlg != HDR_SIG) { + File_Printf(projectLogFile, messageFile, + "Invalid HDR Fields Found: %s\n", + inputAttachmentFileName); + rc = ERROR_CODE; + } + } + if (rc == 0) { + + /* Check Flag bits here */ + if (verbose) fprintf(messageFile, "CheckPrefix: Checking Flag bits %s\n", + inputAttachmentFileName); + + /* NEVER sign w/ attributes or new key (for now) so silently clear those bits */ + hwPrefixHdr->m_flags &= ~(ATTR_FLAG_MASK); + hwPrefixHdr->m_flags &= ~(NEW_KEY_CONTAINER); + putUint32(&(hwPrefixHdr->m_flags), p_flags, 1 ); + fprintf(messageFile, "Updated Prefix flags: %08X\n", getUint32( p_flags,1 )); + + /* Check that at least one of the key sets is set */ + if (hwPrefixHdr->m_flags & OP_BLD_CONTAINER) { + File_Printf(projectLogFile, messageFile, + "Valid op-bld Container: %s\n", + inputAttachmentFileName); + } else if (hwPrefixHdr->m_flags & FIPS_BLD_CONTAINER) { + File_Printf(projectLogFile, messageFile, + "Valid fips-bld Container: %s\n", + inputAttachmentFileName); + } else { + File_Printf(projectLogFile, messageFile, + "Invalid Flag Field Set: %s\n", + inputAttachmentFileName); + rc = ERROR_CODE; + } + } + } + /* Hash prefix and return to "input" file to enable proceeding w/ signing flow */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "CheckPrefix: Create digest of header %s\n", + inputAttachmentFileName); + Ossl_SHA512(digest, + prefixSize, + p_raw_prefix, // p_raw_prefix with any flags cleared + 0L, NULL); + PrintAll(messageFile, + "Updated prefix header", prefixSize, p_raw_prefix); + + PrintAll(messageFile, + "SHA512 of prefix header", SHA512_SIZE, digest); + } + if (rc == 0) { + if (verbose) fprintf(messageFile, "CheckPrefix: Writing digest of header back input file %s\n", + inputAttachmentFileName); + + rc = File_WriteBinaryFile(digest, SHA512_SIZE, + inputAttachmentFileName); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR while writing hash back to input attachment file: %s\n", + inputAttachmentFileName); + } + } + /* clean up */ + free(p_raw_prefix); /* @22 */ + free(hwPrefixHdr); /* @24 */ + + return rc; +} + + +/* Sign() does ECC P521 signing and verification + + */ + +int Sign(const char *keyFileName, /* ECC P521 key token */ + const char *inputAttachmentFileName, /* file holding the digest to be signec */ + const char *outputAttachmentFileName, /* file for signature */ + FILE *projectLogFile) /* audit log file */ +{ + int rc = 0; + /* + signing key + */ + unsigned char *keyToken = NULL; /* CCA key token */ + size_t keyTokenLength; + /* + digest to be signed + */ + unsigned char *digest = NULL; + size_t digestLength; + + /* get the CCA key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Sign: Reading CCA key token file %s\n", + keyFileName); + rc = File_ReadBinaryFile(&keyToken, &keyTokenLength, 2000, keyFileName); /* freed @1 */ + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR1019: Could not open key file: %s\n", keyFileName); + } + } + /* get the input digest */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "Sign: Reading input file %s\n", + inputAttachmentFileName); + size_t length = 0; + rc = File_GetSize(&length, inputAttachmentFileName); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR1020 while opening the attachment, file: %s\n", + inputAttachmentFileName); + } else if (length != SHA512_SIZE) { + rc = 1; + File_Printf(projectLogFile, messageFile, + "ERROR1020 while opening the attachment, incorrect size ACT=%d EXP=%d, file: %s\n", + length, SHA512_SIZE, inputAttachmentFileName); + } else { + rc = File_ReadBinaryFile(&digest , &digestLength, SHA512_SIZE, + inputAttachmentFileName); /* freed @2 */ + } + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR1020 while opening the attachment, file: %s\n", + inputAttachmentFileName); + } + } + if (rc == 0) { + rc = SignECCP521(keyToken, + keyTokenLength, + digest, + digestLength, + outputAttachmentFileName, + projectLogFile); + } + /* clean up */ + free(keyToken); /* @1 */ + free(digest); /* @2 */ + return rc; +} + +int SignECCP521(unsigned char *keyToken, + size_t keyTokenLength, + unsigned char *digest, + size_t digestLength, + const char *outputAttachmentFileName, + FILE *projectLogFile) +{ + int rc = 0; + int valid; /* true if signature verifies */ + + EccKeyTokenPublic eccKeyTokenPublic; /* CCA public key structure */ + /* + signature + */ + unsigned char signature[132]; /* NOTE 132 according to CCA, openssl produces + 139 */ + unsigned long signatureLength; + unsigned long signatureBitLength; + + /* extract the CCA public key from the CCA key token */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignECCP521: key token length %d\n", + (int)keyTokenLength); + if (verbose) + fprintf(messageFile, "SignECCP521: extract the public key from CCA key token\n"); + rc = getPKA96EccPublicKey(&eccKeyTokenPublic, /* output: structure */ + keyTokenLength, + keyToken); /* input: PKA96 key token */ + } + /* sign with the coprocessor. The coprocessor doesn't know the digest algorithm. It just + signs a digest */ + if (rc == 0) { + signatureLength = sizeof(signature); + rc = Digital_Signature_Generate_ECC(&signatureLength, /* i/o */ + &signatureBitLength, /* output */ + signature, /* output */ + keyTokenLength, /* input */ + keyToken, /* input */ + digestLength, /* input */ + digest); /* input */ + + } + /* create the audit log entry */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignECCP521: Updating audit log\n"); + /* binary data as printable */ + char pubkey_string[133 * 4]; + char digest_string[SHA512_SIZE * 4]; /* use the largest */ + char sig_string[132 * 4]; + + /* get the user and group structures */ + /* binary to printable */ + sprintAll(pubkey_string, eccKeyTokenPublic.qLen, eccKeyTokenPublic.publicKey); + sprintAll(digest_string, digestLength, digest); + sprintAll(sig_string, signatureLength, signature); + /* send to audit log */ + fprintf(projectLogFile, "\tPublic Key:\n %s\n", pubkey_string); + fprintf(projectLogFile, "\tDigest:\n %s\n", digest_string); + fprintf(projectLogFile, "\tSignature:\n %s\n", sig_string); + } + /* + The verify functions should never fail. They are just sanity checks on the code. + */ + /* sanity check on the signature length */ + if (rc == 0) { + if (signatureLength != sizeof(signature)) { + File_Printf(projectLogFile, messageFile, + "ERROR1021: signature invalid length %lu\n", signatureLength); + rc = ERROR_CODE; + } + } + /* verify the signature with the coprocessor key CCA token */ + if (rc == 0) { + if (verbose) + fprintf(messageFile, + "SignECCP521: verify signature with the coprocessor key token\n"); + rc = Digital_Signature_Verify_ECC(signatureLength, /* input */ + signature, /* input signature */ + keyTokenLength, /* input */ + keyToken, /* input key */ + digestLength, /* input */ + digest); /* input digest */ + } + /* code to verify the signature using openssl */ + if (rc == 0) { + if (verbose) fprintf(messageFile, + "SignECCP521: " + "verify signature with OpenSSL and the key token\n"); + rc = Ossl_VerifyECC(&valid, + digest, /* input: digest to be verified */ + digestLength, + eccKeyTokenPublic.publicKey, + eccKeyTokenPublic.qLen, + signature, /* input: signature */ + signatureLength); + if (!valid) { + File_Printf(projectLogFile, messageFile, + "ERROR1022 verifying signature with OpenSSL and the key token\n"); + rc = ERROR_CODE; + } + } + /* write the signature to the output attachment */ + if (rc == 0) { + if (verbose) fprintf(messageFile, "SignECCP521: Writing output file %s\n", + outputAttachmentFileName); + rc = File_WriteBinaryFile(signature, signatureLength, outputAttachmentFileName); + if (rc != 0) { + File_Printf(projectLogFile, messageFile, + "ERROR1023 writing signature to file %s\n", outputAttachmentFileName); + rc = ERROR_CODE; + } + } + /* cleanup */ + return rc; +} + +/* GetArgs() gets the command line arguments from the framework. + */ + +int GetArgs(const char **outputBodyFilename, + const char **usr, + const char **password, + const char **projectLogFileName, + const char **sender, + const char **project, + const char **auxcfgFilename, + const char **keyFileName, + const char **inputAttachmentFileName, + const char **outputAttachmentFileName, + int *verbose, + int argc, + char **argv) +{ + long rc = 0; + int i; + FILE *tmpFile; + + /* command line argument defaults */ + *outputBodyFilename = NULL; + *verbose = FALSE; + + /* 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"); + /* 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, + "ERROR1001: -obody option (output email body) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-usr") == 0) { + i++; + if (i < argc) { + *usr = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1002: -usr option (CCA user ID) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-pwd") == 0) { + i++; + if (i < argc) { + *password = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1003: -pwd option (CCA password) needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-log") == 0) { + i++; + if (i < argc) { + *projectLogFileName = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1004: -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, + "ERROR1005: -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, + "ERROR1006: -project option needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-key") == 0) { + i++; + if (i < argc) { + *keyFileName = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1007: -key option needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-auxcfg") == 0) { + i++; + if (i < argc) { + *auxcfgFilename = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1008: -auxcfg option needs a value\n"); + rc = ERROR_CODE; + } + } + else if (strcmp(argv[i],"-di") == 0) { + i++; + if (i < argc) { + *inputAttachmentFileName = argv[i]; + } + else { + fprintf(messageFile, + "ERROR1009: -di 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, + "ERROR1010: -do option needs a value\n"); + 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 mandatory command line arguments */ + if (rc == 0) { + // If the usr isn't specified just use the sender + if (*usr == NULL) { + *usr = *sender; + } + if (*usr == NULL) { + fprintf(messageFile, + "ERROR1011: -usr option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*password == NULL) { + fprintf(messageFile, + "ERROR1012: -pwd option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*sender== NULL) { + fprintf(messageFile, + "ERROR1013: -sender option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*projectLogFileName == NULL) { + fprintf(messageFile, + "ERROR1014: -log option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*keyFileName == NULL) { + fprintf(messageFile, + "ERROR1015: -key option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*auxcfgFilename == NULL) { + fprintf(messageFile, + "ERROR1026: -auxcfg option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*inputAttachmentFileName == NULL) { + fprintf(messageFile, + "ERROR1016: -di option missing\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*outputAttachmentFileName == NULL) { + fprintf(messageFile, + "ERROR1017: -do option missing\n"); + rc = ERROR_CODE; + } + } + return rc; +} + +void PrintUsage() +{ + fprintf(messageFile, "\n"); + fprintf(messageFile, + "\tsignecc usage:\n" + "\n" + "Common arguments:\n" + "\n" + "\t-usr - CCA user (profile) ID\n" + "\t[-v - verbose logging]\n" + "\t[-h - print usage help]\n" + "\n" + "Email only arguments:\n" + "\n" + "\t-project - project name\n" + "\t-epwd - CCA user password (encrypted)\n" + "\n" + "Command line only arguments:\n" + "\n" + "\t[-obody - output email body file name (should be first argument)]\n" + "\t-sender - request sender\n" + "\t-di - input attachment file name\n" + "\t-do - output attachment file name\n" + "\t-log - project audit log file name\n" + "\t-auxcfg - project auxiliary configuration file name\n" + "\t-key - project CCA signing key token\n" + "\t-pwd - CCA user password (plaintext)\n" + ); + fprintf(messageFile, "\n"); + return; +} + +uint16_t getUint16( uint8_t *data, int p_endianess ) +{ + uint16_t value = 0; + + if (p_endianess) + { + value = data[1] | (data[0] << 8); + } + else + { + value = data[0] | (data[1] << 8); + } + + return value; +} + +uint32_t getUint32( uint8_t *data, int p_endianess ) +{ + uint32_t value = 0; + + if (p_endianess) + { + value = (data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24)); + } + else + { + value = (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)); + } + + return value; +} + + +void putUint32( uint32_t *value, uint8_t *data, int p_endianess ) +{ + if (p_endianess) + { + data[0] = (uint8_t)(((*value) & 0xff000000) >> 24); + data[1] = (uint8_t)(((*value) & 0x00ff0000) >> 16); + data[2] = (uint8_t)(((*value) & 0x0000ff00) >> 8); + data[3] = (uint8_t)(((*value) & 0x000000ff)); + } + else + { + data[0] = (uint8_t)(((*value) & 0x000000ff)); + data[1] = (uint8_t)(((*value) & 0x0000ff00) >> 8); + data[2] = (uint8_t)(((*value) & 0x00ff0000) >> 16); + data[3] = (uint8_t)(((*value) & 0xff000000) >> 24); + } + + return; +} diff --git a/src/signframework/signecc/types_t.h b/src/signframework/signecc/types_t.h new file mode 100755 index 0000000..d3650eb --- /dev/null +++ b/src/signframework/signecc/types_t.h @@ -0,0 +1,54 @@ +/* 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. + */ + +#ifndef _TYPES_T_H +#define _TYPES_T_H + +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <stdio.h> + +#ifdef linux +#include <asm/types.h> +typedef __u8 uint8_t; +typedef __u16 uint16_t; +typedef __u32 uint32_t; +#ifndef _STDINT_H +typedef __u64 uint64_t; +#endif +#endif + +#ifdef __unix__ +#define INLINE inline +#else +#define INLINE __inline +#endif + +#if defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN +#define HO2BE_8(_x) (_x) +#define HO2BE_4(_x) (_x) +#else +#define HO2BE_8(_x) ((_x<<56)|((_x<<40)&0xff000000000000ull)|((_x<<24)&0xff0000000000ull)|((_x<<8)&0xff00000000ull)|\ + ((_x>>8)&0xff000000ull)|((_x>>24)&0xff0000ull)|((_x>>40)&0xff00ull)|(_x>>56)) +#define HO2BE_4(_x) ((_x<<24)|((_x<<8)&0xff0000)|((_x>>8)&0xff00)|(_x>>24)) +#endif +#define BE2HO_8(_x) HO2BE_8(_x) +#define BE2HO_4(_x) HO2BE_4(_x) + +#define MAX_UINT64 0xffffffffffffffffull + +#endif diff --git a/src/signframework/signecc/utils.c b/src/signframework/signecc/utils.c new file mode 120000 index 0000000..2f47a21 --- /dev/null +++ b/src/signframework/signecc/utils.c @@ -0,0 +1 @@ +../utils.c
\ No newline at end of file diff --git a/src/signframework/signecc/utils.h b/src/signframework/signecc/utils.h new file mode 120000 index 0000000..6cd5d4f --- /dev/null +++ b/src/signframework/signecc/utils.h @@ -0,0 +1 @@ +../utils.h
\ No newline at end of file diff --git a/src/signframework/systemd/signframework.conf b/src/signframework/systemd/signframework.conf new file mode 100644 index 0000000..3d49b62 --- /dev/null +++ b/src/signframework/systemd/signframework.conf @@ -0,0 +1,4 @@ +# Configuration file for the signing_framework service. + +OPTIONS=-v +FRAMEWORK_CONFIG_FILE=/home/framework/signframework/src/signframework/framework.cfg diff --git a/src/signframework/systemd/signframework.service b/src/signframework/systemd/signframework.service new file mode 100644 index 0000000..7468977 --- /dev/null +++ b/src/signframework/systemd/signframework.service @@ -0,0 +1,19 @@ +[Unit] +Description=Signing Framework daemon +After=network.target sshd.service + +[Service] +Type=simple +#PIDFile=/var/run/framework.pid +WorkingDirectory=/home/framework/signframework/src/signframework +EnvironmentFile=/home/framework/signframework/src/signframework/systemd/signframework.conf +ExecStart=/home/framework/signframework/src/signframework/framework $OPTIONS +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure +RestartSec=42s +User=framework +Group=framework + +[Install] +WantedBy=multi-user.target diff --git a/src/signframework/testproject.cfg.sample b/src/signframework/testproject.cfg.sample new file mode 100644 index 0000000..b46106e --- /dev/null +++ b/src/signframework/testproject.cfg.sample @@ -0,0 +1,41 @@ +# Test project configuration file + +# NOTE: The parameters must be in this order! +# +# program executable filename + +program=framework_test + +# File name for the project audit log + +log=logs/testproject.log + +# project signing key file name + +needkey=true +key=testprojectkey.tok + + +# auxiliary project configuration file + +needauxcfg=false +#auxcfg=athenaaux.cfg + +neediatt=true +needoatt=true +needpwd=true + +# Project administrator email address + +email=framework@email.com + +# Notification receivers + +notifs=1 +joe@email.com + +# Authorized senders (signers) (userid=email) + +needsenders=true +joe=joe@email.com +fred=fred@email.com diff --git a/src/signframework/utils.c b/src/signframework/utils.c new file mode 100644 index 0000000..6edd0cc --- /dev/null +++ b/src/signframework/utils.c @@ -0,0 +1,1355 @@ +/* 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdint.h> +#include <ctype.h> +#include <errno.h> +#include <time.h> + +#include "utils.h" + +/* messages are traced here + + All messages, even error messages, are traced only if verbose is set. There messages are + 'techie' and should not be returned unless the user asks for them. +*/ + +extern FILE* messageFile; +extern int verbose; + +#define COPY_BLOCK_SIZE 1024 + +/* File_Copy() copies the source file to the destination file. + */ + +int File_Copy(const char *destinationFilename, + const char *sourceFilename) +{ + int rc = 0; + char buffer[COPY_BLOCK_SIZE]; + size_t bytesRead; + size_t bytesWritten; + FILE *destination = NULL; /* freed @1 */ + FILE *source = NULL; /* freed @2 */ + + /* open the destination for write */ + if (rc == 0) { + rc = File_Open(&destination, destinationFilename, "w"); /* closed @1 */ + } + /* open the source for read */ + if (rc == 0) { + rc = File_Open(&source, sourceFilename, "r"); /* closed @2 */ + } + /* copy the source to the destination */ + while (rc == 0) { + bytesRead = fread(buffer, 1, COPY_BLOCK_SIZE, source); + if (bytesRead == 0) { + if (feof(source)) { + break; + } + else { + if (verbose) fprintf(messageFile, + "File_Copy: Error reading from %s\n", sourceFilename); + rc = ERROR_CODE; + } + } + bytesWritten = fwrite(buffer, 1, bytesRead, destination); + if (bytesWritten != bytesRead) { + if (verbose) fprintf(messageFile, + "File_Copy: Error writing to %s\n", destinationFilename); + } + } + /* close the destination */ + if (destination != NULL) { + fclose(destination); /* @1 */ + } + /* close the source */ + if (source != NULL) { + fclose(source); /* @2 */ + } + return rc; +} + +/* File_Open() opens the 'filename' for 'mode' + */ + +int File_Open(FILE **file, + const char *filename, + const char* mode) +{ + int rc = 0; + + if (rc == 0) { + *file = fopen(filename, mode); + if (*file == NULL) { + if (verbose) fprintf(messageFile, "File_Open: Error opening %s for %s, %s\n", + filename, mode, strerror(errno)); + rc = ERROR_CODE; + } + } + return rc; +} + +/* File_OpenMessageFile() opens the global messageFile. messageFile is used to fprintf messages to + the returned output email body. + + For each new email, the framework first opens for write. Then the signer program can open it for + append. Finally, the framework can then open for append to add any final messages. + + Returns responseType +*/ + +int File_OpenMessageFile(const char *outputBodyFilename, + const char* mode) +{ + int rc = 0; + + /* Switch messageFile error and trace printing from stdout to the output body. The design sends + framework initialization to stdout so the person initiating the program can see errors. Once + it's running, everything else goes to the output body and (usually) back to the user. */ + if (rc == 0) { + rc = File_Open(&messageFile, outputBodyFilename, mode); + } + /* if the open succeeded */ + if (rc == 0) { + /* turn off buffering as a debug aid, so the file gets updated while stepping with a + debugger */ + setvbuf(messageFile , 0, _IONBF, 0); + } + /* if the open failed */ + else { + fprintf(messageFile, + "File_OpenMessageFile: Error cannot open %s\n", outputBodyFilename); + /* Since the configuration is validated at startup, this should never fail. The only + possibilty is that something happened to the platform while the framework was + running. No email can be returned and messages go to stdout. */ + messageFile= stdout; + rc = RESPONSE_NO_EMAIL; + } + return rc; +} + +/* File_CloseMessageFile() flushes and then closes the global messageFile and them sets it to + stdout. + + If messageFile is already stdout (or NULL), the function does nothing. +*/ + +int File_CloseMessageFile(void) +{ + int rc = 0; + + if ((messageFile != NULL) && /* should never happen after start up */ + (messageFile != stdout)) { + fflush(messageFile); + fclose(messageFile); + messageFile = stdout; /* should never print after this, but the messages should go + somewhere */ + } + return rc; +} + +/* File_Readline() returns the next non-comment, non-whitespace line from the file. + + It replaces the white space at the end of a line with a NUL terminator. +*/ + +int File_ReadLine(int *haveLine, /* TRUE is line returned, otherwise FALSE */ + char *line, /* returned line */ + size_t *lineLength, /* returned actual length */ + size_t lineSize, /* max size of line buffer */ + FILE *file) /* opened file to read */ +{ + int rc = 0; + char *prc = NULL; /* pointer return code */ + + *haveLine = FALSE; + do { + /* read the line */ + if (rc == 0) { + prc = fgets(line, lineSize, file); + } + /* skip comment lines */ + if ((rc == 0) && (prc != NULL)) { + if (line[0] == '#') { + continue; + } + } + /* skip lines beginning with whitespace */ + if ((rc == 0) && (prc != NULL)) { + if (isspace(line[0])) { + continue; + } + } + if ((rc == 0) && (prc != NULL)) { + /* found a line with text */ + *haveLine = TRUE; + /* check for line overflow */ + *lineLength = strlen(line); + if (line[*lineLength -1] != '\n') { /* last character before NUL should be newline */ + if (verbose) fprintf(messageFile, + "File_ReadLine: Error, Line %s is longer that %u bytes\n", + line, (unsigned int)lineSize); + rc = ERROR_CODE; + } + } + /* strip off white space at the end of the line */ + if ((rc == 0) && (prc != NULL)) { + while (*lineLength > 0) { + if (isspace(line[(*lineLength) - 1])) { + line[(*lineLength) - 1] = '\0'; + (*lineLength)--; + } + else { + break; + } + } + } + break; + } + while ((rc == 0) && (prc != NULL)); + return rc; +} + +/* File_ReadBinaryFile() reads 'filename'. The results are put into 'data', which must be freed by + the caller. 'length' indicates the number of bytes read. 'length_max' is the maximum allowed + length. + + If 'length_max' is zero, the caller trusts the file length. +*/ + +int File_ReadBinaryFile(unsigned char **data, /* must be freed by caller */ + size_t *length, + size_t length_max, + const char *filename) +{ + int rc = 0; + long lrc; + size_t src; + int irc; + FILE *file = NULL; + + *data = NULL; + *length = 0; + /* open the file */ + if (rc == 0) { + rc = File_Open(&file, filename, "rb"); /* closed @1 */ + } + /* determine the file length */ + if (rc == 0) { + irc = fseek(file, 0L, SEEK_END); /* seek to end of file */ + if (irc == -1L) { + if (verbose) fprintf(messageFile, + "File_ReadBinaryFile: Error seeking to end of %s\n", filename); + rc = ERROR_CODE; + } + } + if (rc == 0) { + lrc = ftell(file); /* get position in the stream */ + if (lrc == -1L) { + if (verbose) fprintf(messageFile, + "File_ReadBinaryFile: Error ftell'ing %s\n", filename); + rc = ERROR_CODE; + } + else { + *length = (size_t)lrc; /* save the length */ + } + } + if (rc == 0) { + irc = fseek(file, 0L, SEEK_SET); /* seek back to the beginning of the file */ + if (irc == -1L) { + if (verbose) fprintf(messageFile, + "File_ReadBinaryFile: Error seeking to beginning of %s\n", + filename); + rc = ERROR_CODE; + } + } + /* allocate a buffer for the actual data */ + if ((rc == 0) && *length != 0) { + /* if length_max is zero, the caller trusts the file length */ + if (length_max == 0) { + length_max = *length; + } + rc = Malloc_Safe(data, *length, length_max); + } + /* read the contents of the file into the data buffer */ + if ((rc == 0) && *length != 0) { + src = fread(*data, 1, *length, file); + if (src != *length) { + if (verbose) fprintf(messageFile, + "File_ReadBinaryFile: Error reading %s, %u bytes\n", + filename, (unsigned int)*length); + rc = ERROR_CODE; + } + } + if (file != NULL) { + irc = fclose(file); /* @1 */ + if (irc != 0) { + if (verbose) fprintf(messageFile, + "File_ReadBinaryFile: Error closing %s\n", + filename); + rc = ERROR_CODE; + } + } + if (rc != 0) { + if (verbose) fprintf(messageFile, "File_ReadBinaryFile: Error reading %s\n", filename); + free(*data); + data = NULL; + } + return rc; +} + +/* File_ReadTextFile() reads 'filename'. The results are put into 'text', which must be freed by + the caller. 'length' indicates the number of bytes read. + + A NUL terminator is added to 'text', but the bytes are not scanned for e.g., printable + characters. +*/ + +int File_ReadTextFile(char **text, /* must be freed by caller */ + size_t *length, + size_t length_max, + const char *filename) +{ + int rc = 0; + + /* read the file as raw binary data */ + if (rc == 0) { + rc = File_ReadBinaryFile((unsigned char **)text, length, length_max, filename); + } + /* realloc one more byte for the NULL terminator */ + if (rc == 0) { + rc = Realloc_Safe((unsigned char **)text, (*length) + 1); + } + /* NUL terminate the string */ + if (rc == 0) { + (*text)[*length] = '\0'; + } + return rc; +} + +/* File_GetSize() opens a file for read and returns its length + */ + +int File_GetSize(size_t *fileLength, + const char *filename) +{ + int rc = 0; + FILE *file = NULL; /* freed @1 */ + int irc; + long lrc; + + if (rc == 0) { + if (filename == NULL) { + if (verbose) fprintf(messageFile, "File_GetSize: Error, filename is null\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rc = File_Open(&file, filename, "rb"); /* freed @1 */ + } + /* determine the file length */ + if (rc == 0) { + irc = fseek(file, 0L, SEEK_END); /* seek to end of file */ + if (irc == -1L) { + if (verbose) fprintf(messageFile, "File_GetSize: Error fseek'ing %s, %s\n", + filename, strerror(errno)); + rc = ERROR_CODE; + } + } + if (rc == 0) { + lrc = ftell(file); /* get position in the stream */ + if (lrc == -1L) { + if (verbose) fprintf(messageFile, "File_GetSize: Error (fatal) ftell'ing %s, %s\n", + filename, strerror(errno)); + rc = ERROR_CODE; + } + else { + *fileLength = lrc; /* save the length */ + } + } + if (file != NULL) { + fclose(file); /* @1 */ + } + return rc; +} + +/* File_ValidateOpen() validates that a file can be opened for 'mode'. It then closes the file. + */ + +int File_ValidateOpen(const char *filename, + const char *mode) +{ + int rc = 0; + FILE *file = NULL; /* freed @1 */ + + if (rc == 0) { + if (filename == NULL) { + if (verbose) fprintf(messageFile, "File_ValidateOpen: Error, filename is null\n"); + rc = ERROR_CODE; + } + } + if (rc == 0) { + rc = File_Open(&file, filename, mode); /* freed @1 */ + } + /* immediately close */ + if (file != NULL) { + fclose(file); /* @1 */ + } + return rc; +} + +/* File_WriteBinaryFile() writes 'length' bytes of data to filename + */ + +int File_WriteBinaryFile(const unsigned char *data, + size_t length, + const char *filename) +{ + long rc = 0; + size_t src; + int irc; + FILE *file = NULL; + + /* open the file */ + if (rc == 0) { + rc = File_Open(&file, filename, "wb"); /* closed @1 */ + } + /* write the contents of the data buffer into the file */ + if (rc == 0) { + src = fwrite(data, 1, length, file); + if (src != length) { + if (verbose) fprintf(messageFile, "File_WriteBinaryFile: Error writing %s\n", + filename); + rc = ERROR_CODE; + } + } + if (file != NULL) { + irc = fclose(file); /* @1 */ + if (irc != 0) { + if (verbose) fprintf(messageFile, "File_WriteBinaryFile: Error closing %s\n", + filename); + rc = ERROR_CODE; + } + } + return rc; +} + +/* File_WriteBinaryFileVa() writes filename with a varargs list of length / buffer pairs. A zero + length terminates the loop +*/ + +int File_WriteBinaryFileVa(const char *filename, ...) +{ + long rc = 0; + FILE *file = NULL; /* closed @1 */ + va_list ap; + uint32_t length; + unsigned char *buffer; + size_t src; + int irc; + int done = FALSE; + + /* open the file */ + if (rc == 0) { + rc = File_Open(&file, filename, "wb"); /* closed @1 */ + } + if (rc == 0) { + va_start(ap, filename); + } + while ((rc == 0) && !done) { + length = va_arg(ap, size_t); /* first vararg is the length */ + if (length != 0) { /* loop until a zero length argument terminates */ + buffer = va_arg(ap, unsigned char *); /* second vararg is the array */ + src = fwrite(buffer , 1, length, file); /* write the buffer */ + if (src != length) { + if (verbose) fprintf(messageFile, "File_WriteBinaryFileVa: Error writing %s\n", + filename); + rc = ERROR_CODE; + } + } + else { + done = TRUE; + } + } + if (file != NULL) { + irc = fclose(file); /* @1 */ + if (irc != 0) { + if (verbose) fprintf(messageFile, "File_WriteBinaryFileVa: Error closing %s\n", + filename); + rc = ERROR_CODE; + } + } + return rc; +} + +/* File_GetNameValue() reads the next non-comment, non-whitespace line from the file. + + If a line is found, it allocates memory and returns the 'name' and 'value'. name and value are + separated by a '=' character. +*/ + +int File_GetNameValue(int *haveLine, + char **name, /* freed by caller */ + char **value, /* freed by caller */ + char *lineBuffer, + size_t lineBufferLength, + FILE *file) +{ + int rc = 0; + size_t lineLength; + char *token; + + if (rc == 0) { + rc = File_ReadLine(haveLine, lineBuffer, &lineLength, lineBufferLength, file); + } + /* get first token */ + if ((rc == 0) && *haveLine) { + token = strtok(lineBuffer, "="); /* get first token */ + if (token == NULL) { /* malformed line */ + if (verbose) fprintf(messageFile, "File_GetNameValue: Error, bad format, missing =\n"); + if (verbose) fprintf(messageFile, "File_GetNameValue: Line: %s\n", lineBuffer); + rc = ERROR_CODE; + } + } + if ((rc == 0) && *haveLine) { + rc = Malloc_Strcpy(name, token); + } + if ((rc == 0) && *haveLine) { + token = strtok(NULL,"" ); /* get next token, rest of string */ + if (token == NULL) { /* malformed line */ + if (verbose) fprintf(messageFile, "File_GetNameValue: Error, bad format, missing =\n"); + if (verbose) fprintf(messageFile, "File_GetNameValue: Line: %s\n", lineBuffer); + rc = ERROR_CODE; + } + } + if ((rc == 0) && *haveLine) { + rc = Malloc_Strcpy(value, token); + } + return rc; +} + +/* File_MapNameToValue() scans lines from file of the form + + name=value + + if found, memory is malloc'ed for the value and the value is copied + if not found, an error is returned +*/ + +int File_MapNameToValue(char **value, /* freed by caller */ + const char *name, /* name to search for */ + char *lineBuffer, /* supplied buffer for lines */ + size_t lineBufferLength, /* size of the line buffer */ + FILE *file) /* input file stream */ +{ + int rc = 0; + int irc = 0; + int haveLine; /* true if more lines in the file stream */ + size_t lineLength; /* length of the current line */ + char *token; /* tokenizing the line */ + + do { + if (rc == 0) { + rc = File_ReadLine(&haveLine, lineBuffer, &lineLength, lineBufferLength, file); + } + /* out of lines and no match is error */ + if (rc == 0) { + if (!haveLine) { + if (verbose) fprintf(messageFile, + "File_MapNameToValue: Error, missing value for %s\n", name); + rc = ERROR_CODE; + } + } + /* get subject, first token */ + if (rc == 0) { + token = strtok(lineBuffer, "="); /* get first token */ + if (token == NULL) { /* malformed line */ + if (verbose) fprintf(messageFile, + "File_MapNameToValue: Error, bad format, missing =\n"); + rc = ERROR_CODE; + } + } + /* compare name */ + if (rc == 0) { + irc = strcmp(name, token); + if (irc == 0) { + /* match, done */ + break; + } + } + } + while (rc == 0); + + if (rc == 0) { + token = strtok(NULL,"" ); /* get next token, rest of string */ + if (token == NULL) { /* malformed line */ + if (verbose) fprintf(messageFile, + "File_MapNameToValue: Error, bad format, missing value for %s\n", + name); + rc = ERROR_CODE; + } + } + /* copy token to project file name */ + if (rc == 0) { + rc = Malloc_Strcpy(value, token); + } + return rc; +} + +/* File_MapNameToBool() scans lines from file of the form + + name=value + + if found, the value is checked for 'true' or 'false' and the boolean is returned. + if not found, or the value is not true or false, an error is returned +*/ + +int File_MapNameToBool(int *booln, + const char *name, + char *lineBuffer, + size_t lineBufferLength, + FILE *file) +{ + int rc = 0; + char *booleanString = NULL; /* freed @1 */ + + if (rc == 0) { + rc = File_MapNameToValue(&booleanString, /* freed by caller */ + name, + lineBuffer, + lineBufferLength, + file); + } + /* look for true or false, no other string */ + if (rc == 0) { + if (strcmp(booleanString, "true") == 0) { + *booln = TRUE; + } + else if (strcmp(booleanString, "false") == 0) { + *booln = FALSE; + } + else { + if (verbose) fprintf(messageFile, + "File_MapNameToBool: Error mapping %s, value is %s\n", + name, booleanString); + rc = ERROR_CODE; + } + } + free(booleanString); /* @1 */ + return rc; +} + +/* File_MapNameToUint() scans lines from file of the form + + name=value + + if found, the value is checked for an unsigned integer and the integer is returned. + if not found, or the value is not an unsigned integer, an error is returned +*/ + +int File_MapNameToUint(unsigned int *value, + const char *name, + char *lineBuffer, + size_t lineBufferLength, + FILE *file) +{ + int rc = 0; + int irc; + char *intString = NULL; /* freed @1 */ + char dummy; /* extra characters at the end of the line */ + + if (rc == 0) { + rc = File_MapNameToValue(&intString, /* freed by caller */ + name, + lineBuffer, + lineBufferLength, + file); + } + /* look for unsigned int, no other string */ + if (rc == 0) { + irc = sscanf(intString, "%u%c", value, &dummy); + if (irc != 1) { + /* when this function returns an error, set the value to 0 so that the later delete + doesn't think there are items to free */ + *value = 0; + if (verbose) fprintf(messageFile, + "File_MapNameToUint: Error mapping %s, value is %s\n", + name, intString); + rc = ERROR_CODE; + } + } + free(intString); /* @1 */ + return rc; +} + +/* File_GetNameValueArray() parses the rest of 'file' for name=value pairs. + + It allocates the 'names' and 'values' arrays and fills in the elements + + lineBuffer is a temporary, to hold lines. +*/ + +int File_GetNameValueArray(char ***names, /* freed by caller */ + char ***values, /* freed by caller */ + size_t *length, /* final length of the array */ + char *lineBuffer, + size_t lineBufferLength, + FILE *file) +{ + int rc = 0; + int haveLine = TRUE; + char *name = NULL; + char *value = NULL; + char *tmp; /* used by realloc */ + + *length = 0; + + /* check that names and values are NULL at entry, sanity check for memory leak */ + if (rc == 0) { + if (names == NULL || + values == NULL) { + if (verbose) fprintf(messageFile, + "File_GetNameValueArray: names %p or values %p are NULL\n", + names, values); + rc = 1; + } + else if ((*names != NULL) || (*values != NULL)) { + if (verbose) fprintf(messageFile, + "File_GetNameValueArray: names %p or values %p not NULL\n", + *names, *values); + rc = 1; + } + } + while ((rc == 0) && haveLine) { + name = NULL; + value = NULL; + /* get a name - value pair */ + if (rc == 0) { + rc = File_GetNameValue(&haveLine, + &name, /* freed by caller */ + &value, /* freed by caller */ + lineBuffer, + lineBufferLength, + file); + } + /* if found a pair */ + if ((rc == 0) && haveLine) { + /* increase the array size */ + (*length)++; + /* grow the names array */ + tmp = realloc(*names, *length * sizeof(char *)); + if (tmp != NULL) { + *names = (char **)tmp; + } + else { + if (verbose) fprintf(messageFile, + "File_GetNameValueArray: " + "Error allocating memory for %u names\n", + (unsigned int)*length); + rc = ERROR_CODE; + } + } + if ((rc == 0) && haveLine) { + /* grow the values array */ + tmp = realloc(*values, *length * sizeof(char *)); + if (tmp != NULL) { + *values = (char **)tmp; + } + else { + if (verbose) fprintf(messageFile, + "File_GetNameValueArray: " + "Error allocating memory for %u values\n", + (unsigned int)*length); + rc = ERROR_CODE; + } + } + if ((rc == 0) && haveLine) { + /* assign name and value to array */ + (*names)[(*length) - 1] = name; + (*values)[(*length) - 1] = value; + } + } + return rc; +} + +/* File_GetValueArray() parses 'file' for values. + + The number of values is determined by the first line, which is of the form + + 'name'=integer + + It allocates the 'values' array and fills in the elements. +*/ + +int File_GetValueArray(char ***values, /* freed by caller */ + size_t *length, /* items in the array */ + const char *name, + char *lineBuffer, + size_t lineBufferLength, + FILE *file) +{ + int rc = 0; + size_t i; + int haveLine = TRUE; + size_t lineLength; + + *length = 0; + + /* check that values is NULL at entry, sanity check for memory leak */ + if (rc == 0) { + if (*values != NULL) { + if (verbose) fprintf(messageFile, + "File_GetNameValueArray: values %p not NULL\n", + *values); + } + } + /* get the count of values in the array */ + if (rc == 0) { + rc = File_MapNameToUint((unsigned int *)length, + name, + lineBuffer, + lineBufferLength, + file); + } + /* allocate the array for the values */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)values, /* freed by caller */ + *length * sizeof(char *), + *length * sizeof(char *)); /* trust the configuration file */ + } + /* immediately NULL the array so it can be freed */ + for (i = 0 ; (rc == 0) && (i < *length) ; i++) { + (*values)[i] = NULL; + } + /* for each expected value in the array */ + for (i = 0 ; (rc == 0) && (i < *length) ; i++) { + /* each line is a value */ + if (rc == 0) { + rc = File_ReadLine(&haveLine, lineBuffer, &lineLength, lineBufferLength, file); + } + /* insufficient lines is an error */ + if (rc == 0) { + if (!haveLine) { + if (verbose) fprintf(messageFile, + "File_GetValueArray: Error, not %u entries for %s\n", + (unsigned int)*length, name); + rc = ERROR_CODE; + } + } + /* allocate memory for the value and copy into the array entry */ + if (rc == 0) { + rc = Malloc_Strcpy(&((*values)[i]), lineBuffer); /* freed by caller */ + } + } + return rc; +} + +/* File_LogTime() adds the current date and time to 'logFile' + */ + +void File_LogTime(FILE *logFile) +{ + time_t log_time; + log_time = time(NULL); + fprintf(logFile, "\n%s", ctime(&log_time)); + return; +} + +/* File_Printf() does an fprintf of the same parameters to both lFile (typically a log file) and + mfile (typically the messageFile, the output body). + + lFile is prefixed by a tab character. + + If either is NULL, that file is skipped. +*/ + +void File_Printf(FILE *lFile, + FILE *mFile, + const char *format, + ...) +{ + va_list va; + + va_start(va, format); + + /* print to the log file if it's not NULL */ + if (lFile != NULL) { + fprintf(lFile,"\t"); + va_start(va, format); + vfprintf(lFile, format, va); + va_end(va); + } + /* print to the messages file if it's not NULL */ + if (mFile != NULL) { + va_start(va, format); + vfprintf(mFile, format, va); + va_end(va); + } + return; +} + +/* Arguments_Init() sets all elements of the argv array to NULL. + + This permits the free() to be safe. +*/ + +void Arguments_Init(Arguments *arguments) +{ + size_t i; + + arguments->argvBytes = 0; + for (i = 0 ; i < MAX_ARGV_BODY ; i++) { + arguments->argv[i] = NULL; + } + return; +} + +/* Argvuments_Delete() frees all elements of argv and sets them back to NULL. + + Secret values are cleared before the memory is freed. These are currently: + + -pwd +*/ + +void Arguments_Delete(Arguments *arguments) +{ + size_t i; + + /* clear the plaintext password */ + Arguments_ClearSecret("-pwd", arguments); + /* free allocated memory */ + for (i = 0 ; i < MAX_ARGV_BODY ; i++) { + free(arguments->argv[i]); + arguments->argv[i] = NULL; + } + return; +} + +/* Arguments_ClearSecret() looks for 'flag' and zeros the value associated with the flag + */ + +void Arguments_ClearSecret(const char *flag, + Arguments *arguments) +{ + size_t i; + size_t length; + int irc; + + /* start at 1, argvBody[0] is the name of the program, not a flag */ + for (i = 1 ; i < (size_t)arguments->argc ; i++) { + irc = strcmp(arguments->argv[i], flag); + if (irc == 0) { /* found a flag match */ + i++; /* advance past the flag to the value */ + length = strlen(arguments->argv[i]); + memset(arguments->argv[i], '\0', length); + } + } + return; +} + +/* Arguments_AddPairTo() adds the C strings 'flag' and 'value' pair to argv. argc and argvBytes are + updated accordingly. +*/ + +int Arguments_AddPairTo(Arguments *arguments, + const char *flag, + const char *value) +{ + int rc = 0; + + /* check flag format */ + if (rc == 0) { + } + /* add flag */ + if (rc == 0) { + rc = Arguments_AddTo(arguments, + flag, FALSE); + } + /* add value */ + if (rc == 0) { + rc = Arguments_AddTo(arguments, + value, FALSE); + } + return rc; +} + +/* Arguments_AddTo() adds the C string 'data' to argvBody. argvBytes is updated accordingly. + + If zero is TRUE, the item is added at argv[0]. If FALSE, the item is added at the end and argc + is updated accordingly. + + 'zero' handles the speccial case of the program name. +*/ + +int Arguments_AddTo(Arguments *arguments, + const char *data, + int zero) +{ + int rc = 0; + size_t i; + size_t length; + + /* check for NULL argument */ + if (rc == 0) { + if (data == NULL) { + if (verbose) fprintf(messageFile, + "Arguments_AddTo: Error, adding NULL argument\n"); + rc = ERROR_CODE; + } + } + /* check for array overflow */ + if (rc == 0) { + length = strlen(data); + } + /* check data for illegal characters */ + for (i = 0 ; (rc == 0) && (i < length) ; i++) { + if (!isprint(data[i])) { + if (verbose) fprintf(messageFile, + "Arguments_AddTo: Error, argument not printable at index %u\n", + (unsigned int)i); + rc = ERROR_CODE; + } + } + /* check for array overflow. This is a framwork compile time limitation. The -1 reserves the + last entry as a NULL, needed by exec() */ + if ((rc == 0) && !zero) { + if (arguments->argc > (MAX_ARGV_BODY-1)) { + if (verbose) fprintf(messageFile, + "Arguments_AddTo: Error, overflows array of %u entries\n", + MAX_ARGV_BODY); + rc = ERROR_CODE; + + } + } + /* check for total bytes overflow. This is a platform OS limitation */ + if (rc == 0) { + if ((arguments->argvBytes + length) > ARG_MAX) { + if (verbose) fprintf(messageFile, + "Arguments_AddTo: Error, %s overflows argument list length\n", + data); + rc = ERROR_CODE; + } + } + /* malloc and copy */ + if (rc == 0) { + if (!zero) { + rc = Malloc_Strcpy(&(arguments->argv[arguments->argc]), data); + } + else { + rc = Malloc_Strcpy(&(arguments->argv[0]), data); + } + } + if (rc == 0) { + if (!zero) { + arguments->argc++; /* adjust the argument count */ + } + arguments->argvBytes += length; /* adjust the total number of bytes */ + } + return rc; +} + +/* Arguments_GetFrom() iterates through arguments->argv, searching for flag. + + If found, the value is returned. The value is a pointer into arguments->argv. It is not + allocated and should not be freed. + + If not found, an error is returned. +*/ + +int Arguments_GetFrom(const char **value, + const char *flag, + Arguments *arguments) +{ + int rc = 0; + int irc; + int i; + int found = FALSE; + + /* start with [1], [0] is the program name, not a command line argument */ + for (i = 1 ; !found && (i < arguments->argc) ; i++) { + irc = strcmp(flag, arguments->argv[i]); + if ((irc == 0) && ((i+1) < arguments->argc)) { /* if a flag match */ + *value = arguments->argv[i+1]; /* return the value */ + found = TRUE; + } + } + if (!found) { + if (verbose) fprintf(messageFile, "Arguments_GetFrom: %s not found\n", flag); + rc = ERROR_CODE; + } + return rc; +} + +/* + character array handling +*/ + +/* Array_GetLine() acts on a character array. + + It returns the next non-comment, non-whitespace line from the array. + + It replaces the white space at the end of a line with NUL. + + It also returns a pointer to the next line. + + Returns error if the line is too long or contains non-printable characters. +*/ + +int Array_GetLine(int *haveLine, /* TRUE if line returned, otherwise FALSE */ + char *outLine, /* returned line */ + const char **nextLine, /* returned pointer to next line */ + size_t lineSize, /* max size of line buffer */ + const char *inLines, /* input character array */ + FILE *logFile) /* audit log file */ +{ + int rc = 0; + size_t i = 0; /* index into outLine */ + const char *ptr; + + *haveLine = FALSE; + ptr = inLines; /* starting point */ + + /* skip comment lines or lines beginning with whitespace */ + if (rc == 0) { + /* skip comment lines or lines beginning with whitespace */ + while ((*ptr == '#') || + (isspace(*ptr))) { + + /* if the line should be ignored, search for next newline, then increment past it */ + for ( ; *ptr != '\0' ; ptr++) { + if (*ptr == '\n') { + ptr++; /* point to next line and loop back to check for skip */ + break; + } + } + } + /* found another line (or at the end of the buffer) */ + /* scan to the end of the current line or the end of the buffer */ + while ((*ptr != '\0') && (*ptr != '\n')) { + + *haveLine = TRUE; + + /* check for overflow, leave space for NUL */ + if (i == (lineSize-1)) { + outLine[lineSize-1] = '\0'; /* terminate the line for error message */ + File_Printf(logFile, messageFile, + "Error, Line is longer than %u bytes: %s\n", + lineSize, outLine); + if (verbose) fprintf(messageFile, + "Array_GetLine: Error, Line is longer than %u bytes: %s\n", + (unsigned int)lineSize, outLine); + rc = ERROR_CODE; + break; + } + /* scan for non-printable characters */ + if (!isprint(*ptr)) { + outLine[i] = '\0'; /* terminate the line for error message */ + File_Printf(logFile, messageFile, + "Error, Line %s has non-printable character at index %u\n", + outLine, i); + if (verbose) fprintf(messageFile, + "Array_GetLine: " + "Error, Line %s has non-printable character at index %u\n", + outLine, (unsigned int)i); + rc = ERROR_CODE; + break; + } + /* character valid, copy from input array to output line */ + outLine[i] = *ptr; + ptr++; /* next input character */ + i++; /* next output character */ + } + } + /* set next line */ + if (rc == 0) { + if (*ptr == '\0') { /* if finished, just point to NUL terminator for input array */ + *nextLine = ptr; + } + else { + *nextLine = ptr+1; /* if more lines, point past newline */ + } + } + if ((rc == 0) && *haveLine) { + outLine[i] = '\0'; /* terminate the line */ + i--; /* search back from last character */ + /* strip off white space at the end of the line */ + for ( ; (i > 0) && isspace(outLine[i]) ; i--) { + outLine[i] = '\0'; + } + if (outLine[0] == '\0') { + if (verbose) fprintf(messageFile, + "Array_GetLine: Error: Line has only whitespace\n"); + rc = ERROR_CODE; /* this should never occur, line with all whitespace should be + ignored */ + } + } + return rc; +} + +/* Malloc_Strcpy() malloc's an array for the 'in' string and then copies the string and the + terminating NUL */ + +int Malloc_Strcpy(char **out, /* freed by caller */ + const char *in) +{ + int rc = 0; + + /* malloc for the data */ + if (rc == 0) { + rc = Malloc_Safe((unsigned char **)out, + strlen(in) + 1, + strlen(in) + 1); /* trust configuration files */ + } + /* copy the data */ + if (rc == 0) { + strcpy(*out, in); + } + return rc; +} + +/* Malloc_Safe() is a wrapper around malloc that detects memory leaks, uninitialized pointers, or an + unreasonably large request */ + +int Malloc_Safe(unsigned char **ptr, size_t len, size_t max_length) +{ + int rc = 0; + + if (rc == 0) { + if (len > max_length) { + if (verbose) fprintf(messageFile, + "Malloc_Safe: Error, length %u too large\n", (unsigned int)len); + rc = ERROR_CODE; + } + } + if (rc == 0) { + if (*ptr != NULL) { + if (verbose) fprintf(messageFile, + "Malloc_Safe: Error, pointer is not NULL : %p\n", *ptr); + rc = ERROR_CODE; + } + } + if (rc == 0) { + *ptr = malloc(len); + if (*ptr == NULL) { + if (verbose) fprintf(messageFile, + "Malloc_Safe: Error, could not allocate %u bytes : %p\n", (unsigned int)len, *ptr); + rc = ERROR_CODE; + } + } + return rc; +} + +/* Realloc_Safe() is a general purpose wrapper around realloc() + + The caller is responsible for validating that 'size' is reasonable. +*/ + +int Realloc_Safe(unsigned char **buffer, + size_t size) +{ + int rc = 0; + unsigned char *tmpptr = NULL; + + if (rc == 0) { + tmpptr = realloc(*buffer, size); + if (tmpptr == NULL) { + if (verbose) fprintf(messageFile, + "Realloc_Safe: Error reallocating %u bytes\n", (unsigned int)size); + rc = ERROR_CODE; + } + } + if (rc == 0) { + *buffer = tmpptr; + } + return rc; +} + +/* + Format Conversion +*/ + +/* Format_ToHexascii() converts binary to hex ascii and appends a NUL terminator */ + +void Format_ToHexascii(char *string, + unsigned char *binary, + size_t length) +{ + size_t i; + + for (i = 0 ; i < length ; i++, binary++, string += 2) { + sprintf(string, "%02x", *binary); + } + return; +} + +/* Format_FromHexAscii() converts 'string' in hex ascii to 'binary' of 'length' + + It assumes that the string has enough bytes to accommodate the length. +*/ + +int Format_FromHexascii(unsigned char *binary, + const char *string, + size_t length) +{ + int rc = 0; + size_t i; + + for (i = 0 ; (rc == 0) && (i < length) ; i++) { + rc = Format_ByteFromHexascii(binary + i, + string + (i * 2)); + + } + return rc; +} + +/* Format_ByteFromHexAscii() converts two bytes of hex ascii to one byte of binary + */ + +int Format_ByteFromHexascii(unsigned char *byte, + const char *string) +{ + int rc = 0; + size_t i; + char c; + *byte = 0; + + for (i = 0 ; (rc == 0) && (i < 2) ; i++) { + (*byte) <<= 4; /* big endian, shift up the nibble */ + c = *(string + i); /* extract the next character from the string */ + + if ((c >= '0') && (c <= '9')) { + *byte += c - '0'; + } + else if ((c >= 'a') && (c <= 'f')) { + *byte += c + 10 - 'a'; + } + else if ((c >= 'A') && (c <= 'F')) { + *byte += c + 10 - 'A'; + } + else { + if (verbose) fprintf(messageFile, + "Format_ByteFromHexascii: " + "Error: Line has non hex ascii character: %c\n", c); + rc = ERROR_CODE; + } + } + return rc; +} diff --git a/src/signframework/utils.h b/src/signframework/utils.h new file mode 100644 index 0000000..e164b45 --- /dev/null +++ b/src/signframework/utils.h @@ -0,0 +1,196 @@ +/* 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. + */ + +#ifndef UTILS_H +#define UTILS_H + +#include <stdio.h> + +#ifdef Linux +#include <./linux/limits.h> +#endif + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +/* general purpose error flag, gets mapped at a higher layer */ +#define ERROR_CODE 1 + +/* Email response types */ + +/* 0 is success, send back email body and attachment */ + +/* do not send back any email */ +#define RESPONSE_NO_EMAIL 1 + +/* send back email body, no attachment */ +#define RESPONSE_BODY_ONLY 2 + +#if 0 +/* fatal framework error, exit. Don't want to keep looping because it will just fill up the log and + discard email. */ +#define ERROR_FATAL 3 +#endif + +/* send email body to framework admin, no attachment */ +#define RESPONSE_BODY_TO_ADMIN 4 + +/* file handling */ + +/* 1024 should accomodate lines with a 2048-bit key, 256 bytes, 512 characters */ + +#define MAX_LINE_SIZE 1024 + +int File_Copy(const char *destinationFilename, + const char *sourceFilename); +int File_Open(FILE **file, + const char *filename, + const char *mode); +int File_OpenMessageFile(const char *outputBodyFilename, + const char* mode); +int File_CloseMessageFile(void); +int File_ReadLine(int *haveLine, + char *line, + size_t *lineLength, + size_t lineSize, + FILE *file); +int File_ReadBinaryFile(unsigned char **data, + size_t *length, + size_t length_max, + const char *filename); +int File_ReadTextFile(char **text, + size_t *length, + size_t length_max, + const char *filename); +int File_GetSize(size_t *fileLength, + const char *filename); +int File_ValidateOpen(const char *filename, + const char *mode); +int File_WriteBinaryFile(const unsigned char *data, + size_t length, + const char *filename); +int File_WriteBinaryFileVa(const char *filename, ...); +void File_LogTime(FILE *logFile); +void File_Printf(FILE *lFile, + FILE *mFile, + const char *format, + ...); + +/* configuration file parsing */ + +int File_GetNameValue(int *haveLine, + char **name, + char **value, + char *lineBuffer, + size_t lineBufferLength, + FILE *file); +int File_MapNameToValue(char **value, + const char *name, + char *lineBuffer, + size_t lineBufferLength, + FILE *file); +int File_MapNameToBool(int *booln, + const char *name, + char *lineBuffer, + size_t lineBufferLength, + FILE *file); +int File_MapNameToUint(unsigned int *value, + const char *name, + char *lineBuffer, + size_t lineBufferLength, + FILE *file); +int File_GetNameValueArray(char ***names, + char ***values, + size_t *length, + char *lineBuffer, + size_t lineBufferLength, + FILE *file); +int File_GetValueArray(char ***values, + size_t *length, + const char *inName, + char *lineBuffer, + size_t lineBufferLength, + FILE *file); + +/* argv handling */ + +/* Number of arguments (not the number of bytes). Since typical signers take ~10 arguments, this + should be sufficient. Note that arguments with a value count as two arguments. */ + +#define MAX_ARGV_BODY 200 + +#ifdef Windows +#define ARG_MAX 4000 +#endif + +typedef struct tdArguments { + char *argv[MAX_ARGV_BODY]; + int argc; + size_t argvBytes; +} Arguments; + +void Arguments_Init(Arguments *arguments); +void Arguments_Delete(Arguments *arguments); +void Arguments_ClearSecret(const char *flag, + Arguments *arguments); + +int Arguments_AddPairTo(Arguments *arguments, + const char *flag, + const char *value); + +int Arguments_AddTo(Arguments *arguments, + const char *data, + int zero); + +int Arguments_GetFrom(const char **value, + const char *flag, + Arguments *arguments); + +/* character array handling */ + +int Array_GetLine(int *haveLine, + char *outLine, + const char **nextLine, + size_t lineSize, + const char *inLines, + FILE *logFile); + +/* memory allocation */ + +int Malloc_Safe(unsigned char **ptr, size_t len, size_t max_length); +int Malloc_Strcpy(char **out, + const char *in); +int Realloc_Safe(unsigned char **buffer, + size_t size); + +/* + Format Conversion +*/ + +void Format_ToHexascii(char *string, + unsigned char *binary, + size_t length); +int Format_FromHexascii(unsigned char *binary, + const char *string, + size_t length); +int Format_ByteFromHexascii(unsigned char *byte, + const char *string); + +#endif |