summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore26
-rw-r--r--README.md1
-rwxr-xr-xsrc/signframework/adddropbox95
-rw-r--r--src/signframework/athena.cfg.sample41
-rw-r--r--src/signframework/athenaaux.cfg.sample2
-rw-r--r--src/signframework/audit.c726
-rw-r--r--src/signframework/audit.cfg.sample30
-rw-r--r--src/signframework/audit_archive.c346
-rw-r--r--src/signframework/audit_archive.cfg.sample10
-rw-r--r--src/signframework/cca_functions.c2184
-rw-r--r--src/signframework/cca_functions.h92
-rw-r--r--src/signframework/cca_functions_ecc.c231
-rw-r--r--src/signframework/cca_functions_ecc.h36
-rw-r--r--src/signframework/cca_structures.c995
-rw-r--r--src/signframework/cca_structures.h147
-rw-r--r--src/signframework/cca_structures_ecc.c866
-rw-r--r--src/signframework/cca_structures_ecc.h140
-rwxr-xr-xsrc/signframework/cleandropbox39
-rw-r--r--src/signframework/debug.c89
-rw-r--r--src/signframework/debug.h32
-rw-r--r--src/signframework/dropbox_utils.c663
-rw-r--r--src/signframework/dropbox_utils.h35
-rw-r--r--src/signframework/errorcodes.h25
-rw-r--r--src/signframework/framework.c316
-rw-r--r--src/signframework/framework.cfg.sample72
-rw-r--r--src/signframework/framework_test.c1053
-rw-r--r--src/signframework/framework_utils.c2414
-rw-r--r--src/signframework/framework_utils.h219
-rw-r--r--src/signframework/frameworkkey_generate.c171
-rw-r--r--src/signframework/getpubkey.c651
-rw-r--r--src/signframework/getpubkey.cfg.sample37
-rw-r--r--src/signframework/getpubkey.log25
-rw-r--r--src/signframework/getpubkeyecc.c438
-rw-r--r--src/signframework/getpubkeyecc.cfg.sample36
-rw-r--r--src/signframework/json_regression_test/json_test.c750
-rw-r--r--src/signframework/json_regression_test/makefile34
-rw-r--r--src/signframework/keygen.c289
-rw-r--r--src/signframework/keygeneccp521.c266
-rw-r--r--src/signframework/ldap_lookup.c177
-rw-r--r--src/signframework/ldap_lookup.h27
-rw-r--r--src/signframework/logs/.empty0
-rw-r--r--src/signframework/mail.c122
-rw-r--r--src/signframework/mail.h32
-rw-r--r--src/signframework/makefile159
-rw-r--r--src/signframework/ossl_functions.c890
-rw-r--r--src/signframework/ossl_functions.h114
-rw-r--r--src/signframework/ossl_functions_ecc.c187
-rw-r--r--src/signframework/ossl_functions_ecc.h37
-rw-r--r--src/signframework/password_change.c568
-rw-r--r--src/signframework/password_change.cfg.sample32
-rw-r--r--src/signframework/password_generate.c500
-rwxr-xr-xsrc/signframework/pullconfig34
-rwxr-xr-xsrc/signframework/regress2.sh552
-rwxr-xr-xsrc/signframework/regress3.sh527
-rw-r--r--src/signframework/sender_validate.c287
-rw-r--r--src/signframework/sender_validate.cfg.sample6
-rwxr-xr-xsrc/signframework/sendkeys39
-rw-r--r--src/signframework/sendkeys.msg8
-rw-r--r--src/signframework/setclock.c96
-rw-r--r--src/signframework/signecc/Container.h120
l---------src/signframework/signecc/cca_functions.c1
l---------src/signframework/signecc/cca_functions.h1
l---------src/signframework/signecc/cca_functions_ecc.c1
l---------src/signframework/signecc/cca_functions_ecc.h1
l---------src/signframework/signecc/cca_structures.h1
l---------src/signframework/signecc/cca_structures_ecc.c1
l---------src/signframework/signecc/cca_structures_ecc.h1
l---------src/signframework/signecc/debug.c1
l---------src/signframework/signecc/debug.h1
-rw-r--r--src/signframework/signecc/eccutils.c393
-rw-r--r--src/signframework/signecc/eccutils.h41
-rw-r--r--src/signframework/signecc/ecdsa521.h24
-rw-r--r--src/signframework/signecc/getkeysecc.c498
-rw-r--r--src/signframework/signecc/hw_utils.h96
-rw-r--r--src/signframework/signecc/makefile111
l---------src/signframework/signecc/ossl_functions.c1
l---------src/signframework/signecc/ossl_functions.h1
l---------src/signframework/signecc/ossl_functions_ecc.c1
l---------src/signframework/signecc/ossl_functions_ecc.h1
-rwxr-xr-xsrc/signframework/signecc/regress-ecc.sh613
-rwxr-xr-xsrc/signframework/signecc/regress-eccloop.sh55
-rw-r--r--src/signframework/signecc/sha512.h36
-rw-r--r--src/signframework/signecc/signecc-hwa.cfg.sample40
-rw-r--r--src/signframework/signecc/signecc-hwaux.cfg.sample12
-rw-r--r--src/signframework/signecc/signecc-hwb.cfg.sample40
-rw-r--r--src/signframework/signecc/signecc-hwc.cfg.sample41
-rw-r--r--src/signframework/signecc/signecc-swaux.cfg.sample12
-rw-r--r--src/signframework/signecc/signecc-swp.cfg.sample39
-rw-r--r--src/signframework/signecc/signecc-swq.cfg.sample40
-rw-r--r--src/signframework/signecc/signecc-swr.cfg.sample44
-rw-r--r--src/signframework/signecc/signecc.c954
-rwxr-xr-xsrc/signframework/signecc/types_t.h54
l---------src/signframework/signecc/utils.c1
l---------src/signframework/signecc/utils.h1
-rw-r--r--src/signframework/systemd/signframework.conf4
-rw-r--r--src/signframework/systemd/signframework.service19
-rw-r--r--src/signframework/testproject.cfg.sample41
-rw-r--r--src/signframework/utils.c1355
-rw-r--r--src/signframework/utils.h196
99 files changed, 22908 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index fcc4827..d706ff0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/README.md b/README.md
index d9c9cf5..62d4d00 100644
--- a/README.md
+++ b/README.md
@@ -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(&currentTime);
+ 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,
+ &regeneration_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(&notificationFile,
+ 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(&parameters, 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
OpenPOWER on IntegriCloud