/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 | } |