Blame view

crypto/echainiv.c 6.11 KB
a10f554fa   Herbert Xu   crypto: echainiv ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   * echainiv: Encrypted Chain IV Generator
   *
   * This generator generates an IV based on a sequence number by xoring it
   * with a salt and then encrypting it with the same key as used to encrypt
   * 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.
   *
   */
d97de47ca   Herbert Xu   crypto: echainiv ...
20
  #include <crypto/internal/geniv.h>
a10f554fa   Herbert Xu   crypto: echainiv ...
21
  #include <crypto/scatterwalk.h>
0e8bff47f   Herbert Xu   crypto: echainiv ...
22
  #include <crypto/skcipher.h>
a10f554fa   Herbert Xu   crypto: echainiv ...
23
24
25
26
27
28
29
30
31
32
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/mm.h>
  #include <linux/module.h>
  #include <linux/percpu.h>
  #include <linux/spinlock.h>
  #include <linux/string.h>
  
  #define MAX_IV_SIZE 16
a10f554fa   Herbert Xu   crypto: echainiv ...
33
  static DEFINE_PER_CPU(u32 [MAX_IV_SIZE / sizeof(u32)], echainiv_iv);
a10f554fa   Herbert Xu   crypto: echainiv ...
34
  /* We don't care if we get preempted and read/write IVs from the next CPU. */
622ff8752   Wu Fengguang   crypto: echainiv ...
35
  static void echainiv_read_iv(u8 *dst, unsigned size)
a10f554fa   Herbert Xu   crypto: echainiv ...
36
37
38
39
40
41
42
43
44
  {
  	u32 *a = (u32 *)dst;
  	u32 __percpu *b = echainiv_iv;
  
  	for (; size >= 4; size -= 4) {
  		*a++ = this_cpu_read(*b);
  		b++;
  	}
  }
622ff8752   Wu Fengguang   crypto: echainiv ...
45
  static void echainiv_write_iv(const u8 *src, unsigned size)
a10f554fa   Herbert Xu   crypto: echainiv ...
46
47
48
49
50
51
52
53
54
55
  {
  	const u32 *a = (const u32 *)src;
  	u32 __percpu *b = echainiv_iv;
  
  	for (; size >= 4; size -= 4) {
  		this_cpu_write(*b, *a);
  		a++;
  		b++;
  	}
  }
a10f554fa   Herbert Xu   crypto: echainiv ...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  static void echainiv_encrypt_complete2(struct aead_request *req, int err)
  {
  	struct aead_request *subreq = aead_request_ctx(req);
  	struct crypto_aead *geniv;
  	unsigned int ivsize;
  
  	if (err == -EINPROGRESS)
  		return;
  
  	if (err)
  		goto out;
  
  	geniv = crypto_aead_reqtfm(req);
  	ivsize = crypto_aead_ivsize(geniv);
  
  	echainiv_write_iv(subreq->iv, ivsize);
  
  	if (req->iv != subreq->iv)
  		memcpy(req->iv, subreq->iv, ivsize);
  
  out:
  	if (req->iv != subreq->iv)
  		kzfree(subreq->iv);
  }
  
  static void echainiv_encrypt_complete(struct crypto_async_request *base,
  					 int err)
  {
  	struct aead_request *req = base->data;
  
  	echainiv_encrypt_complete2(req, err);
  	aead_request_complete(req, err);
  }
a10f554fa   Herbert Xu   crypto: echainiv ...
89
90
91
  static int echainiv_encrypt(struct aead_request *req)
  {
  	struct crypto_aead *geniv = crypto_aead_reqtfm(req);
376e0d697   Herbert Xu   crypto: echainiv ...
92
  	struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
a10f554fa   Herbert Xu   crypto: echainiv ...
93
94
95
96
  	struct aead_request *subreq = aead_request_ctx(req);
  	crypto_completion_t compl;
  	void *data;
  	u8 *info;
823655c99   Herbert Xu   crypto: echainiv ...
97
  	unsigned int ivsize = crypto_aead_ivsize(geniv);
a10f554fa   Herbert Xu   crypto: echainiv ...
98
  	int err;
823655c99   Herbert Xu   crypto: echainiv ...
99
100
  	if (req->cryptlen < ivsize)
  		return -EINVAL;
376e0d697   Herbert Xu   crypto: echainiv ...
101
  	aead_request_set_tfm(subreq, ctx->child);
a10f554fa   Herbert Xu   crypto: echainiv ...
102
103
104
105
  
  	compl = echainiv_encrypt_complete;
  	data = req;
  	info = req->iv;
a10f554fa   Herbert Xu   crypto: echainiv ...
106
  	if (req->src != req->dst) {
0e8bff47f   Herbert Xu   crypto: echainiv ...
107
  		SKCIPHER_REQUEST_ON_STACK(nreq, ctx->sknull);
a10f554fa   Herbert Xu   crypto: echainiv ...
108

0e8bff47f   Herbert Xu   crypto: echainiv ...
109
110
111
112
113
114
115
116
  		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);
a10f554fa   Herbert Xu   crypto: echainiv ...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  		if (err)
  			return err;
  	}
  
  	if (unlikely(!IS_ALIGNED((unsigned long)info,
  				 crypto_aead_alignmask(geniv) + 1))) {
  		info = kmalloc(ivsize, req->base.flags &
  				       CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL:
  								  GFP_ATOMIC);
  		if (!info)
  			return -ENOMEM;
  
  		memcpy(info, req->iv, ivsize);
  	}
  
  	aead_request_set_callback(subreq, req->base.flags, compl, data);
  	aead_request_set_crypt(subreq, req->dst, req->dst,
5499b1a73   Herbert Xu   crypto: echainiv ...
134
135
  			       req->cryptlen, info);
  	aead_request_set_ad(subreq, req->assoclen);
a10f554fa   Herbert Xu   crypto: echainiv ...
136
137
138
139
140
141
142
143
144
  
  	crypto_xor(info, ctx->salt, ivsize);
  	scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);
  	echainiv_read_iv(info, ivsize);
  
  	err = crypto_aead_encrypt(subreq);
  	echainiv_encrypt_complete2(req, err);
  	return err;
  }
a10f554fa   Herbert Xu   crypto: echainiv ...
145
146
147
  static int echainiv_decrypt(struct aead_request *req)
  {
  	struct crypto_aead *geniv = crypto_aead_reqtfm(req);
376e0d697   Herbert Xu   crypto: echainiv ...
148
  	struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
a10f554fa   Herbert Xu   crypto: echainiv ...
149
150
151
  	struct aead_request *subreq = aead_request_ctx(req);
  	crypto_completion_t compl;
  	void *data;
823655c99   Herbert Xu   crypto: echainiv ...
152
  	unsigned int ivsize = crypto_aead_ivsize(geniv);
5499b1a73   Herbert Xu   crypto: echainiv ...
153
  	if (req->cryptlen < ivsize)
823655c99   Herbert Xu   crypto: echainiv ...
154
  		return -EINVAL;
a10f554fa   Herbert Xu   crypto: echainiv ...
155

376e0d697   Herbert Xu   crypto: echainiv ...
156
  	aead_request_set_tfm(subreq, ctx->child);
a10f554fa   Herbert Xu   crypto: echainiv ...
157
158
159
  
  	compl = req->base.complete;
  	data = req->base.data;
a10f554fa   Herbert Xu   crypto: echainiv ...
160
161
162
  	aead_request_set_callback(subreq, req->base.flags, compl, data);
  	aead_request_set_crypt(subreq, req->src, req->dst,
  			       req->cryptlen - ivsize, req->iv);
374d4ad18   Herbert Xu   crypto: aead - Re...
163
  	aead_request_set_ad(subreq, req->assoclen + ivsize);
a10f554fa   Herbert Xu   crypto: echainiv ...
164
165
  
  	scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
a10f554fa   Herbert Xu   crypto: echainiv ...
166
167
168
  
  	return crypto_aead_decrypt(subreq);
  }
1e419c797   Herbert Xu   crypto: echainiv ...
169
170
  static int echainiv_aead_create(struct crypto_template *tmpl,
  				struct rtattr **tb)
a10f554fa   Herbert Xu   crypto: echainiv ...
171
172
173
174
  {
  	struct aead_instance *inst;
  	struct crypto_aead_spawn *spawn;
  	struct aead_alg *alg;
1e419c797   Herbert Xu   crypto: echainiv ...
175
  	int err;
a10f554fa   Herbert Xu   crypto: echainiv ...
176

1e419c797   Herbert Xu   crypto: echainiv ...
177
  	inst = aead_geniv_alloc(tmpl, tb, 0, 0);
a10f554fa   Herbert Xu   crypto: echainiv ...
178
179
  
  	if (IS_ERR(inst))
1e419c797   Herbert Xu   crypto: echainiv ...
180
  		return PTR_ERR(inst);
a10f554fa   Herbert Xu   crypto: echainiv ...
181

d97de47ca   Herbert Xu   crypto: echainiv ...
182
183
  	spawn = aead_instance_ctx(inst);
  	alg = crypto_spawn_aead_alg(spawn);
1e419c797   Herbert Xu   crypto: echainiv ...
184
  	err = -EINVAL;
d97de47ca   Herbert Xu   crypto: echainiv ...
185
  	if (inst->alg.ivsize & (sizeof(u32) - 1) ||
1e419c797   Herbert Xu   crypto: echainiv ...
186
187
  	    inst->alg.ivsize > MAX_IV_SIZE)
  		goto free_inst;
a10f554fa   Herbert Xu   crypto: echainiv ...
188

f261c5fbe   Herbert Xu   crypto: echainiv ...
189
  	inst->alg.encrypt = echainiv_encrypt;
a10f554fa   Herbert Xu   crypto: echainiv ...
190
  	inst->alg.decrypt = echainiv_decrypt;
376e0d697   Herbert Xu   crypto: echainiv ...
191
192
  	inst->alg.init = aead_init_geniv;
  	inst->alg.exit = aead_exit_geniv;
a10f554fa   Herbert Xu   crypto: echainiv ...
193
194
  
  	inst->alg.base.cra_alignmask |= __alignof__(u32) - 1;
376e0d697   Herbert Xu   crypto: echainiv ...
195
  	inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
9d03aee12   Herbert Xu   crypto: echainiv ...
196
  	inst->alg.base.cra_ctxsize += inst->alg.ivsize;
a10f554fa   Herbert Xu   crypto: echainiv ...
197

5499b1a73   Herbert Xu   crypto: echainiv ...
198
  	inst->free = aead_geniv_free;
1e419c797   Herbert Xu   crypto: echainiv ...
199
200
201
  	err = aead_register_instance(tmpl, inst);
  	if (err)
  		goto free_inst;
a10f554fa   Herbert Xu   crypto: echainiv ...
202
  out:
1e419c797   Herbert Xu   crypto: echainiv ...
203
204
205
206
207
  	return err;
  
  free_inst:
  	aead_geniv_free(inst);
  	goto out;
a10f554fa   Herbert Xu   crypto: echainiv ...
208
  }
a10f554fa   Herbert Xu   crypto: echainiv ...
209
210
211
  static void echainiv_free(struct crypto_instance *inst)
  {
  	aead_geniv_free(aead_instance(inst));
a10f554fa   Herbert Xu   crypto: echainiv ...
212
213
214
215
  }
  
  static struct crypto_template echainiv_tmpl = {
  	.name = "echainiv",
9fcc704df   Herbert Xu   crypto: echainiv ...
216
  	.create = echainiv_aead_create,
a10f554fa   Herbert Xu   crypto: echainiv ...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  	.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");