summaryrefslogtreecommitdiffstats
path: root/arch/s390/crypto/sha_common.c
blob: 961d7d522af157cda3ee489d338eef516f6d5979 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// SPDX-License-Identifier: GPL-2.0+
/*
 * Cryptographic API.
 *
 * s390 generic implementation of the SHA Secure Hash Algorithms.
 *
 * Copyright IBM Corp. 2007
 * Author(s): Jan Glauber (jang@de.ibm.com)
 */

#include <crypto/internal/hash.h>
#include <linux/module.h>
#include <asm/cpacf.h>
#include "sha.h"

int s390_sha_update(struct shash_desc *desc, const u8 *data, unsigned int len)
{
	struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
	unsigned int bsize = crypto_shash_blocksize(desc->tfm);
	unsigned int index, n;
	int fc;

	/* how much is already in the buffer? */
	index = ctx->count % bsize;
	ctx->count += len;

	if ((index + len) < bsize)
		goto store;

	fc = ctx->func;
	if (ctx->first_message_part)
		fc |= test_facility(86) ? CPACF_KIMD_NIP : 0;

	/* process one stored block */
	if (index) {
		memcpy(ctx->buf + index, data, bsize - index);
		cpacf_kimd(fc, ctx->state, ctx->buf, bsize);
		ctx->first_message_part = 0;
		fc &= ~CPACF_KIMD_NIP;
		data += bsize - index;
		len -= bsize - index;
		index = 0;
	}

	/* process as many blocks as possible */
	if (len >= bsize) {
		n = (len / bsize) * bsize;
		cpacf_kimd(fc, ctx->state, data, n);
		ctx->first_message_part = 0;
		data += n;
		len -= n;
	}
store:
	if (len)
		memcpy(ctx->buf + index , data, len);

	return 0;
}
EXPORT_SYMBOL_GPL(s390_sha_update);

static int s390_crypto_shash_parmsize(int func)
{
	switch (func) {
	case CPACF_KLMD_SHA_1:
		return 20;
	case CPACF_KLMD_SHA_256:
		return 32;
	case CPACF_KLMD_SHA_512:
		return 64;
	case CPACF_KLMD_SHA3_224:
	case CPACF_KLMD_SHA3_256:
	case CPACF_KLMD_SHA3_384:
	case CPACF_KLMD_SHA3_512:
		return 200;
	default:
		return -EINVAL;
	}
}

int s390_sha_final(struct shash_desc *desc, u8 *out)
{
	struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
	unsigned int bsize = crypto_shash_blocksize(desc->tfm);
	u64 bits;
	unsigned int n;
	int mbl_offset, fc;

	n = ctx->count % bsize;
	bits = ctx->count * 8;
	mbl_offset = s390_crypto_shash_parmsize(ctx->func);
	if (mbl_offset < 0)
		return -EINVAL;

	mbl_offset = mbl_offset / sizeof(u32);

	/* set total msg bit length (mbl) in CPACF parmblock */
	switch (ctx->func) {
	case CPACF_KLMD_SHA_1:
	case CPACF_KLMD_SHA_256:
		memcpy(ctx->state + mbl_offset, &bits, sizeof(bits));
		break;
	case CPACF_KLMD_SHA_512:
		/*
		 * the SHA512 parmblock has a 128-bit mbl field, clear
		 * high-order u64 field, copy bits to low-order u64 field
		 */
		memset(ctx->state + mbl_offset, 0x00, sizeof(bits));
		mbl_offset += sizeof(u64) / sizeof(u32);
		memcpy(ctx->state + mbl_offset, &bits, sizeof(bits));
		break;
	case CPACF_KLMD_SHA3_224:
	case CPACF_KLMD_SHA3_256:
	case CPACF_KLMD_SHA3_384:
	case CPACF_KLMD_SHA3_512:
		break;
	default:
		return -EINVAL;
	}

	fc = ctx->func;
	fc |= test_facility(86) ? CPACF_KLMD_DUFOP : 0;
	if (ctx->first_message_part)
		fc |= CPACF_KLMD_NIP;
	cpacf_klmd(fc, ctx->state, ctx->buf, n);

	/* copy digest to out */
	memcpy(out, ctx->state, crypto_shash_digestsize(desc->tfm));
	/* wipe context */
	memset(ctx, 0, sizeof *ctx);

	return 0;
}
EXPORT_SYMBOL_GPL(s390_sha_final);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("s390 SHA cipher common functions");