ssh_exchange_hash.c
Go to the documentation of this file.
1 /**
2  * @file ssh_exchange_hash.c
3  * @brief Exchange hash calculation
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSSH Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SSH_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "ssh/ssh_sign_generate.h"
37 #include "ssh/ssh_sign_verify.h"
38 #include "ssh/ssh_exchange_hash.h"
39 #include "ssh/ssh_misc.h"
40 #include "debug.h"
41 
42 //Check SSH stack configuration
43 #if (SSH_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Initialize exchange hash
48  * @param[in] connection Pointer to the SSH connection
49  * @return Error code
50  **/
51 
53 {
54  error_t error;
55  const char_t *kexAlgo;
56  const HashAlgo *hashAlgo;
57 
58  //Initialize status code
59  error = NO_ERROR;
60 
61  //Get the chosen key exchange algorithm
62  kexAlgo = connection->kexAlgo;
63 
64 #if (SSH_SHA1_SUPPORT == ENABLED)
65  //Key exchange with SHA-1 as hash?
66  if(sshCompareAlgo(kexAlgo, "rsa1024-sha1") ||
67  sshCompareAlgo(kexAlgo, "diffie-hellman-group1-sha1") ||
68  sshCompareAlgo(kexAlgo, "diffie-hellman-group14-sha1") ||
69  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha1"))
70  {
71  //Select the relevant hash algorithm
72  hashAlgo = SHA1_HASH_ALGO;
73  }
74  else
75 #endif
76 #if (SSH_SHA224_SUPPORT == ENABLED)
77  //Key exchange with SHA-224 as hash?
78  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha224@ssh.com"))
79  {
80  //Select the relevant hash algorithm
81  hashAlgo = SHA224_HASH_ALGO;
82  }
83  else
84 #endif
85 #if (SSH_SHA256_SUPPORT == ENABLED)
86  //Key exchange with SHA-256 as hash?
87  if(sshCompareAlgo(kexAlgo, "rsa2048-sha256") ||
88  sshCompareAlgo(kexAlgo, "diffie-hellman-group14-sha256") ||
89  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha256") ||
90  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp256") ||
91  sshCompareAlgo(kexAlgo, "curve25519-sha256") ||
92  sshCompareAlgo(kexAlgo, "curve25519-sha256@libssh.org") ||
93  sshCompareAlgo(kexAlgo, "mlkem512-sha256") ||
94  sshCompareAlgo(kexAlgo, "mlkem768-sha256") ||
95  sshCompareAlgo(kexAlgo, "mlkem768nistp256-sha256") ||
96  sshCompareAlgo(kexAlgo, "mlkem768x25519-sha256"))
97  {
98  //Select the relevant hash algorithm
99  hashAlgo = SHA256_HASH_ALGO;
100  }
101  else
102 #endif
103 #if (SSH_SHA384_SUPPORT == ENABLED)
104  //Key exchange with SHA-384 as hash?
105  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha384@ssh.com") ||
106  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp384") ||
107  sshCompareAlgo(kexAlgo, "mlkem1024-sha384") ||
108  sshCompareAlgo(kexAlgo, "mlkem1024nistp384-sha384"))
109  {
110  //Select the relevant hash algorithm
111  hashAlgo = SHA384_HASH_ALGO;
112  }
113  else
114 #endif
115 #if (SSH_SHA512_SUPPORT == ENABLED)
116  //Key exchange with SHA-512 as hash?
117  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group15-sha512") ||
118  sshCompareAlgo(kexAlgo, "diffie-hellman-group16-sha512") ||
119  sshCompareAlgo(kexAlgo, "diffie-hellman-group17-sha512") ||
120  sshCompareAlgo(kexAlgo, "diffie-hellman-group18-sha512") ||
121  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha512@ssh.com") ||
122  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp521") ||
123  sshCompareAlgo(kexAlgo, "curve448-sha512") ||
124  sshCompareAlgo(kexAlgo, "sntrup761x25519-sha512") ||
125  sshCompareAlgo(kexAlgo, "sntrup761x25519-sha512@openssh.com"))
126  {
127  //Select the relevant hash algorithm
128  hashAlgo = SHA512_HASH_ALGO;
129  }
130  else
131 #endif
132  //Unknown key exchange algorithm?
133  {
134  //Just for sanity
135  hashAlgo = NULL;
136  }
137 
138  //Make sure the hash algorithm is supported
139  if(hashAlgo != NULL)
140  {
141  //The hash algorithm for computing the exchange hash is defined by the
142  //method name (refer to RFC 4253, section 8)
143  connection->hashAlgo = hashAlgo;
144 
145  //Initialize exchange hash computation
146  hashAlgo->init(&connection->hashContext);
147  }
148  else
149  {
150  //Report an error
152  }
153 
154  //Return status code
155  return error;
156 }
157 
158 
159 /**
160  * @brief Update exchange hash calculation
161  * @param[in] connection Pointer to the SSH connection
162  * @param[in] data Pointer to the data block to be hashed
163  * @param[in] length Length of the data block, in bytes
164  * @return Error code
165  **/
166 
168  size_t length)
169 {
170  error_t error;
171  uint8_t temp[4];
172 
173  //Initialize status code
174  error = NO_ERROR;
175 
176  //Valid hash algorithm?
177  if(connection->hashAlgo != NULL)
178  {
179  //Encode the length of the data block as a 32-bit big-endian integer
180  STORE32BE(length, temp);
181 
182  //Digest the length field
183  connection->hashAlgo->update(&connection->hashContext, temp, sizeof(temp));
184  //Digest the contents of the data block
185  connection->hashAlgo->update(&connection->hashContext, data, length);
186  }
187  else
188  {
189  //Report an error
190  error = ERROR_FAILURE;
191  }
192 
193  //Return status code
194  return error;
195 }
196 
197 
198 /**
199  * @brief Update exchange hash calculation (raw data)
200  * @param[in] connection Pointer to the SSH connection
201  * @param[in] data Pointer to the data block to be hashed
202  * @param[in] length Length of the data block, in bytes
203  * @return Error code
204  **/
205 
207  size_t length)
208 {
209  error_t error;
210 
211  //Initialize status code
212  error = NO_ERROR;
213 
214  //Valid hash algorithm?
215  if(connection->hashAlgo != NULL)
216  {
217  //Digest the contents of the data block
218  connection->hashAlgo->update(&connection->hashContext, data, length);
219  }
220  else
221  {
222  //Report an error
223  error = ERROR_FAILURE;
224  }
225 
226  //Return status code
227  return error;
228 }
229 
230 
231 /**
232  * @brief Finalize exchange hash calculation
233  * @param[in] connection Pointer to the SSH connection
234  * @param[out] digest Buffer where to store the resulting hash value
235  * @param[out] digestLen Length of the resulting hash value, in bytes
236  * @return Error code
237  **/
238 
239 error_t sshFinalizeExchangeHash(SshConnection *connection, uint8_t *digest,
240  size_t *digestLen)
241 {
242  error_t error;
243 
244  //Initialize status code
245  error = NO_ERROR;
246 
247  //Valid hash algorithm?
248  if(connection->hashAlgo != NULL)
249  {
250  //Compute H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
251  connection->hashAlgo->final(&connection->hashContext, digest);
252  //Return the length of the resulting digest
253  *digestLen = connection->hashAlgo->digestSize;
254  }
255  else
256  {
257  //Report an error
258  error = ERROR_FAILURE;
259  }
260 
261  //Return status code
262  return error;
263 }
264 
265 
266 /**
267  * @brief Compute the signature on the exchange hash
268  * @param[in] connection Pointer to the SSH connection
269  * @param[out] p Output stream where to write the signature
270  * @param[out] written Total number of bytes that have been written
271  * @return Error code
272  **/
273 
275  size_t *written)
276 {
277  error_t error;
278  SshHostKey *hostKey;
279  SshBinaryString exchangeHash;
280 
281  //Compute H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
282  error = sshFinalizeExchangeHash(connection, connection->h,
283  &connection->hLen);
284 
285  //Check status code
286  if(!error)
287  {
288  //First key exchange?
289  if(!connection->newKeysSent)
290  {
291  //The exchange hash H from the first key exchange is used as the session
292  //identifier, which is a unique identifier for this connection. Once
293  //computed, the session identifier is not changed, even if keys are later
294  //re-exchanged (refer to RFC 4253, section 7.2)
295  osMemcpy(connection->sessionId, connection->h, connection->hLen);
296  connection->sessionIdLen = connection->hLen;
297  }
298 
299  //Get the currently selected host key
300  hostKey = sshGetHostKey(connection);
301 
302  //Valid host key?
303  if(hostKey != NULL)
304  {
305  //Get the resulting exchange hash
306  exchangeHash.value = connection->h;
307  exchangeHash.length = connection->hLen;
308 
309  //Compute the signature on the exchange hash
310  error = sshGenerateSignature(connection, connection->serverHostKeyAlgo,
311  hostKey, NULL, &exchangeHash, p, written);
312  }
313  else
314  {
315  //No host key is currently selected
316  error = ERROR_INVALID_KEY;
317  }
318  }
319 
320  //Return status code
321  return error;
322 }
323 
324 
325 /**
326  * @brief Verify the signature on the exchange hash
327  * @param[in] connection Pointer to the SSH connection
328  * @param[in] serverHostKey Server's public host key
329  * @param[in] signature Signature to be verified
330  * @return Error code
331  **/
332 
334  const SshBinaryString *serverHostKey, const SshBinaryString *signature)
335 {
336  error_t error;
337  SshString serverHostKeyAlgo;
338  SshBinaryString exchangeHash;
339 
340  //Compute H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
341  error = sshFinalizeExchangeHash(connection, connection->h,
342  &connection->hLen);
343 
344  //Check status code
345  if(!error)
346  {
347  //First key exchange?
348  if(!connection->newKeysSent)
349  {
350  //The exchange hash H from the first key exchange is used as the
351  //session identifier, which is a unique identifier for this connection.
352  //Once computed, the session identifier is not changed, even if keys
353  //are later re-exchanged (refer to RFC 4253, section 7.2)
354  osMemcpy(connection->sessionId, connection->h, connection->hLen);
355  connection->sessionIdLen = connection->hLen;
356  }
357 
358  //Get the selected server's host key algorithm
359  serverHostKeyAlgo.value = connection->serverHostKeyAlgo;
360  serverHostKeyAlgo.length = osStrlen(connection->serverHostKeyAlgo);
361 
362  //Get the resulting exchange hash
363  exchangeHash.value = connection->h;
364  exchangeHash.length = connection->hLen;
365 
366  //Verify the signature on the exchange hash
367  error = sshVerifySignature(connection, &serverHostKeyAlgo, serverHostKey,
368  NULL, &exchangeHash, signature);
369  }
370 
371  //Return status code
372  return error;
373 }
374 
375 #endif
HashAlgoInit init
Definition: crypto.h:1161
#define SHA256_HASH_ALGO
Definition: sha256.h:49
error_t sshGenerateExchangeHashSignature(SshConnection *connection, uint8_t *p, size_t *written)
Compute the signature on the exchange hash.
#define SHA1_HASH_ALGO
Definition: sha1.h:49
#define SHA512_HASH_ALGO
Definition: sha512.h:49
Binary string.
Definition: ssh_types.h:67
uint8_t p
Definition: ndp.h:300
error_t sshFinalizeExchangeHash(SshConnection *connection, uint8_t *digest, size_t *digestLen)
Finalize exchange hash calculation.
uint8_t data[]
Definition: ethernet.h:224
error_t sshVerifyExchangeHashSignature(SshConnection *connection, const SshBinaryString *serverHostKey, const SshBinaryString *signature)
Verify the signature on the exchange hash.
error_t sshUpdateExchangeHashRaw(SshConnection *connection, const void *data, size_t length)
Update exchange hash calculation (raw data)
size_t length
Definition: ssh_types.h:58
#define osStrlen(s)
Definition: os_port.h:168
RSA/DSA/ECDSA/EdDSA signature generation.
size_t length
Definition: ssh_types.h:69
error_t sshGenerateSignature(SshConnection *connection, const char_t *publicKeyAlgo, const SshHostKey *hostKey, const SshBinaryString *sessionId, const SshBinaryString *message, uint8_t *p, size_t *written)
Signature generation.
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
const char_t * value
Definition: ssh_types.h:57
error_t
Error codes.
Definition: error.h:43
bool_t sshCompareAlgo(const char_t *name1, const char_t *name2)
Compare algorithm names.
Definition: ssh_misc.c:1758
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
error_t sshVerifySignature(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *publicKeyBlob, const SshBinaryString *sessionId, const SshBinaryString *message, const SshBinaryString *signature)
Signature verification.
Host key.
Definition: ssh.h:1169
uint8_t length
Definition: tcp.h:375
String.
Definition: ssh_types.h:56
const uint8_t * value
Definition: ssh_types.h:68
@ ERROR_UNSUPPORTED_KEY_EXCH_ALGO
Definition: error.h:131
error_t sshInitExchangeHash(SshConnection *connection)
Initialize exchange hash.
#define SHA384_HASH_ALGO
Definition: sha384.h:45
char char_t
Definition: compiler_port.h:55
Exchange hash calculation.
#define SshConnection
Definition: ssh.h:896
SshHostKey * sshGetHostKey(SshConnection *connection)
Get the currently selected host key.
Definition: ssh_misc.c:734
SSH helper functions.
#define SHA224_HASH_ALGO
Definition: sha224.h:45
Common interface for hash algorithms.
Definition: crypto.h:1151
error_t sshUpdateExchangeHash(SshConnection *connection, const void *data, size_t length)
Update exchange hash calculation.
Secure Shell (SSH)
RSA/DSA/ECDSA/EdDSA signature verification.
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
@ ERROR_INVALID_KEY
Definition: error.h:106
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.