Received: by 2002:a05:6a10:6744:0:0:0:0 with SMTP id w4csp3808645pxu; Tue, 20 Oct 2020 00:34:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzqbi5bVX0FqeNEXEDIlbFfv8ksK7Jct2DJh6E+aWKsxMYIigSh6LB0cusWSZs977+9Clpo X-Received: by 2002:a50:8426:: with SMTP id 35mr1449566edp.156.1603179297279; Tue, 20 Oct 2020 00:34:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1603179297; cv=none; d=google.com; s=arc-20160816; b=KS6USXKRb74++oFaJVs0oTIyS31Ovp5foJq1a2rotVHYyflSDlsRCAmcFxGaRjS/bd Dc5AvDl+7xHvJawKGH3YX+28g7FFQXhr9+xGJ6sP3Bt/J/PA6SnTgcXNsRx8Ch1BuSwi UlUx0g2CAjHv6ldeHprqXzum06liBIZS5WF+OHk8VPQgYNBan13BqO+bAEfnwDYVP4Ih KszZd4EkdawjLOIq1xaMJ130m6/kRb+QMcFIzdPNrxTpichdKU21M7mlycJ0qt9cfBY4 hl8NTTTesoteO9YXFBJ0yfTBqKWeNv4Z4Pyyln+KBhAP6gZYN+3X15qKr9DY0UhOmGfU Oa0A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=aUdzSh7zvyd5hi5OoyB17iKZFiThsRr2C8UuKPbq7Q0=; b=McCY9Ga85okmPSCEmwPKlEfENiKOQBcQ4EXkMWl8UEFPfsITC9OIUhC6GuBJJCO0RM iGt/acwgNHJ+O1hIiML2sxvhET+9CrqNkMTF1MX0lMJfdILwtQfkPFSQbXSF9qw/Xm1O xXe8FrF99/5aecJ1gi6jbSVfKmFKaVh/jCQRMZpJpfDDnBQcZk5Nz8O1kAFRPj6d9kzj mKVPXAR5RS1JlT/PqxtY+1PAs8gGPEHmrTkHHF72AXEgCb4SZy56yPDeIKxNodcrJOrx XUgaWp+Er/yXSAi3YzPsH3HFHb3f21p3Dsp/T2oqiXWN/59VC5tkeyw8g858Z0qtu4gr AiRA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chronox.de header.s=strato-dkim-0002 header.b=oHAhqs0A; spf=pass (google.com: domain of linux-crypto-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-crypto-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id s8si586689edy.195.2020.10.20.00.34.34; Tue, 20 Oct 2020 00:34:57 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-crypto-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@chronox.de header.s=strato-dkim-0002 header.b=oHAhqs0A; spf=pass (google.com: domain of linux-crypto-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-crypto-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731335AbgJSTxY (ORCPT + 99 others); Mon, 19 Oct 2020 15:53:24 -0400 Received: from mo4-p04-ob.smtp.rzone.de ([81.169.146.176]:27164 "EHLO mo4-p04-ob.smtp.rzone.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731409AbgJSTwr (ORCPT ); Mon, 19 Oct 2020 15:52:47 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1603137161; s=strato-dkim-0002; d=chronox.de; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From: X-RZG-CLASS-ID:X-RZG-AUTH:From:Subject:Sender; bh=aUdzSh7zvyd5hi5OoyB17iKZFiThsRr2C8UuKPbq7Q0=; b=oHAhqs0AgbqLi8PENhLzAhJarN6cT367phFJ2IUoUJcfBLp79BWpEX1YeOt7Ig9ZXa GHi9STD9OFWA464O5zkUKnVHbWCCAQhAQGCK/bXZiiyn2kFACf6kJUfawr6994Tgpx6b yQGym6dn8bgv/V0TahaW6Vy00zjAGXJi3790OTULrbA2QNEHD1g53gfxFuSXG3JQkk6l ph5lcGz9vCPAy+88d9agdLG3l7W7AeKAom7HYFSeH+gSiCe0ezYnqv5RIoDVGB5p85Fa YXjtGSFQwkLimJdQkEGhcBalIBKJMkvgMzDFYTC0pHOk/Qop3epMOcs3m/cJ4Hm+3IwJ di0A== X-RZG-AUTH: ":P2ERcEykfu11Y98lp/T7+hdri+uKZK8TKWEqNyiHySGSa9k9xmwdNnzGHXPbJPSb3t0=" X-RZG-CLASS-ID: mo00 Received: from positron.chronox.de by smtp.strato.de (RZmta 47.2.1 DYNA|AUTH) with ESMTPSA id C0b627w9JJpoU6v (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256 bits)) (Client did not present a certificate); Mon, 19 Oct 2020 21:51:50 +0200 (CEST) From: Stephan =?ISO-8859-1?Q?M=FCller?= To: Torsten Duwe Cc: Willy Tarreau , "Theodore Y. Ts'o" , linux-crypto@vger.kernel.org, Nicolai Stange , LKML , Arnd Bergmann , Greg Kroah-Hartman , "Eric W. Biederman" , "Alexander E. Patrakov" , "Ahmed S. Darwish" , Matthew Garrett , Vito Caputo , Andreas Dilger , Jan Kara , Ray Strode , William Jon McCann , zhangjs , Andy Lutomirski , Florian Weimer , Lennart Poettering , Peter Matthias , Marcelo Henrique Cerri , Neil Horman , Randy Dunlap , Julia Lawall , Dan Carpenter , Andy Lavr , Eric Biggers , "Jason A. Donenfeld" , Petr Tesarik Subject: [PATCH v36 04/13] LRNG - add switchable DRNG support Date: Mon, 19 Oct 2020 21:32:58 +0200 Message-ID: <3557336.kQq0lBPeGt@positron.chronox.de> In-Reply-To: <3073852.aeNJFYEL58@positron.chronox.de> References: <20200921075857.4424-1-nstange@suse.de> <20201016172619.GA18410@lst.de> <3073852.aeNJFYEL58@positron.chronox.de> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="iso-8859-1" Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org The DRNG switch support allows replacing the DRNG mechanism of the LRNG. The switching support rests on the interface definition of include/linux/lrng.h. A new DRNG is implemented by filling in the interface defined in this header file. In addition to the DRNG, the extension also has to provide a hash implementation that is used to hash the entropy pool for random number extraction. Note: It is permissible to implement a DRNG whose operations may sleep. However, the hash function must not sleep. The switchable DRNG support allows replacing the DRNG at runtime. However, only one DRNG extension is allowed to be loaded at any given time. Before replacing it with another DRNG implementation, the possibly existing DRNG extension must be unloaded. The switchable DRNG extension activates the new DRNG during load time. It is expected, however, that such a DRNG switch would be done only once by an administrator to load the intended DRNG implementation. It is permissible to compile DRNG extensions either as kernel modules or statically. The initialization of the DRNG extension should be performed with a late_initcall to ensure the extension is available when user space starts but after all other initialization completed. The initialization is performed by registering the function call data structure with the lrng_set_drng_cb function. In order to unload the DRNG extension, lrng_set_drng_cb must be invoked with the NULL parameter. The DRNG extension should always provide a security strength that is at least as strong as LRNG_DRNG_SECURITY_STRENGTH_BITS. The hash extension must not sleep and must not maintain a separate state. CC: Torsten Duwe CC: "Eric W. Biederman" CC: "Alexander E. Patrakov" CC: "Ahmed S. Darwish" CC: "Theodore Y. Ts'o" CC: Willy Tarreau CC: Matthew Garrett CC: Vito Caputo CC: Andreas Dilger CC: Jan Kara CC: Ray Strode CC: William Jon McCann CC: zhangjs CC: Andy Lutomirski CC: Florian Weimer CC: Lennart Poettering CC: Nicolai Stange Reviewed-by: Marcelo Henrique Cerri Reviewed-by: Roman Drahtmueller Tested-by: Roman Drahtm=FCller Tested-by: Marcelo Henrique Cerri Tested-by: Neil Horman Signed-off-by: Stephan Mueller =2D-- drivers/char/lrng/Kconfig | 7 ++ drivers/char/lrng/Makefile | 1 + drivers/char/lrng/lrng_switch.c | 203 ++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 drivers/char/lrng/lrng_switch.c diff --git a/drivers/char/lrng/Kconfig b/drivers/char/lrng/Kconfig index fbbcf2ef43b6..e211fcf5aa8b 100644 =2D-- a/drivers/char/lrng/Kconfig +++ b/drivers/char/lrng/Kconfig @@ -69,4 +69,11 @@ config LRNG_COLLECTION_SIZE default 512 if LRNG_COLLECTION_SIZE_512 default 1024 if LRNG_COLLECTION_SIZE_1024 =20 +menuconfig LRNG_DRNG_SWITCH + bool "Support DRNG runtime switching" + help + The Linux RNG per default uses a ChaCha20 DRNG that is + accessible via the external interfaces. With this configuration + option other DRNGs can be selected and loaded at runtime. + endif # LRNG diff --git a/drivers/char/lrng/Makefile b/drivers/char/lrng/Makefile index ac97f0b11cb7..0eb4a6849c88 100644 =2D-- a/drivers/char/lrng/Makefile +++ b/drivers/char/lrng/Makefile @@ -10,3 +10,4 @@ obj-y +=3D lrng_pool.o lrng_aux.o \ =20 obj-$(CONFIG_NUMA) +=3D lrng_numa.o obj-$(CONFIG_SYSCTL) +=3D lrng_proc.o +obj-$(CONFIG_LRNG_DRNG_SWITCH) +=3D lrng_switch.o diff --git a/drivers/char/lrng/lrng_switch.c b/drivers/char/lrng/lrng_switc= h.c new file mode 100644 index 000000000000..cbaf5cd544aa =2D-- /dev/null +++ b/drivers/char/lrng/lrng_switch.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * LRNG DRNG switching support + * + * Copyright (C) 2016 - 2020, Stephan Mueller + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include "lrng_internal.h" + +static int lrng_drng_switch(struct lrng_drng *drng_store, + const struct lrng_crypto_cb *cb, int node) +{ + const struct lrng_crypto_cb *old_cb; + unsigned long flags =3D 0, flags2 =3D 0; + int ret; + u8 seed[LRNG_DRNG_SECURITY_STRENGTH_BYTES]; + void *new_drng =3D cb->lrng_drng_alloc(LRNG_DRNG_SECURITY_STRENGTH_BYTES); + void *old_drng, *new_hash, *old_hash; + u32 current_security_strength; + bool sl =3D false, reset_drng =3D !lrng_get_available(); + + if (IS_ERR(new_drng)) { + pr_warn("could not allocate new DRNG for NUMA node %d (%ld)\n", + node, PTR_ERR(new_drng)); + return PTR_ERR(new_drng); + } + + new_hash =3D cb->lrng_hash_alloc(); + if (IS_ERR(new_hash)) { + pr_warn("could not allocate new LRNG pool hash (%ld)\n", + PTR_ERR(new_hash)); + cb->lrng_drng_dealloc(new_drng); + return PTR_ERR(new_hash); + } + + if (cb->lrng_hash_digestsize(new_hash) > LRNG_MAX_DIGESTSIZE) { + pr_warn("digest size of newly requested hash too large\n"); + cb->lrng_hash_dealloc(new_hash); + cb->lrng_drng_dealloc(new_drng); + return -EINVAL; + } + + current_security_strength =3D lrng_security_strength(); + lrng_drng_lock(drng_store, &flags); + + /* + * Pull from existing DRNG to seed new DRNG regardless of seed status + * of old DRNG -- the entropy state for the DRNG is left unchanged which + * implies that als the new DRNG is reseeded when deemed necessary. This + * seeding of the new DRNG shall only ensure that the new DRNG has the + * same entropy as the old DRNG. + */ + ret =3D drng_store->crypto_cb->lrng_drng_generate_helper( + drng_store->drng, seed, sizeof(seed)); + lrng_drng_unlock(drng_store, &flags); + + if (ret < 0) { + reset_drng =3D true; + pr_warn("getting random data from DRNG failed for NUMA node %d (%d)\n", + node, ret); + } else { + /* seed new DRNG with data */ + ret =3D cb->lrng_drng_seed_helper(new_drng, seed, ret); + if (ret < 0) { + reset_drng =3D true; + pr_warn("seeding of new DRNG failed for NUMA node %d (%d)\n", + node, ret); + } else { + pr_debug("seeded new DRNG of NUMA node %d instance from old DRNG instan= ce\n", + node); + } + } + + mutex_lock(&drng_store->lock); + write_lock_irqsave(&drng_store->hash_lock, flags2); + /* + * If we switch the DRNG from the initial ChaCha20 DRNG to something + * else, there is a lock transition from spin lock to mutex (see + * lrng_drng_is_atomic and how the lock is taken in lrng_drng_lock). + * Thus, we need to take both locks during the transition phase. + */ + if (lrng_drng_is_atomic(drng_store)) { + spin_lock_irqsave(&drng_store->spin_lock, flags); + sl =3D true; + } else { + __acquire(&drng_store->spin_lock); + } + + if (reset_drng) + lrng_drng_reset(drng_store); + + old_drng =3D drng_store->drng; + old_cb =3D drng_store->crypto_cb; + drng_store->drng =3D new_drng; + drng_store->crypto_cb =3D cb; + + old_hash =3D drng_store->hash; + drng_store->hash =3D new_hash; + pr_info("Entropy pool read-hash allocated for DRNG for NUMA node %d\n", + node); + + lrng_set_digestsize(cb->lrng_hash_digestsize(new_hash)); + + /* Reseed if previous LRNG security strength was insufficient */ + if (current_security_strength < lrng_security_strength()) + drng_store->force_reseed =3D true; + + if (sl) + spin_unlock_irqrestore(&drng_store->spin_lock, flags); + else + __release(&drng_store->spin_lock); + write_unlock_irqrestore(&drng_store->hash_lock, flags2); + mutex_unlock(&drng_store->lock); + + /* ChaCha20 serves as atomic instance left untouched. */ + if (old_drng !=3D &chacha20) { + old_cb->lrng_drng_dealloc(old_drng); + old_cb->lrng_hash_dealloc(old_hash); + } + + pr_info("DRNG of NUMA node %d switched\n", node); + + return 0; +} + +/* + * Switch the existing DRNG instances with new using the new crypto callba= cks. + * The caller must hold the lrng_crypto_cb_update lock. + */ +static int lrng_drngs_switch(const struct lrng_crypto_cb *cb) +{ + struct lrng_drng **lrng_drng =3D lrng_drng_instances(); + struct lrng_drng *lrng_drng_init =3D lrng_drng_init_instance(); + int ret =3D 0; + + /* Update DRNG */ + if (lrng_drng) { + u32 node; + + for_each_online_node(node) { + if (lrng_drng[node]) + ret =3D lrng_drng_switch(lrng_drng[node], cb, + node); + } + } else { + ret =3D lrng_drng_switch(lrng_drng_init, cb, 0); + } + + if (!ret) + lrng_set_available(); + + return 0; +} + +/** + * lrng_set_drng_cb - Register new cryptographic callback functions for DR= NG + * The registering implies that all old DRNG states are replaced with new + * DRNG states. + * + * @cb: Callback functions to be registered -- if NULL, use the default + * callbacks pointing to the ChaCha20 DRNG. + * + * Return: + * * 0 on success + * * < 0 on error + */ +int lrng_set_drng_cb(const struct lrng_crypto_cb *cb) +{ + struct lrng_drng *lrng_drng_init =3D lrng_drng_init_instance(); + int ret; + + if (!cb) + cb =3D &lrng_cc20_crypto_cb; + + mutex_lock(&lrng_crypto_cb_update); + + /* + * If a callback other than the default is set, allow it only to be + * set back to the default callback. This ensures that multiple + * different callbacks can be registered at the same time. If a + * callback different from the current callback and the default + * callback shall be set, the current callback must be deregistered + * (e.g. the kernel module providing it must be unloaded) and the new + * implementation can be registered. + */ + if ((cb !=3D &lrng_cc20_crypto_cb) && + (lrng_drng_init->crypto_cb !=3D &lrng_cc20_crypto_cb)) { + pr_warn("disallow setting new cipher callbacks, unload the old callbacks= first!\n"); + ret =3D -EINVAL; + goto out; + } + + ret =3D lrng_drngs_switch(cb); + +out: + mutex_unlock(&lrng_crypto_cb_update); + return ret; +} +EXPORT_SYMBOL(lrng_set_drng_cb); =2D-=20 2.26.2