Add support to the crypto framework for separate AAD buffers.

This permits requests to provide the AAD in a separate side buffer
instead of as a region in the crypto request input buffer.  This is
useful when the main data buffer might not contain the full AAD
(e.g. for TLS or IPsec with ESN).

Unlike separate IVs which are constrained in size and stored in an
array in struct cryptop, separate AAD is provided by the caller
setting a new crp_aad pointer to the buffer.  The caller must ensure
the pointer remains valid and the buffer contents static until the
request is completed (e.g. when the callback routine is invoked).

As with separate output buffers, not all drivers support this feature.
Consumers must request use of this feature via a new session flag.

To aid in driver testing, kern.crypto.cryptodev_separate_aad can be
set to force /dev/crypto requests to use a separate AAD buffer.

Discussed with:	cem
Sponsored by:	Chelsio Communications
Differential Revision:	https://reviews.freebsd.org/D25288
This commit is contained in:
John Baldwin 2020-06-22 23:20:43 +00:00
parent e16b207739
commit 9b774dc0c5
6 changed files with 141 additions and 52 deletions

View File

@ -30,7 +30,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd May 25, 2020
.Dd June 22, 2020
.Dt CRYPTO_REQUEST 9
.Os
.Sh NAME
@ -181,7 +181,7 @@ The following regions are defined:
.Bl -column "Payload Output" "Input/Output"
.It Sy Region Ta Sy Buffer Ta Sy Description
.It AAD Ta Input Ta
Additional Authenticated Data
Embedded Additional Authenticated Data
.It IV Ta Input Ta
Embedded IV or nonce
.It Payload Ta Input Ta
@ -256,6 +256,15 @@ If the digests do not match,
fail the request with
.Er EBADMSG .
.El
.Ss Request AAD
AEAD and Encrypt-then-Authenticate requests may optionally include
Additional Authenticated Data.
AAD may either be supplied in the AAD region of the input buffer or
as a single buffer pointed to by
.Fa crp_aad .
In either case,
.Fa crp_aad_length
always indicates the amount of AAD in bytes.
.Ss Request IV and/or Nonce
Some cryptographic operations require an IV or nonce as an input.
An IV may be stored either in the IV region of the data buffer or in

View File

@ -30,7 +30,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd May 25, 2020
.Dd June 22, 2020
.Dt CRYPTO_SESSION 9
.Os
.Sh NAME
@ -194,6 +194,13 @@ that is modified in-place, or requests with separate input and output
buffers.
Sessions without this flag only permit requests with a single buffer that
is modified in-place.
.It Dv CSP_F_SEPARATE_AAD
Support requests that use a separate buffer for AAD rather than providing
AAD as a region in the input buffer.
Sessions with this flag set permit requests with AAD passed in either in
a region of the input buffer or in a single, virtually-contiguous buffer.
Sessions without this flag only permit requests with AAD passed in as
a region in the input buffer.
.El
.It Fa csp_ivlen
If either the cipher or authentication algorithms require an explicit

View File

@ -755,7 +755,8 @@ check_csp(const struct crypto_session_params *csp)
struct auth_hash *axf;
/* Mode-independent checks. */
if ((csp->csp_flags & ~CSP_F_SEPARATE_OUTPUT) != 0)
if ((csp->csp_flags & ~(CSP_F_SEPARATE_OUTPUT | CSP_F_SEPARATE_AAD)) !=
0)
return (false);
if (csp->csp_ivlen < 0 || csp->csp_cipher_klen < 0 ||
csp->csp_auth_klen < 0 || csp->csp_auth_mlen < 0)
@ -771,6 +772,8 @@ check_csp(const struct crypto_session_params *csp)
return (false);
if (csp->csp_flags & CSP_F_SEPARATE_OUTPUT)
return (false);
if (csp->csp_flags & CSP_F_SEPARATE_AAD)
return (false);
if (csp->csp_cipher_klen != 0 || csp->csp_ivlen != 0 ||
csp->csp_auth_alg != 0 || csp->csp_auth_klen != 0 ||
csp->csp_auth_mlen != 0)
@ -779,6 +782,8 @@ check_csp(const struct crypto_session_params *csp)
case CSP_MODE_CIPHER:
if (!alg_is_cipher(csp->csp_cipher_alg))
return (false);
if (csp->csp_flags & CSP_F_SEPARATE_AAD)
return (false);
if (csp->csp_cipher_alg != CRYPTO_NULL_CBC) {
if (csp->csp_cipher_klen == 0)
return (false);
@ -795,6 +800,9 @@ check_csp(const struct crypto_session_params *csp)
if (csp->csp_cipher_alg != 0 || csp->csp_cipher_klen != 0)
return (false);
if (csp->csp_flags & CSP_F_SEPARATE_AAD)
return (false);
/* IV is optional for digests (e.g. GMAC). */
if (csp->csp_ivlen >= EALG_MAX_BLOCK_LEN)
return (false);
@ -1306,16 +1314,27 @@ crp_sanity(struct cryptop *crp)
break;
}
if (csp->csp_mode == CSP_MODE_AEAD || csp->csp_mode == CSP_MODE_ETA) {
KASSERT(crp->crp_aad_start == 0 ||
crp->crp_aad_start < ilen,
("invalid AAD start"));
KASSERT(crp->crp_aad_length != 0 || crp->crp_aad_start == 0,
("AAD with zero length and non-zero start"));
KASSERT(crp->crp_aad_length == 0 ||
crp->crp_aad_start + crp->crp_aad_length <= ilen,
("AAD outside input length"));
if (crp->crp_aad == NULL) {
KASSERT(crp->crp_aad_start == 0 ||
crp->crp_aad_start < ilen,
("invalid AAD start"));
KASSERT(crp->crp_aad_length != 0 ||
crp->crp_aad_start == 0,
("AAD with zero length and non-zero start"));
KASSERT(crp->crp_aad_length == 0 ||
crp->crp_aad_start + crp->crp_aad_length <= ilen,
("AAD outside input length"));
} else {
KASSERT(csp->csp_flags & CSP_F_SEPARATE_AAD,
("session doesn't support separate AAD buffer"));
KASSERT(crp->crp_aad_start == 0,
("separate AAD buffer with non-zero AAD start"));
KASSERT(crp->crp_aad_length != 0,
("separate AAD buffer with zero length"));
}
} else {
KASSERT(crp->crp_aad_start == 0 && crp->crp_aad_length == 0,
KASSERT(crp->crp_aad == NULL && crp->crp_aad_start == 0 &&
crp->crp_aad_length == 0,
("AAD region in request not supporting AAD"));
}
if (csp->csp_ivlen == 0) {

View File

@ -283,6 +283,7 @@ struct cryptop_data {
char *buf;
char *obuf;
char *aad;
bool done;
};
@ -297,6 +298,11 @@ SYSCTL_BOOL(_kern_crypto, OID_AUTO, cryptodev_use_output, CTLFLAG_RW,
&use_outputbuffers, 0,
"Use separate output buffers for /dev/crypto requests.");
static bool use_separate_aad;
SYSCTL_BOOL(_kern_crypto, OID_AUTO, cryptodev_separate_aad, CTLFLAG_RW,
&use_separate_aad, 0,
"Use separate AAD buffer for /dev/crypto requests.");
static int cryptof_ioctl(struct file *, u_long, void *,
struct ucred *, struct thread *);
static int cryptof_stat(struct file *, struct stat *,
@ -604,6 +610,14 @@ cryptof_ioctl(
else
csp.csp_mode = CSP_MODE_DIGEST;
switch (csp.csp_mode) {
case CSP_MODE_AEAD:
case CSP_MODE_ETA:
if (use_separate_aad)
csp.csp_flags |= CSP_F_SEPARATE_AAD;
break;
}
if (txform) {
csp.csp_cipher_alg = txform->type;
csp.csp_cipher_klen = sop->keylen;
@ -819,14 +833,19 @@ cryptof_ioctl(
static int cryptodev_cb(struct cryptop *);
static struct cryptop_data *
cod_alloc(struct csession *cse, size_t len, struct thread *td)
cod_alloc(struct csession *cse, size_t aad_len, size_t len, struct thread *td)
{
struct cryptop_data *cod;
cod = malloc(sizeof(struct cryptop_data), M_XDATA, M_WAITOK | M_ZERO);
cod->cse = cse;
cod->buf = malloc(len, M_XDATA, M_WAITOK);
if (crypto_get_params(cse->cses)->csp_flags & CSP_F_SEPARATE_AAD) {
if (aad_len != 0)
cod->aad = malloc(aad_len, M_XDATA, M_WAITOK);
cod->buf = malloc(len, M_XDATA, M_WAITOK);
} else
cod->buf = malloc(aad_len + len, M_XDATA, M_WAITOK);
if (crypto_get_params(cse->cses)->csp_flags & CSP_F_SEPARATE_OUTPUT)
cod->obuf = malloc(len, M_XDATA, M_WAITOK);
return (cod);
@ -836,6 +855,7 @@ static void
cod_free(struct cryptop_data *cod)
{
free(cod->aad, M_XDATA);
free(cod->obuf, M_XDATA);
free(cod->buf, M_XDATA);
free(cod, M_XDATA);
@ -881,7 +901,7 @@ cryptodev_op(
}
}
cod = cod_alloc(cse, cop->len + cse->hashsize, td);
cod = cod_alloc(cse, 0, cop->len + cse->hashsize, td);
crp = crypto_getreq(cse->cses, M_WAITOK);
@ -1087,29 +1107,38 @@ cryptodev_aead(
}
}
cod = cod_alloc(cse, caead->aadlen + caead->len + cse->hashsize, td);
cod = cod_alloc(cse, caead->aadlen, caead->len + cse->hashsize, td);
crp = crypto_getreq(cse->cses, M_WAITOK);
error = copyin(caead->aad, cod->buf, caead->aadlen);
if (cod->aad != NULL)
error = copyin(caead->aad, cod->aad, caead->aadlen);
else
error = copyin(caead->aad, cod->buf, caead->aadlen);
if (error) {
SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__);
goto bail;
}
crp->crp_aad = cod->aad;
crp->crp_aad_start = 0;
crp->crp_aad_length = caead->aadlen;
error = copyin(caead->src, cod->buf + caead->aadlen, caead->len);
if (cod->aad != NULL)
crp->crp_payload_start = 0;
else
crp->crp_payload_start = caead->aadlen;
error = copyin(caead->src, cod->buf + crp->crp_payload_start,
caead->len);
if (error) {
SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__);
goto bail;
}
crp->crp_payload_start = caead->aadlen;
crp->crp_payload_length = caead->len;
if (caead->op == COP_ENCRYPT && cod->obuf != NULL)
crp->crp_digest_start = caead->len;
crp->crp_digest_start = crp->crp_payload_output_start +
caead->len;
else
crp->crp_digest_start = caead->aadlen + caead->len;
crp->crp_digest_start = crp->crp_payload_start + caead->len;
switch (cse->mode) {
case CSP_MODE_AEAD:
@ -1136,7 +1165,7 @@ cryptodev_aead(
}
crp->crp_flags = CRYPTO_F_CBIMM | (caead->flags & COP_F_BATCH);
crypto_use_buf(crp, cod->buf, caead->aadlen + caead->len +
crypto_use_buf(crp, cod->buf, crp->crp_payload_start + caead->len +
cse->hashsize);
if (cod->obuf != NULL)
crypto_use_output_buf(crp, cod->obuf, caead->len +

View File

@ -384,6 +384,7 @@ struct crypto_session_params {
int csp_flags;
#define CSP_F_SEPARATE_OUTPUT 0x0001 /* Requests can use separate output */
#define CSP_F_SEPARATE_AAD 0x0002 /* Requests can use separate AAD */
int csp_ivlen; /* IV length in bytes. */
@ -479,6 +480,7 @@ struct cryptop {
struct crypto_buffer crp_buf;
struct crypto_buffer crp_obuf;
void *crp_aad; /* AAD buffer. */
int crp_aad_start; /* Location of AAD. */
int crp_aad_length; /* 0 => no AAD. */
int crp_iv_start; /* Location of IV. IV length is from

View File

@ -335,8 +335,11 @@ swcr_authcompute(struct swcr_session *ses, struct cryptop *crp)
bcopy(sw->sw_ictx, &ctx, axf->ctxsize);
err = crypto_apply(crp, crp->crp_aad_start, crp->crp_aad_length,
axf->Update, &ctx);
if (crp->crp_aad != NULL)
err = axf->Update(&ctx, crp->crp_aad, crp->crp_aad_length);
else
err = crypto_apply(crp, crp->crp_aad_start, crp->crp_aad_length,
axf->Update, &ctx);
if (err)
return err;
@ -503,7 +506,7 @@ swcr_gcm(struct swcr_session *ses, struct cryptop *crp)
blksz = GMAC_BLOCK_LEN;
KASSERT(axf->blocksize == blksz, ("%s: axf block size mismatch",
__func__));
swe = &ses->swcr_encdec;
exf = swe->sw_exf;
KASSERT(axf->blocksize == exf->native_blocksize,
@ -520,25 +523,38 @@ swcr_gcm(struct swcr_session *ses, struct cryptop *crp)
axf->Reinit(&ctx, iv, ivlen);
/* Supply MAC with AAD */
crypto_cursor_init(&cc_in, &crp->crp_buf);
crypto_cursor_advance(&cc_in, crp->crp_aad_start);
for (resid = crp->crp_aad_length; resid >= blksz; resid -= len) {
len = crypto_cursor_seglen(&cc_in);
if (len >= blksz) {
inblk = crypto_cursor_segbase(&cc_in);
len = rounddown(MIN(len, resid), blksz);
crypto_cursor_advance(&cc_in, len);
} else {
len = blksz;
crypto_cursor_copydata(&cc_in, len, blk);
inblk = blk;
if (crp->crp_aad != NULL) {
len = rounddown(crp->crp_aad_length, blksz);
if (len != 0)
axf->Update(&ctx, crp->crp_aad, len);
if (crp->crp_aad_length != len) {
memset(blk, 0, blksz);
memcpy(blk, (char *)crp->crp_aad + len,
crp->crp_aad_length - len);
axf->Update(&ctx, blk, blksz);
}
} else {
crypto_cursor_init(&cc_in, &crp->crp_buf);
crypto_cursor_advance(&cc_in, crp->crp_aad_start);
for (resid = crp->crp_aad_length; resid >= blksz;
resid -= len) {
len = crypto_cursor_seglen(&cc_in);
if (len >= blksz) {
inblk = crypto_cursor_segbase(&cc_in);
len = rounddown(MIN(len, resid), blksz);
crypto_cursor_advance(&cc_in, len);
} else {
len = blksz;
crypto_cursor_copydata(&cc_in, len, blk);
inblk = blk;
}
axf->Update(&ctx, inblk, len);
}
if (resid > 0) {
memset(blk, 0, blksz);
crypto_cursor_copydata(&cc_in, resid, blk);
axf->Update(&ctx, blk, blksz);
}
axf->Update(&ctx, inblk, len);
}
if (resid > 0) {
memset(blk, 0, blksz);
crypto_cursor_copydata(&cc_in, resid, blk);
axf->Update(&ctx, blk, blksz);
}
exf->reinit(swe->sw_kschedule, iv);
@ -607,7 +623,7 @@ swcr_gcm(struct swcr_session *ses, struct cryptop *crp)
error = EBADMSG;
goto out;
}
/* tag matches, decrypt data */
crypto_cursor_init(&cc_in, &crp->crp_buf);
crypto_cursor_advance(&cc_in, crp->crp_payload_start);
@ -675,8 +691,11 @@ swcr_ccm_cbc_mac(struct swcr_session *ses, struct cryptop *crp)
ctx.aes_cbc_mac_ctx.cryptDataLength = 0;
axf->Reinit(&ctx, iv, ivlen);
error = crypto_apply(crp, crp->crp_payload_start,
crp->crp_payload_length, axf->Update, &ctx);
if (crp->crp_aad != NULL)
error = axf->Update(&ctx, crp->crp_aad, crp->crp_aad_length);
else
error = crypto_apply(crp, crp->crp_payload_start,
crp->crp_payload_length, axf->Update, &ctx);
if (error)
return (error);
@ -724,7 +743,7 @@ swcr_ccm(struct swcr_session *ses, struct cryptop *crp)
blksz = AES_BLOCK_LEN;
KASSERT(axf->blocksize == blksz, ("%s: axf block size mismatch",
__func__));
swe = &ses->swcr_encdec;
exf = swe->sw_exf;
KASSERT(axf->blocksize == exf->native_blocksize,
@ -748,8 +767,11 @@ swcr_ccm(struct swcr_session *ses, struct cryptop *crp)
axf->Reinit(&ctx, iv, ivlen);
/* Supply MAC with AAD */
error = crypto_apply(crp, crp->crp_aad_start, crp->crp_aad_length,
axf->Update, &ctx);
if (crp->crp_aad != NULL)
error = axf->Update(&ctx, crp->crp_aad, crp->crp_aad_length);
else
error = crypto_apply(crp, crp->crp_aad_start,
crp->crp_aad_length, axf->Update, &ctx);
if (error)
return (error);
@ -1013,7 +1035,7 @@ swcr_setup_auth(struct swcr_session *ses,
swa->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT);
if (swa->sw_ictx == NULL)
return (ENOBUFS);
switch (csp->csp_auth_alg) {
case CRYPTO_SHA1_HMAC:
case CRYPTO_SHA2_224_HMAC:
@ -1236,7 +1258,8 @@ static int
swcr_probesession(device_t dev, const struct crypto_session_params *csp)
{
if ((csp->csp_flags & ~(CSP_F_SEPARATE_OUTPUT)) != 0)
if ((csp->csp_flags & ~(CSP_F_SEPARATE_OUTPUT | CSP_F_SEPARATE_AAD)) !=
0)
return (EINVAL);
switch (csp->csp_mode) {
case CSP_MODE_COMPRESS: