18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Cryptographic API.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * MD5 Message Digest Algorithm (RFC1321).
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Adapted for OCTEON by Aaro Koskinen <aaro.koskinen@iki.fi>.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Based on crypto/md5.c, which is:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Derived from cryptoapi implementation, originally based on the
118c2ecf20Sopenharmony_ci * public domain implementation written by Colin Plumb in 1993.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Copyright (c) Cryptoapi developers.
148c2ecf20Sopenharmony_ci * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
178c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the Free
188c2ecf20Sopenharmony_ci * Software Foundation; either version 2 of the License, or (at your option)
198c2ecf20Sopenharmony_ci * any later version.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <crypto/md5.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/types.h>
258c2ecf20Sopenharmony_ci#include <linux/module.h>
268c2ecf20Sopenharmony_ci#include <linux/string.h>
278c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
288c2ecf20Sopenharmony_ci#include <asm/octeon/octeon.h>
298c2ecf20Sopenharmony_ci#include <crypto/internal/hash.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "octeon-crypto.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * We pass everything as 64-bit. OCTEON can handle misaligned data.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void octeon_md5_store_hash(struct md5_state *ctx)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	u64 *hash = (u64 *)ctx->hash;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	write_octeon_64bit_hash_dword(hash[0], 0);
428c2ecf20Sopenharmony_ci	write_octeon_64bit_hash_dword(hash[1], 1);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void octeon_md5_read_hash(struct md5_state *ctx)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	u64 *hash = (u64 *)ctx->hash;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	hash[0] = read_octeon_64bit_hash_dword(0);
508c2ecf20Sopenharmony_ci	hash[1] = read_octeon_64bit_hash_dword(1);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void octeon_md5_transform(const void *_block)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	const u64 *block = _block;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	write_octeon_64bit_block_dword(block[0], 0);
588c2ecf20Sopenharmony_ci	write_octeon_64bit_block_dword(block[1], 1);
598c2ecf20Sopenharmony_ci	write_octeon_64bit_block_dword(block[2], 2);
608c2ecf20Sopenharmony_ci	write_octeon_64bit_block_dword(block[3], 3);
618c2ecf20Sopenharmony_ci	write_octeon_64bit_block_dword(block[4], 4);
628c2ecf20Sopenharmony_ci	write_octeon_64bit_block_dword(block[5], 5);
638c2ecf20Sopenharmony_ci	write_octeon_64bit_block_dword(block[6], 6);
648c2ecf20Sopenharmony_ci	octeon_md5_start(block[7]);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int octeon_md5_init(struct shash_desc *desc)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct md5_state *mctx = shash_desc_ctx(desc);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	mctx->hash[0] = cpu_to_le32(MD5_H0);
728c2ecf20Sopenharmony_ci	mctx->hash[1] = cpu_to_le32(MD5_H1);
738c2ecf20Sopenharmony_ci	mctx->hash[2] = cpu_to_le32(MD5_H2);
748c2ecf20Sopenharmony_ci	mctx->hash[3] = cpu_to_le32(MD5_H3);
758c2ecf20Sopenharmony_ci	mctx->byte_count = 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int octeon_md5_update(struct shash_desc *desc, const u8 *data,
818c2ecf20Sopenharmony_ci			     unsigned int len)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct md5_state *mctx = shash_desc_ctx(desc);
848c2ecf20Sopenharmony_ci	const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
858c2ecf20Sopenharmony_ci	struct octeon_cop2_state state;
868c2ecf20Sopenharmony_ci	unsigned long flags;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	mctx->byte_count += len;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (avail > len) {
918c2ecf20Sopenharmony_ci		memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
928c2ecf20Sopenharmony_ci		       data, len);
938c2ecf20Sopenharmony_ci		return 0;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), data,
978c2ecf20Sopenharmony_ci	       avail);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	flags = octeon_crypto_enable(&state);
1008c2ecf20Sopenharmony_ci	octeon_md5_store_hash(mctx);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	octeon_md5_transform(mctx->block);
1038c2ecf20Sopenharmony_ci	data += avail;
1048c2ecf20Sopenharmony_ci	len -= avail;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	while (len >= sizeof(mctx->block)) {
1078c2ecf20Sopenharmony_ci		octeon_md5_transform(data);
1088c2ecf20Sopenharmony_ci		data += sizeof(mctx->block);
1098c2ecf20Sopenharmony_ci		len -= sizeof(mctx->block);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	octeon_md5_read_hash(mctx);
1138c2ecf20Sopenharmony_ci	octeon_crypto_disable(&state, flags);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	memcpy(mctx->block, data, len);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int octeon_md5_final(struct shash_desc *desc, u8 *out)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct md5_state *mctx = shash_desc_ctx(desc);
1238c2ecf20Sopenharmony_ci	const unsigned int offset = mctx->byte_count & 0x3f;
1248c2ecf20Sopenharmony_ci	char *p = (char *)mctx->block + offset;
1258c2ecf20Sopenharmony_ci	int padding = 56 - (offset + 1);
1268c2ecf20Sopenharmony_ci	struct octeon_cop2_state state;
1278c2ecf20Sopenharmony_ci	unsigned long flags;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	*p++ = 0x80;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	flags = octeon_crypto_enable(&state);
1328c2ecf20Sopenharmony_ci	octeon_md5_store_hash(mctx);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (padding < 0) {
1358c2ecf20Sopenharmony_ci		memset(p, 0x00, padding + sizeof(u64));
1368c2ecf20Sopenharmony_ci		octeon_md5_transform(mctx->block);
1378c2ecf20Sopenharmony_ci		p = (char *)mctx->block;
1388c2ecf20Sopenharmony_ci		padding = 56;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	memset(p, 0, padding);
1428c2ecf20Sopenharmony_ci	mctx->block[14] = cpu_to_le32(mctx->byte_count << 3);
1438c2ecf20Sopenharmony_ci	mctx->block[15] = cpu_to_le32(mctx->byte_count >> 29);
1448c2ecf20Sopenharmony_ci	octeon_md5_transform(mctx->block);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	octeon_md5_read_hash(mctx);
1478c2ecf20Sopenharmony_ci	octeon_crypto_disable(&state, flags);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	memcpy(out, mctx->hash, sizeof(mctx->hash));
1508c2ecf20Sopenharmony_ci	memset(mctx, 0, sizeof(*mctx));
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int octeon_md5_export(struct shash_desc *desc, void *out)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct md5_state *ctx = shash_desc_ctx(desc);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	memcpy(out, ctx, sizeof(*ctx));
1608c2ecf20Sopenharmony_ci	return 0;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int octeon_md5_import(struct shash_desc *desc, const void *in)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct md5_state *ctx = shash_desc_ctx(desc);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	memcpy(ctx, in, sizeof(*ctx));
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic struct shash_alg alg = {
1728c2ecf20Sopenharmony_ci	.digestsize	=	MD5_DIGEST_SIZE,
1738c2ecf20Sopenharmony_ci	.init		=	octeon_md5_init,
1748c2ecf20Sopenharmony_ci	.update		=	octeon_md5_update,
1758c2ecf20Sopenharmony_ci	.final		=	octeon_md5_final,
1768c2ecf20Sopenharmony_ci	.export		=	octeon_md5_export,
1778c2ecf20Sopenharmony_ci	.import		=	octeon_md5_import,
1788c2ecf20Sopenharmony_ci	.descsize	=	sizeof(struct md5_state),
1798c2ecf20Sopenharmony_ci	.statesize	=	sizeof(struct md5_state),
1808c2ecf20Sopenharmony_ci	.base		=	{
1818c2ecf20Sopenharmony_ci		.cra_name	=	"md5",
1828c2ecf20Sopenharmony_ci		.cra_driver_name=	"octeon-md5",
1838c2ecf20Sopenharmony_ci		.cra_priority	=	OCTEON_CR_OPCODE_PRIORITY,
1848c2ecf20Sopenharmony_ci		.cra_blocksize	=	MD5_HMAC_BLOCK_SIZE,
1858c2ecf20Sopenharmony_ci		.cra_module	=	THIS_MODULE,
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int __init md5_mod_init(void)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	if (!octeon_has_crypto())
1928c2ecf20Sopenharmony_ci		return -ENOTSUPP;
1938c2ecf20Sopenharmony_ci	return crypto_register_shash(&alg);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic void __exit md5_mod_fini(void)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	crypto_unregister_shash(&alg);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cimodule_init(md5_mod_init);
2028c2ecf20Sopenharmony_cimodule_exit(md5_mod_fini);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MD5 Message Digest Algorithm (OCTEON)");
2068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
207