//--------------------------------------------------------------------------- void __fastcall TKeyGenerator::AddEntropy(TEntropyBit Entropy) { DebugAssert(FState == kgInitializing); DebugAssert(FEntropy); DebugAssert(FEntropyGot < FEntropyRequired); FEntropy[FEntropyGot++] = Entropy; if (FEntropyGot == FEntropyRequired) { FState = kgInitialized; random_add_heavynoise(FEntropy, FEntropyRequired * sizeof(TEntropyBit)); delete FEntropy; FEntropy = NULL; } }
int main(int argc, char **argv) { char *infile = NULL; Filename infilename; enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; Filename outfilename; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE; int bits = 1024; char *comment = NULL, *origcomment = NULL; int change_passphrase = FALSE; int errs = FALSE, nogo = FALSE; int intype = SSH_KEYTYPE_UNOPENABLE; int sshver = 0; struct ssh2_userkey *ssh2key = NULL; struct RSAKey *ssh1key = NULL; char *ssh2blob = NULL, *ssh2alg = NULL; const struct ssh_signkey *ssh2algf = NULL; int ssh2bloblen; char *passphrase = NULL; int load_encrypted; progfn_t progressfn = is_interactive() ? progress_update : no_progress; /* ------------------------------------------------------------------ * Parse the command line to figure out what we've been asked to do. */ /* * If run with no arguments at all, print the usage message and * return success. */ if (argc <= 1) { usage(); return 0; } /* * Parse command line arguments. */ while (--argc) { char *p = *++argv; if (*p == '-') { /* * An option. */ while (p && *++p) { char c = *p; switch (c) { case '-': /* * Long option. */ { char *opt, *val; opt = p++; /* opt will have _one_ leading - */ while (*p && *p != '=') p++; /* find end of option */ if (*p == '=') { *p++ = '\0'; val = p; } else val = NULL; if (!strcmp(opt, "-help")) { help(); nogo = TRUE; } else if (!strcmp(opt, "-version")) { showversion(); nogo = TRUE; } /* * A sample option requiring an argument: * * else if (!strcmp(opt, "-output")) { * if (!val) * errs = TRUE, error(err_optnoarg, opt); * else * ofile = val; * } */ else { errs = TRUE; fprintf(stderr, "puttygen: no such option `--%s'\n", opt); } } p = NULL; break; case 'h': case 'V': case 'P': case 'l': case 'L': case 'p': case 'q': /* * Option requiring no parameter. */ switch (c) { case 'h': help(); nogo = TRUE; break; case 'V': showversion(); nogo = TRUE; break; case 'P': change_passphrase = TRUE; break; case 'l': outtype = FP; break; case 'L': outtype = PUBLICO; break; case 'p': outtype = PUBLIC; break; case 'q': progressfn = no_progress; break; } break; case 't': case 'b': case 'C': case 'O': case 'o': /* * Option requiring parameter. */ p++; if (!*p && argc > 1) --argc, p = *++argv; else if (!*p) { fprintf(stderr, "puttygen: option `-%c' expects a" " parameter\n", c); errs = TRUE; } /* * Now c is the option and p is the parameter. */ switch (c) { case 't': if (!strcmp(p, "rsa") || !strcmp(p, "rsa2")) keytype = RSA2, sshver = 2; else if (!strcmp(p, "rsa1")) keytype = RSA1, sshver = 1; else if (!strcmp(p, "dsa") || !strcmp(p, "dss")) keytype = DSA, sshver = 2; else { fprintf(stderr, "puttygen: unknown key type `%s'\n", p); errs = TRUE; } break; case 'b': bits = atoi(p); break; case 'C': comment = p; break; case 'O': if (!strcmp(p, "public")) outtype = PUBLIC; else if (!strcmp(p, "public-openssh")) outtype = PUBLICO; else if (!strcmp(p, "private")) outtype = PRIVATE; else if (!strcmp(p, "fingerprint")) outtype = FP; else if (!strcmp(p, "private-openssh")) outtype = OPENSSH, sshver = 2; else if (!strcmp(p, "private-sshcom")) outtype = SSHCOM, sshver = 2; else { fprintf(stderr, "puttygen: unknown output type `%s'\n", p); errs = TRUE; } break; case 'o': outfile = p; break; } p = NULL; /* prevent continued processing */ break; default: /* * Unrecognised option. */ errs = TRUE; fprintf(stderr, "puttygen: no such option `-%c'\n", c); break; } } } else { /* * A non-option argument. */ if (!infile) infile = p; else { errs = TRUE; fprintf(stderr, "puttygen: cannot handle more than one" " input file\n"); } } } if (errs) return 1; if (nogo) return 0; /* * If run with at least one argument _but_ not the required * ones, print the usage message and return failure. */ if (!infile && keytype == NOKEYGEN) { usage(); return 1; } /* ------------------------------------------------------------------ * Figure out further details of exactly what we're going to do. */ /* * Bomb out if we've been asked to both load and generate a * key. */ if (keytype != NOKEYGEN && intype) { fprintf(stderr, "puttygen: cannot both load and generate a key\n"); return 1; } /* * Analyse the type of the input file, in case this affects our * course of action. */ if (infile) { infilename = filename_from_str(infile); intype = key_type(&infilename); switch (intype) { /* * It would be nice here to be able to load _public_ * key files, in any of a number of forms, and (a) * convert them to other public key types, (b) print * out their fingerprints. Or, I suppose, for real * orthogonality, (c) change their comment! * * In fact this opens some interesting possibilities. * Suppose ssh2_userkey_loadpub() were able to load * public key files as well as extracting the public * key from private ones. And suppose I did the thing * I've been wanting to do, where specifying a * particular private key file for authentication * causes any _other_ key in the agent to be discarded. * Then, if you had an agent forwarded to the machine * you were running Unix PuTTY or Plink on, and you * needed to specify which of the keys in the agent it * should use, you could do that by supplying a * _public_ key file, thus not needing to trust even * your encrypted private key file to the network. Ooh! */ case SSH_KEYTYPE_UNOPENABLE: case SSH_KEYTYPE_UNKNOWN: fprintf(stderr, "puttygen: unable to load file `%s': %s\n", infile, key_type_to_str(intype)); return 1; case SSH_KEYTYPE_SSH1: if (sshver == 2) { fprintf(stderr, "puttygen: conversion from SSH1 to SSH2 keys" " not supported\n"); return 1; } sshver = 1; break; case SSH_KEYTYPE_SSH2: case SSH_KEYTYPE_OPENSSH: case SSH_KEYTYPE_SSHCOM: if (sshver == 1) { fprintf(stderr, "puttygen: conversion from SSH2 to SSH1 keys" " not supported\n"); return 1; } sshver = 2; break; } } /* * Determine the default output file, if none is provided. * * This will usually be equal to stdout, except that if the * input and output file formats are the same then the default * output is to overwrite the input. * * Also in this code, we bomb out if the input and output file * formats are the same and no other action is performed. */ if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) || (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) || (intype == SSH_KEYTYPE_OPENSSH && outtype == OPENSSH) || (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) { if (!outfile) { outfile = infile; outfiletmp = dupcat(outfile, ".tmp", NULL); } if (!change_passphrase && !comment) { fprintf(stderr, "puttygen: this command would perform no useful" " action\n"); return 1; } } else { if (!outfile) { /* * Bomb out rather than automatically choosing to write * a private key file to stdout. */ if (outtype==PRIVATE || outtype==OPENSSH || outtype==SSHCOM) { fprintf(stderr, "puttygen: need to specify an output file\n"); return 1; } } } /* * Figure out whether we need to load the encrypted part of the * key. This will be the case if either (a) we need to write * out a private key format, or (b) the entire input key file * is encrypted. */ if (outtype == PRIVATE || outtype == OPENSSH || outtype == SSHCOM || intype == SSH_KEYTYPE_OPENSSH || intype == SSH_KEYTYPE_SSHCOM) load_encrypted = TRUE; else load_encrypted = FALSE; /* ------------------------------------------------------------------ * Now we're ready to actually do some stuff. */ /* * Either load or generate a key. */ if (keytype != NOKEYGEN) { char *entropy; char default_comment[80]; time_t t; struct tm *tm; struct progress prog; prog.phase = -1; prog.current = -1; time(&t); tm = localtime(&t); if (keytype == DSA) strftime(default_comment, 30, "dsa-key-%Y%m%d", tm); else strftime(default_comment, 30, "rsa-key-%Y%m%d", tm); random_init(); entropy = get_random_data(bits / 8); random_add_heavynoise(entropy, bits / 8); memset(entropy, 0, bits/8); sfree(entropy); if (keytype == DSA) { struct dss_key *dsskey = snew(struct dss_key); dsa_generate(dsskey, bits, progressfn, &prog); ssh2key = snew(struct ssh2_userkey); ssh2key->data = dsskey; ssh2key->alg = &ssh_dss; ssh1key = NULL; } else {
/* * Dialog-box function for the main PuTTYgen dialog box. */ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { enum { controlidstart = 100, IDC_TITLE, IDC_BOX_KEY, IDC_NOKEY, IDC_GENERATING, IDC_PROGRESS, IDC_PKSTATIC, IDC_KEYDISPLAY, IDC_FPSTATIC, IDC_FINGERPRINT, IDC_COMMENTSTATIC, IDC_COMMENTEDIT, IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, IDC_BOX_ACTIONS, IDC_GENSTATIC, IDC_GENERATE, IDC_LOADSTATIC, IDC_LOAD, IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB, IDC_BOX_PARAMS, IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA, IDC_BITSSTATIC, IDC_BITS, IDC_ABOUT, }; static const int nokey_ids[] = { IDC_NOKEY, 0 }; static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 }; static const int gotkey_ids[] = { IDC_PKSTATIC, IDC_KEYDISPLAY, IDC_FPSTATIC, IDC_FINGERPRINT, IDC_COMMENTSTATIC, IDC_COMMENTEDIT, IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0 }; static const char generating_msg[] = "Please wait while a key is generated..."; static const char entropy_msg[] = "Please generate some randomness by moving the mouse over the blank area."; struct MainDlgState *state; switch (msg) { case WM_INITDIALOG: if (help_path) SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP); else { /* * If we add a Help button, this is where we destroy it * if the help file isn't present. */ } requested_help = FALSE; /* * Centre the window. */ { /* centre the window */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, TRUE); } state = smalloc(sizeof(*state)); state->generation_thread_exists = FALSE; state->collecting_entropy = FALSE; state->entropy = NULL; state->key_exists = FALSE; SetWindowLong(hwnd, GWL_USERDATA, (LONG) state); { struct ctlpos cp, cp2; /* Accelerators used: acglops1rbd */ ctlposinit(&cp, hwnd, 4, 4, 4); bartitle(&cp, "Public and private key generation for PuTTY", IDC_TITLE); beginbox(&cp, "Key", IDC_BOX_KEY); cp2 = cp; statictext(&cp2, "No key.", 1, IDC_NOKEY); cp2 = cp; statictext(&cp2, "", 1, IDC_GENERATING); progressbar(&cp2, IDC_PROGRESS); bigeditctrl(&cp, "&Public key for pasting into authorized_keys file:", IDC_PKSTATIC, IDC_KEYDISPLAY, 5); SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0); staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC, IDC_FINGERPRINT, 75); SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1, 0); staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC, IDC_COMMENTEDIT, 75); staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, 75); staticpassedit(&cp, "C&onfirm passphrase:", IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75); endbox(&cp); beginbox(&cp, "Actions", IDC_BOX_ACTIONS); staticbtn(&cp, "Generate a public/private key pair", IDC_GENSTATIC, "&Generate", IDC_GENERATE); staticbtn(&cp, "Load an existing private key file", IDC_LOADSTATIC, "&Load", IDC_LOAD); static2btn(&cp, "Save the generated key", IDC_SAVESTATIC, "Save p&ublic key", IDC_SAVEPUB, "&Save private key", IDC_SAVE); endbox(&cp); beginbox(&cp, "Parameters", IDC_BOX_PARAMS); radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3, "SSH&1 (RSA)", IDC_KEYSSH1, "SSH2 &RSA", IDC_KEYSSH2RSA, "SSH2 &DSA", IDC_KEYSSH2DSA, NULL); staticedit(&cp, "Number of &bits in a generated key:", IDC_BITSSTATIC, IDC_BITS, 20); endbox(&cp); } CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1); SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE); /* * Initially, hide the progress bar and the key display, * and show the no-key display. Also disable the Save * buttons, because with no key we obviously can't save * anything. */ hidemany(hwnd, nokey_ids, FALSE); hidemany(hwnd, generating_ids, TRUE); hidemany(hwnd, gotkey_ids, TRUE); EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0); EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0); return 1; case WM_MOUSEMOVE: state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (state->collecting_entropy && state->entropy && state->entropy_got < state->entropy_required) { state->entropy[state->entropy_got++] = lParam; state->entropy[state->entropy_got++] = GetMessageTime(); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, state->entropy_got, 0); if (state->entropy_got >= state->entropy_required) { struct rsa_key_thread_params *params; DWORD threadid; /* * Seed the entropy pool */ random_add_heavynoise(state->entropy, state->entropy_size); memset(state->entropy, 0, state->entropy_size); sfree(state->entropy); state->collecting_entropy = FALSE; SetDlgItemText(hwnd, IDC_GENERATING, generating_msg); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); params = smalloc(sizeof(*params)); params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS); params->dialog = hwnd; params->keysize = state->keysize; params->is_dsa = state->is_dsa; params->key = &state->key; params->dsskey = &state->dsskey; if (!CreateThread(NULL, 0, generate_rsa_key_thread, params, 0, &threadid)) { MessageBox(hwnd, "Out of thread resources", "Key generation error", MB_OK | MB_ICONERROR); sfree(params); } else { state->generation_thread_exists = TRUE; } } } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_COMMENTEDIT: if (HIWORD(wParam) == EN_CHANGE) { state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (state->key_exists) { HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT); int len = GetWindowTextLength(editctl); if (*state->commentptr) sfree(*state->commentptr); *state->commentptr = smalloc(len + 1); GetWindowText(editctl, *state->commentptr, len + 1); if (state->ssh2) { setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->ssh2key); } else { setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->key); } } } break; case IDC_ABOUT: EnableWindow(hwnd, 0); DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc); EnableWindow(hwnd, 1); SetActiveWindow(hwnd); return 0; case IDC_GENERATE: state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (!state->generation_thread_exists) { BOOL ok; state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE); if (!ok) state->keysize = DEFAULT_KEYSIZE; /* If we ever introduce a new key type, check it here! */ state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1); state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA); if (state->keysize < 256) { int ret = MessageBox(hwnd, "PuTTYgen will not generate a key" " smaller than 256 bits.\n" "Key length reset to 256. Continue?", "PuTTYgen Warning", MB_ICONWARNING | MB_OKCANCEL); if (ret != IDOK) break; state->keysize = 256; SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE); } hidemany(hwnd, nokey_ids, TRUE); hidemany(hwnd, generating_ids, FALSE); hidemany(hwnd, gotkey_ids, TRUE); EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0); EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0); EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0); EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0); state->key_exists = FALSE; SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg); state->collecting_entropy = TRUE; /* * My brief statistical tests on mouse movements * suggest that there are about 2.5 bits of * randomness in the x position, 2.5 in the y * position, and 1.7 in the message time, making * 5.7 bits of unpredictability per mouse movement. * However, other people have told me it's far less * than that, so I'm going to be stupidly cautious * and knock that down to a nice round 2. With this * method, we require two words per mouse movement, * so with 2 bits per mouse movement we expect 2 * bits every 2 words. */ state->entropy_required = (state->keysize / 2) * 2; state->entropy_got = 0; state->entropy_size = (state->entropy_required * sizeof(*state->entropy)); state->entropy = smalloc(state->entropy_size); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, state->entropy_required)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); } break; case IDC_SAVE: state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (state->key_exists) { char filename[FILENAME_MAX]; char passphrase[PASSPHRASE_MAXLEN]; char passphrase2[PASSPHRASE_MAXLEN]; GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, passphrase, sizeof(passphrase)); GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, passphrase2, sizeof(passphrase2)); if (strcmp(passphrase, passphrase2)) { MessageBox(hwnd, "The two passphrases given do not match.", "PuTTYgen Error", MB_OK | MB_ICONERROR); break; } if (!*passphrase) { int ret; ret = MessageBox(hwnd, "Are you sure you want to save this key\n" "without a passphrase to protect it?", "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); if (ret != IDYES) break; } if (prompt_keyfile(hwnd, "Save private key as:", filename, 1)) { int ret; FILE *fp = fopen(filename, "r"); if (fp) { char buffer[FILENAME_MAX + 80]; fclose(fp); sprintf(buffer, "Overwrite existing file\n%.*s?", FILENAME_MAX, filename); ret = MessageBox(hwnd, buffer, "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); if (ret != IDYES) break; } if (state->ssh2) { ret = ssh2_save_userkey(filename, &state->ssh2key, *passphrase ? passphrase : NULL); } else { ret = saversakey(filename, &state->key, *passphrase ? passphrase : NULL); } if (ret <= 0) { MessageBox(hwnd, "Unable to save key file", "PuTTYgen Error", MB_OK | MB_ICONERROR); } } } break; case IDC_SAVEPUB: state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (state->key_exists) { char filename[FILENAME_MAX]; if (prompt_keyfile(hwnd, "Save public key as:", filename, 1)) { int ret; FILE *fp = fopen(filename, "r"); if (fp) { char buffer[FILENAME_MAX + 80]; fclose(fp); sprintf(buffer, "Overwrite existing file\n%.*s?", FILENAME_MAX, filename); ret = MessageBox(hwnd, buffer, "PuTTYgen Warning", MB_YESNO | MB_ICONWARNING); if (ret != IDYES) break; } if (state->ssh2) { ret = save_ssh2_pubkey(filename, &state->ssh2key); } else { ret = save_ssh1_pubkey(filename, &state->key); } if (ret <= 0) { MessageBox(hwnd, "Unable to save key file", "PuTTYgen Error", MB_OK | MB_ICONERROR); } } } break; case IDC_LOAD: state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (!state->generation_thread_exists) { char filename[FILENAME_MAX]; if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) { char passphrase[PASSPHRASE_MAXLEN]; int needs_pass; int ver; int ret; char *comment; struct PassphraseProcStruct pps; struct RSAKey newkey1; struct ssh2_userkey *newkey2 = NULL; ver = keyfile_version(filename); if (ver == 0) { MessageBox(NULL, "Couldn't load private key.", "PuTTYgen Error", MB_OK | MB_ICONERROR); break; } comment = NULL; if (ver == 1) needs_pass = rsakey_encrypted(filename, &comment); else needs_pass = ssh2_userkey_encrypted(filename, &comment); pps.passphrase = passphrase; pps.comment = comment; do { if (needs_pass) { int dlgret; dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), NULL, PassphraseProc, (LPARAM) & pps); if (!dlgret) { ret = -2; break; } } else *passphrase = '\0'; if (ver == 1) ret = loadrsakey(filename, &newkey1, passphrase); else { newkey2 = ssh2_load_userkey(filename, passphrase); if (newkey2 == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!newkey2) ret = 0; else ret = 1; } } while (ret == -1); if (comment) sfree(comment); if (ret == 0) { MessageBox(NULL, "Couldn't load private key.", "PuTTYgen Error", MB_OK | MB_ICONERROR); } else if (ret == 1) { EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1); EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1); EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1); EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); /* * Now update the key controls with all the * key data. */ { SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, passphrase); SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, passphrase); if (ver == 1) { char buf[128]; char *savecomment; state->ssh2 = FALSE; state->commentptr = &state->key.comment; state->key = newkey1; /* * Set the key fingerprint. */ savecomment = state->key.comment; state->key.comment = NULL; rsa_fingerprint(buf, sizeof(buf), &state->key); state->key.comment = savecomment; SetDlgItemText(hwnd, IDC_FINGERPRINT, buf); /* * Construct a decimal representation * of the key, for pasting into * .ssh/authorized_keys on a Unix box. */ setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->key); } else { char *fp; char *savecomment; state->ssh2 = TRUE; state->commentptr = &state->ssh2key.comment; state->ssh2key = *newkey2; /* structure copy */ sfree(newkey2); savecomment = state->ssh2key.comment; state->ssh2key.comment = NULL; fp = state->ssh2key.alg-> fingerprint(state->ssh2key.data); state->ssh2key.comment = savecomment; SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); sfree(fp); setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->ssh2key); } SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr); } /* * Finally, hide the progress bar and show * the key data. */ hidemany(hwnd, nokey_ids, TRUE); hidemany(hwnd, generating_ids, TRUE); hidemany(hwnd, gotkey_ids, FALSE); state->key_exists = TRUE; } } } break; } return 0; case WM_DONEKEY: state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); state->generation_thread_exists = FALSE; state->key_exists = TRUE; SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0); EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1); EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1); EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1); EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); if (state->ssh2) { if (state->is_dsa) { state->ssh2key.data = &state->dsskey; state->ssh2key.alg = &ssh_dss; } else { state->ssh2key.data = &state->key; state->ssh2key.alg = &ssh_rsa; } state->commentptr = &state->ssh2key.comment; } else { state->commentptr = &state->key.comment; } /* * Invent a comment for the key. We'll do this by including * the date in it. This will be so horrifyingly ugly that * the user will immediately want to change it, which is * what we want :-) */ *state->commentptr = smalloc(30); { time_t t; struct tm *tm; time(&t); tm = localtime(&t); if (state->is_dsa) strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm); else strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm); } /* * Now update the key controls with all the key data. */ { char *savecomment; /* * Blank passphrase, initially. This isn't dangerous, * because we will warn (Are You Sure?) before allowing * the user to save an unprotected private key. */ SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, ""); SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, ""); /* * Set the comment. */ SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr); /* * Set the key fingerprint. */ savecomment = *state->commentptr; *state->commentptr = NULL; if (state->ssh2) { char *fp; fp = state->ssh2key.alg->fingerprint(state->ssh2key.data); SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); sfree(fp); } else { char buf[128]; rsa_fingerprint(buf, sizeof(buf), &state->key); SetDlgItemText(hwnd, IDC_FINGERPRINT, buf); } *state->commentptr = savecomment; /* * Construct a decimal representation of the key, for * pasting into .ssh/authorized_keys or * .ssh/authorized_keys2 on a Unix box. */ if (state->ssh2) { setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->ssh2key); } else { setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->key); } } /* * Finally, hide the progress bar and show the key data. */ hidemany(hwnd, nokey_ids, TRUE); hidemany(hwnd, generating_ids, TRUE); hidemany(hwnd, gotkey_ids, FALSE); break; case WM_HELP: if (help_path) { int id = ((LPHELPINFO)lParam)->iCtrlId; char *cmd = NULL; switch (id) { case IDC_GENERATING: case IDC_PROGRESS: case IDC_GENSTATIC: case IDC_GENERATE: cmd = "JI(`',`puttygen.generate')"; break; case IDC_PKSTATIC: case IDC_KEYDISPLAY: cmd = "JI(`',`puttygen.pastekey')"; break; case IDC_FPSTATIC: case IDC_FINGERPRINT: cmd = "JI(`',`puttygen.fingerprint')"; break; case IDC_COMMENTSTATIC: case IDC_COMMENTEDIT: cmd = "JI(`',`puttygen.comment')"; break; case IDC_PASSPHRASE1STATIC: case IDC_PASSPHRASE1EDIT: case IDC_PASSPHRASE2STATIC: case IDC_PASSPHRASE2EDIT: cmd = "JI(`',`puttygen.passphrase')"; break; case IDC_LOADSTATIC: case IDC_LOAD: cmd = "JI(`',`puttygen.load')"; break; case IDC_SAVESTATIC: case IDC_SAVE: cmd = "JI(`',`puttygen.savepriv')"; break; case IDC_SAVEPUB: cmd = "JI(`',`puttygen.savepub')"; break; case IDC_TYPESTATIC: case IDC_KEYSSH1: case IDC_KEYSSH2RSA: case IDC_KEYSSH2DSA: cmd = "JI(`',`puttygen.keytype')"; break; case IDC_BITSSTATIC: case IDC_BITS: cmd = "JI(`',`puttygen.bits')"; break; } if (cmd) { WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd); requested_help = TRUE; } else { MessageBeep(0); } } break; case WM_CLOSE: state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); sfree(state); if (requested_help) { WinHelp(hwnd, help_path, HELP_QUIT, 0); requested_help = FALSE; } EndDialog(hwnd, 1); return 0; } return 0; }
/* * Dialog-box function for the main PuTTYgen dialog box. */ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static const char generating_msg[] = "Please wait while a key is generated..."; static const char entropy_msg[] = "Please generate some randomness by moving the mouse over the blank area."; struct MainDlgState *state; switch (msg) { case WM_INITDIALOG: if (help_path) SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP); else { /* * If we add a Help button, this is where we destroy it * if the help file isn't present. */ } requested_help = FALSE; SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200))); state = snew(struct MainDlgState); state->generation_thread_exists = FALSE; state->collecting_entropy = FALSE; state->entropy = NULL; state->key_exists = FALSE; SetWindowLong(hwnd, GWL_USERDATA, (LONG) state); { HMENU menu, menu1; menu = CreateMenu(); menu1 = CreateMenu(); AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key"); AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key"); AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File"); state->filemenu = menu1; menu1 = CreateMenu(); AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key"); state->keymenu = menu1; menu1 = CreateMenu(); AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH, "Export &OpenSSH key"); AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM, "Export &ssh.com key"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "Con&versions"); state->cvtmenu = menu1; menu1 = CreateMenu(); AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About"); if (help_path) AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help"); SetMenu(hwnd, menu); } /* * Centre the window. */ { /* centre the window */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, TRUE); } { struct ctlpos cp, cp2; /* Accelerators used: acglops1rbd */ ctlposinit(&cp, hwnd, 4, 4, 4); beginbox(&cp, "Key", IDC_BOX_KEY); cp2 = cp; statictext(&cp2, "No key.", 1, IDC_NOKEY); cp2 = cp; statictext(&cp2, "", 1, IDC_GENERATING); progressbar(&cp2, IDC_PROGRESS); bigeditctrl(&cp, "&Public key for pasting into authorized_keys file:", IDC_PKSTATIC, IDC_KEYDISPLAY, 5); SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0); staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC, IDC_FINGERPRINT, 75); SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1, 0); staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC, IDC_COMMENTEDIT, 75); staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, 75); staticpassedit(&cp, "C&onfirm passphrase:", IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75); endbox(&cp); beginbox(&cp, "Actions", IDC_BOX_ACTIONS); staticbtn(&cp, "Generate a public/private key pair", IDC_GENSTATIC, "&Generate", IDC_GENERATE); staticbtn(&cp, "Load an existing private key file", IDC_LOADSTATIC, "&Load", IDC_LOAD); static2btn(&cp, "Save the generated key", IDC_SAVESTATIC, "Save p&ublic key", IDC_SAVEPUB, "&Save private key", IDC_SAVE); endbox(&cp); beginbox(&cp, "Parameters", IDC_BOX_PARAMS); radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3, "SSH&1 (RSA)", IDC_KEYSSH1, "SSH2 &RSA", IDC_KEYSSH2RSA, "SSH2 &DSA", IDC_KEYSSH2DSA, NULL); staticedit(&cp, "Number of &bits in a generated key:", IDC_BITSSTATIC, IDC_BITS, 20); endbox(&cp); } CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1); CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1, MF_BYCOMMAND); SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE); /* * Initially, hide the progress bar and the key display, * and show the no-key display. Also disable the Save * buttons, because with no key we obviously can't save * anything. */ ui_set_state(hwnd, state, 0); /* * Load a key file if one was provided on the command line. */ if (cmdline_keyfile) load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0); return 1; case WM_MOUSEMOVE: state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (state->collecting_entropy && state->entropy && state->entropy_got < state->entropy_required) { state->entropy[state->entropy_got++] = lParam; state->entropy[state->entropy_got++] = GetMessageTime(); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, state->entropy_got, 0); if (state->entropy_got >= state->entropy_required) { struct rsa_key_thread_params *params; DWORD threadid; /* * Seed the entropy pool */ random_add_heavynoise(state->entropy, state->entropy_size); memset(state->entropy, 0, state->entropy_size); sfree(state->entropy); state->collecting_entropy = FALSE; SetDlgItemText(hwnd, IDC_GENERATING, generating_msg); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); params = snew(struct rsa_key_thread_params); params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS); params->dialog = hwnd; params->keysize = state->keysize; params->is_dsa = state->is_dsa; params->key = &state->key; params->dsskey = &state->dsskey; if (!CreateThread(NULL, 0, generate_rsa_key_thread, params, 0, &threadid)) { MessageBox(hwnd, "Out of thread resources", "Key generation error", MB_OK | MB_ICONERROR); sfree(params); } else { state->generation_thread_exists = TRUE; } } } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_KEYSSH1: case IDC_KEYSSH2RSA: case IDC_KEYSSH2DSA: { state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (!IsDlgButtonChecked(hwnd, LOWORD(wParam))) CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, LOWORD(wParam)); CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA, LOWORD(wParam), MF_BYCOMMAND); } break; case IDC_QUIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break; case IDC_COMMENTEDIT: if (HIWORD(wParam) == EN_CHANGE) { state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA); if (state->key_exists) { HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT); int len = GetWindowTextLength(editctl); if (*state->commentptr) sfree(*state->commentptr); *state->commentptr = snewn(len + 1, char); GetWindowText(editctl, *state->commentptr, len + 1); if (state->ssh2) { setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->ssh2key); } else { setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->key); } } }
int main(int argc, char **argv) { char *infile = NULL; Filename *infilename = NULL, *outfilename = NULL; enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, ED25519 } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH_AUTO, OPENSSH_NEW, SSHCOM } outtype = PRIVATE; int bits = -1; char *comment = NULL, *origcomment = NULL; int change_passphrase = FALSE; int errs = FALSE, nogo = FALSE; int intype = SSH_KEYTYPE_UNOPENABLE; int sshver = 0; struct ssh2_userkey *ssh2key = NULL; struct RSAKey *ssh1key = NULL; unsigned char *ssh2blob = NULL; char *ssh2alg = NULL; const struct ssh_signkey *ssh2algf = NULL; int ssh2bloblen; char *old_passphrase = NULL, *new_passphrase = NULL; int load_encrypted; progfn_t progressfn = is_interactive() ? progress_update : no_progress; const char *random_device = NULL; /* ------------------------------------------------------------------ * Parse the command line to figure out what we've been asked to do. */ /* * If run with no arguments at all, print the usage message and * return success. */ if (argc <= 1) { usage(TRUE); return 0; } /* * Parse command line arguments. */ while (--argc) { char *p = *++argv; if (*p == '-') { /* * An option. */ while (p && *++p) { char c = *p; switch (c) { case '-': /* * Long option. */ { char *opt, *val; opt = p++; /* opt will have _one_ leading - */ while (*p && *p != '=') p++; /* find end of option */ if (*p == '=') { *p++ = '\0'; val = p; } else val = NULL; if (!strcmp(opt, "-help")) { if (val) { errs = TRUE; fprintf(stderr, "puttygen: option `-%s'" " expects no argument\n", opt); } else { help(); nogo = TRUE; } } else if (!strcmp(opt, "-version")) { if (val) { errs = TRUE; fprintf(stderr, "puttygen: option `-%s'" " expects no argument\n", opt); } else { showversion(); nogo = TRUE; } } else if (!strcmp(opt, "-pgpfp")) { if (val) { errs = TRUE; fprintf(stderr, "puttygen: option `-%s'" " expects no argument\n", opt); } else { /* support --pgpfp for consistency */ pgp_fingerprints(); nogo = TRUE; } } else if (!strcmp(opt, "-old-passphrase")) { if (!val && argc > 1) --argc, val = *++argv; if (!val) { errs = TRUE; fprintf(stderr, "puttygen: option `-%s'" " expects an argument\n", opt); } else { old_passphrase = readpassphrase(val); if (!old_passphrase) errs = TRUE; } } else if (!strcmp(opt, "-new-passphrase")) { if (!val && argc > 1) --argc, val = *++argv; if (!val) { errs = TRUE; fprintf(stderr, "puttygen: option `-%s'" " expects an argument\n", opt); } else { new_passphrase = readpassphrase(val); if (!new_passphrase) errs = TRUE; } } else if (!strcmp(opt, "-random-device")) { if (!val && argc > 1) --argc, val = *++argv; if (!val) { errs = TRUE; fprintf(stderr, "puttygen: option `-%s'" " expects an argument\n", opt); } else { random_device = val; } } else { errs = TRUE; fprintf(stderr, "puttygen: no such option `-%s'\n", opt); } } p = NULL; break; case 'h': case 'V': case 'P': case 'l': case 'L': case 'p': case 'q': /* * Option requiring no parameter. */ switch (c) { case 'h': help(); nogo = TRUE; break; case 'V': showversion(); nogo = TRUE; break; case 'P': change_passphrase = TRUE; break; case 'l': outtype = FP; break; case 'L': outtype = PUBLICO; break; case 'p': outtype = PUBLIC; break; case 'q': progressfn = no_progress; break; } break; case 't': case 'b': case 'C': case 'O': case 'o': /* * Option requiring parameter. */ p++; if (!*p && argc > 1) --argc, p = *++argv; else if (!*p) { fprintf(stderr, "puttygen: option `-%c' expects a" " parameter\n", c); errs = TRUE; } /* * Now c is the option and p is the parameter. */ switch (c) { case 't': if (!strcmp(p, "rsa") || !strcmp(p, "rsa2")) keytype = RSA2, sshver = 2; else if (!strcmp(p, "rsa1")) keytype = RSA1, sshver = 1; else if (!strcmp(p, "dsa") || !strcmp(p, "dss")) keytype = DSA, sshver = 2; else if (!strcmp(p, "ecdsa")) keytype = ECDSA, sshver = 2; else if (!strcmp(p, "ed25519")) keytype = ED25519, sshver = 2; else { fprintf(stderr, "puttygen: unknown key type `%s'\n", p); errs = TRUE; } break; case 'b': bits = atoi(p); break; case 'C': comment = p; break; case 'O': if (!strcmp(p, "public")) outtype = PUBLIC; else if (!strcmp(p, "public-openssh")) outtype = PUBLICO; else if (!strcmp(p, "private")) outtype = PRIVATE; else if (!strcmp(p, "fingerprint")) outtype = FP; else if (!strcmp(p, "private-openssh")) outtype = OPENSSH_AUTO, sshver = 2; else if (!strcmp(p, "private-openssh-new")) outtype = OPENSSH_NEW, sshver = 2; else if (!strcmp(p, "private-sshcom")) outtype = SSHCOM, sshver = 2; else { fprintf(stderr, "puttygen: unknown output type `%s'\n", p); errs = TRUE; } break; case 'o': outfile = p; break; } p = NULL; /* prevent continued processing */ break; default: /* * Unrecognised option. */ errs = TRUE; fprintf(stderr, "puttygen: no such option `-%c'\n", c); break; } } } else { /* * A non-option argument. */ if (!infile) infile = p; else { errs = TRUE; fprintf(stderr, "puttygen: cannot handle more than one" " input file\n"); } } } if (bits == -1) { /* * No explicit key size was specified. Default varies * depending on key type. */ switch (keytype) { case ECDSA: bits = 384; break; case ED25519: bits = 256; break; default: bits = DEFAULT_RSADSA_BITS; break; } } if (keytype == ECDSA && (bits != 256 && bits != 384 && bits != 521)) { fprintf(stderr, "puttygen: invalid bits for ECDSA, choose 256, 384 or 521\n"); errs = TRUE; } if (keytype == ED25519 && (bits != 256)) { fprintf(stderr, "puttygen: invalid bits for ED25519, choose 256\n"); errs = TRUE; } if (keytype == RSA2 || keytype == RSA1 || keytype == DSA) { if (bits < 256) { fprintf(stderr, "puttygen: cannot generate %s keys shorter than" " 256 bits\n", (keytype == DSA ? "DSA" : "RSA")); errs = TRUE; } else if (bits < DEFAULT_RSADSA_BITS) { fprintf(stderr, "puttygen: warning: %s keys shorter than" " %d bits are probably not secure\n", (keytype == DSA ? "DSA" : "RSA"), DEFAULT_RSADSA_BITS); /* but this is just a warning, so proceed anyway */ } } if (errs) return 1; if (nogo) return 0; /* * If run with at least one argument _but_ not the required * ones, print the usage message and return failure. */ if (!infile && keytype == NOKEYGEN) { usage(TRUE); return 1; } /* ------------------------------------------------------------------ * Figure out further details of exactly what we're going to do. */ /* * Bomb out if we've been asked to both load and generate a * key. */ if (keytype != NOKEYGEN && infile) { fprintf(stderr, "puttygen: cannot both load and generate a key\n"); return 1; } /* * We must save the private part when generating a new key. */ if (keytype != NOKEYGEN && (outtype != PRIVATE && outtype != OPENSSH_AUTO && outtype != OPENSSH_NEW && outtype != SSHCOM)) { fprintf(stderr, "puttygen: this would generate a new key but " "discard the private part\n"); return 1; } /* * Analyse the type of the input file, in case this affects our * course of action. */ if (infile) { infilename = filename_from_str(infile); intype = key_type(infilename); switch (intype) { case SSH_KEYTYPE_UNOPENABLE: case SSH_KEYTYPE_UNKNOWN: fprintf(stderr, "puttygen: unable to load file `%s': %s\n", infile, key_type_to_str(intype)); return 1; case SSH_KEYTYPE_SSH1: case SSH_KEYTYPE_SSH1_PUBLIC: if (sshver == 2) { fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys" " not supported\n"); return 1; } sshver = 1; break; case SSH_KEYTYPE_SSH2: case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: case SSH_KEYTYPE_OPENSSH_PEM: case SSH_KEYTYPE_OPENSSH_NEW: case SSH_KEYTYPE_SSHCOM: if (sshver == 1) { fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys" " not supported\n"); return 1; } sshver = 2; break; case SSH_KEYTYPE_OPENSSH_AUTO: default: assert(0 && "Should never see these types on an input file"); } } /* * Determine the default output file, if none is provided. * * This will usually be equal to stdout, except that if the * input and output file formats are the same then the default * output is to overwrite the input. * * Also in this code, we bomb out if the input and output file * formats are the same and no other action is performed. */ if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) || (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) || (intype == SSH_KEYTYPE_OPENSSH_PEM && outtype == OPENSSH_AUTO) || (intype == SSH_KEYTYPE_OPENSSH_NEW && outtype == OPENSSH_NEW) || (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) { if (!outfile) { outfile = infile; outfiletmp = dupcat(outfile, ".tmp", NULL); } if (!change_passphrase && !comment) { fprintf(stderr, "puttygen: this command would perform no useful" " action\n"); return 1; } } else { if (!outfile) { /* * Bomb out rather than automatically choosing to write * a private key file to stdout. */ if (outtype == PRIVATE || outtype == OPENSSH_AUTO || outtype == OPENSSH_NEW || outtype == SSHCOM) { fprintf(stderr, "puttygen: need to specify an output file\n"); return 1; } } } /* * Figure out whether we need to load the encrypted part of the * key. This will be the case if either (a) we need to write * out a private key format, or (b) the entire input key file * is encrypted. */ if (outtype == PRIVATE || outtype == OPENSSH_AUTO || outtype == OPENSSH_NEW || outtype == SSHCOM || intype == SSH_KEYTYPE_OPENSSH_PEM || intype == SSH_KEYTYPE_OPENSSH_NEW || intype == SSH_KEYTYPE_SSHCOM) load_encrypted = TRUE; else load_encrypted = FALSE; if (load_encrypted && (intype == SSH_KEYTYPE_SSH1_PUBLIC || intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 || intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)) { fprintf(stderr, "puttygen: cannot perform this action on a " "public-key-only input file\n"); return 1; } /* ------------------------------------------------------------------ * Now we're ready to actually do some stuff. */ /* * Either load or generate a key. */ if (keytype != NOKEYGEN) { char *entropy; char default_comment[80]; struct tm tm; struct progress prog; prog.phase = -1; prog.current = -1; tm = ltime(); if (keytype == DSA) strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm); else if (keytype == ECDSA) strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm); else if (keytype == ED25519) strftime(default_comment, 30, "ed25519-key-%Y%m%d", &tm); else strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm); random_ref(); entropy = get_random_data(bits / 8, random_device); if (!entropy) { fprintf(stderr, "puttygen: failed to collect entropy, " "could not generate key\n"); return 1; } random_add_heavynoise(entropy, bits / 8); smemclr(entropy, bits/8); sfree(entropy); if (keytype == DSA) { struct dss_key *dsskey = snew(struct dss_key); dsa_generate(dsskey, bits, progressfn, &prog); ssh2key = snew(struct ssh2_userkey); ssh2key->data = dsskey; ssh2key->alg = &ssh_dss; ssh1key = NULL; } else if (keytype == ECDSA) {