linux/crypto/seqiv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * seqiv: Sequence Number IV Generator
   4 *
   5 * This generator generates an IV based on a sequence number by xoring it
   6 * with a salt.  This algorithm is mainly useful for CTR and similar modes.
   7 *
   8 * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
   9 */
  10
  11#include <crypto/internal/geniv.h>
  12#include <crypto/scatterwalk.h>
  13#include <crypto/skcipher.h>
  14#include <linux/err.h>
  15#include <linux/init.h>
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/slab.h>
  19#include <linux/string.h>
  20
  21static void seqiv_aead_encrypt_complete2(struct aead_request *req, int err)
  22{
  23        struct aead_request *subreq = aead_request_ctx(req);
  24        struct crypto_aead *geniv;
  25
  26        if (err == -EINPROGRESS || err == -EBUSY)
  27                return;
  28
  29        if (err)
  30                goto out;
  31
  32        geniv = crypto_aead_reqtfm(req);
  33        memcpy(req->iv, subreq->iv, crypto_aead_ivsize(geniv));
  34
  35out:
  36        kfree_sensitive(subreq->iv);
  37}
  38
  39static void seqiv_aead_encrypt_complete(void *data, int err)
  40{
  41        struct aead_request *req = data;
  42
  43        seqiv_aead_encrypt_complete2(req, err);
  44        aead_request_complete(req, err);
  45}
  46
  47static int seqiv_aead_encrypt(struct aead_request *req)
  48{
  49        struct crypto_aead *geniv = crypto_aead_reqtfm(req);
  50        struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
  51        struct aead_request *subreq = aead_request_ctx(req);
  52        crypto_completion_t compl;
  53        void *data;
  54        u8 *info;
  55        unsigned int ivsize = 8;
  56        int err;
  57
  58        if (req->cryptlen < ivsize)
  59                return -EINVAL;
  60
  61        aead_request_set_tfm(subreq, ctx->child);
  62
  63        compl = req->base.complete;
  64        data = req->base.data;
  65        info = req->iv;
  66
  67        if (req->src != req->dst) {
  68                SYNC_SKCIPHER_REQUEST_ON_STACK(nreq, ctx->sknull);
  69
  70                skcipher_request_set_sync_tfm(nreq, ctx->sknull);
  71                skcipher_request_set_callback(nreq, req->base.flags,
  72                                              NULL, NULL);
  73                skcipher_request_set_crypt(nreq, req->src, req->dst,
  74                                           req->assoclen + req->cryptlen,
  75                                           NULL);
  76
  77                err = crypto_skcipher_encrypt(nreq);
  78                if (err)
  79                        return err;
  80        }
  81
  82        if (unlikely(!IS_ALIGNED((unsigned long)info,
  83                                 crypto_aead_alignmask(geniv) + 1))) {
  84                info = kmemdup(req->iv, ivsize, req->base.flags &
  85                               CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
  86                               GFP_ATOMIC);
  87                if (!info)
  88                        return -ENOMEM;
  89
  90                compl = seqiv_aead_encrypt_complete;
  91                data = req;
  92        }
  93
  94        aead_request_set_callback(subreq, req->base.flags, compl, data);
  95        aead_request_set_crypt(subreq, req->dst, req->dst,
  96                               req->cryptlen - ivsize, info);
  97        aead_request_set_ad(subreq, req->assoclen + ivsize);
  98
  99        crypto_xor(info, ctx->salt, ivsize);
 100        scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);
 101
 102        err = crypto_aead_encrypt(subreq);
 103        if (unlikely(info != req->iv))
 104                seqiv_aead_encrypt_complete2(req, err);
 105        return err;
 106}
 107
 108static int seqiv_aead_decrypt(struct aead_request *req)
 109{
 110        struct crypto_aead *geniv = crypto_aead_reqtfm(req);
 111        struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
 112        struct aead_request *subreq = aead_request_ctx(req);
 113        crypto_completion_t compl;
 114        void *data;
 115        unsigned int ivsize = 8;
 116
 117        if (req->cryptlen < ivsize + crypto_aead_authsize(geniv))
 118                return -EINVAL;
 119
 120        aead_request_set_tfm(subreq, ctx->child);
 121
 122        compl = req->base.complete;
 123        data = req->base.data;
 124
 125        aead_request_set_callback(subreq, req->base.flags, compl, data);
 126        aead_request_set_crypt(subreq, req->src, req->dst,
 127                               req->cryptlen - ivsize, req->iv);
 128        aead_request_set_ad(subreq, req->assoclen + ivsize);
 129
 130        scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
 131
 132        return crypto_aead_decrypt(subreq);
 133}
 134
 135static int seqiv_aead_create(struct crypto_template *tmpl, struct rtattr **tb)
 136{
 137        struct aead_instance *inst;
 138        int err;
 139
 140        inst = aead_geniv_alloc(tmpl, tb);
 141
 142        if (IS_ERR(inst))
 143                return PTR_ERR(inst);
 144
 145        err = -EINVAL;
 146        if (inst->alg.ivsize != sizeof(u64))
 147                goto free_inst;
 148
 149        inst->alg.encrypt = seqiv_aead_encrypt;
 150        inst->alg.decrypt = seqiv_aead_decrypt;
 151
 152        inst->alg.init = aead_init_geniv;
 153        inst->alg.exit = aead_exit_geniv;
 154
 155        inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
 156        inst->alg.base.cra_ctxsize += inst->alg.ivsize;
 157
 158        err = aead_register_instance(tmpl, inst);
 159        if (err) {
 160free_inst:
 161                inst->free(inst);
 162        }
 163        return err;
 164}
 165
 166static struct crypto_template seqiv_tmpl = {
 167        .name = "seqiv",
 168        .create = seqiv_aead_create,
 169        .module = THIS_MODULE,
 170};
 171
 172static int __init seqiv_module_init(void)
 173{
 174        return crypto_register_template(&seqiv_tmpl);
 175}
 176
 177static void __exit seqiv_module_exit(void)
 178{
 179        crypto_unregister_template(&seqiv_tmpl);
 180}
 181
 182subsys_initcall(seqiv_module_init);
 183module_exit(seqiv_module_exit);
 184
 185MODULE_LICENSE("GPL");
 186MODULE_DESCRIPTION("Sequence Number IV Generator");
 187MODULE_ALIAS_CRYPTO("seqiv");
 188