New utility program psa/psa_constant_names

Print the symbolic name corresponding to a numerical value.

Supported types: status values, algorithms, elliptic curves,
key types, key usage masks.

The program is partly generated from parsing psa/crypto.h with a few
hard-coded assumptions. This isn't ideal but it works and requires
little machinery.
diff --git a/programs/.gitignore b/programs/.gitignore
index 0241896..327dbdc 100644
--- a/programs/.gitignore
+++ b/programs/.gitignore
@@ -29,6 +29,8 @@
 pkey/rsa_sign_pss
 pkey/rsa_verify
 pkey/rsa_verify_pss
+psa/psa_constant_names
+psa/psa_constant_names_generated.c
 random/gen_entropy
 random/gen_random_ctr_drbg
 random/gen_random_havege
diff --git a/programs/Makefile b/programs/Makefile
index b6d1fa2..c65a10c 100644
--- a/programs/Makefile
+++ b/programs/Makefile
@@ -60,6 +60,7 @@
 	pkey/rsa_decrypt$(EXEXT)	pkey/rsa_encrypt$(EXEXT)	\
 	pkey/rsa_sign$(EXEXT)		pkey/rsa_verify$(EXEXT)		\
 	pkey/rsa_sign_pss$(EXEXT)	pkey/rsa_verify_pss$(EXEXT)	\
+	psa/psa_constant_names$(EXEXT)	\
 	ssl/dtls_client$(EXEXT)		ssl/dtls_server$(EXEXT)		\
 	ssl/ssl_client1$(EXEXT)		ssl/ssl_client2$(EXEXT)		\
 	ssl/ssl_server$(EXEXT)		ssl/ssl_server2$(EXEXT)		\
@@ -83,6 +84,8 @@
 APPS += test/cpp_dummy_build$(EXEXT)
 endif
 
+EXTRA_GENERATED =
+
 .SILENT:
 
 .PHONY: all clean list
@@ -92,6 +95,11 @@
 $(DEP):
 	$(MAKE) -C ../library
 
+EXTRA_GENERATED += psa/psa_constant_names_generated.c
+psa/psa_constant_names$(EXEXT): psa/psa_constant_names_generated.c
+psa/psa_constant_names_generated.c: ../scripts/generate_psa_constants.py ../include/psa/crypto.h
+	../scripts/generate_psa_constants.py
+
 aes/aescrypt2$(EXEXT): aes/aescrypt2.c $(DEP)
 	echo "  CC    aes/aescrypt2.c"
 	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) aes/aescrypt2.c    $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@
@@ -188,6 +196,10 @@
 	echo "  CC    pkey/rsa_encrypt.c"
 	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) pkey/rsa_encrypt.c    $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@
 
+psa/psa_constant_names$(EXEXT): psa/psa_constant_names.c $(DEP)
+	echo "  CC    psa/psa_constant_names.c"
+	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) psa/psa_constant_names.c    $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@
+
 random/gen_entropy$(EXEXT): random/gen_entropy.c $(DEP)
 	echo "  CC    random/gen_entropy.c"
 	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) random/gen_entropy.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@
@@ -294,9 +306,9 @@
 
 clean:
 ifndef WINDOWS
-	rm -f $(APPS)
+	rm -f $(APPS) $(EXTRA_GENERATED)
 else
-	del /S /Q /F *.o *.exe
+	del /S /Q /F *.o *.exe $(EXTRA_GENERATED)
 endif
 
 list:
diff --git a/programs/psa/psa_constant_names.c b/programs/psa/psa_constant_names.c
new file mode 100644
index 0000000..d422e14
--- /dev/null
+++ b/programs/psa/psa_constant_names.c
@@ -0,0 +1,157 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "psa/crypto.h"
+
+/* There are different GET_HASH macros for different kinds of algorithms
+ * built from hashes, but the values are all constructed on the
+ * same model. */
+#define PSA_ALG_GET_HASH(alg)                                   \
+    (((alg) & PSA_ALG_HASH_MASK) | PSA_ALG_CATEGORY_HASH)
+
+static void append(char **buffer, size_t buffer_size,
+                   size_t *required_size,
+                   const char *string, size_t length)
+{
+    *required_size += length;
+    if (*required_size < buffer_size) {
+        memcpy(*buffer, string, length);
+        *buffer += length;
+    }
+}
+
+/* The code of these function is automatically generated and included below. */
+static const char *psa_ecc_curve_name(psa_ecc_curve_t curve);
+static const char *psa_hash_algorithm_name(psa_algorithm_t hash_alg);
+
+static void append_with_curve(char **buffer, size_t buffer_size,
+                              size_t *required_size,
+                              const char *string, size_t length,
+                              psa_ecc_curve_t curve)
+{
+    const char *curve_name = psa_ecc_curve_name(curve);
+    append(buffer, buffer_size, required_size, string, length);
+    append(buffer, buffer_size, required_size, "(", 1);
+    if (curve_name != NULL) {
+        append(buffer, buffer_size, required_size,
+               curve_name, strlen(curve_name));
+    } else {
+        size_t n = snprintf(*buffer, buffer_size - *required_size,
+                            "0x%04x", (unsigned) curve);
+        if (n < buffer_size - *required_size) *buffer += n;
+        *required_size += n;
+    }
+    append(buffer, buffer_size, required_size, ")", 1);
+}
+
+static void append_with_hash(char **buffer, size_t buffer_size,
+                             size_t *required_size,
+                             const char *string, size_t length,
+                             psa_algorithm_t hash_alg)
+{
+    const char *hash_name = psa_hash_algorithm_name(hash_alg);
+    append(buffer, buffer_size, required_size, string, length);
+    append(buffer, buffer_size, required_size, "(", 1);
+    if (hash_name != NULL) {
+        append(buffer, buffer_size, required_size,
+               hash_name, strlen(hash_name));
+    } else {
+        size_t n = snprintf(*buffer, buffer_size - *required_size,
+                            "0x%08lx", (unsigned long) hash_alg);
+        if (n < buffer_size - *required_size) *buffer += n;
+        *required_size += n;
+    }
+    append(buffer, buffer_size, required_size, ")", 1);
+}
+
+#include "psa_constant_names_generated.c"
+
+static int psa_snprint_status(char *buffer, size_t buffer_size,
+                              psa_status_t status)
+{
+    const char *name = psa_strerror(status);
+    if (name == NULL) {
+        return snprintf(buffer, buffer_size, "%ld", (long) status);
+    } else {
+        size_t length = strlen(name);
+        if (length < buffer_size) {
+            memcpy(buffer, name, length + 1);
+            return length;
+        } else {
+            return buffer_size;
+        }
+    }
+}
+
+static int psa_snprint_ecc_curve(char *buffer, size_t buffer_size,
+                                 psa_ecc_curve_t curve)
+{
+    const char *name = psa_ecc_curve_name(curve);
+    if (name == NULL) {
+        return snprintf(buffer, buffer_size, "0x%04x", (unsigned) curve);
+    } else {
+        size_t length = strlen(name);
+        if (length < buffer_size) {
+            memcpy(buffer, name, length + 1);
+            return length;
+        } else {
+            return buffer_size;
+        }
+    }
+}
+
+static void usage(const char *program_name)
+{
+    printf("Usage: %s TYPE VALUE\n",
+           program_name == NULL ? "psa_constant_names" : program_name);
+    printf("Print the symbolic name whose numerical value is VALUE in TYPE.\n");
+    printf("Supported types (with = between aliases):\n");
+    printf("  alg=algorithm         Status code (psa_algorithm_t)\n");
+    printf("  curve=ecc_curve       Elliptic curve identifier (psa_ecc_curve_t)\n");
+    printf("  type=key_type         Status code (psa_key_type_t)\n");
+    printf("  usage=key_usage       Key usage (psa_key_usage_t)\n");
+    printf("  error=status          Status code (psa_status_t)\n");
+}
+
+int main(int argc, char *argv[])
+{
+    char buffer[200];
+    unsigned long value;
+    char *end;
+
+    if (argc <= 1 ||
+        !strcmp(argv[1], "help") ||
+        !strcmp(argv[1], "--help"))
+    {
+        usage(argv[0]);
+        return EXIT_FAILURE;
+    }
+    if (argc != 3) {
+        usage(argv[0]);
+        return EXIT_FAILURE;
+    }
+    value = strtoul(argv[2], &end, 0);
+    if (*end) {
+        printf("Non-numeric value: %s\n", argv[2]);
+        return EXIT_FAILURE;
+    }
+
+    if (!strcmp(argv[1], "error") || !strcmp(argv[1], "status"))
+        psa_snprint_status(buffer, sizeof(buffer), value);
+    else if (!strcmp(argv[1], "alg") || !strcmp(argv[1], "algorithm"))
+        psa_snprint_algorithm(buffer, sizeof(buffer), value);
+    else if (!strcmp(argv[1], "curve") || !strcmp(argv[1], "ecc_curve"))
+        psa_snprint_ecc_curve(buffer, sizeof(buffer), value);
+    else if (!strcmp(argv[1], "type") || !strcmp(argv[1], "key_type"))
+        psa_snprint_key_type(buffer, sizeof(buffer), value);
+    else if (!strcmp(argv[1], "usage") || !strcmp(argv[1], "key_usage"))
+        psa_snprint_key_usage(buffer, sizeof(buffer), value);
+    else {
+        printf("Unknown type: %s\n", argv[1]);
+        return EXIT_FAILURE;
+    }
+
+    puts(buffer);
+    return EXIT_SUCCESS;
+}