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-2024 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.4.4
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, "mlkem768nistp256-sha256") ||
94  sshCompareAlgo(kexAlgo, "mlkem768x25519-sha256"))
95  {
96  //Select the relevant hash algorithm
97  hashAlgo = SHA256_HASH_ALGO;
98  }
99  else
100 #endif
101 #if (SSH_SHA384_SUPPORT == ENABLED)
102  //Key exchange with SHA-384 as hash?
103  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha384@ssh.com") ||
104  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp384") ||
105  sshCompareAlgo(kexAlgo, "mlkem1024nistp384-sha384"))
106  {
107  //Select the relevant hash algorithm
108  hashAlgo = SHA384_HASH_ALGO;
109  }
110  else
111 #endif
112 #if (SSH_SHA512_SUPPORT == ENABLED)
113  //Key exchange with SHA-512 as hash?
114  if(sshCompareAlgo(kexAlgo, "diffie-hellman-group15-sha512") ||
115  sshCompareAlgo(kexAlgo, "diffie-hellman-group16-sha512") ||
116  sshCompareAlgo(kexAlgo, "diffie-hellman-group17-sha512") ||
117  sshCompareAlgo(kexAlgo, "diffie-hellman-group18-sha512") ||
118  sshCompareAlgo(kexAlgo, "diffie-hellman-group-exchange-sha512@ssh.com") ||
119  sshCompareAlgo(kexAlgo, "ecdh-sha2-nistp521") ||
120  sshCompareAlgo(kexAlgo, "curve448-sha512") ||
121  sshCompareAlgo(kexAlgo, "sntrup761x25519-sha512") ||
122  sshCompareAlgo(kexAlgo, "sntrup761x25519-sha512@openssh.com"))
123  {
124  //Select the relevant hash algorithm
125  hashAlgo = SHA512_HASH_ALGO;
126  }
127  else
128 #endif
129  //Unknown key exchange algorithm?
130  {
131  //Just for sanity
132  hashAlgo = NULL;
133  }
134 
135  //Make sure the hash algorithm is supported
136  if(hashAlgo != NULL)
137  {
138  //The hash algorithm for computing the exchange hash is defined by the
139  //method name (refer to RFC 4253, section 8)
140  connection->hashAlgo = hashAlgo;
141 
142  //Initialize exchange hash computation
143  hashAlgo->init(&connection->hashContext);
144  }
145  else
146  {
147  //Report an error
149  }
150 
151  //Return status code
152  return error;
153 }
154 
155 
156 /**
157  * @brief Update exchange hash calculation
158  * @param[in] connection Pointer to the SSH connection
159  * @param[in] data Pointer to the data block to be hashed
160  * @param[in] length Length of the data block, in bytes
161  * @return Error code
162  **/
163 
165  size_t length)
166 {
167  error_t error;
168  uint8_t temp[4];
169 
170  //Initialize status code
171  error = NO_ERROR;
172 
173  //Valid hash algorithm?
174  if(connection->hashAlgo != NULL)
175  {
176  //Encode the length of the data block as a 32-bit big-endian integer
177  STORE32BE(length, temp);
178 
179  //Digest the length field
180  connection->hashAlgo->update(&connection->hashContext, temp, sizeof(temp));
181  //Digest the contents of the data block
182  connection->hashAlgo->update(&connection->hashContext, data, length);
183  }
184  else
185  {
186  //Report an error
187  error = ERROR_FAILURE;
188  }
189 
190  //Return status code
191  return error;
192 }
193 
194 
195 /**
196  * @brief Update exchange hash calculation (raw data)
197  * @param[in] connection Pointer to the SSH connection
198  * @param[in] data Pointer to the data block to be hashed
199  * @param[in] length Length of the data block, in bytes
200  * @return Error code
201  **/
202 
204  size_t length)
205 {
206  error_t error;
207 
208  //Initialize status code
209  error = NO_ERROR;
210 
211  //Valid hash algorithm?
212  if(connection->hashAlgo != NULL)
213  {
214  //Digest the contents of the data block
215  connection->hashAlgo->update(&connection->hashContext, data, length);
216  }
217  else
218  {
219  //Report an error
220  error = ERROR_FAILURE;
221  }
222 
223  //Return status code
224  return error;
225 }
226 
227 
228 /**
229  * @brief Finalize exchange hash calculation
230  * @param[in] connection Pointer to the SSH connection
231  * @param[out] digest Buffer where to store the resulting hash value
232  * @param[out] digestLen Length of the resulting hash value, in bytes
233  * @return Error code
234  **/
235 
236 error_t sshFinalizeExchangeHash(SshConnection *connection, uint8_t *digest,
237  size_t *digestLen)
238 {
239  error_t error;
240 
241  //Initialize status code
242  error = NO_ERROR;
243 
244  //Valid hash algorithm?
245  if(connection->hashAlgo != NULL)
246  {
247  //Compute H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
248  connection->hashAlgo->final(&connection->hashContext, digest);
249  //Return the length of the resulting digest
250  *digestLen = connection->hashAlgo->digestSize;
251  }
252  else
253  {
254  //Report an error
255  error = ERROR_FAILURE;
256  }
257 
258  //Return status code
259  return error;
260 }
261 
262 
263 /**
264  * @brief Compute the signature on the exchange hash
265  * @param[in] connection Pointer to the SSH connection
266  * @param[out] p Output stream where to write the signature
267  * @param[out] written Total number of bytes that have been written
268  * @return Error code
269  **/
270 
272  size_t *written)
273 {
274  error_t error;
275  SshHostKey *hostKey;
276  SshBinaryString exchangeHash;
277 
278  //Compute H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
279  error = sshFinalizeExchangeHash(connection, connection->h,
280  &connection->hLen);
281 
282  //Check status code
283  if(!error)
284  {
285  //First key exchange?
286  if(!connection->newKeysSent)
287  {
288  //The exchange hash H from the first key exchange is used as the session
289  //identifier, which is a unique identifier for this connection. Once
290  //computed, the session identifier is not changed, even if keys are later
291  //re-exchanged (refer to RFC 4253, section 7.2)
292  osMemcpy(connection->sessionId, connection->h, connection->hLen);
293  connection->sessionIdLen = connection->hLen;
294  }
295 
296  //Get the currently selected host key
297  hostKey = sshGetHostKey(connection);
298 
299  //Valid host key?
300  if(hostKey != NULL)
301  {
302  //Get the resulting exchange hash
303  exchangeHash.value = connection->h;
304  exchangeHash.length = connection->hLen;
305 
306  //Compute the signature on the exchange hash
307  error = sshGenerateSignature(connection, connection->serverHostKeyAlgo,
308  hostKey, NULL, &exchangeHash, p, written);
309  }
310  else
311  {
312  //No host key is currently selected
313  error = ERROR_INVALID_KEY;
314  }
315  }
316 
317  //Return status code
318  return error;
319 }
320 
321 
322 /**
323  * @brief Verify the signature on the exchange hash
324  * @param[in] connection Pointer to the SSH connection
325  * @param[in] serverHostKey Server's public host key
326  * @param[in] signature Signature to be verified
327  * @return Error code
328  **/
329 
331  const SshBinaryString *serverHostKey, const SshBinaryString *signature)
332 {
333  error_t error;
334  SshString serverHostKeyAlgo;
335  SshBinaryString exchangeHash;
336 
337  //Compute H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
338  error = sshFinalizeExchangeHash(connection, connection->h,
339  &connection->hLen);
340 
341  //Check status code
342  if(!error)
343  {
344  //First key exchange?
345  if(!connection->newKeysSent)
346  {
347  //The exchange hash H from the first key exchange is used as the
348  //session identifier, which is a unique identifier for this connection.
349  //Once computed, the session identifier is not changed, even if keys
350  //are later re-exchanged (refer to RFC 4253, section 7.2)
351  osMemcpy(connection->sessionId, connection->h, connection->hLen);
352  connection->sessionIdLen = connection->hLen;
353  }
354 
355  //Get the selected server's host key algorithm
356  serverHostKeyAlgo.value = connection->serverHostKeyAlgo;
357  serverHostKeyAlgo.length = osStrlen(connection->serverHostKeyAlgo);
358 
359  //Get the resulting exchange hash
360  exchangeHash.value = connection->h;
361  exchangeHash.length = connection->hLen;
362 
363  //Verify the signature on the exchange hash
364  error = sshVerifySignature(connection, &serverHostKeyAlgo, serverHostKey,
365  NULL, &exchangeHash, signature);
366  }
367 
368  //Return status code
369  return error;
370 }
371 
372 #endif
HashAlgoInit init
Definition: crypto.h:1056
#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:222
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:165
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:141
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:1653
@ 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:1143
uint8_t length
Definition: tcp.h:368
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:48
Exchange hash calculation.
#define SshConnection
Definition: ssh.h:874
SshHostKey * sshGetHostKey(SshConnection *connection)
Get the currently selected host key.
Definition: ssh_misc.c:722
SSH helper functions.
#define SHA224_HASH_ALGO
Definition: sha224.h:45
Common interface for hash algorithms.
Definition: crypto.h:1046
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.