diff --git a/include/linux/security.h b/include/linux/security.h
index 8436f9abf43d..94661398708a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -508,6 +508,7 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
 int security_locked_down(enum lockdown_reason what);
 int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, size_t *uctx_len,
 		      void *val, size_t val_len, u64 id, u64 flags);
+void late_init_clavis_setup(void);
 #else /* CONFIG_SECURITY */
 
 static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
@@ -2223,6 +2224,9 @@ static inline int security_uring_cmd(struct io_uring_cmd *ioucmd)
 {
 	return 0;
 }
+static inline void late_init_clavis_setup(void)
+{
+}
 #endif /* CONFIG_SECURITY */
 #endif /* CONFIG_IO_URING */
 
diff --git a/security/Kconfig b/security/Kconfig
index 52c9af08ad35..7f5a52b7cefd 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -194,6 +194,7 @@ source "security/yama/Kconfig"
 source "security/safesetid/Kconfig"
 source "security/lockdown/Kconfig"
 source "security/landlock/Kconfig"
+source "security/clavis/Kconfig"
 
 source "security/integrity/Kconfig"
 
diff --git a/security/Makefile b/security/Makefile
index 59f238490665..01eb82bdd74f 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown/
 obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
 obj-$(CONFIG_BPF_LSM)			+= bpf/
 obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
+obj-$(CONFIG_SECURITY_CLAVIS_KEYRING)	+= clavis/
 
 # Object integrity file lists
 obj-$(CONFIG_INTEGRITY)			+= integrity/
diff --git a/security/clavis/Kconfig b/security/clavis/Kconfig
new file mode 100644
index 000000000000..ca72a9e43089
--- /dev/null
+++ b/security/clavis/Kconfig
@@ -0,0 +1,9 @@
+config SECURITY_CLAVIS_KEYRING
+	bool "Clavis keyring"
+	depends on SECURITY
+	help
+	  Enable the clavis keyring. This keyring shall contain a single asymmetric key.
+	  This key shall be linked to a key already contained in one of the system
+	  keyrings (builtin, secondary, or platform).  The only way to add this key
+	  is during boot by passing in the asymmetric key id within the "clavis=" boot
+	  param.  This keyring is used by the Clavis LSM.
diff --git a/security/clavis/Makefile b/security/clavis/Makefile
new file mode 100644
index 000000000000..ff19c1e240fd
--- /dev/null
+++ b/security/clavis/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_SECURITY_CLAVIS_KEYRING) += clavis_keyring.o
diff --git a/security/clavis/clavis_keyring.c b/security/clavis/clavis_keyring.c
new file mode 100644
index 000000000000..9f1aede81992
--- /dev/null
+++ b/security/clavis/clavis_keyring.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/security.h>
+#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
+
+static struct key *clavis_keyring;
+static struct asymmetric_key_id *setup_keyid;
+
+#define MAX_BIN_KID   32
+
+static struct {
+	struct asymmetric_key_id id;
+	unsigned char data[MAX_BIN_KID];
+} setup_key;
+
+static int restrict_link_for_clavis(struct key *dest_keyring,
+		const struct key_type *type, const union key_payload *payload,
+		struct key *restrict_key)
+{
+	static bool first_pass = true;
+
+	/*
+	 * Allow a single asymmetric key into this keyring. This key is used as the
+	 * root of trust for anything added afterwards.
+	 */
+	if (type == &key_type_asymmetric && dest_keyring == clavis_keyring && first_pass) {
+		first_pass = false;
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int __init clavis_param(char *kid)
+{
+	struct asymmetric_key_id *p = &setup_key.id;
+	int error, hex_len, ascii_len = strlen(kid);
+
+	if (!kid)
+		return 1;
+
+	hex_len = ascii_len / 2;
+
+	if (hex_len > sizeof(setup_key.data))
+		return 1;
+
+	p->len = hex_len;
+	error = hex2bin(p->data, kid, p->len);
+
+	if (error < 0)
+		pr_err("Unparsable clavis key id\n");
+	else {
+		setup_keyid = p;
+		pr_info("clavis key id: %s\n", kid);
+	}
+
+	return 1;
+}
+__setup("clavis=", clavis_param);
+
+static int __init clavis_keyring_init(void)
+{
+	struct key_restriction *restriction;
+
+	restriction = kzalloc(sizeof(*restriction), GFP_KERNEL);
+	if (!restriction)
+		panic("Can't allocate clavis keyring restriction\n");
+	restriction->check = restrict_link_for_clavis;
+	clavis_keyring = keyring_alloc(".clavis",
+			      GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), KEY_POS_VIEW |
+			      KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_WRITE | KEY_USR_VIEW |
+			      KEY_USR_READ | KEY_USR_SEARCH | KEY_USR_WRITE,
+			      KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_SET_KEEP,
+			      restriction, NULL);
+
+	if (IS_ERR(clavis_keyring))
+		panic("Can't allocate clavis keyring\n");
+
+	return 0;
+}
+
+void __init late_init_clavis_setup(void)
+{
+	if (!setup_keyid)
+		return;
+
+	clavis_keyring_init();
+	system_key_link(clavis_keyring, setup_keyid);
+}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 068ac6c2ae1e..87a8bfc0662f 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -36,6 +36,8 @@ int integrity_kernel_read(struct file *file, loff_t offset,
  */
 void __init integrity_load_keys(void)
 {
+	late_init_clavis_setup();
+
 	ima_load_x509();
 
 	if (!IS_ENABLED(CONFIG_IMA_LOAD_X509))
