[][openwrt][common][Add libkcapi to test fips140-3]

[Description]
Add libkcapi to pass fips140-3.
Libkcapi allow user-space to access the Linux kernel crypto API.
In this package, we add two command, `kcapi-drbg` and `kcapi-mtk-digst`
The `kcapi-drbg` is to test drbg in linux kernel cryto.
Usage:
        -b --bytes <BYTES>      Number of bytes to generate
				(required option)
        -r --rng name <RNGNAME> DRNG name as advertised in
				/proc/crypto(require option))
        -e --entropy <RNGNAME>  DRBG entropy(require option))
        -n --nonce <RNGNAME>    DRBG nonce(require option))
        -p --personalization string <RNGNAME> DRBG personaliztion
				string(require option))
           --hex                The random number is returned
				in hexadecimal notation
        -h --help               This help information
For example:
	kcapi-drbg -e entropy -n nonce -p personalization string \
	  --hex -b 80 -r drbg_nopr_hmac_sha1

The `kcapi-mtk-digest` is to test hmac and sha in linux kernel crypto.
Usage:
        -k      Key (hmac required option)
        -n      Digest name such as sha1, sha224, sha256 sha512
                        hmac-sha1 hmac-sha224 hmac-sha256
			hmac-sha384 hmac-sha512
        -e 	empty input
	-m      message(require option))
        -l      output len(require option))
                                notation
        -h --help               This help information
Worth a question, it only use key in hmac.
For example:
	kcapi-mtk-dgst -n sha512 -m data  -l 10
	kcapi-mtk-dgst -k key -n hmac-sha512 -m data  -l 10

[Release-log]

Change-Id: I8a7061873a4cd52cbc02c0f2e2c6b188c7de456f
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/8248293
diff --git a/feed/libkcapi/Makefile b/feed/libkcapi/Makefile
new file mode 100644
index 0000000..874e919
--- /dev/null
+++ b/feed/libkcapi/Makefile
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2023 Mediatek Ltd.
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=libkcapi
+PKG_RELEASE:=1.4.0
+PKG_VERSION:=1.4.0
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
+PKG_FIXUP:=autoreconf
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/smuellerDD/libkcapi/tar.gz/refs/tags/v$(PKG_VERSION)
+PKG_HASH:=10f96ccb2799f3ef810f3956c48a2d791a9221c9a545de51bd79e5a81030f38e
+
+include $(INCLUDE_DIR)/package.mk
+MAKE_FLAGS:=
+
+define Package/libkcapi
+  SECTION:=Utilities
+  CATEGORY:=Utilities
+  TITLE:=libkcapi
+  SUBMENU:=FIPS
+endef
+
+define Package/libkcapi/description
+  Libkcapi allow user-space to access the Linux kernel crypto API.
+endef
+
+define Build/Configure
+	cd $(PKG_BUILD_DIR) && $(STAGING_DIR_HOST)/bin/autoreconf -i
+	cd $(PKG_BUILD_DIR) && CC=aarch64-openwrt-linux-musl-gcc \
+		CFLAGS="-Wno-error" $(PKG_BUILD_DIR)/configure \
+		--enable-kcapi-test  --enable-kcapi-speed \
+		--enable-kcapi-hasher --enable-kcapi-rngapp \
+		--enable-kcapi-encapp --enable-kcapi-dgstapp \
+		--host=x86
+endef
+
+define Package/libkcapi/install
+	$(INSTALL_DIR) $(1)/usr/bin
+	$(CP) $(PKG_BUILD_DIR)/bin/* $(1)/usr/bin
+endef
+
+$(eval $(call BuildPackage,libkcapi))
diff --git a/feed/libkcapi/patches/0001-add-command-to-test-drbg.patch b/feed/libkcapi/patches/0001-add-command-to-test-drbg.patch
new file mode 100644
index 0000000..c26cc6d
--- /dev/null
+++ b/feed/libkcapi/patches/0001-add-command-to-test-drbg.patch
@@ -0,0 +1,295 @@
+--- a/libkcapi/Makefile.am
++++ b/libkcapi/Makefile.am
+@@ -193,6 +193,16 @@ EXTRA_bin_kcapi_rng_DEPENDENCIES = libto
+ 
+ SCAN_FILES += $(bin_kcapi_rng_SOURCES)
+ man_MANS += apps/kcapi-rng.1
++
++bin_PROGRAMS += bin/kcapi-drbg
++
++bin_kcapi_drbg_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/lib/
++bin_kcapi_drbg_LDFLAGS = $(COMMON_LDFLAGS)
++bin_kcapi_drbg_LDADD = libkcapi.la
++bin_kcapi_drbg_SOURCES = apps/kcapi-drbg.c apps/app-internal.c
++EXTRA_bin_kcapi_rng_DEPENDENCIES = libtool
++
++SCAN_FILES += $(bin_kcapi_drbg_SOURCES)
+ endif
+ 
+ if ENABLE_KCAPI_ENCAPP
+--- /dev/null
++++ b/libkcapi/apps/kcapi-drbg.c
+@@ -0,0 +1,273 @@
++// SPDX-License-Identifier: BSD-2-Clause
++/*
++ * Copyright (C) 2023, MediaTek Inc. All rights reserved.
++ */
++
++#define _GNU_SOURCE
++#include <unistd.h>
++#include <sys/syscall.h>
++#include <errno.h>
++#include <limits.h>
++#include <stdint.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <getopt.h>
++
++#include <kcapi.h>
++
++#include "app-internal.h"
++
++/* For efficiency reasons, this should be identical to algif_rng.c:MAXSIZE. */
++#define KCAPI_RNG_BUFSIZE  2048
++
++static struct kcapi_handle *rng = NULL;
++static char *rng_name = NULL;
++static bool hexout = false;
++static unsigned char* entropy = NULL;
++static unsigned char* nonce = NULL;
++static unsigned char* per = NULL;
++static unsigned char* additional_1 = NULL;
++static unsigned char* additional_2 = NULL;
++static uint32_t entropy_len = 0;
++static uint32_t nonce_len = 0;
++static uint32_t per_len = 0;
++static uint32_t add1_len = 0;
++static uint32_t add2_len = 0;
++
++
++
++static unsigned char* hextobin(char* hexstr, uint32_t *ret_len) {
++	uint32_t len = strlen(hexstr);
++	size_t i = 0;
++	char *pos = hexstr;
++	unsigned char *ret = (unsigned char*) malloc((len + 1) / 2);
++
++	for (i = 0; i < (len + 1) / 2; i++) {
++		if(i == 0 && (len % 2) == 1) {
++			sscanf(pos, "%1hhx", &ret[i]);
++			pos += 1;
++		} else {
++			sscanf(pos, "%2hhx", &ret[i]);
++			pos += 2;
++		}
++	}
++	*ret_len = (len + 1) / 2;
++
++	return ret;
++}
++
++static void uninit() {
++	memset(entropy, 0, entropy_len);
++	memset(nonce, 0, nonce_len);
++	memset(per, 0, per_len);
++	memset(additional_1, 0, add1_len);
++	memset(additional_2, 0, add2_len);
++	if(entropy)
++		free(entropy);
++	if(nonce)
++		free(nonce);
++	if(per)
++		free(per);
++}
++
++static void usage(void)
++{
++	fprintf(stderr, "\nKernel Crypto API DRBG\n");
++	fprintf(stderr, "Usage:\n");
++	fprintf(stderr, "\t-b --bytes <BYTES>\tNumber of bytes to generate (required option)\n");
++	fprintf(stderr, "\t-r --rng name <RNGNAME>\tDRNG name as advertised in /proc/crypto(require option))\n");
++	fprintf(stderr, "\t-e --entropy <RNGNAME>\tDRBG entropy(require option))\n");
++	fprintf(stderr, "\t-n --nonce <RNGNAME>\tDRBG nonce(require option))\n");
++	fprintf(stderr, "\t-p --personaliztion string <RNGNAME>\tDRBG personaliztion string(require option))\n");
++	fprintf(stderr, "\t   --hex\t\tThe random number is returned in hexadecimal\n");
++	fprintf(stderr, "\t\t\t\tnotation\n");
++	fprintf(stderr, "\t-h --help\t\tThis help information\n");
++
++	uninit();
++	exit(1);
++}
++
++static int parse_opts(int argc, char *argv[], size_t *outlen)
++{
++	int c = 0;
++	size_t bytes = 0;
++
++	while (1) {
++		int opt_index = 0;
++		static struct option opts[] = {
++			{"entropy",	           required_argument,	0, 'e'},
++			{"nonce",	           required_argument,	0, 'n'},
++			{"bytes",	           required_argument,	0, 'b'},
++			{"rng_name",               required_argument,	0, 'r'},
++			{"personal_strng ",        required_argument,   0, 'p'},
++			{"help",                   no_argument,		0, 'h'},
++			{"hex",		           no_argument,		0,  0},
++			{"additionalinput1 ",      required_argument,   0, 'a'},
++			{"additionalinput2 ",      required_argument,   0, 'c'},
++			{0, 0, 0, 0}
++		};
++		c = getopt_long(argc, argv, "e:n:b:r:p:ha:c:", opts, &opt_index);
++		if (-1 == c)
++			break;
++		switch (c) {
++		case 0:
++			switch (opt_index) {
++			case 0:
++				entropy = hextobin(optarg, &entropy_len);
++				break;
++			case 1:
++				nonce = hextobin(optarg, &nonce_len);
++				break;
++			case 2:
++				bytes = strtoul(optarg, NULL, 10);
++				if (bytes == ULONG_MAX) {
++					usage();
++					return -EINVAL;
++				}
++				break;
++			case 3:
++				rng_name = optarg;
++				break;
++			case 4:
++				per = hextobin(optarg, &per_len);
++				break;
++			case 5:
++				usage();
++				break;
++			case 6:
++				hexout = true;
++				break;
++			case 7:
++				additional_1 = hextobin(optarg, &add1_len);
++				break;
++			case 8:
++				additional_2 = hextobin(optarg, &add2_len);
++				break;
++			default:
++				usage();
++			}
++			break;
++		case 'e':
++			entropy = hextobin(optarg, &entropy_len);
++			break;
++		case 'n':
++			nonce = hextobin(optarg, &nonce_len);
++			break;
++		case 'b':
++			bytes = strtoul(optarg, NULL, 10);
++			if (bytes == ULONG_MAX) {
++				usage();
++				return -EINVAL;
++			}
++			break;
++		case 'r':
++			rng_name = optarg;
++			break;
++		case 'p':
++			per = hextobin(optarg, &per_len);
++			break;
++		case 'a':
++			additional_1 = hextobin(optarg, &add1_len);
++			break;
++		case 'c':
++			additional_2 = hextobin(optarg, &add2_len);
++			break;
++		default:
++			usage();
++		}
++	}
++	if (!bytes || !nonce || !entropy || !rng_name)
++		usage();
++
++	*outlen = (size_t)bytes;
++	return 0;
++}
++
++int main(int argc, char *argv[])
++{
++	ssize_t ret = 0;
++	uint8_t buf[KCAPI_RNG_BUFSIZE] __aligned(KCAPI_APP_ALIGN);
++	uint8_t *seedbuf = buf;
++	uint32_t seedsize = 0;
++	size_t outlen = 0;
++	unsigned char *ent = NULL;
++	int count = 2;
++
++	ret = parse_opts(argc, argv, &outlen);
++	if (ret)
++		return (int)ret;
++
++	ret = kcapi_rng_init(&rng, rng_name, 0);
++	if (ret)
++		return (int)ret;
++
++	seedsize = kcapi_rng_seedsize(rng);
++
++	ent = (unsigned char*)malloc(entropy_len + nonce_len + per_len);
++	memset(ent, 0, entropy_len + nonce_len + per_len);
++	memcpy(ent, entropy, entropy_len);
++	memcpy(ent + entropy_len, nonce, nonce_len);
++	memcpy(ent + entropy_len + nonce_len, per, per_len);
++
++	kcapi_rng_setentropy(rng, ent, entropy_len + nonce_len + per_len);
++
++	ret = kcapi_rng_seed(rng, seedbuf, seedsize);
++	if (ret)
++		goto out;
++
++	while(count --) {
++		size_t len = outlen;
++		while (len) {
++			size_t todo = (len < KCAPI_RNG_BUFSIZE) ?
++						len : KCAPI_RNG_BUFSIZE;
++
++			if(count == 1 && additional_1) {
++				kcapi_rng_setaddtl(rng, additional_1, add1_len);
++			} else if(count == 0 && additional_2 ){
++				kcapi_rng_setaddtl(rng, additional_2, add2_len);
++			}
++
++			ret = kcapi_rng_generate(rng, buf, todo);
++			if (ret < 0)
++				goto out;
++
++			if ((uint32_t)ret == 0) {
++				ret = -EFAULT;
++				goto out;
++			}
++
++			if(count == 0) {
++				if (hexout) {
++					char hexbuf[2 * KCAPI_RNG_BUFSIZE];
++
++					bin2hex(buf, (size_t)ret, hexbuf, sizeof(hexbuf), 0);
++					fwrite(hexbuf, 2 * (size_t)ret, 1, stdout);
++				} else {
++					fwrite(buf, (size_t)ret, 1, stdout);
++				}
++			}
++			len -= (size_t)ret;
++		}
++	}
++
++	ret = 0;
++out:
++	if (rng)
++		kcapi_rng_destroy(rng);
++	kcapi_memset_secure(buf, 0, sizeof(buf));
++
++	if (seedbuf && (seedbuf != buf)) {
++		kcapi_memset_secure(seedbuf, 0, seedsize);
++		free(seedbuf);
++	}
++
++	uninit();
++	if(!ent)
++		free(ent);
++
++	return (int)ret;
++}
diff --git a/feed/libkcapi/patches/0002-add-command-to-test-hmac-sha.patch b/feed/libkcapi/patches/0002-add-command-to-test-hmac-sha.patch
new file mode 100644
index 0000000..25a4e63
--- /dev/null
+++ b/feed/libkcapi/patches/0002-add-command-to-test-hmac-sha.patch
@@ -0,0 +1,309 @@
+--- a/libkcapi/Makefile.am
++++ b/libkcapi/Makefile.am
+@@ -229,6 +229,16 @@ EXTRA_bin_kcapi_dgst_DEPENDENCIES = libt
+ 
+ SCAN_FILES += $(bin_kcapi_dgst_SOURCES)
+ man_MANS += apps/kcapi-dgst.1
++
++bin_PROGRAMS += bin/kcapi-mtk-dgst
++
++bin_kcapi_mtk_dgst_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/lib/
++bin_kcapi_mtk_dgst_LDFLAGS = $(COMMON_LDFLAGS)
++bin_kcapi_mtk_dgst_LDADD = libkcapi.la
++bin_kcapi_mtk_dgst_SOURCES = apps/kcapi-mtk-dgst.c apps/app-internal.c
++EXTRA_bin_kcapi_dgst_DEPENDENCIES = libtool
++
++SCAN_FILES += $(bin_kcapi_mtk_dgst_SOURCES)
+ endif
+ 
+ if HAVE_CLANG
+--- /dev/null
++++ b/libkcapi/apps/kcapi-mtk-dgst.c
+@@ -0,0 +1,287 @@
++// SPDX-License-Identifier: BSD-2-Clause
++/*
++ * Copyright (C) 2023, MediaTek Inc. All rights reserved.
++ */
++
++#define _GNU_SOURCE
++#include <unistd.h>
++#include <sys/syscall.h>
++#include <errno.h>
++#include <limits.h>
++#include <stdint.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <getopt.h>
++
++#include <kcapi.h>
++
++#include "app-internal.h"
++
++#define TYPE_SHA    0
++#define TYPE_HMAC   1
++
++static char *name = NULL;
++static unsigned char* key = NULL;
++static unsigned char* message = NULL;
++static uint32_t key_len = 0;
++static uint32_t message_len = 0;
++int type = -1, type_index = -1;
++int empty = 0;
++struct sha {
++	char *name;
++	ssize_t (*fun)(const uint8_t*, size_t,
++			uint8_t*, size_t);
++};
++
++struct hmac {
++	char *name;
++	ssize_t (*fun)(const uint8_t*, uint32_t,
++			const uint8_t*, size_t,
++			uint8_t*, size_t);
++};
++
++struct sha msg_digest[5] = {
++	{
++		.name = "sha1",
++		.fun = kcapi_md_sha1,
++	},
++	{
++		.name = "sha224",
++		.fun = kcapi_md_sha224,
++	},
++	{
++		.name = "sha256",
++		.fun = kcapi_md_sha256,
++	},
++	{
++		.name = "sha384",
++		.fun = kcapi_md_sha384,
++	},
++	{
++		.name = "sha512",
++		.fun = kcapi_md_sha512,
++	},
++};
++
++struct hmac key_msg_digest[5] = {
++	{
++		.name = "hmac-sha1",
++		.fun = kcapi_md_hmac_sha1,
++	},
++	{
++		.name = "hmac-sha224",
++		.fun = kcapi_md_hmac_sha224,
++	},
++	{
++		.name = "hmac-sha256",
++		.fun = kcapi_md_hmac_sha256,
++	},
++	{
++		.name = "hmac-sha384",
++		.fun = kcapi_md_hmac_sha384,
++	},
++	{
++		.name = "hmac-sha512",
++		.fun = kcapi_md_hmac_sha512,
++	},
++};
++
++static unsigned char* hextobin(char* hexstr, uint32_t *ret_len) {
++	size_t len = strlen(hexstr);
++	size_t i = 0;
++	char *pos = hexstr;
++	unsigned char *ret = (unsigned char*) malloc((len + 1) / 2);
++
++	for (i = 0; i < (len + 1) / 2; i++) {
++		if(i == 0 && (len % 2) == 1) {
++			sscanf(pos, "%1hhx", &ret[i]);
++			pos += 1;
++		} else {
++			sscanf(pos, "%2hhx", &ret[i]);
++			pos += 2;
++		}
++	}
++	*ret_len = (len + 1) / 2;
++
++	return ret;
++}
++
++static void uninit() {
++	memset(key, 0, key_len);
++	memset(message, 0, message_len);
++	if(key)
++		free(key);
++	if(message)
++		free(message);
++}
++
++static void usage(void)
++{
++	fprintf(stderr, "\nKernel Crypto API SHA & HMAC\n");
++	fprintf(stderr, "Usage:\n");
++	fprintf(stderr, "\t-k  \tKey (hmac required option)\n");
++	fprintf(stderr, "\t-n  \tDigest name such as sha1, sha224, sha256 sha512\n");
++	fprintf(stderr, "\t          \thmac-sha1 hmac-sha224 hmac-sha256 hmac-sha384 hmac-sha512\n");
++	fprintf(stderr, "\t-m  \tmessage(require option))\n");
++	fprintf(stderr, "\t-e  \tempty message \n");
++	fprintf(stderr, "\t-l  \toutput len(require option))\n");
++	fprintf(stderr, "\t\t\t\tnotation\n");
++	fprintf(stderr, "\t-h --help\t\tThis help information\n");
++
++	uninit();
++	exit(1);
++}
++
++static int check_type(char* name) {
++	int i = 0;
++
++	for(i = 0; i < 5; i++) {
++		if(!strncmp(name, msg_digest[i].name, strlen(name))) {
++			type_index = i;
++			return TYPE_SHA;
++		}
++	}
++
++	for(i = 0; i < 5; i++) {
++		if(!strncmp(name, key_msg_digest[i].name, strlen(name))) {
++			type_index = i;
++			return TYPE_HMAC;
++		}
++	}
++
++	return TYPE_HMAC + 1;
++}
++
++static int parse_opts(int argc, char *argv[], size_t *outlen)
++{
++	int c = 0;
++	size_t len = 0;
++
++	while (1) {
++		int opt_index = 0;
++		static struct option opts[] = {
++			{"key",	     required_argument,	0, 'k'},
++			{"name",     required_argument,	0, 'n'},
++			{"message",  required_argument,	0, 'm'},
++			{"len",      required_argument,	0, 'l'},
++			{"help",     required_argument,	0, 'h'},
++			{"empty",    required_argument,	0, 'e'},
++			{0, 0, 0, 0}
++		};
++		c = getopt_long(argc, argv, "k:n:m:l:he", opts, &opt_index);
++		if (-1 == c)
++			break;
++		switch (c) {
++		case 0:
++			switch (opt_index) {
++			case 0:
++				key = hextobin(optarg, &key_len);
++				break;
++			case 1:
++				name = optarg;
++				type = check_type(name);
++				if(type > TYPE_HMAC){
++					usage();
++					return -EINVAL;
++				}
++
++				break;
++			case 2:
++				message =  hextobin(optarg, &message_len);
++				break;
++			case 3:
++				len = strtoul(optarg, NULL, 10);
++				if (len == ULONG_MAX) {
++					usage();
++					return -EINVAL;
++				}
++				break;
++			default:
++				usage();
++			}
++			break;
++		case 'k':
++			key = hextobin(optarg, &key_len);
++			break;
++		case 'n':
++			name = optarg;
++			type = check_type(name);
++			if(type > TYPE_HMAC){
++				usage();
++				return -EINVAL;
++			}
++			break;
++		case 'm':
++			message =  hextobin(optarg, &message_len);
++			break;
++		case 'l':
++			len = strtoul(optarg, NULL, 10);
++			if (len == ULONG_MAX) {
++				usage();
++				return -EINVAL;
++			}
++			break;
++		case 'e':
++			empty = 1;
++			break;
++		default:
++			usage();
++		}
++	}
++
++	if(type == TYPE_HMAC)
++		if(!key)
++			usage();
++	if (!name || !message || !len)
++		usage();
++
++	*outlen = (size_t)len;
++	return 0;
++}
++
++static void print(unsigned char *out, int outlen) {
++	int i = 0;
++
++	for(i = 0; i < outlen; i++)
++		printf("%02x", out[i]);
++	printf("\n");
++}
++
++int main(int argc, char *argv[])
++{
++	size_t outlen = 0;
++	ssize_t ret = 0;
++	unsigned char *out = NULL;
++
++	ret = parse_opts(argc, argv, &outlen);
++	if (ret)
++		return (int)ret;
++
++	out = (unsigned char*)malloc(outlen);
++	if (type == TYPE_SHA) {
++		if (empty)
++			ret = msg_digest[type_index].fun(message, 0, out, outlen);
++		else
++			ret = msg_digest[type_index].fun(message, message_len, out, outlen);
++	} else if (type == TYPE_HMAC){
++		if(empty)
++			ret = key_msg_digest[type_index].fun(key, key_len, message, 0,
++				out, outlen);
++		else
++			ret = key_msg_digest[type_index].fun(key, key_len, message, message_len,
++				out, outlen);
++	}
++	if(ret < 0)
++		goto out;
++	print(out, outlen);
++
++out:
++	uninit();
++	if(out)
++		free(out);
++	return (int)ret;
++}
diff --git a/feed/libkcapi/patches/0003-change-rng-bufsisze-to-256.patch b/feed/libkcapi/patches/0003-change-rng-bufsisze-to-256.patch
new file mode 100644
index 0000000..60cd92b
--- /dev/null
+++ b/feed/libkcapi/patches/0003-change-rng-bufsisze-to-256.patch
@@ -0,0 +1,11 @@
+--- a/libkcapi/lib/kcapi-rng.c
++++ b/libkcapi/lib/kcapi-rng.c
+@@ -181,7 +181,7 @@ static int get_random(uint8_t *buf, size
+ }
+ 
+ /* For efficiency reasons, this should be identical to algif_rng.c:MAXSIZE. */
+-#define KCAPI_RNG_BUFSIZE  128
++#define KCAPI_RNG_BUFSIZE  256
+ /* Minimum seed is 256 bits. */
+ #define KCAPI_RNG_MINSEEDSIZE 32
+ #define KCAPI_APP_ALIGN 8