regression: 4005: add NIST aes-gcm vectors

Adds NIST AES-GCM test vectors to regression case 4005 with
CFG_GCM_NIST_VECTORS=y. Only the first test in each group is
used if CFG_GCM_NIST_VECTORS_LIMITED=y.

With CFG_GCM_NIST_VECTORS=y the size of the xtest binary grows with more
than 10 MiB, unless CFG_GCM_NIST_VECTORS_LIMITED=y is given, then only
by ~1 MiB.

Without CFG_GCM_NIST_VECTORS=y, CFG_GCM_NIST_VECTORS_LIMITED has no
effect.

Android not supported.

Reviewed-by: Jerome Forissier <jerome.forissier@linaro.org>
Tested-by: Jerome Forissier <jerome.forissier@linaro.org> (HiKey960)
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
diff --git a/host/xtest/Makefile b/host/xtest/Makefile
index 2313b37..f5d0739 100644
--- a/host/xtest/Makefile
+++ b/host/xtest/Makefile
@@ -71,6 +71,9 @@
 CFLAGS += -I./
 CFLAGS += -I./adbg/include
 CFLAGS += -I./xml/include
+ifeq ($(CFG_GCM_NIST_VECTORS),y)
+CFLAGS += -I$(out-dir)/xtest
+endif
 
 CFLAGS += -I$(OPTEE_CLIENT_EXPORT)/include
 CFLAGS += -I$(TA_DEV_KIT_DIR)/host_include
@@ -166,9 +169,44 @@
 	$(q)$(RMDIR) $(out-dir) 2> /dev/null; true
 endef
 
+ifeq ($(CFG_GCM_NIST_VECTORS),y)
+GCM_NIST_VECTORS_DECRYPT = gcmDecrypt128 gcmDecrypt192 gcmDecrypt256
+GCM_NIST_VECTORS_ENCRYPT = gcmEncryptExtIV128 gcmEncryptExtIV192 \
+			   gcmEncryptExtIV256
+
+cleanfiles += $(out-dir)/gcmtestvectors.zip
+$(out-dir)/gcmtestvectors.zip:
+	@echo '  DL      $@'
+	$(q)curl https://csrc.nist.gov/csrc/media/projects/cryptographic-algorithm-validation-program/documents/mac/gcmtestvectors.zip -o $@
+
+define create-nist-gcm-vectors
+cleanfiles += $(out-dir)/xtest/$(1).h $(out-dir)/$(1).rsp
+
+$(out-dir)/$(1).rsp: $(out-dir)/gcmtestvectors.zip
+	@echo '  UNZIP   $$@'
+	$(q)unzip -o $$< $$(notdir $$@) -d $$(dir $$@)
+	$(q)touch $$@
+
+
+$(out-dir)/xtest/$(1).h: $(out-dir)/$(1).rsp
+	@echo '  GEN     $$@'
+	$(q)../../scripts/rsp_to_gcm_test.py --inf $$< --outf $$@ --mode=$(2) \
+		$(if $(filter y,$(CFG_GCM_NIST_VECTORS_LIMITED)),--limited)
+
+$(CURDIR)/regression_4000.c: $(out-dir)/xtest/$(1).h
+endef
+
+$(foreach v, $(GCM_NIST_VECTORS_DECRYPT), $(eval $(call \
+	create-nist-gcm-vectors,$v,decrypt)))
+$(foreach v, $(GCM_NIST_VECTORS_ENCRYPT), $(eval $(call \
+	create-nist-gcm-vectors,$v,encrypt)))
+endif
+
+
 .PHONY: clean
 clean:
 	@echo '  CLEAN $(out-dir)'
 	$(q)rm -f $(out-dir)/xtest/xtest
 	$(q)$(foreach obj,$(objs), rm -f $(obj))
+	$(q)rm -f $(cleanfiles)
 	$(call rm-build-dirs,adbg/src adbg)
diff --git a/host/xtest/regression_4000.c b/host/xtest/regression_4000.c
index 19092d3..f315978 100644
--- a/host/xtest/regression_4000.c
+++ b/host/xtest/regression_4000.c
@@ -2670,6 +2670,14 @@
 	XTEST_AE_CASE_AES_GCM(vect16, 5, 9, ARRAY, ARRAY, ARRAY),
 	XTEST_AE_CASE_AES_GCM(vect17, 5, 9, ARRAY, ARRAY, ARRAY),
 	XTEST_AE_CASE_AES_GCM(vect18, 5, 9, ARRAY, ARRAY, ARRAY),
+#ifdef CFG_GCM_NIST_VECTORS
+#include "gcmDecrypt128.h"
+#include "gcmDecrypt192.h"
+#include "gcmDecrypt256.h"
+#include "gcmEncryptExtIV128.h"
+#include "gcmEncryptExtIV192.h"
+#include "gcmEncryptExtIV256.h"
+#endif
 };
 
 static void xtest_tee_test_4005(ADBG_Case_t *c)
diff --git a/scripts/rsp_to_gcm_test.py b/scripts/rsp_to_gcm_test.py
new file mode 100755
index 0000000..0543541
--- /dev/null
+++ b/scripts/rsp_to_gcm_test.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+
+modes = {'encrypt': 0, 'decrypt': 1}
+
+limited = False
+
+
+def to_compound_str(val):
+    assert len(val) % 2 == 0, "Only even sized values supported"
+    if len(val) > 0:
+        import re
+        a = re.findall('..', val)
+        b = "(const uint8_t []){"
+        for s in a:
+            b += "0x" + s + ", "
+        b += "}, " + repr(len(val) / 2) + ","
+    else:
+        b = "NULL, 0,"
+    return b
+
+
+def generate_case(outf, myvars, mode):
+    if 'PT' not in myvars:
+        myvars['PT'] = ''
+    if 'FAIL' in myvars:
+        return
+    if limited and myvars['Count'] != '0':
+        return
+    # Skip cases not supported by GP
+    if len(myvars['Tag']) / 2 < 96 / 8:
+        return
+
+    outf.write('{ TEE_ALG_AES_GCM, ' + mode + ', TEE_TYPE_AES,\n')
+    outf.write('/* Key */ ' + to_compound_str(myvars['Key']) + '\n')
+    outf.write('/* IV  */ ' + to_compound_str(myvars['IV']) + '\n')
+    outf.write('0,\n')
+    outf.write('/* AAD */ ' + to_compound_str(myvars['AAD']) + '\n')
+    outf.write('0,\n')
+    outf.write('/* PT  */ ' + to_compound_str(myvars['PT']) + '\n')
+    outf.write('/* CT  */ ' + to_compound_str(myvars['CT']) + '\n')
+    outf.write('/* Tag */ ' + to_compound_str(myvars['Tag']) + '\n')
+    outf.write(repr(myvars['Line']) + '},\n')
+
+
+def get_args():
+    import argparse
+
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument('--inf', required=True,
+                        type=argparse.FileType('r'),
+                        help='Name of input RSP file')
+
+    parser.add_argument('--outf', required=True,
+                        type=argparse.FileType('w'),
+                        help='Name of output C file')
+
+    parser.add_argument('--mode', required=True, choices=modes.keys(),
+                        help='mode: encrypt or decrypt')
+
+    parser.add_argument('--limited', action="store_true",
+                        help='Only run one test case from each group')
+
+    return parser.parse_args()
+
+
+def main():
+    import re
+    global limited
+    args = get_args()
+    inf = args.inf
+    outf = args.outf
+    myvars = {}
+    line_num = 0
+
+    if args.mode == "encrypt":
+        mode = "TEE_MODE_ENCRYPT"
+    else:
+        mode = "TEE_MODE_DECRYPT"
+
+    limited = args.limited
+
+    for line in inf:
+        line_num += 1
+        myl = line.strip()
+        if len(myl) == 0:
+            continue
+        if re.match('^#', myl):
+            continue
+        if re.match('^\[', myl):
+            continue
+        s = re.split('\W+', myl)
+        if len(s) == 0:
+            continue
+        name = s[0]
+        if name == 'Count':
+            if len(myvars) > 1:
+                generate_case(outf, myvars, mode)
+                myvars = {}
+            myvars['Line'] = line_num
+
+        if len(s) < 2:
+            myvars[s[0]] = ''
+        else:
+            myvars[s[0]] = s[1]
+
+        if len(s) < 2:
+            continue
+        val = s[1]
+
+    if len(myvars) > 1:
+        generate_case(outf, myvars, mode)
+
+
+if __name__ == "__main__":
+    main()