xcbc.c 9.1 KB
Newer Older
K
Kazunori MIYAZAWA 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright (C)2006 USAGI/WIDE Project
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author:
 * 	Kazunori Miyazawa <miyazawa@linux-ipv6.org>
 */

#include <linux/crypto.h>
#include <linux/err.h>
24
#include <linux/hardirq.h>
K
Kazunori MIYAZAWA 已提交
25 26 27 28 29 30 31
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include "internal.h"

32 33 34
static u_int32_t ks[12] = {0x01010101, 0x01010101, 0x01010101, 0x01010101,
			   0x02020202, 0x02020202, 0x02020202, 0x02020202,
			   0x03030303, 0x03030303, 0x03030303, 0x03030303};
K
Kazunori MIYAZAWA 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
/*
 * +------------------------
 * | <parent tfm>
 * +------------------------
 * | crypto_xcbc_ctx
 * +------------------------
 * | odds (block size)
 * +------------------------
 * | prev (block size)
 * +------------------------
 * | key (block size)
 * +------------------------
 * | consts (block size * 3)
 * +------------------------
 */
struct crypto_xcbc_ctx {
51
	struct crypto_cipher *child;
K
Kazunori MIYAZAWA 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	u8 *odds;
	u8 *prev;
	u8 *key;
	u8 *consts;
	void (*xor)(u8 *a, const u8 *b, unsigned int bs);
	unsigned int keylen;
	unsigned int len;
};

static void xor_128(u8 *a, const u8 *b, unsigned int bs)
{
	((u32 *)a)[0] ^= ((u32 *)b)[0];
	((u32 *)a)[1] ^= ((u32 *)b)[1];
	((u32 *)a)[2] ^= ((u32 *)b)[2];
	((u32 *)a)[3] ^= ((u32 *)b)[3];
}

static int _crypto_xcbc_digest_setkey(struct crypto_hash *parent,
				      struct crypto_xcbc_ctx *ctx)
{
	int bs = crypto_hash_blocksize(parent);
	int err = 0;
	u8 key1[bs];

	if ((err = crypto_cipher_setkey(ctx->child, ctx->key, ctx->keylen)))
	    return err;

79
	crypto_cipher_encrypt_one(ctx->child, key1, ctx->consts);
K
Kazunori MIYAZAWA 已提交
80 81 82 83 84 85 86 87 88

	return crypto_cipher_setkey(ctx->child, key1, bs);
}

static int crypto_xcbc_digest_setkey(struct crypto_hash *parent,
				     const u8 *inkey, unsigned int keylen)
{
	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);

89
	if (keylen != crypto_cipher_blocksize(ctx->child))
K
Kazunori MIYAZAWA 已提交
90 91 92 93 94 95 96 97 98
		return -EINVAL;

	ctx->keylen = keylen;
	memcpy(ctx->key, inkey, keylen);
	ctx->consts = (u8*)ks;

	return _crypto_xcbc_digest_setkey(parent, ctx);
}

99
static int crypto_xcbc_digest_init(struct hash_desc *pdesc)
K
Kazunori MIYAZAWA 已提交
100 101 102 103 104 105 106 107 108 109 110
{
	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(pdesc->tfm);
	int bs = crypto_hash_blocksize(pdesc->tfm);

	ctx->len = 0;
	memset(ctx->odds, 0, bs);
	memset(ctx->prev, 0, bs);

	return 0;
}

111 112 113
static int crypto_xcbc_digest_update2(struct hash_desc *pdesc,
				      struct scatterlist *sg,
				      unsigned int nbytes)
K
Kazunori MIYAZAWA 已提交
114 115 116
{
	struct crypto_hash *parent = pdesc->tfm;
	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);
117
	struct crypto_cipher *tfm = ctx->child;
K
Kazunori MIYAZAWA 已提交
118 119 120 121 122
	int bs = crypto_hash_blocksize(parent);
	unsigned int i = 0;

	do {

J
Jens Axboe 已提交
123
		struct page *pg = sg_page(&sg[i]);
K
Kazunori MIYAZAWA 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
		unsigned int offset = sg[i].offset;
		unsigned int slen = sg[i].length;

		while (slen > 0) {
			unsigned int len = min(slen, ((unsigned int)(PAGE_SIZE)) - offset);
			char *p = crypto_kmap(pg, 0) + offset;

			/* checking the data can fill the block */
			if ((ctx->len + len) <= bs) {
				memcpy(ctx->odds + ctx->len, p, len);
				ctx->len += len;
				slen -= len;

				/* checking the rest of the page */
				if (len + offset >= PAGE_SIZE) {
					offset = 0;
					pg++;
				} else
					offset += len;

				crypto_kunmap(p, 0);
145
				crypto_yield(pdesc->flags);
K
Kazunori MIYAZAWA 已提交
146 147 148 149 150 151 152 153 154
				continue;
			}

			/* filling odds with new data and encrypting it */
			memcpy(ctx->odds + ctx->len, p, bs - ctx->len);
			len -= bs - ctx->len;
			p += bs - ctx->len;

			ctx->xor(ctx->prev, ctx->odds, bs);
155
			crypto_cipher_encrypt_one(tfm, ctx->prev, ctx->prev);
K
Kazunori MIYAZAWA 已提交
156 157 158 159 160 161 162

			/* clearing the length */
			ctx->len = 0;

			/* encrypting the rest of data */
			while (len > bs) {
				ctx->xor(ctx->prev, p, bs);
163 164
				crypto_cipher_encrypt_one(tfm, ctx->prev,
							  ctx->prev);
K
Kazunori MIYAZAWA 已提交
165 166 167 168 169 170 171 172 173 174
				p += bs;
				len -= bs;
			}

			/* keeping the surplus of blocksize */
			if (len) {
				memcpy(ctx->odds, p, len);
				ctx->len = len;
			}
			crypto_kunmap(p, 0);
175
			crypto_yield(pdesc->flags);
K
Kazunori MIYAZAWA 已提交
176 177 178 179 180 181 182 183 184 185 186
			slen -= min(slen, ((unsigned int)(PAGE_SIZE)) - offset);
			offset = 0;
			pg++;
		}
		nbytes-=sg[i].length;
		i++;
	} while (nbytes>0);

	return 0;
}

187 188 189 190 191 192 193 194 195
static int crypto_xcbc_digest_update(struct hash_desc *pdesc,
				     struct scatterlist *sg,
				     unsigned int nbytes)
{
	if (WARN_ON_ONCE(in_irq()))
		return -EDEADLK;
	return crypto_xcbc_digest_update2(pdesc, sg, nbytes);
}

196
static int crypto_xcbc_digest_final(struct hash_desc *pdesc, u8 *out)
K
Kazunori MIYAZAWA 已提交
197 198 199
{
	struct crypto_hash *parent = pdesc->tfm;
	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);
200
	struct crypto_cipher *tfm = ctx->child;
K
Kazunori MIYAZAWA 已提交
201 202 203 204 205 206 207 208 209
	int bs = crypto_hash_blocksize(parent);
	int err = 0;

	if (ctx->len == bs) {
		u8 key2[bs];

		if ((err = crypto_cipher_setkey(tfm, ctx->key, ctx->keylen)) != 0)
			return err;

210 211
		crypto_cipher_encrypt_one(tfm, key2,
					  (u8 *)(ctx->consts + bs));
K
Kazunori MIYAZAWA 已提交
212 213 214 215 216

		ctx->xor(ctx->prev, ctx->odds, bs);
		ctx->xor(ctx->prev, key2, bs);
		_crypto_xcbc_digest_setkey(parent, ctx);

217
		crypto_cipher_encrypt_one(tfm, out, ctx->prev);
K
Kazunori MIYAZAWA 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230 231
	} else {
		u8 key3[bs];
		unsigned int rlen;
		u8 *p = ctx->odds + ctx->len;
		*p = 0x80;
		p++;

		rlen = bs - ctx->len -1;
		if (rlen)
			memset(p, 0, rlen);

		if ((err = crypto_cipher_setkey(tfm, ctx->key, ctx->keylen)) != 0)
			return err;

232 233
		crypto_cipher_encrypt_one(tfm, key3,
					  (u8 *)(ctx->consts + bs * 2));
K
Kazunori MIYAZAWA 已提交
234 235 236 237 238 239

		ctx->xor(ctx->prev, ctx->odds, bs);
		ctx->xor(ctx->prev, key3, bs);

		_crypto_xcbc_digest_setkey(parent, ctx);

240
		crypto_cipher_encrypt_one(tfm, out, ctx->prev);
K
Kazunori MIYAZAWA 已提交
241 242 243 244 245 246 247 248
	}

	return 0;
}

static int crypto_xcbc_digest(struct hash_desc *pdesc,
		  struct scatterlist *sg, unsigned int nbytes, u8 *out)
{
249 250 251
	if (WARN_ON_ONCE(in_irq()))
		return -EDEADLK;

K
Kazunori MIYAZAWA 已提交
252
	crypto_xcbc_digest_init(pdesc);
253
	crypto_xcbc_digest_update2(pdesc, sg, nbytes);
K
Kazunori MIYAZAWA 已提交
254 255 256 257 258
	return crypto_xcbc_digest_final(pdesc, out);
}

static int xcbc_init_tfm(struct crypto_tfm *tfm)
{
H
Herbert Xu 已提交
259
	struct crypto_cipher *cipher;
K
Kazunori MIYAZAWA 已提交
260 261 262 263 264
	struct crypto_instance *inst = (void *)tfm->__crt_alg;
	struct crypto_spawn *spawn = crypto_instance_ctx(inst);
	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(__crypto_hash_cast(tfm));
	int bs = crypto_hash_blocksize(__crypto_hash_cast(tfm));

H
Herbert Xu 已提交
265 266 267
	cipher = crypto_spawn_cipher(spawn);
	if (IS_ERR(cipher))
		return PTR_ERR(cipher);
K
Kazunori MIYAZAWA 已提交
268 269 270 271 272 273 274 275 276

	switch(bs) {
	case 16:
		ctx->xor = xor_128;
		break;
	default:
		return -EINVAL;
	}

H
Herbert Xu 已提交
277
	ctx->child = cipher;
K
Kazunori MIYAZAWA 已提交
278 279 280 281 282 283 284 285 286 287 288 289 290
	ctx->odds = (u8*)(ctx+1);
	ctx->prev = ctx->odds + bs;
	ctx->key = ctx->prev + bs;

	return 0;
};

static void xcbc_exit_tfm(struct crypto_tfm *tfm)
{
	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(__crypto_hash_cast(tfm));
	crypto_free_cipher(ctx->child);
}

291
static struct crypto_instance *xcbc_alloc(struct rtattr **tb)
K
Kazunori MIYAZAWA 已提交
292 293 294
{
	struct crypto_instance *inst;
	struct crypto_alg *alg;
295 296 297 298 299 300 301 302
	int err;

	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_HASH);
	if (err)
		return ERR_PTR(err);

	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER,
				  CRYPTO_ALG_TYPE_MASK);
K
Kazunori MIYAZAWA 已提交
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
	if (IS_ERR(alg))
		return ERR_PTR(PTR_ERR(alg));

	switch(alg->cra_blocksize) {
	case 16:
		break;
	default:
		return ERR_PTR(PTR_ERR(alg));
	}

	inst = crypto_alloc_instance("xcbc", alg);
	if (IS_ERR(inst))
		goto out_put_alg;

	inst->alg.cra_flags = CRYPTO_ALG_TYPE_HASH;
	inst->alg.cra_priority = alg->cra_priority;
	inst->alg.cra_blocksize = alg->cra_blocksize;
	inst->alg.cra_alignmask = alg->cra_alignmask;
	inst->alg.cra_type = &crypto_hash_type;

	inst->alg.cra_hash.digestsize =
		(alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
		CRYPTO_ALG_TYPE_HASH ? alg->cra_hash.digestsize :
				       alg->cra_blocksize;
	inst->alg.cra_ctxsize = sizeof(struct crypto_xcbc_ctx) +
				ALIGN(inst->alg.cra_blocksize * 3, sizeof(void *));
	inst->alg.cra_init = xcbc_init_tfm;
	inst->alg.cra_exit = xcbc_exit_tfm;

	inst->alg.cra_hash.init = crypto_xcbc_digest_init;
	inst->alg.cra_hash.update = crypto_xcbc_digest_update;
	inst->alg.cra_hash.final = crypto_xcbc_digest_final;
	inst->alg.cra_hash.digest = crypto_xcbc_digest;
	inst->alg.cra_hash.setkey = crypto_xcbc_digest_setkey;

out_put_alg:
	crypto_mod_put(alg);
	return inst;
}

static void xcbc_free(struct crypto_instance *inst)
{
	crypto_drop_spawn(crypto_instance_ctx(inst));
	kfree(inst);
}

static struct crypto_template crypto_xcbc_tmpl = {
	.name = "xcbc",
	.alloc = xcbc_alloc,
	.free = xcbc_free,
	.module = THIS_MODULE,
};

static int __init crypto_xcbc_module_init(void)
{
	return crypto_register_template(&crypto_xcbc_tmpl);
}

static void __exit crypto_xcbc_module_exit(void)
{
	crypto_unregister_template(&crypto_xcbc_tmpl);
}

module_init(crypto_xcbc_module_init);
module_exit(crypto_xcbc_module_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("XCBC keyed hash algorithm");