xref: /optee_os/core/tee/tee_cryp_hkdf.c (revision 9403c583381528e7fb391e3769644cc9653cfbb6)
1 /*
2  * Copyright (c) 2014, Linaro Limited
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <tee/tee_cryp_hkdf.h>
29 #include <tee/tee_cryp_provider.h>
30 #include <tee/tee_cryp_utl.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <utee_defines.h>
34 
35 
36 static const uint8_t zero_salt[TEE_MAX_HASH_SIZE];
37 
38 static TEE_Result hkdf_extract(uint32_t hash_id, const uint8_t *ikm,
39 			       size_t ikm_len, const uint8_t *salt,
40 			       size_t salt_len, uint8_t *prk, size_t *prk_len)
41 {
42 	TEE_Result res;
43 	size_t ctx_size;
44 	void *ctx = NULL;
45 	uint32_t hash_algo = TEE_ALG_HASH_ALGO(hash_id);
46 	uint32_t hmac_algo = (TEE_OPERATION_MAC << 28) | hash_id;
47 	const struct mac_ops *m = &crypto_ops.mac;
48 
49 	if (!m->get_ctx_size || !m->init || !m->update) {
50 		res = TEE_ERROR_NOT_IMPLEMENTED;
51 		goto out;
52 	}
53 
54 	if (!salt || !salt_len) {
55 		/*
56 		 * RFC 5869 section 2.2:
57 		 * If not provided, [the salt] is set to a string of HashLen
58 		 * zeros
59 		 */
60 		salt = zero_salt;
61 		res = tee_hash_get_digest_size(hash_algo, &salt_len);
62 		if (res != TEE_SUCCESS)
63 			goto out;
64 	}
65 
66 	res = m->get_ctx_size(hmac_algo, &ctx_size);
67 	if (res != TEE_SUCCESS)
68 		goto out;
69 
70 	ctx = malloc(ctx_size);
71 	if (!ctx) {
72 		res = TEE_ERROR_OUT_OF_MEMORY;
73 		goto out;
74 	}
75 
76 	/*
77 	 * RFC 5869 section 2.1: "Note that in the extract step, 'IKM' is used
78 	 * as the HMAC input, not as the HMAC key."
79 	 * Therefore, salt is the HMAC key in the formula from section 2.2:
80 	 * "PRK = HMAC-Hash(salt, IKM)"
81 	 */
82 	res = m->init(ctx, hmac_algo, salt, salt_len);
83 	if (res != TEE_SUCCESS)
84 		goto out;
85 
86 	res = m->update(ctx, hmac_algo, ikm, ikm_len);
87 	if (res != TEE_SUCCESS)
88 		goto out;
89 
90 	res = m->final(ctx, hmac_algo, prk, *prk_len);
91 	if (res != TEE_SUCCESS)
92 		goto out;
93 
94 	res = tee_hash_get_digest_size(hash_algo, prk_len);
95 out:
96 	free(ctx);
97 	return res;
98 }
99 
100 static TEE_Result hkdf_expand(uint32_t hash_id, const uint8_t *prk,
101 			      size_t prk_len, const uint8_t *info,
102 			      size_t info_len, uint8_t *okm, size_t okm_len)
103 {
104 	uint8_t tn[TEE_MAX_HASH_SIZE];
105 	size_t tn_len, hash_len, i, n, where, ctx_size;
106 	TEE_Result res = TEE_SUCCESS;
107 	void *ctx = NULL;
108 	const struct mac_ops *m = &crypto_ops.mac;
109 	uint32_t hash_algo = TEE_ALG_HASH_ALGO(hash_id);
110 	uint32_t hmac_algo = TEE_ALG_HMAC_ALGO(hash_id);
111 
112 	if (!m->get_ctx_size || !m->init || !m->update || !m->final) {
113 		res = TEE_ERROR_NOT_IMPLEMENTED;
114 		goto out;
115 	}
116 
117 	res = tee_hash_get_digest_size(hash_algo, &hash_len);
118 	if (res != TEE_SUCCESS)
119 		goto out;
120 
121 	if (!okm || prk_len < hash_len) {
122 		res = TEE_ERROR_BAD_STATE;
123 		goto out;
124 	}
125 
126 	if (!info)
127 		info_len = 0;
128 
129 	res = m->get_ctx_size(hmac_algo, &ctx_size);
130 	if (res != TEE_SUCCESS)
131 		goto out;
132 
133 	ctx = malloc(ctx_size);
134 	if (!ctx) {
135 		res = TEE_ERROR_OUT_OF_MEMORY;
136 		goto out;
137 	}
138 
139 	/* N = ceil(L/HashLen) */
140 	n = okm_len / hash_len;
141 	if ((okm_len % hash_len) != 0)
142 		n++;
143 
144 	if (n > 255) {
145 		res = TEE_ERROR_BAD_PARAMETERS;
146 		goto out;
147 	}
148 
149 
150 	/*
151 	 * RFC 5869 section 2.3
152 	 *   T = T(1) | T(2) | T(3) | ... | T(N)
153 	 *   OKM = first L octets of T
154 	 *   T(0) = empty string (zero length)
155 	 *   T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
156 	 *   T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
157 	 *   T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
158 	 *   ...
159 	 */
160 	tn_len = 0;
161 	where = 0;
162 	for (i = 1; i <= n; i++) {
163 		uint8_t c = i;
164 
165 		res = m->init(ctx, hmac_algo, prk, prk_len);
166 		if (res != TEE_SUCCESS)
167 			goto out;
168 		res = m->update(ctx, hmac_algo, tn, tn_len);
169 		if (res != TEE_SUCCESS)
170 			goto out;
171 		res = m->update(ctx, hmac_algo, info, info_len);
172 		if (res != TEE_SUCCESS)
173 			goto out;
174 		res = m->update(ctx, hmac_algo, &c, 1);
175 		if (res != TEE_SUCCESS)
176 			goto out;
177 		res = m->final(ctx, hmac_algo, tn, sizeof(tn));
178 		if (res != TEE_SUCCESS)
179 			goto out;
180 
181 		memcpy(okm + where, tn, (i < n) ? hash_len : (okm_len - where));
182 		where += hash_len;
183 		tn_len = hash_len;
184 	}
185 
186 out:
187 	free(ctx);
188 	return res;
189 }
190 
191 TEE_Result tee_cryp_hkdf(uint32_t hash_id, const uint8_t *ikm, size_t ikm_len,
192 			 const uint8_t *salt, size_t salt_len,
193 			 const uint8_t *info, size_t info_len, uint8_t *okm,
194 			 size_t okm_len)
195 {
196 	TEE_Result res;
197 	uint8_t prk[TEE_MAX_HASH_SIZE];
198 	size_t prk_len = sizeof(prk);
199 
200 	res = hkdf_extract(hash_id, ikm, ikm_len, salt, salt_len, prk,
201 			   &prk_len);
202 	if (res != TEE_SUCCESS)
203 		return res;
204 	res = hkdf_expand(hash_id, prk, prk_len, info, info_len, okm,
205 			  okm_len);
206 
207 	return res;
208 }
209