Coverage Report

Created: 2026-02-26 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/packet_crypt.c
Line
Count
Source
1
/*
2
 * crypt.c - blowfish-cbc code
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2003 by Aris Adamantiadis
7
 *
8
 * The SSH Library is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or (at your
11
 * option) any later version.
12
 *
13
 * The SSH Library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16
 * License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with the SSH Library; see the file COPYING.  If not, write to
20
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21
 * MA 02111-1307, USA.
22
 */
23
24
#include "config.h"
25
#include <assert.h>
26
#include <stdlib.h>
27
#include <stdio.h>
28
#include <string.h>
29
30
#ifndef _WIN32
31
#include <netinet/in.h>
32
#include <arpa/inet.h>
33
#endif
34
35
#ifdef OPENSSL_CRYPTO
36
#include <openssl/evp.h>
37
#include <openssl/hmac.h>
38
#endif
39
40
#include "libssh/priv.h"
41
#include "libssh/session.h"
42
#include "libssh/wrapper.h"
43
#include "libssh/crypto.h"
44
#include "libssh/buffer.h"
45
#include "libssh/bytearray.h"
46
47
/** @internal
48
 * @brief decrypt the packet length from a raw encrypted packet, and store the first decrypted
49
 * blocksize.
50
 * @returns native byte-ordered decrypted length of the upcoming packet
51
 */
52
uint32_t ssh_packet_decrypt_len(ssh_session session,
53
                                uint8_t *destination,
54
                                uint8_t *source)
55
533k
{
56
533k
    struct ssh_crypto_struct *crypto = NULL;
57
533k
    uint32_t decrypted;
58
533k
    int rc;
59
60
533k
    crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
61
533k
    if (crypto != NULL) {
62
109k
        if (crypto->in_cipher->aead_decrypt_length != NULL) {
63
0
            rc = crypto->in_cipher->aead_decrypt_length(
64
0
                    crypto->in_cipher, source, destination,
65
0
                    crypto->in_cipher->lenfield_blocksize,
66
0
                    session->recv_seq);
67
109k
        } else {
68
109k
            rc = ssh_packet_decrypt(
69
109k
                    session,
70
109k
                    destination,
71
109k
                    source,
72
109k
                    0,
73
109k
                    crypto->in_cipher->blocksize);
74
109k
        }
75
109k
        if (rc < 0) {
76
0
            return 0;
77
0
        }
78
424k
    } else {
79
424k
        memcpy(destination, source, 8);
80
424k
    }
81
533k
    memcpy(&decrypted,destination,sizeof(decrypted));
82
83
533k
    return ntohl(decrypted);
84
533k
}
85
86
/** @internal
87
 * @brief decrypts the content of an SSH packet.
88
 * @param[source] source packet, including the encrypted length field
89
 * @param[start] index in the packet that was not decrypted yet.
90
 * @param[encrypted_size] size of the encrypted data to be decrypted after start.
91
 */
92
int ssh_packet_decrypt(ssh_session session,
93
                       uint8_t *destination,
94
                       uint8_t *source,
95
                       size_t start,
96
                       size_t encrypted_size)
97
145k
{
98
145k
    struct ssh_crypto_struct *crypto = NULL;
99
145k
    struct ssh_cipher_struct *cipher = NULL;
100
101
145k
    if (encrypted_size <= 0) {
102
0
        return SSH_ERROR;
103
0
    }
104
105
145k
    crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
106
145k
    if (crypto == NULL) {
107
0
        return SSH_ERROR;
108
0
    }
109
145k
    cipher = crypto->in_cipher;
110
111
145k
    if (encrypted_size % cipher->blocksize != 0) {
112
50
        ssh_set_error(session,
113
50
                      SSH_FATAL,
114
50
                      "Cryptographic functions must be used on multiple of "
115
50
                      "blocksize (received %zu)",
116
50
                      encrypted_size);
117
50
        return SSH_ERROR;
118
50
    }
119
120
145k
    if (cipher->aead_decrypt != NULL) {
121
0
        return cipher->aead_decrypt(cipher,
122
0
                                    source,
123
0
                                    destination,
124
0
                                    encrypted_size,
125
0
                                    session->recv_seq);
126
145k
    } else {
127
145k
        cipher->decrypt(cipher, source + start, destination, encrypted_size);
128
145k
    }
129
130
145k
    return 0;
131
145k
}
132
133
unsigned char *ssh_packet_encrypt(ssh_session session, void *data, size_t len)
134
344k
{
135
344k
  struct ssh_crypto_struct *crypto = NULL;
136
344k
  struct ssh_cipher_struct *cipher = NULL;
137
344k
  HMACCTX ctx = NULL;
138
344k
  char *out = NULL;
139
344k
  int etm_packet_offset = 0, rc;
140
344k
  unsigned int blocksize;
141
344k
  size_t finallen = DIGEST_MAX_LEN;
142
344k
  uint32_t seq, lenfield_blocksize;
143
344k
  enum ssh_hmac_e type;
144
344k
  bool etm;
145
146
344k
  assert(len);
147
148
344k
  crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
149
344k
  if (crypto == NULL) {
150
295k
      return NULL; /* nothing to do here */
151
295k
  }
152
153
49.0k
  blocksize = crypto->out_cipher->blocksize;
154
49.0k
  lenfield_blocksize = crypto->out_cipher->lenfield_blocksize;
155
156
49.0k
  type = crypto->out_hmac;
157
49.0k
  etm = crypto->out_hmac_etm;
158
159
49.0k
  if (etm) {
160
0
      etm_packet_offset = sizeof(uint32_t);
161
0
  }
162
163
49.0k
  if ((len - lenfield_blocksize - etm_packet_offset) % blocksize != 0) {
164
0
      ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set"
165
0
                    " on at least one blocksize (received %zu)", len);
166
0
      return NULL;
167
0
  }
168
49.0k
  out = calloc(1, len);
169
49.0k
  if (out == NULL) {
170
0
    return NULL;
171
0
  }
172
173
49.0k
  seq = ntohl(session->send_seq);
174
49.0k
  cipher = crypto->out_cipher;
175
176
49.0k
  if (cipher->aead_encrypt != NULL) {
177
0
      cipher->aead_encrypt(cipher, data, out, len,
178
0
            crypto->hmacbuf, session->send_seq);
179
0
      memcpy(data, out, len);
180
49.0k
  } else {
181
49.0k
      if (type != SSH_HMAC_NONE) {
182
0
          ctx = hmac_init(crypto->encryptMAC, hmac_digest_len(type), type);
183
0
          if (ctx == NULL) {
184
0
              SAFE_FREE(out);
185
0
              return NULL;
186
0
          }
187
188
0
          if (!etm) {
189
0
              rc = hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
190
0
              if (rc != 1) {
191
0
                  SAFE_FREE(out);
192
0
                  return NULL;
193
0
              }
194
0
              rc = hmac_update(ctx, data, len);
195
0
              if (rc != 1) {
196
0
                  SAFE_FREE(out);
197
0
                  return NULL;
198
0
              }
199
0
              rc = hmac_final(ctx, crypto->hmacbuf, &finallen);
200
0
              if (rc != 1) {
201
0
                  SAFE_FREE(out);
202
0
                  return NULL;
203
0
              }
204
0
          }
205
0
      }
206
207
49.0k
      cipher->encrypt(cipher, (uint8_t*)data + etm_packet_offset, out, len - etm_packet_offset);
208
49.0k
      memcpy((uint8_t*)data + etm_packet_offset, out, len - etm_packet_offset);
209
210
49.0k
      if (type != SSH_HMAC_NONE) {
211
0
          if (etm) {
212
0
              PUSH_BE_U32(data, 0, len - etm_packet_offset);
213
0
              rc = hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
214
0
              if (rc != 1) {
215
0
                  SAFE_FREE(out);
216
0
                  return NULL;
217
0
              }
218
0
              rc = hmac_update(ctx, data, len);
219
0
              if (rc != 1) {
220
0
                  SAFE_FREE(out);
221
0
                  return NULL;
222
0
              }
223
0
              rc = hmac_final(ctx, crypto->hmacbuf, &finallen);
224
0
              if (rc != 1) {
225
0
                  SAFE_FREE(out);
226
0
                  return NULL;
227
0
              }
228
0
          }
229
#ifdef DEBUG_CRYPTO
230
          ssh_log_hexdump("mac: ", data, len);
231
          if (finallen != hmac_digest_len(type)) {
232
              printf("Final len is %zu\n", finallen);
233
          }
234
          ssh_log_hexdump("Packet hmac", crypto->hmacbuf, hmac_digest_len(type));
235
#endif
236
0
      }
237
49.0k
  }
238
49.0k
  ssh_burn(out, len);
239
49.0k
  SAFE_FREE(out);
240
241
49.0k
  return crypto->hmacbuf;
242
49.0k
}
243
244
/**
245
 * @internal
246
 *
247
 * @brief Verify the hmac of a packet
248
 *
249
 * @param  session      The session to use.
250
 * @param  data         The pointer to the data to verify the hmac from.
251
 * @param  len          The length of the given data.
252
 * @param  mac          The mac to compare with the hmac.
253
 *
254
 * @return              0 if hmac and mac are equal, < 0 if not or an error
255
 *                      occurred.
256
 */
257
int ssh_packet_hmac_verify(ssh_session session,
258
                           const void *data,
259
                           size_t len,
260
                           uint8_t *mac,
261
                           enum ssh_hmac_e type)
262
0
{
263
0
    struct ssh_crypto_struct *crypto = NULL;
264
0
    unsigned char hmacbuf[DIGEST_MAX_LEN] = {0};
265
0
    HMACCTX ctx = NULL;
266
0
    size_t hmaclen = DIGEST_MAX_LEN;
267
0
    uint32_t seq;
268
0
    int cmp;
269
0
    int rc;
270
271
    /* AEAD types have no mac checking */
272
0
    if (type == SSH_HMAC_AEAD_POLY1305 ||
273
0
        type == SSH_HMAC_AEAD_GCM) {
274
0
        return SSH_OK;
275
0
    }
276
277
0
    crypto = ssh_packet_get_current_crypto(session,
278
0
                                           SSH_DIRECTION_IN);
279
0
    if (crypto == NULL) {
280
0
        return SSH_ERROR;
281
0
    }
282
283
0
    ctx = hmac_init(crypto->decryptMAC,
284
0
                    hmac_digest_len(type),
285
0
                    type);
286
0
    if (ctx == NULL) {
287
0
        return SSH_ERROR;
288
0
    }
289
290
0
    seq = htonl(session->recv_seq);
291
292
0
    rc = hmac_update(ctx,
293
0
                     (unsigned char *) &seq,
294
0
                     sizeof(uint32_t));
295
0
    if (rc != 1) {
296
0
        return SSH_ERROR;
297
0
    }
298
0
    rc = hmac_update(ctx,
299
0
                     data,
300
0
                     len);
301
0
    if (rc != 1) {
302
0
        return SSH_ERROR;
303
0
    }
304
0
    rc = hmac_final(ctx,
305
0
                    hmacbuf,
306
0
                    &hmaclen);
307
0
    if (rc != 1) {
308
0
        return SSH_ERROR;
309
0
    }
310
311
#ifdef DEBUG_CRYPTO
312
    ssh_log_hexdump("received mac",
313
                    mac,
314
                    hmaclen);
315
    ssh_log_hexdump("Computed mac",
316
                    hmacbuf,
317
                    hmaclen);
318
    ssh_log_hexdump("seq",
319
                    (unsigned char *)&seq,
320
                    sizeof(uint32_t));
321
#endif
322
0
    cmp = secure_memcmp(mac,
323
0
                        hmacbuf,
324
0
                        hmaclen);
325
0
    if (cmp == 0) {
326
0
        return SSH_OK;
327
0
    }
328
329
0
    return SSH_ERROR;
330
0
}