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