crypto: af_alg - add extra parameters for DRBG interface

Extend the user-space RNG interface:
  1. Add entropy input via ALG_SET_DRBG_ENTROPY setsockopt option;
  2. Add additional data input via sendmsg syscall.

This allows DRBG to be tested with test vectors, for example for the
purpose of CAVP testing, which otherwise isn't possible.

To prevent erroneous use of entropy input, it is hidden under
CRYPTO_USER_API_RNG_CAVP config option and requires CAP_SYS_ADMIN to
succeed.

Signed-off-by: Elena Petrova <lenaptr@google.com>
Acked-by: Stephan Müller <smueller@chronox.de>
Reviewed-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Elena Petrova 2020-09-18 16:42:16 +01:00 committed by Herbert Xu
parent fcf5d2dcad
commit 77ebdabe8d
6 changed files with 205 additions and 15 deletions

View File

@ -296,15 +296,16 @@ follows:
struct sockaddr_alg sa = { struct sockaddr_alg sa = {
.salg_family = AF_ALG, .salg_family = AF_ALG,
.salg_type = "rng", /* this selects the symmetric cipher */ .salg_type = "rng", /* this selects the random number generator */
.salg_name = "drbg_nopr_sha256" /* this is the cipher name */ .salg_name = "drbg_nopr_sha256" /* this is the RNG name */
}; };
Depending on the RNG type, the RNG must be seeded. The seed is provided Depending on the RNG type, the RNG must be seeded. The seed is provided
using the setsockopt interface to set the key. For example, the using the setsockopt interface to set the key. For example, the
ansi_cprng requires a seed. The DRBGs do not require a seed, but may be ansi_cprng requires a seed. The DRBGs do not require a seed, but may be
seeded. seeded. The seed is also known as a *Personalization String* in NIST SP 800-90A
standard.
Using the read()/recvmsg() system calls, random numbers can be obtained. Using the read()/recvmsg() system calls, random numbers can be obtained.
The kernel generates at most 128 bytes in one call. If user space The kernel generates at most 128 bytes in one call. If user space
@ -314,6 +315,16 @@ WARNING: The user space caller may invoke the initially mentioned accept
system call multiple times. In this case, the returned file descriptors system call multiple times. In this case, the returned file descriptors
have the same state. have the same state.
Following CAVP testing interfaces are enabled when kernel is built with
CRYPTO_USER_API_RNG_CAVP option:
- the concatenation of *Entropy* and *Nonce* can be provided to the RNG via
ALG_SET_DRBG_ENTROPY setsockopt interface. Setting the entropy requires
CAP_SYS_ADMIN permission.
- *Additional Data* can be provided using the send()/sendmsg() system calls,
but only after the entropy has been set.
Zero-Copy Interface Zero-Copy Interface
------------------- -------------------
@ -377,6 +388,9 @@ mentioned optname:
provided ciphertext is assumed to contain an authentication tag of provided ciphertext is assumed to contain an authentication tag of
the given size (see section about AEAD memory layout below). the given size (see section about AEAD memory layout below).
- ALG_SET_DRBG_ENTROPY -- Setting the entropy of the random number generator.
This option is applicable to RNG cipher type only.
User space API example User space API example
---------------------- ----------------------

View File

@ -1875,6 +1875,15 @@ config CRYPTO_USER_API_RNG
This option enables the user-spaces interface for random This option enables the user-spaces interface for random
number generator algorithms. number generator algorithms.
config CRYPTO_USER_API_RNG_CAVP
bool "Enable CAVP testing of DRBG"
depends on CRYPTO_USER_API_RNG && CRYPTO_DRBG
help
This option enables extra API for CAVP testing via the user-space
interface: resetting of DRBG entropy, and providing Additional Data.
This should only be enabled for CAVP testing. You should say
no unless you know what this is.
config CRYPTO_USER_API_AEAD config CRYPTO_USER_API_AEAD
tristate "User-space interface for AEAD cipher algorithms" tristate "User-space interface for AEAD cipher algorithms"
depends on NET depends on NET

View File

@ -253,6 +253,14 @@ static int alg_setsockopt(struct socket *sock, int level, int optname,
if (!type->setauthsize) if (!type->setauthsize)
goto unlock; goto unlock;
err = type->setauthsize(ask->private, optlen); err = type->setauthsize(ask->private, optlen);
break;
case ALG_SET_DRBG_ENTROPY:
if (sock->state == SS_CONNECTED)
goto unlock;
if (!type->setentropy)
goto unlock;
err = type->setentropy(ask->private, optval, optlen);
} }
unlock: unlock:
@ -285,6 +293,11 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern)
security_sock_graft(sk2, newsock); security_sock_graft(sk2, newsock);
security_sk_clone(sk, sk2); security_sk_clone(sk, sk2);
/*
* newsock->ops assigned here to allow type->accept call to override
* them when required.
*/
newsock->ops = type->ops;
err = type->accept(ask->private, sk2); err = type->accept(ask->private, sk2);
nokey = err == -ENOKEY; nokey = err == -ENOKEY;
@ -303,7 +316,6 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern)
alg_sk(sk2)->parent = sk; alg_sk(sk2)->parent = sk;
alg_sk(sk2)->type = type; alg_sk(sk2)->type = type;
newsock->ops = type->ops;
newsock->state = SS_CONNECTED; newsock->state = SS_CONNECTED;
if (nokey) if (nokey)

View File

@ -38,6 +38,7 @@
* DAMAGE. * DAMAGE.
*/ */
#include <linux/capability.h>
#include <linux/module.h> #include <linux/module.h>
#include <crypto/rng.h> #include <crypto/rng.h>
#include <linux/random.h> #include <linux/random.h>
@ -53,15 +54,26 @@ struct rng_ctx {
#define MAXSIZE 128 #define MAXSIZE 128
unsigned int len; unsigned int len;
struct crypto_rng *drng; struct crypto_rng *drng;
u8 *addtl;
size_t addtl_len;
}; };
static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, struct rng_parent_ctx {
int flags) struct crypto_rng *drng;
u8 *entropy;
};
static void rng_reset_addtl(struct rng_ctx *ctx)
{ {
struct sock *sk = sock->sk; kfree_sensitive(ctx->addtl);
struct alg_sock *ask = alg_sk(sk); ctx->addtl = NULL;
struct rng_ctx *ctx = ask->private; ctx->addtl_len = 0;
int err; }
static int _rng_recvmsg(struct crypto_rng *drng, struct msghdr *msg, size_t len,
u8 *addtl, size_t addtl_len)
{
int err = 0;
int genlen = 0; int genlen = 0;
u8 result[MAXSIZE]; u8 result[MAXSIZE];
@ -82,7 +94,7 @@ static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
* seeding as they automatically seed. The X9.31 DRNG will return * seeding as they automatically seed. The X9.31 DRNG will return
* an error if it was not seeded properly. * an error if it was not seeded properly.
*/ */
genlen = crypto_rng_get_bytes(ctx->drng, result, len); genlen = crypto_rng_generate(drng, addtl, addtl_len, result, len);
if (genlen < 0) if (genlen < 0)
return genlen; return genlen;
@ -92,6 +104,63 @@ static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
return err ? err : len; return err ? err : len;
} }
static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
int flags)
{
struct sock *sk = sock->sk;
struct alg_sock *ask = alg_sk(sk);
struct rng_ctx *ctx = ask->private;
return _rng_recvmsg(ctx->drng, msg, len, NULL, 0);
}
static int rng_test_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
int flags)
{
struct sock *sk = sock->sk;
struct alg_sock *ask = alg_sk(sk);
struct rng_ctx *ctx = ask->private;
int ret;
lock_sock(sock->sk);
ret = _rng_recvmsg(ctx->drng, msg, len, ctx->addtl, ctx->addtl_len);
rng_reset_addtl(ctx);
release_sock(sock->sk);
return ret;
}
static int rng_test_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
{
int err;
struct alg_sock *ask = alg_sk(sock->sk);
struct rng_ctx *ctx = ask->private;
lock_sock(sock->sk);
if (len > MAXSIZE) {
err = -EMSGSIZE;
goto unlock;
}
rng_reset_addtl(ctx);
ctx->addtl = kmalloc(len, GFP_KERNEL);
if (!ctx->addtl) {
err = -ENOMEM;
goto unlock;
}
err = memcpy_from_msg(ctx->addtl, msg, len);
if (err) {
rng_reset_addtl(ctx);
goto unlock;
}
ctx->addtl_len = len;
unlock:
release_sock(sock->sk);
return err ? err : len;
}
static struct proto_ops algif_rng_ops = { static struct proto_ops algif_rng_ops = {
.family = PF_ALG, .family = PF_ALG,
@ -111,14 +180,53 @@ static struct proto_ops algif_rng_ops = {
.recvmsg = rng_recvmsg, .recvmsg = rng_recvmsg,
}; };
static struct proto_ops __maybe_unused algif_rng_test_ops = {
.family = PF_ALG,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.getname = sock_no_getname,
.ioctl = sock_no_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.mmap = sock_no_mmap,
.bind = sock_no_bind,
.accept = sock_no_accept,
.sendpage = sock_no_sendpage,
.release = af_alg_release,
.recvmsg = rng_test_recvmsg,
.sendmsg = rng_test_sendmsg,
};
static void *rng_bind(const char *name, u32 type, u32 mask) static void *rng_bind(const char *name, u32 type, u32 mask)
{ {
return crypto_alloc_rng(name, type, mask); struct rng_parent_ctx *pctx;
struct crypto_rng *rng;
pctx = kzalloc(sizeof(*pctx), GFP_KERNEL);
if (!pctx)
return ERR_PTR(-ENOMEM);
rng = crypto_alloc_rng(name, type, mask);
if (IS_ERR(rng)) {
kfree(pctx);
return ERR_CAST(rng);
}
pctx->drng = rng;
return pctx;
} }
static void rng_release(void *private) static void rng_release(void *private)
{ {
crypto_free_rng(private); struct rng_parent_ctx *pctx = private;
if (unlikely(!pctx))
return;
crypto_free_rng(pctx->drng);
kfree_sensitive(pctx->entropy);
kfree_sensitive(pctx);
} }
static void rng_sock_destruct(struct sock *sk) static void rng_sock_destruct(struct sock *sk)
@ -126,6 +234,7 @@ static void rng_sock_destruct(struct sock *sk)
struct alg_sock *ask = alg_sk(sk); struct alg_sock *ask = alg_sk(sk);
struct rng_ctx *ctx = ask->private; struct rng_ctx *ctx = ask->private;
rng_reset_addtl(ctx);
sock_kfree_s(sk, ctx, ctx->len); sock_kfree_s(sk, ctx, ctx->len);
af_alg_release_parent(sk); af_alg_release_parent(sk);
} }
@ -133,6 +242,7 @@ static void rng_sock_destruct(struct sock *sk)
static int rng_accept_parent(void *private, struct sock *sk) static int rng_accept_parent(void *private, struct sock *sk)
{ {
struct rng_ctx *ctx; struct rng_ctx *ctx;
struct rng_parent_ctx *pctx = private;
struct alg_sock *ask = alg_sk(sk); struct alg_sock *ask = alg_sk(sk);
unsigned int len = sizeof(*ctx); unsigned int len = sizeof(*ctx);
@ -141,6 +251,8 @@ static int rng_accept_parent(void *private, struct sock *sk)
return -ENOMEM; return -ENOMEM;
ctx->len = len; ctx->len = len;
ctx->addtl = NULL;
ctx->addtl_len = 0;
/* /*
* No seeding done at that point -- if multiple accepts are * No seeding done at that point -- if multiple accepts are
@ -148,20 +260,58 @@ static int rng_accept_parent(void *private, struct sock *sk)
* state of the RNG. * state of the RNG.
*/ */
ctx->drng = private; ctx->drng = pctx->drng;
ask->private = ctx; ask->private = ctx;
sk->sk_destruct = rng_sock_destruct; sk->sk_destruct = rng_sock_destruct;
/*
* Non NULL pctx->entropy means that CAVP test has been initiated on
* this socket, replace proto_ops algif_rng_ops with algif_rng_test_ops.
*/
if (IS_ENABLED(CONFIG_CRYPTO_USER_API_RNG_CAVP) && pctx->entropy)
sk->sk_socket->ops = &algif_rng_test_ops;
return 0; return 0;
} }
static int rng_setkey(void *private, const u8 *seed, unsigned int seedlen) static int rng_setkey(void *private, const u8 *seed, unsigned int seedlen)
{ {
struct rng_parent_ctx *pctx = private;
/* /*
* Check whether seedlen is of sufficient size is done in RNG * Check whether seedlen is of sufficient size is done in RNG
* implementations. * implementations.
*/ */
return crypto_rng_reset(private, seed, seedlen); return crypto_rng_reset(pctx->drng, seed, seedlen);
}
static int __maybe_unused rng_setentropy(void *private, sockptr_t entropy,
unsigned int len)
{
struct rng_parent_ctx *pctx = private;
u8 *kentropy = NULL;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (pctx->entropy)
return -EINVAL;
if (len > MAXSIZE)
return -EMSGSIZE;
if (len) {
kentropy = memdup_sockptr(entropy, len);
if (IS_ERR(kentropy))
return PTR_ERR(kentropy);
}
crypto_rng_alg(pctx->drng)->set_ent(pctx->drng, kentropy, len);
/*
* Since rng doesn't perform any memory management for the entropy
* buffer, save kentropy pointer to pctx now to free it after use.
*/
pctx->entropy = kentropy;
return 0;
} }
static const struct af_alg_type algif_type_rng = { static const struct af_alg_type algif_type_rng = {
@ -169,6 +319,9 @@ static const struct af_alg_type algif_type_rng = {
.release = rng_release, .release = rng_release,
.accept = rng_accept_parent, .accept = rng_accept_parent,
.setkey = rng_setkey, .setkey = rng_setkey,
#ifdef CONFIG_CRYPTO_USER_API_RNG_CAVP
.setentropy = rng_setentropy,
#endif
.ops = &algif_rng_ops, .ops = &algif_rng_ops,
.name = "rng", .name = "rng",
.owner = THIS_MODULE .owner = THIS_MODULE

View File

@ -46,6 +46,7 @@ struct af_alg_type {
void *(*bind)(const char *name, u32 type, u32 mask); void *(*bind)(const char *name, u32 type, u32 mask);
void (*release)(void *private); void (*release)(void *private);
int (*setkey)(void *private, const u8 *key, unsigned int keylen); int (*setkey)(void *private, const u8 *key, unsigned int keylen);
int (*setentropy)(void *private, sockptr_t entropy, unsigned int len);
int (*accept)(void *private, struct sock *sk); int (*accept)(void *private, struct sock *sk);
int (*accept_nokey)(void *private, struct sock *sk); int (*accept_nokey)(void *private, struct sock *sk);
int (*setauthsize)(void *private, unsigned int authsize); int (*setauthsize)(void *private, unsigned int authsize);

View File

@ -35,6 +35,7 @@ struct af_alg_iv {
#define ALG_SET_OP 3 #define ALG_SET_OP 3
#define ALG_SET_AEAD_ASSOCLEN 4 #define ALG_SET_AEAD_ASSOCLEN 4
#define ALG_SET_AEAD_AUTHSIZE 5 #define ALG_SET_AEAD_AUTHSIZE 5
#define ALG_SET_DRBG_ENTROPY 6
/* Operations */ /* Operations */
#define ALG_OP_DECRYPT 0 #define ALG_OP_DECRYPT 0