echainiv.c 4.59 KB
Newer Older
1 2 3
/*
 * echainiv: Encrypted Chain IV Generator
 *
4 5
 * This generator generates an IV based on a sequence number by multiplying
 * it with a salt and then encrypting it with the same key as used to encrypt
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * the plain text.  This algorithm requires that the block size be equal
 * to the IV size.  It is mainly useful for CBC.
 *
 * This generator can only be used by algorithms where authentication
 * is performed after encryption (i.e., authenc).
 *
 * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 */

21
#include <crypto/internal/geniv.h>
22
#include <crypto/scatterwalk.h>
Herbert Xu's avatar
Herbert Xu committed
23
#include <crypto/skcipher.h>
24 25 26 27
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
28
#include <linux/slab.h>
29 30 31 32 33
#include <linux/string.h>

static int echainiv_encrypt(struct aead_request *req)
{
	struct crypto_aead *geniv = crypto_aead_reqtfm(req);
34
	struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
35
	struct aead_request *subreq = aead_request_ctx(req);
36 37
	__be64 nseqno;
	u64 seqno;
38
	u8 *info;
39
	unsigned int ivsize = crypto_aead_ivsize(geniv);
40 41
	int err;

42 43 44
	if (req->cryptlen < ivsize)
		return -EINVAL;

45
	aead_request_set_tfm(subreq, ctx->child);
46 47 48 49

	info = req->iv;

	if (req->src != req->dst) {
Herbert Xu's avatar
Herbert Xu committed
50
		SKCIPHER_REQUEST_ON_STACK(nreq, ctx->sknull);
51

Herbert Xu's avatar
Herbert Xu committed
52 53 54 55 56 57 58 59
		skcipher_request_set_tfm(nreq, ctx->sknull);
		skcipher_request_set_callback(nreq, req->base.flags,
					      NULL, NULL);
		skcipher_request_set_crypt(nreq, req->src, req->dst,
					   req->assoclen + req->cryptlen,
					   NULL);

		err = crypto_skcipher_encrypt(nreq);
60 61 62 63
		if (err)
			return err;
	}

64 65
	aead_request_set_callback(subreq, req->base.flags,
				  req->base.complete, req->base.data);
66
	aead_request_set_crypt(subreq, req->dst, req->dst,
67 68
			       req->cryptlen, info);
	aead_request_set_ad(subreq, req->assoclen);
69

70 71 72 73
	memcpy(&nseqno, info + ivsize - 8, 8);
	seqno = be64_to_cpu(nseqno);
	memset(info, 0, ivsize);

74 75
	scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);

76 77 78 79 80 81 82 83 84 85 86 87
	do {
		u64 a;

		memcpy(&a, ctx->salt + ivsize - 8, 8);

		a |= 1;
		a *= seqno;

		memcpy(info + ivsize - 8, &a, 8);
	} while ((ivsize -= 8));

	return crypto_aead_encrypt(subreq);
88 89 90 91 92
}

static int echainiv_decrypt(struct aead_request *req)
{
	struct crypto_aead *geniv = crypto_aead_reqtfm(req);
93
	struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
94 95 96
	struct aead_request *subreq = aead_request_ctx(req);
	crypto_completion_t compl;
	void *data;
97 98
	unsigned int ivsize = crypto_aead_ivsize(geniv);

99
	if (req->cryptlen < ivsize)
100
		return -EINVAL;
101

102
	aead_request_set_tfm(subreq, ctx->child);
103 104 105 106 107 108 109

	compl = req->base.complete;
	data = req->base.data;

	aead_request_set_callback(subreq, req->base.flags, compl, data);
	aead_request_set_crypt(subreq, req->src, req->dst,
			       req->cryptlen - ivsize, req->iv);
110
	aead_request_set_ad(subreq, req->assoclen + ivsize);
111 112 113 114 115 116

	scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);

	return crypto_aead_decrypt(subreq);
}

117 118
static int echainiv_aead_create(struct crypto_template *tmpl,
				struct rtattr **tb)
119 120 121 122
{
	struct aead_instance *inst;
	struct crypto_aead_spawn *spawn;
	struct aead_alg *alg;
123
	int err;
124

125
	inst = aead_geniv_alloc(tmpl, tb, 0, 0);
126 127

	if (IS_ERR(inst))
128
		return PTR_ERR(inst);
129

130 131 132
	spawn = aead_instance_ctx(inst);
	alg = crypto_spawn_aead_alg(spawn);

133
	err = -EINVAL;
134
	if (inst->alg.ivsize & (sizeof(u64) - 1) || !inst->alg.ivsize)
135
		goto free_inst;
136

137
	inst->alg.encrypt = echainiv_encrypt;
138 139
	inst->alg.decrypt = echainiv_decrypt;

140 141
	inst->alg.init = aead_init_geniv;
	inst->alg.exit = aead_exit_geniv;
142

143
	inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
144
	inst->alg.base.cra_ctxsize += inst->alg.ivsize;
145

146 147
	inst->free = aead_geniv_free;

148 149 150 151
	err = aead_register_instance(tmpl, inst);
	if (err)
		goto free_inst;

152
out:
153 154 155 156 157
	return err;

free_inst:
	aead_geniv_free(inst);
	goto out;
158 159 160 161 162 163 164 165 166
}

static void echainiv_free(struct crypto_instance *inst)
{
	aead_geniv_free(aead_instance(inst));
}

static struct crypto_template echainiv_tmpl = {
	.name = "echainiv",
167
	.create = echainiv_aead_create,
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
	.free = echainiv_free,
	.module = THIS_MODULE,
};

static int __init echainiv_module_init(void)
{
	return crypto_register_template(&echainiv_tmpl);
}

static void __exit echainiv_module_exit(void)
{
	crypto_unregister_template(&echainiv_tmpl);
}

module_init(echainiv_module_init);
module_exit(echainiv_module_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Encrypted Chain IV Generator");
MODULE_ALIAS_CRYPTO("echainiv");