at v2.6.32-rc7 240 lines 5.8 kB view raw
1/* 2 * Cryptographic API. 3 * 4 * Digest operations. 5 * 6 * Copyright (c) 2002 James Morris <jmorris@intercode.com.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 15#include <crypto/internal/hash.h> 16#include <crypto/scatterwalk.h> 17#include <linux/mm.h> 18#include <linux/errno.h> 19#include <linux/hardirq.h> 20#include <linux/highmem.h> 21#include <linux/kernel.h> 22#include <linux/module.h> 23#include <linux/scatterlist.h> 24 25#include "internal.h" 26 27static int init(struct hash_desc *desc) 28{ 29 struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm); 30 31 tfm->__crt_alg->cra_digest.dia_init(tfm); 32 return 0; 33} 34 35static int update2(struct hash_desc *desc, 36 struct scatterlist *sg, unsigned int nbytes) 37{ 38 struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm); 39 unsigned int alignmask = crypto_tfm_alg_alignmask(tfm); 40 41 if (!nbytes) 42 return 0; 43 44 for (;;) { 45 struct page *pg = sg_page(sg); 46 unsigned int offset = sg->offset; 47 unsigned int l = sg->length; 48 49 if (unlikely(l > nbytes)) 50 l = nbytes; 51 nbytes -= l; 52 53 do { 54 unsigned int bytes_from_page = min(l, ((unsigned int) 55 (PAGE_SIZE)) - 56 offset); 57 char *src = crypto_kmap(pg, 0); 58 char *p = src + offset; 59 60 if (unlikely(offset & alignmask)) { 61 unsigned int bytes = 62 alignmask + 1 - (offset & alignmask); 63 bytes = min(bytes, bytes_from_page); 64 tfm->__crt_alg->cra_digest.dia_update(tfm, p, 65 bytes); 66 p += bytes; 67 bytes_from_page -= bytes; 68 l -= bytes; 69 } 70 tfm->__crt_alg->cra_digest.dia_update(tfm, p, 71 bytes_from_page); 72 crypto_kunmap(src, 0); 73 crypto_yield(desc->flags); 74 offset = 0; 75 pg++; 76 l -= bytes_from_page; 77 } while (l > 0); 78 79 if (!nbytes) 80 break; 81 sg = scatterwalk_sg_next(sg); 82 } 83 84 return 0; 85} 86 87static int update(struct hash_desc *desc, 88 struct scatterlist *sg, unsigned int nbytes) 89{ 90 if (WARN_ON_ONCE(in_irq())) 91 return -EDEADLK; 92 return update2(desc, sg, nbytes); 93} 94 95static int final(struct hash_desc *desc, u8 *out) 96{ 97 struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm); 98 unsigned long alignmask = crypto_tfm_alg_alignmask(tfm); 99 struct digest_alg *digest = &tfm->__crt_alg->cra_digest; 100 101 if (unlikely((unsigned long)out & alignmask)) { 102 unsigned long align = alignmask + 1; 103 unsigned long addr = (unsigned long)crypto_tfm_ctx(tfm); 104 u8 *dst = (u8 *)ALIGN(addr, align) + 105 ALIGN(tfm->__crt_alg->cra_ctxsize, align); 106 107 digest->dia_final(tfm, dst); 108 memcpy(out, dst, digest->dia_digestsize); 109 } else 110 digest->dia_final(tfm, out); 111 112 return 0; 113} 114 115static int nosetkey(struct crypto_hash *tfm, const u8 *key, unsigned int keylen) 116{ 117 crypto_hash_clear_flags(tfm, CRYPTO_TFM_RES_MASK); 118 return -ENOSYS; 119} 120 121static int setkey(struct crypto_hash *hash, const u8 *key, unsigned int keylen) 122{ 123 struct crypto_tfm *tfm = crypto_hash_tfm(hash); 124 125 crypto_hash_clear_flags(hash, CRYPTO_TFM_RES_MASK); 126 return tfm->__crt_alg->cra_digest.dia_setkey(tfm, key, keylen); 127} 128 129static int digest(struct hash_desc *desc, 130 struct scatterlist *sg, unsigned int nbytes, u8 *out) 131{ 132 if (WARN_ON_ONCE(in_irq())) 133 return -EDEADLK; 134 135 init(desc); 136 update2(desc, sg, nbytes); 137 return final(desc, out); 138} 139 140int crypto_init_digest_ops(struct crypto_tfm *tfm) 141{ 142 struct hash_tfm *ops = &tfm->crt_hash; 143 struct digest_alg *dalg = &tfm->__crt_alg->cra_digest; 144 145 if (dalg->dia_digestsize > PAGE_SIZE / 8) 146 return -EINVAL; 147 148 ops->init = init; 149 ops->update = update; 150 ops->final = final; 151 ops->digest = digest; 152 ops->setkey = dalg->dia_setkey ? setkey : nosetkey; 153 ops->digestsize = dalg->dia_digestsize; 154 155 return 0; 156} 157 158void crypto_exit_digest_ops(struct crypto_tfm *tfm) 159{ 160} 161 162static int digest_async_nosetkey(struct crypto_ahash *tfm_async, const u8 *key, 163 unsigned int keylen) 164{ 165 crypto_ahash_clear_flags(tfm_async, CRYPTO_TFM_RES_MASK); 166 return -ENOSYS; 167} 168 169static int digest_async_setkey(struct crypto_ahash *tfm_async, const u8 *key, 170 unsigned int keylen) 171{ 172 struct crypto_tfm *tfm = crypto_ahash_tfm(tfm_async); 173 struct digest_alg *dalg = &tfm->__crt_alg->cra_digest; 174 175 crypto_ahash_clear_flags(tfm_async, CRYPTO_TFM_RES_MASK); 176 return dalg->dia_setkey(tfm, key, keylen); 177} 178 179static int digest_async_init(struct ahash_request *req) 180{ 181 struct crypto_tfm *tfm = req->base.tfm; 182 struct digest_alg *dalg = &tfm->__crt_alg->cra_digest; 183 184 dalg->dia_init(tfm); 185 return 0; 186} 187 188static int digest_async_update(struct ahash_request *req) 189{ 190 struct crypto_tfm *tfm = req->base.tfm; 191 struct hash_desc desc = { 192 .tfm = __crypto_hash_cast(tfm), 193 .flags = req->base.flags, 194 }; 195 196 update(&desc, req->src, req->nbytes); 197 return 0; 198} 199 200static int digest_async_final(struct ahash_request *req) 201{ 202 struct crypto_tfm *tfm = req->base.tfm; 203 struct hash_desc desc = { 204 .tfm = __crypto_hash_cast(tfm), 205 .flags = req->base.flags, 206 }; 207 208 final(&desc, req->result); 209 return 0; 210} 211 212static int digest_async_digest(struct ahash_request *req) 213{ 214 struct crypto_tfm *tfm = req->base.tfm; 215 struct hash_desc desc = { 216 .tfm = __crypto_hash_cast(tfm), 217 .flags = req->base.flags, 218 }; 219 220 return digest(&desc, req->src, req->nbytes, req->result); 221} 222 223int crypto_init_digest_ops_async(struct crypto_tfm *tfm) 224{ 225 struct ahash_tfm *crt = &tfm->crt_ahash; 226 struct digest_alg *dalg = &tfm->__crt_alg->cra_digest; 227 228 if (dalg->dia_digestsize > PAGE_SIZE / 8) 229 return -EINVAL; 230 231 crt->init = digest_async_init; 232 crt->update = digest_async_update; 233 crt->final = digest_async_final; 234 crt->digest = digest_async_digest; 235 crt->setkey = dalg->dia_setkey ? digest_async_setkey : 236 digest_async_nosetkey; 237 crt->digestsize = dalg->dia_digestsize; 238 239 return 0; 240}