Skip to content

Commit

Permalink
crypto: skcipher - Add internal state support
Browse files Browse the repository at this point in the history
Unlike chaining modes such as CBC, stream ciphers other than CTR
usually hold an internal state that must be preserved if the
operation is to be done piecemeal.  This has not been represented
in the API, resulting in the inability to split up stream cipher
operations.

This patch adds the basic representation of an internal state to
skcipher and lskcipher.  In the interest of backwards compatibility,
the default has been set such that existing users are assumed to
be operating in one go as opposed to piecemeal.

With the new API, each lskcipher/skcipher algorithm has a new
attribute called statesize.  For skcipher, this is the size of
the buffer that can be exported or imported similar to ahash.
For lskcipher, instead of providing a buffer of ivsize, the user
now has to provide a buffer of ivsize + statesize.

Each skcipher operation is assumed to be final as they are now,
but this may be overridden with a request flag.  When the override
occurs, the user may then export the partial state and reimport
it later.

For lskcipher operations this is reversed.  All operations are
not final and the state will be exported unless the FINAL bit is
set.  However, the CONT bit still has to be set for the state
to be used.

Signed-off-by: Herbert Xu <[email protected]>
  • Loading branch information
herbertx committed Dec 8, 2023
1 parent 412ac51 commit 0ae4dcc
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 22 deletions.
2 changes: 1 addition & 1 deletion crypto/arc4.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static int crypto_arc4_setkey(struct crypto_lskcipher *tfm, const u8 *in_key,
}

static int crypto_arc4_crypt(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned nbytes, u8 *iv, bool final)
u8 *dst, unsigned nbytes, u8 *iv, u32 flags)
{
struct arc4_ctx *ctx = crypto_lskcipher_ctx(tfm);

Expand Down
6 changes: 4 additions & 2 deletions crypto/cbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ static int crypto_cbc_encrypt_inplace(struct crypto_lskcipher *tfm,
}

static int crypto_cbc_encrypt(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv, bool final)
u8 *dst, unsigned len, u8 *iv, u32 flags)
{
struct crypto_lskcipher **ctx = crypto_lskcipher_ctx(tfm);
bool final = flags & CRYPTO_LSKCIPHER_FLAG_FINAL;
struct crypto_lskcipher *cipher = *ctx;
int rem;

Expand Down Expand Up @@ -119,9 +120,10 @@ static int crypto_cbc_decrypt_inplace(struct crypto_lskcipher *tfm,
}

static int crypto_cbc_decrypt(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv, bool final)
u8 *dst, unsigned len, u8 *iv, u32 flags)
{
struct crypto_lskcipher **ctx = crypto_lskcipher_ctx(tfm);
bool final = flags & CRYPTO_LSKCIPHER_FLAG_FINAL;
struct crypto_lskcipher *cipher = *ctx;
int rem;

Expand Down
10 changes: 6 additions & 4 deletions crypto/ecb.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,24 @@ static int crypto_ecb_crypt(struct crypto_cipher *cipher, const u8 *src,
}

static int crypto_ecb_encrypt2(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv, bool final)
u8 *dst, unsigned len, u8 *iv, u32 flags)
{
struct crypto_cipher **ctx = crypto_lskcipher_ctx(tfm);
struct crypto_cipher *cipher = *ctx;

return crypto_ecb_crypt(cipher, src, dst, len, final,
return crypto_ecb_crypt(cipher, src, dst, len,
flags & CRYPTO_LSKCIPHER_FLAG_FINAL,
crypto_cipher_alg(cipher)->cia_encrypt);
}

static int crypto_ecb_decrypt2(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv, bool final)
u8 *dst, unsigned len, u8 *iv, u32 flags)
{
struct crypto_cipher **ctx = crypto_lskcipher_ctx(tfm);
struct crypto_cipher *cipher = *ctx;

return crypto_ecb_crypt(cipher, src, dst, len, final,
return crypto_ecb_crypt(cipher, src, dst, len,
flags & CRYPTO_LSKCIPHER_FLAG_FINAL,
crypto_cipher_alg(cipher)->cia_decrypt);
}

Expand Down
14 changes: 8 additions & 6 deletions crypto/lskcipher.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ EXPORT_SYMBOL_GPL(crypto_lskcipher_setkey);
static int crypto_lskcipher_crypt_unaligned(
struct crypto_lskcipher *tfm, const u8 *src, u8 *dst, unsigned len,
u8 *iv, int (*crypt)(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv, bool final))
u8 *dst, unsigned len, u8 *iv, u32 flags))
{
unsigned ivsize = crypto_lskcipher_ivsize(tfm);
unsigned bs = crypto_lskcipher_blocksize(tfm);
Expand Down Expand Up @@ -119,7 +119,7 @@ static int crypto_lskcipher_crypt_unaligned(
chunk &= ~(cs - 1);

memcpy(p, src, chunk);
err = crypt(tfm, p, p, chunk, tiv, true);
err = crypt(tfm, p, p, chunk, tiv, CRYPTO_LSKCIPHER_FLAG_FINAL);
if (err)
goto out;

Expand All @@ -143,7 +143,7 @@ static int crypto_lskcipher_crypt(struct crypto_lskcipher *tfm, const u8 *src,
int (*crypt)(struct crypto_lskcipher *tfm,
const u8 *src, u8 *dst,
unsigned len, u8 *iv,
bool final))
u32 flags))
{
unsigned long alignmask = crypto_lskcipher_alignmask(tfm);
struct lskcipher_alg *alg = crypto_lskcipher_alg(tfm);
Expand All @@ -156,7 +156,7 @@ static int crypto_lskcipher_crypt(struct crypto_lskcipher *tfm, const u8 *src,
goto out;
}

ret = crypt(tfm, src, dst, len, iv, true);
ret = crypt(tfm, src, dst, len, iv, CRYPTO_LSKCIPHER_FLAG_FINAL);

out:
return crypto_lskcipher_errstat(alg, ret);
Expand Down Expand Up @@ -198,7 +198,7 @@ static int crypto_lskcipher_crypt_sg(struct skcipher_request *req,
int (*crypt)(struct crypto_lskcipher *tfm,
const u8 *src, u8 *dst,
unsigned len, u8 *iv,
bool final))
u32 flags))
{
struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
struct crypto_lskcipher **ctx = crypto_skcipher_ctx(skcipher);
Expand All @@ -210,7 +210,9 @@ static int crypto_lskcipher_crypt_sg(struct skcipher_request *req,

while (walk.nbytes) {
err = crypt(tfm, walk.src.virt.addr, walk.dst.virt.addr,
walk.nbytes, walk.iv, walk.nbytes == walk.total);
walk.nbytes, walk.iv,
walk.nbytes == walk.total ?
CRYPTO_LSKCIPHER_FLAG_FINAL : 0);
err = skcipher_walk_done(&walk, err);
}

Expand Down
84 changes: 75 additions & 9 deletions include/crypto/skcipher.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
#include <linux/string.h>
#include <linux/types.h>

/* Set this bit if the lskcipher operation is a continuation. */
#define CRYPTO_LSKCIPHER_FLAG_CONT 0x00000001
/* Set this bit if the lskcipher operation is final. */
#define CRYPTO_LSKCIPHER_FLAG_FINAL 0x00000002
/* The bit CRYPTO_TFM_REQ_MAY_SLEEP can also be set if needed. */

/* Set this bit if the skcipher operation is a continuation. */
#define CRYPTO_SKCIPHER_REQ_CONT 0x00000001
/* Set this bit if the skcipher operation is not final. */
#define CRYPTO_SKCIPHER_REQ_NOTFINAL 0x00000002

struct scatterlist;

/**
Expand Down Expand Up @@ -91,6 +102,7 @@ struct crypto_istat_cipher {
* IV of exactly that size to perform the encrypt or decrypt operation.
* @chunksize: Equal to the block size except for stream ciphers such as
* CTR where it is set to the underlying block size.
* @statesize: Size of the internal state for the algorithm.
* @stat: Statistics for cipher algorithm
* @base: Definition of a generic crypto algorithm.
*/
Expand All @@ -99,6 +111,7 @@ struct crypto_istat_cipher {
unsigned int max_keysize; \
unsigned int ivsize; \
unsigned int chunksize; \
unsigned int statesize; \
\
SKCIPHER_ALG_COMMON_STAT \
\
Expand Down Expand Up @@ -141,6 +154,17 @@ struct skcipher_alg_common SKCIPHER_ALG_COMMON;
* be called in parallel with the same transformation object.
* @decrypt: Decrypt a single block. This is a reverse counterpart to @encrypt
* and the conditions are exactly the same.
* @export: Export partial state of the transformation. This function dumps the
* entire state of the ongoing transformation into a provided block of
* data so it can be @import 'ed back later on. This is useful in case
* you want to save partial result of the transformation after
* processing certain amount of data and reload this partial result
* multiple times later on for multiple re-use. No data processing
* happens at this point.
* @import: Import partial state of the transformation. This function loads the
* entire state of the ongoing transformation from a provided block of
* data so the transformation can continue from this point onward. No
* data processing happens at this point.
* @init: Initialize the cryptographic transformation object. This function
* is used to initialize the cryptographic transformation object.
* This function is called only once at the instantiation time, right
Expand Down Expand Up @@ -170,6 +194,8 @@ struct skcipher_alg {
unsigned int keylen);
int (*encrypt)(struct skcipher_request *req);
int (*decrypt)(struct skcipher_request *req);
int (*export)(struct skcipher_request *req, void *out);
int (*import)(struct skcipher_request *req, const void *in);
int (*init)(struct crypto_skcipher *tfm);
void (*exit)(struct crypto_skcipher *tfm);

Expand Down Expand Up @@ -200,6 +226,9 @@ struct skcipher_alg {
* may be left over if length is not a multiple of blocks
* and there is more to come (final == false). The number of
* left-over bytes should be returned in case of success.
* The siv field shall be as long as ivsize + statesize with
* the IV placed at the front. The state will be used by the
* algorithm internally.
* @decrypt: Decrypt a number of bytes. This is a reverse counterpart to
* @encrypt and the conditions are exactly the same.
* @init: Initialize the cryptographic transformation object. This function
Expand All @@ -215,9 +244,9 @@ struct lskcipher_alg {
int (*setkey)(struct crypto_lskcipher *tfm, const u8 *key,
unsigned int keylen);
int (*encrypt)(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv, bool final);
u8 *dst, unsigned len, u8 *siv, u32 flags);
int (*decrypt)(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv, bool final);
u8 *dst, unsigned len, u8 *siv, u32 flags);
int (*init)(struct crypto_lskcipher *tfm);
void (*exit)(struct crypto_lskcipher *tfm);

Expand Down Expand Up @@ -496,6 +525,40 @@ static inline unsigned int crypto_lskcipher_chunksize(
return crypto_lskcipher_alg(tfm)->co.chunksize;
}

/**
* crypto_skcipher_statesize() - obtain state size
* @tfm: cipher handle
*
* Some algorithms cannot be chained with the IV alone. They carry
* internal state which must be replicated if data is to be processed
* incrementally. The size of that state can be obtained with this
* function.
*
* Return: state size in bytes
*/
static inline unsigned int crypto_skcipher_statesize(
struct crypto_skcipher *tfm)
{
return crypto_skcipher_alg_common(tfm)->statesize;
}

/**
* crypto_lskcipher_statesize() - obtain state size
* @tfm: cipher handle
*
* Some algorithms cannot be chained with the IV alone. They carry
* internal state which must be replicated if data is to be processed
* incrementally. The size of that state can be obtained with this
* function.
*
* Return: state size in bytes
*/
static inline unsigned int crypto_lskcipher_statesize(
struct crypto_lskcipher *tfm)
{
return crypto_lskcipher_alg(tfm)->co.statesize;
}

static inline unsigned int crypto_sync_skcipher_blocksize(
struct crypto_sync_skcipher *tfm)
{
Expand Down Expand Up @@ -689,26 +752,29 @@ int crypto_skcipher_decrypt(struct skcipher_request *req);
* @src: source buffer
* @dst: destination buffer
* @len: number of bytes to process
* @iv: IV for the cipher operation which must comply with the IV size defined
* by crypto_lskcipher_ivsize
*
* @siv: IV + state for the cipher operation. The length of the IV must
* comply with the IV size defined by crypto_lskcipher_ivsize. The
* IV is then followed with a buffer with the length as specified by
* crypto_lskcipher_statesize.
* Encrypt plaintext data using the lskcipher handle.
*
* Return: >=0 if the cipher operation was successful, if positive
* then this many bytes have been left unprocessed;
* < 0 if an error occurred
*/
int crypto_lskcipher_encrypt(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv);
u8 *dst, unsigned len, u8 *siv);

/**
* crypto_lskcipher_decrypt() - decrypt ciphertext
* @tfm: lskcipher handle
* @src: source buffer
* @dst: destination buffer
* @len: number of bytes to process
* @iv: IV for the cipher operation which must comply with the IV size defined
* by crypto_lskcipher_ivsize
* @siv: IV + state for the cipher operation. The length of the IV must
* comply with the IV size defined by crypto_lskcipher_ivsize. The
* IV is then followed with a buffer with the length as specified by
* crypto_lskcipher_statesize.
*
* Decrypt ciphertext data using the lskcipher handle.
*
Expand All @@ -717,7 +783,7 @@ int crypto_lskcipher_encrypt(struct crypto_lskcipher *tfm, const u8 *src,
* < 0 if an error occurred
*/
int crypto_lskcipher_decrypt(struct crypto_lskcipher *tfm, const u8 *src,
u8 *dst, unsigned len, u8 *iv);
u8 *dst, unsigned len, u8 *siv);

/**
* DOC: Symmetric Key Cipher Request Handle
Expand Down

0 comments on commit 0ae4dcc

Please sign in to comment.