From 4c27df62aa425745620f45710465b0264acacbb0 Mon Sep 17 00:00:00 2001
From: Hans Petter Jansson <hpj@cl.no>
Date: Wed, 20 Nov 2019 08:23:35 +0100
Subject: [PATCH] 21

---
 nss/cmd/fipstest/fipstest.c | 304 ++++++++++++++++++++++++++++++++++++
 nss/cmd/fipstest/kas.sh     |  22 +++
 2 files changed, 326 insertions(+)

diff --git a/nss/cmd/fipstest/fipstest.c b/nss/cmd/fipstest/fipstest.c
index eba22fa..9bdd21c 100644
--- a/nss/cmd/fipstest/fipstest.c
+++ b/nss/cmd/fipstest/fipstest.c
@@ -8571,6 +8571,301 @@ out:
     }
 }
 
+typedef struct
+{
+    char param_name [2];
+    ECParams *ecparams;
+    int hash_len;
+    HASH_HashType hash_type;
+}
+ParamSpec;
+
+#define PARAM_SPECS_MAX 12
+
+static int
+find_free_param_spec (const ParamSpec *pspecs)
+{
+    int i;
+
+    for (i = 0; i < PARAM_SPECS_MAX; i++)
+    {
+        if (pspecs [i].param_name [0] == 0
+            && pspecs [i].param_name [1] == 0)
+            return i;
+    }
+
+    return 0;
+}
+
+static int
+find_param_spec (const ParamSpec *pspecs, char *name)
+{
+    int i;
+
+    for (i = 0; i < PARAM_SPECS_MAX; i++)
+    {
+        if (pspecs [i].param_name [0] == name [0]
+            && pspecs [i].param_name [1] == name [1])
+            return i;
+    }
+
+    return 0;
+}
+
+static void
+free_param_specs (ParamSpec *pspecs)
+{
+    int i;
+
+    for (i = 0; i < PARAM_SPECS_MAX; i++)
+    {
+        if (pspecs [i].ecparams)
+            PORT_FreeArena(pspecs [i].ecparams->arena, PR_FALSE);
+    }
+}
+
+#define CURVE_NAME_MAX 64
+
+static ECParams *
+get_and_decode_nistp_params (int n)
+{
+    char curve_name [CURVE_NAME_MAX];
+    SECItem *encodedparams;
+    ECParams *ecparams = NULL;
+
+    snprintf (curve_name, CURVE_NAME_MAX, "nistp%d", n);
+
+    encodedparams = getECParams (curve_name);
+    if (!encodedparams)
+        return NULL;
+
+    EC_DecodeParams (encodedparams, &ecparams);
+    SECITEM_FreeItem(encodedparams, PR_TRUE);
+    return ecparams;
+}
+
+void
+kas_ecc_test(char *reqfn, int do_validity)
+{
+    char buf[2048];
+    FILE *req;  /* input stream from the REQUEST file */
+    FILE *resp; /* output stream to the RESPONSE file */
+    ParamSpec pspecs [PARAM_SPECS_MAX];
+    SECItem x_ephem_cavs;
+    SECItem y_ephem_cavs;
+    SECItem x_ephem_iut;
+    SECItem y_ephem_iut;
+    SECItem d_ephem_iut;
+    SECItem cavs_hash_zz;
+    SECItem publicValue;
+    int current_pspec_def = -1;
+
+    req = fopen(reqfn, "r");
+    resp = stdout;
+    memset(&pspecs, 0, sizeof (pspecs));
+    memset(&x_ephem_cavs, 0, sizeof(x_ephem_cavs));
+    memset(&y_ephem_cavs, 0, sizeof(y_ephem_cavs));
+    memset(&x_ephem_iut, 0, sizeof(x_ephem_iut));
+    memset(&y_ephem_iut, 0, sizeof(y_ephem_iut));
+    memset(&d_ephem_iut, 0, sizeof(d_ephem_iut));
+    memset(&cavs_hash_zz, 0, sizeof(cavs_hash_zz));
+    memset(&publicValue, 0, sizeof(publicValue));
+
+    while (fgets(buf, sizeof buf, req) != NULL) {
+        /* [xx] or
+         * [xx - SHAxxx] or
+         * [SHA(s) supported (Used for hashing Z):  SHAxxx] */
+        if (buf[0] == '[') {
+            char tbuf [2];
+            int num;
+
+            if (strlen (buf) >= 4 && buf [3] == ']'
+                && sscanf(buf, "[%c%c]", &tbuf [0], &tbuf [1]) == 2) {
+                int i = current_pspec_def = find_free_param_spec (pspecs);
+                if (i < 0)
+                    goto out;
+
+                pspecs [i].param_name [0] = tbuf [0];
+                pspecs [i].param_name [1] = tbuf [1];
+
+                fputs(buf, resp);
+                continue;
+            }
+
+            if (strlen (buf) >= 6 && buf [3] == ' ' && buf [4] == '-'
+                && sscanf(buf, "[%c%c - ", &tbuf [0], &tbuf [1]) == 2) {
+                current_pspec_def = find_param_spec (pspecs, tbuf);
+                if (current_pspec_def < 0)
+                    goto out;
+
+                fputs(buf, resp);
+                continue;
+            }
+
+            if (!strncmp(buf, "[Curve selected:", strlen ("[Curve selected:"))) {
+                char *p = buf + strlen ("[Curve selected:");
+                p += strcspn (p, "0123456789");
+                if (!*p)
+                    goto out;
+                if (sscanf(p, "%d", &num) != 1)
+                    goto out;
+
+                if (current_pspec_def < 0)
+                    goto out;
+
+                pspecs [current_pspec_def].ecparams = get_and_decode_nistp_params (num);
+                if (!pspecs [current_pspec_def].ecparams)
+                    goto out;
+
+                fputs(buf, resp);
+                continue;
+            }
+
+            if (sscanf(buf, "[SHA(s) supported (Used for hashing Z): SHA%d", &num) == 1) {
+                if (current_pspec_def < 0)
+                    goto out;
+
+                pspecs [current_pspec_def].hash_len = num;
+                pspecs [current_pspec_def].hash_type = sha_get_hashType(num);
+                fputs(buf, resp);
+                continue;
+            }
+
+            fputs(buf, resp);
+            continue;
+        } else if (parse_secitem ("QeCAVSx", buf, &x_ephem_cavs)) {
+            fputs(buf, resp);
+            continue;
+        } else if (parse_secitem ("QeCAVSy", buf, &y_ephem_cavs)) {
+            fputs(buf, resp);
+
+            if (!do_validity) {
+                SECItem ZZ;
+                unsigned char ZZ_hash_buf [1024];
+                int field_len;
+                int len;
+                ECPrivateKey *privKey;
+
+                field_len = (pspecs [current_pspec_def].ecparams->fieldID.size + 7) >> 3;
+
+                if (EC_NewKey(pspecs [current_pspec_def].ecparams, &privKey) != SECSuccess)
+                    goto out;
+
+                len = privKey->publicValue.len;
+                if (len % 2 == 0) {
+                    goto out;
+                }
+                len = (len - 1) / 2;
+                if (privKey->publicValue.data[0] !=
+                    EC_POINT_FORM_UNCOMPRESSED) {
+                    goto out;
+                }
+
+                to_hex_str(buf, &privKey->publicValue.data[1], len);
+                fprintf (resp, "QeIUTx = %s\n", buf);
+                to_hex_str(buf, &privKey->publicValue.data[1 + len], len);
+                fprintf (resp, "QeIUTy = %s\n", buf);
+
+                SECITEM_AllocItem(NULL, &publicValue, 1 + 2 * field_len);
+                publicValue.len = 1 + 2 * field_len;
+                publicValue.data [0] = EC_POINT_FORM_UNCOMPRESSED;
+                memcpy (&publicValue.data [1], x_ephem_cavs.data + x_ephem_cavs.len - field_len, field_len);
+                memcpy (&publicValue.data [1 + field_len], y_ephem_cavs.data + y_ephem_cavs.len - field_len, field_len);
+
+                if (ECDH_Derive (&publicValue, pspecs [current_pspec_def].ecparams, &privKey->privateValue, PR_TRUE, &ZZ) != SECSuccess) {
+                    goto out;
+                }
+
+                SECITEM_ZfreeItem(&publicValue, PR_FALSE);
+                publicValue.data = NULL;
+
+                fips_hashBuf_zeropad(pspecs [current_pspec_def].hash_type, ZZ_hash_buf, ZZ.data, ZZ.len, len);
+
+                to_hex_str(buf, ZZ_hash_buf, pspecs [current_pspec_def].hash_len / 8);
+                fprintf (resp, "HashZZ = %s\n", buf);
+
+                PORT_FreeArena(privKey->ecParams.arena, PR_TRUE);
+            }
+
+            continue;
+        } else if (parse_secitem ("deIUT", buf, &d_ephem_iut)) {
+            fputs(buf, resp);
+            continue;
+        } else if (parse_secitem ("QeIUTx", buf, &x_ephem_iut)) {
+            fputs(buf, resp);
+            continue;
+        } else if (parse_secitem ("QeIUTy", buf, &y_ephem_iut)) {
+            fputs(buf, resp);
+            continue;
+        } else if (parse_secitem ("CAVSHashZZ", buf, &cavs_hash_zz)) {
+            if (do_validity) {
+                SECItem ZZ;
+                unsigned char ZZ_hash_buf [1024];
+                char Z_buf [1024];
+                int field_len;
+
+                field_len = (pspecs [current_pspec_def].ecparams->fieldID.size + 7) >> 3;
+
+                SECITEM_AllocItem(NULL, &publicValue, 1 + 2 * field_len);
+                publicValue.len = 1 + 2 * field_len;
+                publicValue.data [0] = EC_POINT_FORM_UNCOMPRESSED;
+                memcpy (&publicValue.data [1], x_ephem_cavs.data + x_ephem_cavs.len - field_len, field_len);
+                memcpy (&publicValue.data [1 + field_len], y_ephem_cavs.data + y_ephem_cavs.len - field_len, field_len);
+
+                if (ECDH_Derive (&publicValue, pspecs [current_pspec_def].ecparams, &d_ephem_iut, PR_TRUE, &ZZ) != SECSuccess) {
+                    goto out;
+                }
+
+                SECITEM_ZfreeItem(&publicValue, PR_FALSE);
+                publicValue.data = NULL;
+
+                fputs(buf, resp);
+
+                fips_hashBuf_zeropad(pspecs [current_pspec_def].hash_type, ZZ_hash_buf, ZZ.data, ZZ.len, field_len);
+                to_hex_str(Z_buf, ZZ_hash_buf, pspecs [current_pspec_def].hash_len / 8);
+                fprintf(resp, "IUTHashZZ = %s\n", Z_buf);
+
+                fprintf(resp, "Result = %s\n",
+                        (cavs_hash_zz.len == pspecs [current_pspec_def].hash_len / 8
+                         && memcmp (cavs_hash_zz.data, ZZ_hash_buf, pspecs [current_pspec_def].hash_len / 8) == 0) ? "P" : "F");
+            } else {
+                fputs(buf, resp);
+            }
+            continue;
+        } else {
+            /* Comments, blank lines, ... */
+            fputs(buf, resp);
+        }
+    }
+
+out:
+    fclose(req);
+
+    if (d_ephem_iut.data) {
+        SECITEM_ZfreeItem(&d_ephem_iut, PR_FALSE);
+    }
+    if (x_ephem_iut.data) {
+        SECITEM_ZfreeItem(&x_ephem_iut, PR_FALSE);
+    }
+    if (y_ephem_iut.data) {
+        SECITEM_ZfreeItem(&y_ephem_iut, PR_FALSE);
+    }
+    if (x_ephem_cavs.data) {
+        SECITEM_ZfreeItem(&x_ephem_cavs, PR_FALSE);
+    }
+    if (y_ephem_cavs.data) {
+        SECITEM_ZfreeItem(&y_ephem_cavs, PR_FALSE);
+    }
+    if (cavs_hash_zz.data) {
+        SECITEM_ZfreeItem(&cavs_hash_zz, PR_FALSE);
+    }
+    if (publicValue.data) {
+        SECITEM_ZfreeItem(&publicValue, PR_FALSE);
+    }
+
+    free_param_specs (pspecs);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -8764,6 +9059,15 @@ main(int argc, char **argv)
         } else {
             kas_ffc_test(argv[3], PR_FALSE);
         }
+    } else if (strcmp(argv[1], "kasecc") == 0) {
+        /***************/
+        /* KAS ECC */
+        /***************/
+        if (strcmp(argv[2], "validity") == 0) {
+            kas_ecc_test(argv[3], PR_TRUE);
+        } else {
+            kas_ecc_test(argv[3], PR_FALSE);
+        }
     }
     return 0;
 }
diff --git a/nss/cmd/fipstest/kas.sh b/nss/cmd/fipstest/kas.sh
index 148d7c9..dc1627d 100644
--- a/nss/cmd/fipstest/kas.sh
+++ b/nss/cmd/fipstest/kas.sh
@@ -27,6 +27,16 @@ KASValidityTest_FFCEphem_NOKC_ZZOnly_init.req
 KASValidityTest_FFCEphem_NOKC_ZZOnly_resp.req
 "
 
+kas_requests_ecc_function="
+KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_init.req
+KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_resp.req
+"
+
+kas_requests_ecc_validity="
+KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init.req
+KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_resp.req
+"
+
 if [ ${COMMAND} = "verify" ]; then
     for request in $kas_requests; do
 	sh ./validate1.sh ${TESTDIR} $request
@@ -45,3 +55,15 @@ for request in $kas_requests_ffc_validity; do
     echo $request $response
     fipstest kasffc validity ${REQDIR}/$request > ${RSPDIR}/$response
 done
+
+for request in $kas_requests_ecc_function; do
+    response=`echo $request | sed -e "s/req/rsp/"`
+    echo $request $response
+    fipstest kasecc function ${REQDIR}/$request > ${RSPDIR}/$response
+done
+
+for request in $kas_requests_ecc_validity; do
+    response=`echo $request | sed -e "s/req/rsp/"`
+    echo $request $response
+    fipstest kasecc validity ${REQDIR}/$request > ${RSPDIR}/$response
+done
-- 
2.21.0

