blob: d3e827a141f11cf458d848e0e54c042895cc169e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 */
Herbert Xu055bcee2006-08-19 22:24:23 +100014
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/mm.h>
16#include <linux/errno.h>
Herbert Xufb469842006-12-10 10:45:28 +110017#include <linux/hardirq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/highmem.h>
Herbert Xufb469842006-12-10 10:45:28 +110019#include <linux/kernel.h>
Herbert Xu055bcee2006-08-19 22:24:23 +100020#include <linux/module.h>
21#include <linux/scatterlist.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Herbert Xu055bcee2006-08-19 22:24:23 +100023#include "internal.h"
Herbert Xu055bcee2006-08-19 22:24:23 +100024
Herbert Xu055bcee2006-08-19 22:24:23 +100025static int init(struct hash_desc *desc)
26{
27 struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
28
Herbert Xu6c2bb982006-05-16 22:09:29 +100029 tfm->__crt_alg->cra_digest.dia_init(tfm);
Herbert Xu055bcee2006-08-19 22:24:23 +100030 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070031}
32
Herbert Xufb469842006-12-10 10:45:28 +110033static int update2(struct hash_desc *desc,
34 struct scatterlist *sg, unsigned int nbytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -070035{
Herbert Xu055bcee2006-08-19 22:24:23 +100036 struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
Atsushi Nemotoe1147d82006-04-10 08:42:35 +100037 unsigned int alignmask = crypto_tfm_alg_alignmask(tfm);
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Herbert Xu055bcee2006-08-19 22:24:23 +100039 if (!nbytes)
40 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
Herbert Xu055bcee2006-08-19 22:24:23 +100042 for (;;) {
Jens Axboe78c2f0b2007-10-22 19:40:16 +020043 struct page *pg = sg_page(sg);
Herbert Xu055bcee2006-08-19 22:24:23 +100044 unsigned int offset = sg->offset;
45 unsigned int l = sg->length;
46
47 if (unlikely(l > nbytes))
48 l = nbytes;
49 nbytes -= l;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
51 do {
52 unsigned int bytes_from_page = min(l, ((unsigned int)
53 (PAGE_SIZE)) -
54 offset);
Atsushi Nemotoe1147d82006-04-10 08:42:35 +100055 char *src = crypto_kmap(pg, 0);
56 char *p = src + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
Atsushi Nemotoe1147d82006-04-10 08:42:35 +100058 if (unlikely(offset & alignmask)) {
59 unsigned int bytes =
60 alignmask + 1 - (offset & alignmask);
61 bytes = min(bytes, bytes_from_page);
Herbert Xu6c2bb982006-05-16 22:09:29 +100062 tfm->__crt_alg->cra_digest.dia_update(tfm, p,
63 bytes);
Atsushi Nemotoe1147d82006-04-10 08:42:35 +100064 p += bytes;
65 bytes_from_page -= bytes;
66 l -= bytes;
67 }
Herbert Xu6c2bb982006-05-16 22:09:29 +100068 tfm->__crt_alg->cra_digest.dia_update(tfm, p,
69 bytes_from_page);
Atsushi Nemotoe1147d82006-04-10 08:42:35 +100070 crypto_kunmap(src, 0);
Herbert Xu055bcee2006-08-19 22:24:23 +100071 crypto_yield(desc->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 offset = 0;
73 pg++;
74 l -= bytes_from_page;
75 } while (l > 0);
Herbert Xu055bcee2006-08-19 22:24:23 +100076
77 if (!nbytes)
78 break;
Herbert Xu468577a2007-11-15 12:08:45 +080079 sg = sg_next(sg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 }
Herbert Xu055bcee2006-08-19 22:24:23 +100081
82 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083}
84
Herbert Xufb469842006-12-10 10:45:28 +110085static int update(struct hash_desc *desc,
86 struct scatterlist *sg, unsigned int nbytes)
87{
88 if (WARN_ON_ONCE(in_irq()))
89 return -EDEADLK;
90 return update2(desc, sg, nbytes);
91}
92
Herbert Xu055bcee2006-08-19 22:24:23 +100093static int final(struct hash_desc *desc, u8 *out)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094{
Herbert Xu055bcee2006-08-19 22:24:23 +100095 struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
Atsushi Nemotoe1147d82006-04-10 08:42:35 +100096 unsigned long alignmask = crypto_tfm_alg_alignmask(tfm);
Herbert Xuee756412006-07-09 14:49:42 +100097 struct digest_alg *digest = &tfm->__crt_alg->cra_digest;
98
Atsushi Nemotoe1147d82006-04-10 08:42:35 +100099 if (unlikely((unsigned long)out & alignmask)) {
Herbert Xuee756412006-07-09 14:49:42 +1000100 unsigned long align = alignmask + 1;
101 unsigned long addr = (unsigned long)crypto_tfm_ctx(tfm);
102 u8 *dst = (u8 *)ALIGN(addr, align) +
103 ALIGN(tfm->__crt_alg->cra_ctxsize, align);
104
105 digest->dia_final(tfm, dst);
106 memcpy(out, dst, digest->dia_digestsize);
Atsushi Nemotoe1147d82006-04-10 08:42:35 +1000107 } else
Herbert Xuee756412006-07-09 14:49:42 +1000108 digest->dia_final(tfm, out);
Herbert Xu055bcee2006-08-19 22:24:23 +1000109
110 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111}
112
Herbert Xu055bcee2006-08-19 22:24:23 +1000113static int nosetkey(struct crypto_hash *tfm, const u8 *key, unsigned int keylen)
Herbert Xu560c06a2006-08-13 14:16:39 +1000114{
Herbert Xu055bcee2006-08-19 22:24:23 +1000115 crypto_hash_clear_flags(tfm, CRYPTO_TFM_RES_MASK);
Herbert Xu560c06a2006-08-13 14:16:39 +1000116 return -ENOSYS;
117}
118
Herbert Xu055bcee2006-08-19 22:24:23 +1000119static int setkey(struct crypto_hash *hash, const u8 *key, unsigned int keylen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120{
Herbert Xu055bcee2006-08-19 22:24:23 +1000121 struct crypto_tfm *tfm = crypto_hash_tfm(hash);
122
123 crypto_hash_clear_flags(hash, CRYPTO_TFM_RES_MASK);
Herbert Xu560c06a2006-08-13 14:16:39 +1000124 return tfm->__crt_alg->cra_digest.dia_setkey(tfm, key, keylen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125}
126
Herbert Xu055bcee2006-08-19 22:24:23 +1000127static int digest(struct hash_desc *desc,
128 struct scatterlist *sg, unsigned int nbytes, u8 *out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
Herbert Xufb469842006-12-10 10:45:28 +1100130 if (WARN_ON_ONCE(in_irq()))
131 return -EDEADLK;
132
Herbert Xu055bcee2006-08-19 22:24:23 +1000133 init(desc);
Herbert Xufb469842006-12-10 10:45:28 +1100134 update2(desc, sg, nbytes);
Herbert Xu055bcee2006-08-19 22:24:23 +1000135 return final(desc, out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138int crypto_init_digest_ops(struct crypto_tfm *tfm)
139{
Herbert Xu055bcee2006-08-19 22:24:23 +1000140 struct hash_tfm *ops = &tfm->crt_hash;
Herbert Xu560c06a2006-08-13 14:16:39 +1000141 struct digest_alg *dalg = &tfm->__crt_alg->cra_digest;
Herbert Xu055bcee2006-08-19 22:24:23 +1000142
143 if (dalg->dia_digestsize > crypto_tfm_alg_blocksize(tfm))
144 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
Herbert Xu055bcee2006-08-19 22:24:23 +1000146 ops->init = init;
147 ops->update = update;
148 ops->final = final;
149 ops->digest = digest;
150 ops->setkey = dalg->dia_setkey ? setkey : nosetkey;
151 ops->digestsize = dalg->dia_digestsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Herbert Xu84251652006-08-20 15:25:22 +1000153 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154}
155
156void crypto_exit_digest_ops(struct crypto_tfm *tfm)
157{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158}