Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 1 | /* |
| 2 | * Cryptographic API for algorithms (i.e., low-level API). |
| 3 | * |
| 4 | * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify it |
| 7 | * under the terms of the GNU General Public License as published by the Free |
| 8 | * Software Foundation; either version 2 of the License, or (at your option) |
| 9 | * any later version. |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | #include <linux/errno.h> |
| 14 | #include <linux/init.h> |
| 15 | #include <linux/kernel.h> |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 16 | #include <linux/list.h> |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 17 | #include <linux/module.h> |
| 18 | #include <linux/string.h> |
| 19 | |
| 20 | #include "internal.h" |
| 21 | |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 22 | static LIST_HEAD(crypto_template_list); |
| 23 | |
Herbert Xu | 492e2b6 | 2006-09-21 11:35:17 +1000 | [diff] [blame^] | 24 | void crypto_larval_error(const char *name, u32 type, u32 mask) |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 25 | { |
| 26 | struct crypto_alg *alg; |
| 27 | |
| 28 | down_read(&crypto_alg_sem); |
Herbert Xu | 492e2b6 | 2006-09-21 11:35:17 +1000 | [diff] [blame^] | 29 | alg = __crypto_alg_lookup(name, type, mask); |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 30 | up_read(&crypto_alg_sem); |
| 31 | |
| 32 | if (alg) { |
| 33 | if (crypto_is_larval(alg)) { |
| 34 | struct crypto_larval *larval = (void *)alg; |
| 35 | complete(&larval->completion); |
| 36 | } |
| 37 | crypto_mod_put(alg); |
| 38 | } |
| 39 | } |
| 40 | EXPORT_SYMBOL_GPL(crypto_larval_error); |
| 41 | |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 42 | static inline int crypto_set_driver_name(struct crypto_alg *alg) |
| 43 | { |
| 44 | static const char suffix[] = "-generic"; |
| 45 | char *driver_name = alg->cra_driver_name; |
| 46 | int len; |
| 47 | |
| 48 | if (*driver_name) |
| 49 | return 0; |
| 50 | |
| 51 | len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME); |
| 52 | if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME) |
| 53 | return -ENAMETOOLONG; |
| 54 | |
| 55 | memcpy(driver_name + len, suffix, sizeof(suffix)); |
| 56 | return 0; |
| 57 | } |
| 58 | |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 59 | static int crypto_check_alg(struct crypto_alg *alg) |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 60 | { |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 61 | if (alg->cra_alignmask & (alg->cra_alignmask + 1)) |
| 62 | return -EINVAL; |
| 63 | |
| 64 | if (alg->cra_alignmask & alg->cra_blocksize) |
| 65 | return -EINVAL; |
| 66 | |
| 67 | if (alg->cra_blocksize > PAGE_SIZE / 8) |
| 68 | return -EINVAL; |
| 69 | |
| 70 | if (alg->cra_priority < 0) |
| 71 | return -EINVAL; |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 72 | |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 73 | return crypto_set_driver_name(alg); |
| 74 | } |
| 75 | |
| 76 | static int __crypto_register_alg(struct crypto_alg *alg) |
| 77 | { |
| 78 | struct crypto_alg *q; |
| 79 | int ret = -EEXIST; |
| 80 | |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 81 | atomic_set(&alg->cra_refcnt, 1); |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 82 | list_for_each_entry(q, &crypto_alg_list, cra_list) { |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 83 | if (q == alg) |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 84 | goto out; |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 85 | if (crypto_is_larval(q) && |
| 86 | (!strcmp(alg->cra_name, q->cra_name) || |
| 87 | !strcmp(alg->cra_driver_name, q->cra_name))) { |
| 88 | struct crypto_larval *larval = (void *)q; |
| 89 | |
Herbert Xu | 492e2b6 | 2006-09-21 11:35:17 +1000 | [diff] [blame^] | 90 | if ((q->cra_flags ^ alg->cra_flags) & larval->mask) |
| 91 | continue; |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 92 | if (!crypto_mod_get(alg)) |
| 93 | continue; |
| 94 | larval->adult = alg; |
| 95 | complete(&larval->completion); |
| 96 | } |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | list_add(&alg->cra_list, &crypto_alg_list); |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 100 | |
| 101 | crypto_notify(CRYPTO_MSG_ALG_REGISTER, alg); |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 102 | ret = 0; |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 103 | |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 104 | out: |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 105 | return ret; |
| 106 | } |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 107 | |
| 108 | int crypto_register_alg(struct crypto_alg *alg) |
| 109 | { |
| 110 | int err; |
| 111 | |
| 112 | err = crypto_check_alg(alg); |
| 113 | if (err) |
| 114 | return err; |
| 115 | |
| 116 | down_write(&crypto_alg_sem); |
| 117 | err = __crypto_register_alg(alg); |
| 118 | up_write(&crypto_alg_sem); |
| 119 | |
| 120 | return err; |
| 121 | } |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 122 | EXPORT_SYMBOL_GPL(crypto_register_alg); |
| 123 | |
| 124 | int crypto_unregister_alg(struct crypto_alg *alg) |
| 125 | { |
| 126 | int ret = -ENOENT; |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 127 | |
| 128 | down_write(&crypto_alg_sem); |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 129 | if (likely(!list_empty(&alg->cra_list))) { |
| 130 | list_del_init(&alg->cra_list); |
| 131 | ret = 0; |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 132 | } |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 133 | crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, alg); |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 134 | up_write(&crypto_alg_sem); |
| 135 | |
| 136 | if (ret) |
| 137 | return ret; |
| 138 | |
| 139 | BUG_ON(atomic_read(&alg->cra_refcnt) != 1); |
| 140 | if (alg->cra_destroy) |
| 141 | alg->cra_destroy(alg); |
| 142 | |
| 143 | return 0; |
| 144 | } |
| 145 | EXPORT_SYMBOL_GPL(crypto_unregister_alg); |
| 146 | |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 147 | int crypto_register_template(struct crypto_template *tmpl) |
| 148 | { |
| 149 | struct crypto_template *q; |
| 150 | int err = -EEXIST; |
| 151 | |
| 152 | down_write(&crypto_alg_sem); |
| 153 | |
| 154 | list_for_each_entry(q, &crypto_template_list, list) { |
| 155 | if (q == tmpl) |
| 156 | goto out; |
| 157 | } |
| 158 | |
| 159 | list_add(&tmpl->list, &crypto_template_list); |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 160 | crypto_notify(CRYPTO_MSG_TMPL_REGISTER, tmpl); |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 161 | err = 0; |
| 162 | out: |
| 163 | up_write(&crypto_alg_sem); |
| 164 | return err; |
| 165 | } |
| 166 | EXPORT_SYMBOL_GPL(crypto_register_template); |
| 167 | |
| 168 | void crypto_unregister_template(struct crypto_template *tmpl) |
| 169 | { |
| 170 | struct crypto_instance *inst; |
| 171 | struct hlist_node *p, *n; |
| 172 | struct hlist_head *list; |
| 173 | |
| 174 | down_write(&crypto_alg_sem); |
| 175 | |
| 176 | BUG_ON(list_empty(&tmpl->list)); |
| 177 | list_del_init(&tmpl->list); |
| 178 | |
| 179 | list = &tmpl->instances; |
| 180 | hlist_for_each_entry(inst, p, list, list) { |
| 181 | BUG_ON(list_empty(&inst->alg.cra_list)); |
| 182 | list_del_init(&inst->alg.cra_list); |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 183 | crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, &inst->alg); |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 184 | } |
| 185 | |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 186 | crypto_notify(CRYPTO_MSG_TMPL_UNREGISTER, tmpl); |
| 187 | |
Herbert Xu | 4cc7720 | 2006-08-06 21:16:34 +1000 | [diff] [blame] | 188 | up_write(&crypto_alg_sem); |
| 189 | |
| 190 | hlist_for_each_entry_safe(inst, p, n, list, list) { |
| 191 | BUG_ON(atomic_read(&inst->alg.cra_refcnt) != 1); |
| 192 | tmpl->free(inst); |
| 193 | } |
| 194 | } |
| 195 | EXPORT_SYMBOL_GPL(crypto_unregister_template); |
| 196 | |
| 197 | static struct crypto_template *__crypto_lookup_template(const char *name) |
| 198 | { |
| 199 | struct crypto_template *q, *tmpl = NULL; |
| 200 | |
| 201 | down_read(&crypto_alg_sem); |
| 202 | list_for_each_entry(q, &crypto_template_list, list) { |
| 203 | if (strcmp(q->name, name)) |
| 204 | continue; |
| 205 | if (unlikely(!crypto_tmpl_get(q))) |
| 206 | continue; |
| 207 | |
| 208 | tmpl = q; |
| 209 | break; |
| 210 | } |
| 211 | up_read(&crypto_alg_sem); |
| 212 | |
| 213 | return tmpl; |
| 214 | } |
| 215 | |
| 216 | struct crypto_template *crypto_lookup_template(const char *name) |
| 217 | { |
| 218 | return try_then_request_module(__crypto_lookup_template(name), name); |
| 219 | } |
| 220 | EXPORT_SYMBOL_GPL(crypto_lookup_template); |
| 221 | |
| 222 | int crypto_register_instance(struct crypto_template *tmpl, |
| 223 | struct crypto_instance *inst) |
| 224 | { |
| 225 | int err = -EINVAL; |
| 226 | |
| 227 | if (inst->alg.cra_destroy) |
| 228 | goto err; |
| 229 | |
| 230 | err = crypto_check_alg(&inst->alg); |
| 231 | if (err) |
| 232 | goto err; |
| 233 | |
| 234 | inst->alg.cra_module = tmpl->module; |
| 235 | |
| 236 | down_write(&crypto_alg_sem); |
| 237 | |
| 238 | err = __crypto_register_alg(&inst->alg); |
| 239 | if (err) |
| 240 | goto unlock; |
| 241 | |
| 242 | hlist_add_head(&inst->list, &tmpl->instances); |
| 243 | inst->tmpl = tmpl; |
| 244 | |
| 245 | unlock: |
| 246 | up_write(&crypto_alg_sem); |
| 247 | |
| 248 | err: |
| 249 | return err; |
| 250 | } |
| 251 | EXPORT_SYMBOL_GPL(crypto_register_instance); |
| 252 | |
Herbert Xu | 2825982 | 2006-08-06 21:23:26 +1000 | [diff] [blame] | 253 | int crypto_register_notifier(struct notifier_block *nb) |
| 254 | { |
| 255 | return blocking_notifier_chain_register(&crypto_chain, nb); |
| 256 | } |
| 257 | EXPORT_SYMBOL_GPL(crypto_register_notifier); |
| 258 | |
| 259 | int crypto_unregister_notifier(struct notifier_block *nb) |
| 260 | { |
| 261 | return blocking_notifier_chain_unregister(&crypto_chain, nb); |
| 262 | } |
| 263 | EXPORT_SYMBOL_GPL(crypto_unregister_notifier); |
| 264 | |
Herbert Xu | cce9e06 | 2006-08-21 21:08:13 +1000 | [diff] [blame] | 265 | static int __init crypto_algapi_init(void) |
| 266 | { |
| 267 | crypto_init_proc(); |
| 268 | return 0; |
| 269 | } |
| 270 | |
| 271 | static void __exit crypto_algapi_exit(void) |
| 272 | { |
| 273 | crypto_exit_proc(); |
| 274 | } |
| 275 | |
| 276 | module_init(crypto_algapi_init); |
| 277 | module_exit(crypto_algapi_exit); |
| 278 | |
| 279 | MODULE_LICENSE("GPL"); |
| 280 | MODULE_DESCRIPTION("Cryptographic algorithms API"); |