Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 1 | /* |
| 2 | * AEAD: Authenticated Encryption with Associated Data |
| 3 | * |
| 4 | * This file provides API support for AEAD algorithms. |
| 5 | * |
| 6 | * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au> |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify it |
| 9 | * under the terms of the GNU General Public License as published by the Free |
| 10 | * Software Foundation; either version 2 of the License, or (at your option) |
| 11 | * any later version. |
| 12 | * |
| 13 | */ |
| 14 | |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 15 | #include <crypto/internal/aead.h> |
| 16 | #include <linux/err.h> |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 17 | #include <linux/init.h> |
| 18 | #include <linux/kernel.h> |
| 19 | #include <linux/module.h> |
| 20 | #include <linux/slab.h> |
| 21 | #include <linux/seq_file.h> |
| 22 | |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 23 | #include "internal.h" |
| 24 | |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 25 | static int setkey_unaligned(struct crypto_aead *tfm, const u8 *key, |
| 26 | unsigned int keylen) |
| 27 | { |
| 28 | struct aead_alg *aead = crypto_aead_alg(tfm); |
| 29 | unsigned long alignmask = crypto_aead_alignmask(tfm); |
| 30 | int ret; |
| 31 | u8 *buffer, *alignbuffer; |
| 32 | unsigned long absize; |
| 33 | |
| 34 | absize = keylen + alignmask; |
| 35 | buffer = kmalloc(absize, GFP_ATOMIC); |
| 36 | if (!buffer) |
| 37 | return -ENOMEM; |
| 38 | |
| 39 | alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); |
| 40 | memcpy(alignbuffer, key, keylen); |
| 41 | ret = aead->setkey(tfm, alignbuffer, keylen); |
| 42 | memset(alignbuffer, 0, keylen); |
| 43 | kfree(buffer); |
| 44 | return ret; |
| 45 | } |
| 46 | |
| 47 | static int setkey(struct crypto_aead *tfm, const u8 *key, unsigned int keylen) |
| 48 | { |
| 49 | struct aead_alg *aead = crypto_aead_alg(tfm); |
| 50 | unsigned long alignmask = crypto_aead_alignmask(tfm); |
| 51 | |
| 52 | if ((unsigned long)key & alignmask) |
| 53 | return setkey_unaligned(tfm, key, keylen); |
| 54 | |
| 55 | return aead->setkey(tfm, key, keylen); |
| 56 | } |
| 57 | |
Herbert Xu | 7ba683a | 2007-12-02 18:49:21 +1100 | [diff] [blame] | 58 | int crypto_aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize) |
| 59 | { |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 60 | struct aead_tfm *crt = crypto_aead_crt(tfm); |
Herbert Xu | 7ba683a | 2007-12-02 18:49:21 +1100 | [diff] [blame] | 61 | int err; |
| 62 | |
| 63 | if (authsize > crypto_aead_alg(tfm)->maxauthsize) |
| 64 | return -EINVAL; |
| 65 | |
| 66 | if (crypto_aead_alg(tfm)->setauthsize) { |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 67 | err = crypto_aead_alg(tfm)->setauthsize(crt->base, authsize); |
Herbert Xu | 7ba683a | 2007-12-02 18:49:21 +1100 | [diff] [blame] | 68 | if (err) |
| 69 | return err; |
| 70 | } |
| 71 | |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 72 | crypto_aead_crt(crt->base)->authsize = authsize; |
| 73 | crt->authsize = authsize; |
Herbert Xu | 7ba683a | 2007-12-02 18:49:21 +1100 | [diff] [blame] | 74 | return 0; |
| 75 | } |
| 76 | EXPORT_SYMBOL_GPL(crypto_aead_setauthsize); |
| 77 | |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 78 | static unsigned int crypto_aead_ctxsize(struct crypto_alg *alg, u32 type, |
| 79 | u32 mask) |
| 80 | { |
| 81 | return alg->cra_ctxsize; |
| 82 | } |
| 83 | |
Herbert Xu | aedb30d | 2007-12-12 19:27:25 +0800 | [diff] [blame] | 84 | static int no_givcrypt(struct aead_givcrypt_request *req) |
Herbert Xu | 743edf5 | 2007-12-10 16:18:01 +0800 | [diff] [blame] | 85 | { |
| 86 | return -ENOSYS; |
| 87 | } |
| 88 | |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 89 | static int crypto_init_aead_ops(struct crypto_tfm *tfm, u32 type, u32 mask) |
| 90 | { |
| 91 | struct aead_alg *alg = &tfm->__crt_alg->cra_aead; |
| 92 | struct aead_tfm *crt = &tfm->crt_aead; |
| 93 | |
Herbert Xu | 7ba683a | 2007-12-02 18:49:21 +1100 | [diff] [blame] | 94 | if (max(alg->maxauthsize, alg->ivsize) > PAGE_SIZE / 8) |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 95 | return -EINVAL; |
| 96 | |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 97 | crt->setkey = tfm->__crt_alg->cra_flags & CRYPTO_ALG_GENIV ? |
| 98 | alg->setkey : setkey; |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 99 | crt->encrypt = alg->encrypt; |
| 100 | crt->decrypt = alg->decrypt; |
Herbert Xu | aedb30d | 2007-12-12 19:27:25 +0800 | [diff] [blame] | 101 | crt->givencrypt = alg->givencrypt ?: no_givcrypt; |
| 102 | crt->givdecrypt = alg->givdecrypt ?: no_givcrypt; |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 103 | crt->base = __crypto_aead_cast(tfm); |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 104 | crt->ivsize = alg->ivsize; |
Herbert Xu | 7ba683a | 2007-12-02 18:49:21 +1100 | [diff] [blame] | 105 | crt->authsize = alg->maxauthsize; |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 106 | |
| 107 | return 0; |
| 108 | } |
| 109 | |
| 110 | static void crypto_aead_show(struct seq_file *m, struct crypto_alg *alg) |
| 111 | __attribute__ ((unused)); |
| 112 | static void crypto_aead_show(struct seq_file *m, struct crypto_alg *alg) |
| 113 | { |
| 114 | struct aead_alg *aead = &alg->cra_aead; |
| 115 | |
| 116 | seq_printf(m, "type : aead\n"); |
| 117 | seq_printf(m, "blocksize : %u\n", alg->cra_blocksize); |
| 118 | seq_printf(m, "ivsize : %u\n", aead->ivsize); |
Herbert Xu | 7ba683a | 2007-12-02 18:49:21 +1100 | [diff] [blame] | 119 | seq_printf(m, "maxauthsize : %u\n", aead->maxauthsize); |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 120 | seq_printf(m, "geniv : %s\n", aead->geniv ?: "<built-in>"); |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | const struct crypto_type crypto_aead_type = { |
| 124 | .ctxsize = crypto_aead_ctxsize, |
| 125 | .init = crypto_init_aead_ops, |
| 126 | #ifdef CONFIG_PROC_FS |
| 127 | .show = crypto_aead_show, |
| 128 | #endif |
| 129 | }; |
| 130 | EXPORT_SYMBOL_GPL(crypto_aead_type); |
| 131 | |
Herbert Xu | 5b6d2d7 | 2007-12-12 19:23:36 +0800 | [diff] [blame^] | 132 | static int aead_null_givencrypt(struct aead_givcrypt_request *req) |
| 133 | { |
| 134 | return crypto_aead_encrypt(&req->areq); |
| 135 | } |
| 136 | |
| 137 | static int aead_null_givdecrypt(struct aead_givcrypt_request *req) |
| 138 | { |
| 139 | return crypto_aead_decrypt(&req->areq); |
| 140 | } |
| 141 | |
| 142 | static int crypto_init_nivaead_ops(struct crypto_tfm *tfm, u32 type, u32 mask) |
| 143 | { |
| 144 | struct aead_alg *alg = &tfm->__crt_alg->cra_aead; |
| 145 | struct aead_tfm *crt = &tfm->crt_aead; |
| 146 | |
| 147 | if (max(alg->maxauthsize, alg->ivsize) > PAGE_SIZE / 8) |
| 148 | return -EINVAL; |
| 149 | |
| 150 | crt->setkey = setkey; |
| 151 | crt->encrypt = alg->encrypt; |
| 152 | crt->decrypt = alg->decrypt; |
| 153 | if (!alg->ivsize) { |
| 154 | crt->givencrypt = aead_null_givencrypt; |
| 155 | crt->givdecrypt = aead_null_givdecrypt; |
| 156 | } |
| 157 | crt->base = __crypto_aead_cast(tfm); |
| 158 | crt->ivsize = alg->ivsize; |
| 159 | crt->authsize = alg->maxauthsize; |
| 160 | |
| 161 | return 0; |
| 162 | } |
| 163 | |
| 164 | static void crypto_nivaead_show(struct seq_file *m, struct crypto_alg *alg) |
| 165 | __attribute__ ((unused)); |
| 166 | static void crypto_nivaead_show(struct seq_file *m, struct crypto_alg *alg) |
| 167 | { |
| 168 | struct aead_alg *aead = &alg->cra_aead; |
| 169 | |
| 170 | seq_printf(m, "type : nivaead\n"); |
| 171 | seq_printf(m, "blocksize : %u\n", alg->cra_blocksize); |
| 172 | seq_printf(m, "ivsize : %u\n", aead->ivsize); |
| 173 | seq_printf(m, "maxauthsize : %u\n", aead->maxauthsize); |
| 174 | seq_printf(m, "geniv : %s\n", aead->geniv); |
| 175 | } |
| 176 | |
| 177 | const struct crypto_type crypto_nivaead_type = { |
| 178 | .ctxsize = crypto_aead_ctxsize, |
| 179 | .init = crypto_init_nivaead_ops, |
| 180 | #ifdef CONFIG_PROC_FS |
| 181 | .show = crypto_nivaead_show, |
| 182 | #endif |
| 183 | }; |
| 184 | EXPORT_SYMBOL_GPL(crypto_nivaead_type); |
| 185 | |
| 186 | static int crypto_grab_nivaead(struct crypto_aead_spawn *spawn, |
| 187 | const char *name, u32 type, u32 mask) |
| 188 | { |
| 189 | struct crypto_alg *alg; |
| 190 | int err; |
| 191 | |
| 192 | type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV); |
| 193 | type |= CRYPTO_ALG_TYPE_AEAD; |
| 194 | mask |= CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV; |
| 195 | |
| 196 | alg = crypto_alg_mod_lookup(name, type, mask); |
| 197 | if (IS_ERR(alg)) |
| 198 | return PTR_ERR(alg); |
| 199 | |
| 200 | err = crypto_init_spawn(&spawn->base, alg, spawn->base.inst, mask); |
| 201 | crypto_mod_put(alg); |
| 202 | return err; |
| 203 | } |
| 204 | |
| 205 | struct crypto_instance *aead_geniv_alloc(struct crypto_template *tmpl, |
| 206 | struct rtattr **tb, u32 type, |
| 207 | u32 mask) |
| 208 | { |
| 209 | const char *name; |
| 210 | struct crypto_aead_spawn *spawn; |
| 211 | struct crypto_attr_type *algt; |
| 212 | struct crypto_instance *inst; |
| 213 | struct crypto_alg *alg; |
| 214 | int err; |
| 215 | |
| 216 | algt = crypto_get_attr_type(tb); |
| 217 | err = PTR_ERR(algt); |
| 218 | if (IS_ERR(algt)) |
| 219 | return ERR_PTR(err); |
| 220 | |
| 221 | if ((algt->type ^ (CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_GENIV)) & |
| 222 | algt->mask) |
| 223 | return ERR_PTR(-EINVAL); |
| 224 | |
| 225 | name = crypto_attr_alg_name(tb[1]); |
| 226 | err = PTR_ERR(name); |
| 227 | if (IS_ERR(name)) |
| 228 | return ERR_PTR(err); |
| 229 | |
| 230 | inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL); |
| 231 | if (!inst) |
| 232 | return ERR_PTR(-ENOMEM); |
| 233 | |
| 234 | spawn = crypto_instance_ctx(inst); |
| 235 | |
| 236 | /* Ignore async algorithms if necessary. */ |
| 237 | mask |= crypto_requires_sync(algt->type, algt->mask); |
| 238 | |
| 239 | crypto_set_aead_spawn(spawn, inst); |
| 240 | err = crypto_grab_nivaead(spawn, name, type, mask); |
| 241 | if (err) |
| 242 | goto err_free_inst; |
| 243 | |
| 244 | alg = crypto_aead_spawn_alg(spawn); |
| 245 | |
| 246 | err = -EINVAL; |
| 247 | if (!alg->cra_aead.ivsize) |
| 248 | goto err_drop_alg; |
| 249 | |
| 250 | /* |
| 251 | * This is only true if we're constructing an algorithm with its |
| 252 | * default IV generator. For the default generator we elide the |
| 253 | * template name and double-check the IV generator. |
| 254 | */ |
| 255 | if (algt->mask & CRYPTO_ALG_GENIV) { |
| 256 | if (strcmp(tmpl->name, alg->cra_aead.geniv)) |
| 257 | goto err_drop_alg; |
| 258 | |
| 259 | memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME); |
| 260 | memcpy(inst->alg.cra_driver_name, alg->cra_driver_name, |
| 261 | CRYPTO_MAX_ALG_NAME); |
| 262 | } else { |
| 263 | err = -ENAMETOOLONG; |
| 264 | if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, |
| 265 | "%s(%s)", tmpl->name, alg->cra_name) >= |
| 266 | CRYPTO_MAX_ALG_NAME) |
| 267 | goto err_drop_alg; |
| 268 | if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, |
| 269 | "%s(%s)", tmpl->name, alg->cra_driver_name) >= |
| 270 | CRYPTO_MAX_ALG_NAME) |
| 271 | goto err_drop_alg; |
| 272 | } |
| 273 | |
| 274 | inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_GENIV; |
| 275 | inst->alg.cra_flags |= alg->cra_flags & CRYPTO_ALG_ASYNC; |
| 276 | inst->alg.cra_priority = alg->cra_priority; |
| 277 | inst->alg.cra_blocksize = alg->cra_blocksize; |
| 278 | inst->alg.cra_alignmask = alg->cra_alignmask; |
| 279 | inst->alg.cra_type = &crypto_aead_type; |
| 280 | |
| 281 | inst->alg.cra_aead.ivsize = alg->cra_aead.ivsize; |
| 282 | inst->alg.cra_aead.maxauthsize = alg->cra_aead.maxauthsize; |
| 283 | inst->alg.cra_aead.geniv = alg->cra_aead.geniv; |
| 284 | |
| 285 | inst->alg.cra_aead.setkey = alg->cra_aead.setkey; |
| 286 | inst->alg.cra_aead.setauthsize = alg->cra_aead.setauthsize; |
| 287 | inst->alg.cra_aead.encrypt = alg->cra_aead.encrypt; |
| 288 | inst->alg.cra_aead.decrypt = alg->cra_aead.decrypt; |
| 289 | |
| 290 | out: |
| 291 | return inst; |
| 292 | |
| 293 | err_drop_alg: |
| 294 | crypto_drop_aead(spawn); |
| 295 | err_free_inst: |
| 296 | kfree(inst); |
| 297 | inst = ERR_PTR(err); |
| 298 | goto out; |
| 299 | } |
| 300 | EXPORT_SYMBOL_GPL(aead_geniv_alloc); |
| 301 | |
| 302 | void aead_geniv_free(struct crypto_instance *inst) |
| 303 | { |
| 304 | crypto_drop_aead(crypto_instance_ctx(inst)); |
| 305 | kfree(inst); |
| 306 | } |
| 307 | EXPORT_SYMBOL_GPL(aead_geniv_free); |
| 308 | |
| 309 | int aead_geniv_init(struct crypto_tfm *tfm) |
| 310 | { |
| 311 | struct crypto_instance *inst = (void *)tfm->__crt_alg; |
| 312 | struct crypto_aead *aead; |
| 313 | |
| 314 | aead = crypto_spawn_aead(crypto_instance_ctx(inst)); |
| 315 | if (IS_ERR(aead)) |
| 316 | return PTR_ERR(aead); |
| 317 | |
| 318 | tfm->crt_aead.base = aead; |
| 319 | tfm->crt_aead.reqsize += crypto_aead_reqsize(aead); |
| 320 | |
| 321 | return 0; |
| 322 | } |
| 323 | EXPORT_SYMBOL_GPL(aead_geniv_init); |
| 324 | |
| 325 | void aead_geniv_exit(struct crypto_tfm *tfm) |
| 326 | { |
| 327 | crypto_free_aead(tfm->crt_aead.base); |
| 328 | } |
| 329 | EXPORT_SYMBOL_GPL(aead_geniv_exit); |
| 330 | |
Herbert Xu | 1ae9782 | 2007-08-30 15:36:14 +0800 | [diff] [blame] | 331 | MODULE_LICENSE("GPL"); |
| 332 | MODULE_DESCRIPTION("Authenticated Encryption with Associated Data (AEAD)"); |