1/*
2 * Cryptographic API.
3 *
4 * MD5 Message Digest Algorithm (RFC1321).
5 *
6 * Adapted for OCTEON by Aaro Koskinen <aaro.koskinen@iki.fi>.
7 *
8 * Based on crypto/md5.c, which is:
9 *
10 * Derived from cryptoapi implementation, originally based on the
11 * public domain implementation written by Colin Plumb in 1993.
12 *
13 * Copyright (c) Cryptoapi developers.
14 * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
15 *
16 * This program is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by the Free
18 * Software Foundation; either version 2 of the License, or (at your option)
19 * any later version.
20 */
21
22#include <asm/octeon/octeon.h>
23#include <crypto/internal/hash.h>
24#include <crypto/md5.h>
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/string.h>
28#include <linux/unaligned.h>
29
30#include "octeon-crypto.h"
31
32struct octeon_md5_state {
33 __le32 hash[MD5_HASH_WORDS];
34 u64 byte_count;
35};
36
37/*
38 * We pass everything as 64-bit. OCTEON can handle misaligned data.
39 */
40
41static void octeon_md5_store_hash(struct octeon_md5_state *ctx)
42{
43 u64 *hash = (u64 *)ctx->hash;
44
45 write_octeon_64bit_hash_dword(hash[0], 0);
46 write_octeon_64bit_hash_dword(hash[1], 1);
47}
48
49static void octeon_md5_read_hash(struct octeon_md5_state *ctx)
50{
51 u64 *hash = (u64 *)ctx->hash;
52
53 hash[0] = read_octeon_64bit_hash_dword(0);
54 hash[1] = read_octeon_64bit_hash_dword(1);
55}
56
57static void octeon_md5_transform(const void *_block)
58{
59 const u64 *block = _block;
60
61 write_octeon_64bit_block_dword(block[0], 0);
62 write_octeon_64bit_block_dword(block[1], 1);
63 write_octeon_64bit_block_dword(block[2], 2);
64 write_octeon_64bit_block_dword(block[3], 3);
65 write_octeon_64bit_block_dword(block[4], 4);
66 write_octeon_64bit_block_dword(block[5], 5);
67 write_octeon_64bit_block_dword(block[6], 6);
68 octeon_md5_start(block[7]);
69}
70
71static int octeon_md5_init(struct shash_desc *desc)
72{
73 struct octeon_md5_state *mctx = shash_desc_ctx(desc);
74
75 mctx->hash[0] = cpu_to_le32(MD5_H0);
76 mctx->hash[1] = cpu_to_le32(MD5_H1);
77 mctx->hash[2] = cpu_to_le32(MD5_H2);
78 mctx->hash[3] = cpu_to_le32(MD5_H3);
79 mctx->byte_count = 0;
80
81 return 0;
82}
83
84static int octeon_md5_update(struct shash_desc *desc, const u8 *data,
85 unsigned int len)
86{
87 struct octeon_md5_state *mctx = shash_desc_ctx(desc);
88 struct octeon_cop2_state state;
89 unsigned long flags;
90
91 mctx->byte_count += len;
92 flags = octeon_crypto_enable(state: &state);
93 octeon_md5_store_hash(ctx: mctx);
94
95 do {
96 octeon_md5_transform(block: data);
97 data += MD5_HMAC_BLOCK_SIZE;
98 len -= MD5_HMAC_BLOCK_SIZE;
99 } while (len >= MD5_HMAC_BLOCK_SIZE);
100
101 octeon_md5_read_hash(ctx: mctx);
102 octeon_crypto_disable(state: &state, flags);
103 mctx->byte_count -= len;
104 return len;
105}
106
107static int octeon_md5_finup(struct shash_desc *desc, const u8 *src,
108 unsigned int offset, u8 *out)
109{
110 struct octeon_md5_state *mctx = shash_desc_ctx(desc);
111 int padding = 56 - (offset + 1);
112 struct octeon_cop2_state state;
113 u32 block[MD5_BLOCK_WORDS];
114 unsigned long flags;
115 char *p;
116
117 p = memcpy(block, src, offset);
118 p += offset;
119 *p++ = 0x80;
120
121 flags = octeon_crypto_enable(state: &state);
122 octeon_md5_store_hash(ctx: mctx);
123
124 if (padding < 0) {
125 memset(p, 0x00, padding + sizeof(u64));
126 octeon_md5_transform(block: block);
127 p = (char *)block;
128 padding = 56;
129 }
130
131 memset(p, 0, padding);
132 mctx->byte_count += offset;
133 block[14] = mctx->byte_count << 3;
134 block[15] = mctx->byte_count >> 29;
135 cpu_to_le32_array(buf: block + 14, words: 2);
136 octeon_md5_transform(block: block);
137
138 octeon_md5_read_hash(ctx: mctx);
139 octeon_crypto_disable(state: &state, flags);
140
141 memzero_explicit(s: block, count: sizeof(block));
142 memcpy(out, mctx->hash, sizeof(mctx->hash));
143
144 return 0;
145}
146
147static int octeon_md5_export(struct shash_desc *desc, void *out)
148{
149 struct octeon_md5_state *ctx = shash_desc_ctx(desc);
150 union {
151 u8 *u8;
152 u32 *u32;
153 u64 *u64;
154 } p = { .u8 = out };
155 int i;
156
157 for (i = 0; i < MD5_HASH_WORDS; i++)
158 put_unaligned(le32_to_cpu(ctx->hash[i]), p.u32++);
159 put_unaligned(ctx->byte_count, p.u64);
160 return 0;
161}
162
163static int octeon_md5_import(struct shash_desc *desc, const void *in)
164{
165 struct octeon_md5_state *ctx = shash_desc_ctx(desc);
166 union {
167 const u8 *u8;
168 const u32 *u32;
169 const u64 *u64;
170 } p = { .u8 = in };
171 int i;
172
173 for (i = 0; i < MD5_HASH_WORDS; i++)
174 ctx->hash[i] = cpu_to_le32(get_unaligned(p.u32++));
175 ctx->byte_count = get_unaligned(p.u64);
176 return 0;
177}
178
179static struct shash_alg alg = {
180 .digestsize = MD5_DIGEST_SIZE,
181 .init = octeon_md5_init,
182 .update = octeon_md5_update,
183 .finup = octeon_md5_finup,
184 .export = octeon_md5_export,
185 .import = octeon_md5_import,
186 .statesize = MD5_STATE_SIZE,
187 .descsize = sizeof(struct octeon_md5_state),
188 .base = {
189 .cra_name = "md5",
190 .cra_driver_name= "octeon-md5",
191 .cra_priority = OCTEON_CR_OPCODE_PRIORITY,
192 .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
193 .cra_blocksize = MD5_HMAC_BLOCK_SIZE,
194 .cra_module = THIS_MODULE,
195 }
196};
197
198static int __init md5_mod_init(void)
199{
200 if (!octeon_has_crypto())
201 return -ENOTSUPP;
202 return crypto_register_shash(alg: &alg);
203}
204
205static void __exit md5_mod_fini(void)
206{
207 crypto_unregister_shash(alg: &alg);
208}
209
210module_init(md5_mod_init);
211module_exit(md5_mod_fini);
212
213MODULE_LICENSE("GPL");
214MODULE_DESCRIPTION("MD5 Message Digest Algorithm (OCTEON)");
215MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
216

source code of linux/arch/mips/cavium-octeon/crypto/octeon-md5.c