summaryrefslogtreecommitdiff
path: root/drivers/mxc/security/sahara2/fsl_shw_hash.c
blob: 7d4db53480f6b46675fb5c9931e3e82109dadf9d (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
 * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file fsl_shw_hash.c
 *
 * This file implements Cryptographic Hashing functions of the FSL SHW API
 * for Sahara.  This does not include HMAC.
 */

#include "sahara.h"
#include "sf_util.h"

#ifdef LINUX_KERNEL
EXPORT_SYMBOL(fsl_shw_hash);
#endif

/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
/*!
 * Hash a stream of data with a cryptographic hash algorithm.
 *
 * The flags in the @a hash_ctx control the operation of this function.
 *
 * Hashing functions work on 64 octets of message at a time.  Therefore, when
 * any partial hashing of a long message is performed, the message @a length of
 * each segment must be a multiple of 64.  When ready to
 * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
 *
 * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
 * one-shot complete hash, including padding, will be performed.  The @a length
 * may be any value.
 *
 * The first octets of a data stream can be hashed by setting the
 * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags.  The @a length must be
 * a multiple of 64.
 *
 * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
 * #FSL_HASH_FLAGS_SAVE.  The two in combination will allow a (multiple-of-64
 * octets) 'middle sequence' of the data stream to be hashed with the
 * beginning.  The @a length must again be a multiple of 64.
 *
 * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
 * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
 * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
 * stream.  The @a length may be any value.
 *
 * If the user program wants to do the padding for the hash, it can leave off
 * the #FSL_HASH_FLAGS_FINALIZE flag.  The @a length must then be a multiple of
 * 64 octets.
 *
 * @param      user_ctx  A user context from #fsl_shw_register_user().
 * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
 * @param      msg       Pointer to the data to be hashed.
 * @param      length    Length, in octets, of the @a msg.
 * @param[out] result    If not null, pointer to where to store the hash
 *                       digest.
 * @param      result_len Number of octets to store in @a result.
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
			      fsl_shw_hco_t * hash_ctx,
			      const uint8_t * msg,
			      uint32_t length,
			      uint8_t * result, uint32_t result_len)
{
	SAH_SF_DCLS;
	unsigned ctx_flags = (hash_ctx->flags & (FSL_HASH_FLAGS_INIT
						 | FSL_HASH_FLAGS_LOAD
						 | FSL_HASH_FLAGS_SAVE
						 | FSL_HASH_FLAGS_FINALIZE));

	SAH_SF_USER_CHECK();

	/* Reset expectations if user gets overly zealous. */
	if (result_len > hash_ctx->digest_length) {
		result_len = hash_ctx->digest_length;
	}

	/* Validate hash ctx flags.
	 * Need INIT or LOAD but not both.
	 * Need SAVE or digest ptr (both is ok).
	 */
	if (((ctx_flags & (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD))
	     == (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD))
	    || ((ctx_flags & (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD)) == 0)
	    || (!(ctx_flags & FSL_HASH_FLAGS_SAVE) && (result == NULL))) {
		ret = FSL_RETURN_BAD_FLAG_S;
		goto out;
	}

	if (ctx_flags & FSL_HASH_FLAGS_INIT) {
		sah_Oct_Str out_ptr;
		unsigned out_len;

		/* Create desc to perform the initial hashing operation */
		/* Desc. #8 w/INIT and algorithm */
		header = SAH_HDR_MDHA_SET_MODE_HASH
		    ^ sah_insert_mdha_init
		    ^ sah_insert_mdha_algorithm[hash_ctx->algorithm];

		/* If user wants one-shot, set padding operation. */
		if (ctx_flags & FSL_HASH_FLAGS_FINALIZE) {
			header ^= sah_insert_mdha_pdata;
		}

		/* Determine where Digest will go - hash_ctx or result */
		if (ctx_flags & FSL_HASH_FLAGS_SAVE) {
			out_ptr = (sah_Oct_Str) hash_ctx->context;
			out_len = hash_ctx->context_register_length;
		} else {
			out_ptr = result;
			out_len = (result_len > hash_ctx->digest_length)
			    ? hash_ctx->digest_length : result_len;
		}

		DESC_IN_OUT(header, length, (sah_Oct_Str) msg, out_len,
			    out_ptr);
	} else {		/* not doing hash INIT */
		void *out_ptr;
		unsigned out_len;

		/*
		 * Build two descriptors -- one to load in context/set mode, the
		 * other to compute & retrieve hash/context value.
		 *
		 * First up - Desc. #6 to load context.
		 */
		/* Desc. #8 w/algorithm */
		header = SAH_HDR_MDHA_SET_MODE_MD_KEY
		    ^ sah_insert_mdha_algorithm[hash_ctx->algorithm];

		if (ctx_flags & FSL_HASH_FLAGS_FINALIZE) {
			header ^= sah_insert_mdha_pdata;
		}

		/* Message Digest (in) */
		DESC_IN_IN(header,
			   hash_ctx->context_register_length,
			   (sah_Oct_Str) hash_ctx->context, 0, NULL);

		if (ctx_flags & FSL_HASH_FLAGS_SAVE) {
			out_ptr = hash_ctx->context;
			out_len = hash_ctx->context_register_length;
		} else {
			out_ptr = result;
			out_len = result_len;
		}

		/* Second -- run data through and retrieve ctx regs */
		/* Desc. #10 - no mode register with this. */
		header = SAH_HDR_MDHA_HASH;
		DESC_IN_OUT(header, length, (sah_Oct_Str) msg, out_len,
			    out_ptr);
	}			/* else not INIT */

	/* Now that execution is rejoined, we can append another descriptor
	   to extract the digest/context a second time, into the result. */
	if ((ctx_flags & FSL_HASH_FLAGS_SAVE)
	    && (result != NULL) && (result_len != 0)) {

		header = SAH_HDR_MDHA_STORE_DIGEST;

		/* Message Digest (out) */
		DESC_IN_OUT(header, 0, NULL,
			    (result_len > hash_ctx->digest_length)
			    ? hash_ctx->digest_length : result_len, result);
	}

	SAH_SF_EXECUTE();

      out:
	SAH_SF_DESC_CLEAN();

	return ret;
}				/* fsl_shw_hash() */