1 /*
2 * Modified in July 2012 by Dhiru Kholia <dhiru at openwall.com> to be
3 * standalone and compilable.
4 *
5 * p-ppk-crack v0.5 made by michu@neophob.com -- PuTTY private key cracker
6 *
7 * Source code based on putty svn version, see [1] for licensing information.
8 *
9 * [1] http://www.chiark.greenend.org.uk/~sgtatham/putty/licence.html
10 */
11
12 #ifndef PUTTY_COMMON_H
13 #define PUTTY_COMMON_H
14 #if AC_BUILT
15 #include "autoconfig.h"
16 #endif
17
18 #include <stdint.h>
19 #include <stddef.h> /* for size_t */
20 #include <string.h> /* for memcpy() */
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #if !AC_BUILT || HAVE_LIMITS_H
25 #include <limits.h>
26 #endif
27 #include <sys/types.h>
28 #if !AC_BUILT || HAVE_FCNTL_H
29 #include <fcntl.h>
30 #endif
31 #include "memory.h"
32 #include "jumbo.h"
33 #if _MSC_VER
34 #include <io.h>
35 #endif
36
37 #ifndef FALSE
38 #define FALSE 0
39 #endif
40 #ifndef TRUE
41 #define TRUE 1
42 #endif
43
44 struct ssh2_userkey {
45 const struct ssh_signkey *alg; /* the key algorithm */
46 void *data; /* the key data */
47 char *comment; /* the key comment */
48 };
49
50 enum {
51 SSH_KEYTYPE_UNOPENABLE,
52 SSH_KEYTYPE_UNKNOWN,
53 SSH_KEYTYPE_SSH1, SSH_KEYTYPE_SSH2,
54 SSH_KEYTYPE_OPENSSH, SSH_KEYTYPE_SSHCOM
55 };
56
57 static int base64_decode_atom(char *atom, unsigned char *out);
58 #endif
59
60 typedef struct Filename {
61 char path[4096];
62 } Filename;
63
64 static char header[40], *b, *encryption, *comment, *mac;
65 static const char *putty_error = NULL;
66 static int i, is_mac, old_fmt;
67 static char alg[32];
68 static int cipher, cipherblk;
69 static unsigned char *public_blob, *private_blob;
70 static int public_blob_len, private_blob_len;
71
read_body(FILE * fp)72 static char *read_body(FILE * fp)
73 {
74 char *text;
75 int len;
76 int size;
77 int c;
78
79 size = 128 * 1024;
80 text = (char*)malloc(size);
81 if (!text) {
82 fprintf(stderr, "malloc failed in read_body, exiting!\n");
83 exit(-1);
84 }
85 len = 0;
86 text[len] = '\0';
87
88 while (1) {
89 c = fgetc(fp);
90 if (c == '\r' || c == '\n') {
91 c = fgetc(fp);
92 if (c != '\r' && c != '\n' && c != EOF)
93 ungetc(c, fp);
94 return text;
95 }
96 if (c == EOF) {
97 MEM_FREE(text);
98 return NULL;
99 }
100 text[len++] = c;
101 text[len] = '\0';
102 }
103 }
104
read_blob(FILE * fp,int nlines,int * bloblen)105 static unsigned char *read_blob(FILE * fp, int nlines, int *bloblen)
106 {
107 unsigned char *blob;
108 char *line;
109 int linelen, len;
110 int i, j, k;
111
112 /* Sanity check nlines */
113 if (nlines < 0 || nlines > (1024 * 1024))
114 return NULL;
115
116 /* We expect at most 64 base64 characters, ie 48 real bytes, per line. */
117 blob = (unsigned char*)malloc(48 * nlines);
118 if (!blob) {
119 fprintf(stderr, "malloc failed in read_blob, exiting!\n");
120 exit(-1);
121 }
122 len = 0;
123 for (i = 0; i < nlines; i++) {
124 line = read_body(fp);
125 if (!line) {
126 MEM_FREE(blob);
127 return NULL;
128 }
129 linelen = strlen(line);
130 if (linelen % 4 != 0 || linelen > 64) {
131 MEM_FREE(blob);
132 MEM_FREE(line);
133 line = NULL;
134 return NULL;
135 }
136 for (j = 0; j < linelen; j += 4) {
137 k = base64_decode_atom(line + j, blob + len);
138 if (!k) {
139 MEM_FREE(blob);
140 MEM_FREE(line);
141 return NULL;
142 }
143 len += k;
144 }
145 MEM_FREE(line);
146 }
147 *bloblen = len;
148
149 return blob;
150 }
151
read_header(FILE * fp,char * header)152 static int read_header(FILE * fp, char *header)
153 {
154 int len = 39;
155 int c;
156
157 while (len > 0) {
158 c = fgetc(fp);
159 if (c == '\n' || c == '\r' || c == EOF)
160 return 0; /* failure */
161 if (c == ':') {
162 c = fgetc(fp);
163 if (c != ' ')
164 return 0;
165 *header = '\0';
166 return 1; /* success! */
167 }
168 if (len == 0)
169 return 0; /* failure */
170 *header++ = c;
171 len--;
172 }
173 return 0; /* failure */
174 }
175
176
init_LAME(const Filename * filename)177 static int init_LAME(const Filename *filename) {
178 FILE *fp;
179
180 encryption = comment = mac = NULL;
181 public_blob = private_blob = NULL;
182
183 fp = fopen(filename->path, "rb" );
184 if (!fp) {
185 putty_error = "can't open file";
186 goto error;
187 }
188
189 /* Read the first header line which contains the key type. */
190 if (!read_header(fp, header))
191 goto error;
192 if (0 == strcmp(header, "PuTTY-User-Key-File-2")) {
193 old_fmt = 0;
194 } else if (0 == strcmp(header, "PuTTY-User-Key-File-1")) {
195 /* this is an old key file; warn and then continue */
196 // old_keyfile_warning();
197 old_fmt = 1;
198 } else {
199 putty_error = "not a PuTTY SSH-2 private key";
200 goto error;
201 }
202 putty_error = "file format error";
203 if ((b = read_body(fp)) == NULL)
204 goto error;
205 /* Select key algorithm structure. */
206 if (!strcmp(b, "ssh-rsa"))
207 strcpy(alg, "ssh-rsa");
208 else if (!strcmp(b, "ssh-dss"))
209 strcpy(alg, "ssh-dss");
210 else if (!strcmp(b, "ecdsa-sha2-nistp256"))
211 strcpy(alg, "ecdsa-sha2-nistp256");
212 else if (strlen(b) < sizeof(alg) - 1)
213 strcpy(alg, b);
214 MEM_FREE(b);
215
216 /* Read the Encryption header line. */
217 if (!read_header(fp, header) || 0 != strcmp(header, "Encryption"))
218 goto error;
219 if ((encryption = read_body(fp)) == NULL)
220 goto error;
221 if (!strcmp(encryption, "aes256-cbc")) {
222 cipher = 1;
223 cipherblk = 16;
224 } else if (!strcmp(encryption, "none")) {
225 cipher = 0;
226 cipherblk = 1;
227 } else {
228 MEM_FREE(encryption);
229 goto error;
230 }
231
232 /* Read the Comment header line. */
233 if (!read_header(fp, header) || 0 != strcmp(header, "Comment"))
234 goto error;
235 if ((comment = read_body(fp)) == NULL)
236 goto error;
237
238 /* Read the Public-Lines header line and the public blob. */
239 if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines"))
240 goto error;
241 if ((b = read_body(fp)) == NULL)
242 goto error;
243 i = atoi(b);
244 MEM_FREE(b);
245 if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
246 goto error;
247
248 /* Read the Private-Lines header line and the Private blob. */
249 if (!read_header(fp, header) || 0 != strcmp(header, "Private-Lines"))
250 goto error;
251 if ((b = read_body(fp)) == NULL)
252 goto error;
253 i = atoi(b);
254 MEM_FREE(b);
255 if ((private_blob = read_blob(fp, i, &private_blob_len)) == NULL)
256 goto error;
257
258 /* Read the Private-MAC or Private-Hash header line. */
259 if (!read_header(fp, header))
260 goto error;
261 if (0 == strcmp(header, "Private-MAC")) {
262 if ((mac = read_body(fp)) == NULL)
263 goto error;
264 is_mac = 1;
265 } else if (0 == strcmp(header, "Private-Hash") && old_fmt) {
266 if ((mac = read_body(fp)) == NULL)
267 goto error;
268 is_mac = 0;
269 } else
270 goto error;
271
272 fclose(fp);
273 fp = NULL;
274 return 0;
275
276 error:
277 if (fp)
278 fclose(fp);
279 MEM_FREE(comment);
280 MEM_FREE(encryption);
281 MEM_FREE(mac);
282 MEM_FREE(public_blob);
283 MEM_FREE(private_blob);
284 return 1;
285 }
286
print_hex(unsigned char * str,int len)287 static void print_hex(unsigned char *str, int len)
288 {
289 int i;
290 for (i = 0; i < len; ++i)
291 printf("%02x", str[i]);
292 }
293
LAME_ssh2_load_userkey(char * path,const char ** errorstr)294 static void LAME_ssh2_load_userkey(char *path, const char **errorstr)
295 {
296 const char *ext[] = {".ppk"};
297 char *fname;
298 /*
299 * Decrypt the private blob.
300 */
301 if (cipher) {
302 if (private_blob_len % cipherblk)
303 goto error;
304 }
305
306 {
307 fname = strip_suffixes(basename(path), ext, 1);
308 printf("%s:$putty$%d*%d*%d*%d*%s*%d*", fname, cipher, cipherblk, is_mac, old_fmt, mac, public_blob_len);
309 print_hex(public_blob, public_blob_len);
310 printf("*%d*", private_blob_len);
311 print_hex(private_blob, private_blob_len);
312 if (!old_fmt) {
313 printf("*%s*%s*%s\n", alg, encryption, comment);
314 }
315 else {
316 printf("\n");
317 }
318 MEM_FREE(comment);
319 return;
320 }
321 error:
322 fprintf(stderr, "Something failed!\n");
323 MEM_FREE(comment);
324 MEM_FREE(encryption);
325 MEM_FREE(mac);
326 MEM_FREE(public_blob);
327 MEM_FREE(private_blob);
328 }
329
f_open(const Filename * filename,char const * mode,int is_private)330 static FILE *f_open(const Filename *filename, char const *mode, int is_private)
331 {
332 if (!is_private) {
333 return fopen(filename->path, mode);
334 } else {
335 int fd;
336 fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
337 if (fd < 0)
338 return NULL;
339 return fdopen(fd, mode);
340 }
341 }
342
343 /* ----------------------------------------------------------------------
344 * A function to determine the type of a private key file. Returns
345 * 0 on failure, 1 or 2 on success.
346 */
347 #define rsa_signature "SSH PRIVATE KEY FILE FORMAT 1.1\n"
348
key_type(const Filename * filename)349 static int key_type(const Filename *filename)
350 {
351 FILE *fp;
352 char buf[32];
353 const char putty2_sig[] = "PuTTY-User-Key-File-";
354 const char sshcom_sig[] = "---- BEGIN SSH2 ENCRYPTED PRIVAT";
355 const char openssh_sig[] = "-----BEGIN ";
356 int i;
357
358 fp = f_open(filename, "r", FALSE);
359 if (!fp)
360 return SSH_KEYTYPE_UNOPENABLE;
361 i = fread(buf, 1, sizeof(buf), fp);
362 fclose(fp);
363 if (i < 0)
364 return SSH_KEYTYPE_UNOPENABLE;
365 if (i < 32)
366 return SSH_KEYTYPE_UNKNOWN;
367 if (!memcmp(buf, rsa_signature, sizeof(rsa_signature)-1))
368 return SSH_KEYTYPE_SSH1;
369 if (!memcmp(buf, putty2_sig, sizeof(putty2_sig)-1))
370 return SSH_KEYTYPE_SSH2;
371 if (!memcmp(buf, openssh_sig, sizeof(openssh_sig)-1))
372 return SSH_KEYTYPE_OPENSSH;
373 if (!memcmp(buf, sshcom_sig, sizeof(sshcom_sig)-1))
374 return SSH_KEYTYPE_SSHCOM;
375 return SSH_KEYTYPE_UNKNOWN; /* unrecognised or EOF */
376 }
377
ssh2_userkey_encrypted(const Filename * filename,char ** commentptr)378 static int ssh2_userkey_encrypted(const Filename *filename, char **commentptr)
379 {
380 FILE *fp;
381 char header[40], *b, *comment;
382 int ret;
383
384 if (commentptr)
385 *commentptr = NULL;
386
387 fp = f_open(filename, "rb", FALSE);
388 if (!fp)
389 return 0;
390 if (!read_header(fp, header)
391 || (0 != strcmp(header, "PuTTY-User-Key-File-2") &&
392 0 != strcmp(header, "PuTTY-User-Key-File-1"))) {
393 fclose(fp);
394 return 0;
395 }
396 if ((b = read_body(fp)) == NULL) {
397 fclose(fp);
398 return 0;
399 }
400 MEM_FREE(b); /* we don't care about key type here */
401 /* Read the Encryption header line. */
402 if (!read_header(fp, header) || 0 != strcmp(header, "Encryption")) {
403 fclose(fp);
404 return 0;
405 }
406 if ((b = read_body(fp)) == NULL) {
407 fclose(fp);
408 return 0;
409 }
410
411 /* Read the Comment header line. */
412 if (!read_header(fp, header) || 0 != strcmp(header, "Comment")) {
413 fclose(fp);
414 MEM_FREE(b);
415 return 1;
416 }
417 if ((comment = read_body(fp)) == NULL) {
418 fclose(fp);
419 MEM_FREE(b);
420 return 1;
421 }
422
423 if (commentptr)
424 *commentptr = comment;
425
426 fclose(fp);
427 if (!strcmp(b, "aes256-cbc"))
428 ret = 1;
429 else
430 ret = 0;
431 MEM_FREE(b);
432 return ret;
433 }
434
base64_decode_atom(char * atom,unsigned char * out)435 static int base64_decode_atom(char *atom, unsigned char *out)
436 {
437 int vals[4];
438 int i, v, len;
439 unsigned word;
440 char c;
441
442 for (i = 0; i < 4; i++) {
443 c = atom[i];
444 if (c >= 'A' && c <= 'Z')
445 v = c - 'A';
446 else if (c >= 'a' && c <= 'z')
447 v = c - 'a' + 26;
448 else if (c >= '0' && c <= '9')
449 v = c - '0' + 52;
450 else if (c == '+')
451 v = 62;
452 else if (c == '/')
453 v = 63;
454 else if (c == '=')
455 v = -1;
456 else
457 return 0; /* invalid atom */
458 vals[i] = v;
459 }
460
461 if (vals[0] == -1 || vals[1] == -1)
462 return 0;
463 if (vals[2] == -1 && vals[3] != -1)
464 return 0;
465
466 if (vals[3] != -1)
467 len = 3;
468 else if (vals[2] != -1)
469 len = 2;
470 else
471 len = 1;
472
473 word = ((vals[0] << 18) |
474 (vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F));
475 out[0] = (word >> 16) & 0xFF;
476 if (len > 1)
477 out[1] = (word >> 8) & 0xFF;
478 if (len > 2)
479 out[2] = word & 0xFF;
480 return len;
481 }
482
process_file(const char * fname)483 static void process_file(const char *fname)
484 {
485 FILE *fp;
486 int type, realtype;
487 char *comment;
488 Filename filename;
489 int needs_pass = 0;
490 const char *errmsg = NULL;
491
492 /* check if file exists */
493 if ((fp = fopen(fname, "r")) == NULL) {
494 fprintf(stderr, "Error: Cannot open %s.\n", fname);
495 return;
496 }
497 fclose(fp);
498
499 strcpy(filename.path, fname);
500
501 // src: winpgen.c
502 type = realtype = key_type(&filename);
503 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
504 fprintf(stderr, "Error: Couldn't load private key (%s)\n", filename.path);
505 return;
506 }
507 if (type == SSH_KEYTYPE_SSH1) {
508 fprintf(stderr, "%s : SSH1 RSA private keys are not supported currently!\n", filename.path);
509 return;
510
511 }
512 if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
513 realtype = type;
514 }
515
516 comment = NULL;
517 if (realtype == SSH_KEYTYPE_SSH2) {
518 needs_pass = ssh2_userkey_encrypted(&filename, &comment);
519 if (needs_pass == 0) {
520 fprintf(stderr, "%s : this private key doesn't need a passphrase!\n", fname);
521 goto out;
522 }
523 }
524
525 if (init_LAME(&filename)==1) {
526 fprintf(stderr, "error, not valid private key!\n");
527 goto out;
528 }
529 if (type == SSH_KEYTYPE_SSH1) {
530 fprintf(stderr, "SSH1 key type not supported!\n");
531 goto out;
532 } else { // SSH_KEYTYPE_SSH2
533 if (realtype == type) {
534 LAME_ssh2_load_userkey(filename.path, &errmsg);
535 }
536 }
537
538 out:
539
540 MEM_FREE(comment);
541 MEM_FREE(encryption);
542 MEM_FREE(mac);
543 MEM_FREE(public_blob);
544 MEM_FREE(private_blob);
545 }
546
547 #ifdef HAVE_LIBFUZZER
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)548 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
549 {
550 int fd;
551 char name[] = "/tmp/libFuzzer-XXXXXX";
552
553 fd = mkstemp(name);
554 if (fd < 0) {
555 fprintf(stderr, "Problem detected while creating the input file, %s, aborting!\n", strerror(errno));
556 exit(-1);
557 }
558 write(fd, data, size);
559 close(fd);
560 process_file(name);
561 remove(name);
562
563 return 0;
564 }
565 #else
main(int argc,char ** argv)566 int main(int argc, char **argv)
567 {
568 int i;
569
570 if (argc < 2) {
571 printf( "Usage: putty2john [.ppk PuTTY-Private-Key-File(s)]\n");
572 printf( "\nKey types supported: RSA, DSA, ECDSA, ED25519\n");
573 exit(1);
574 }
575
576 for (i = 1; i < argc; i++)
577 process_file(argv[i]);
578
579 return 0;
580 }
581 #endif
582