xref: /openssh-portable/ssh-keygen.c (revision 31d8d231)
1*31d8d231Sdjm@openbsd.org /* $OpenBSD: ssh-keygen.c,v 1.429 2021/04/03 06:18:41 djm Exp $ */
2d4a8b7e3SDamien Miller /*
395def098SDamien Miller  * Author: Tatu Ylonen <ylo@cs.hut.fi>
495def098SDamien Miller  * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
595def098SDamien Miller  *                    All rights reserved
695def098SDamien Miller  * Identity and host key generation and maintenance.
7e4340be5SDamien Miller  *
8e4340be5SDamien Miller  * As far as I am concerned, the code I have written for this software
9e4340be5SDamien Miller  * can be used freely for any purpose.  Any derived versions of this
10e4340be5SDamien Miller  * software must be clearly marked as such, and if the derived work is
11e4340be5SDamien Miller  * incompatible with the protocol description in the RFC file, it must be
12e4340be5SDamien Miller  * called by a name other than "ssh" or "Secure Shell".
13d4a8b7e3SDamien Miller  */
14d4a8b7e3SDamien Miller 
15d4a8b7e3SDamien Miller #include "includes.h"
16f17883e6SDamien Miller 
17f17883e6SDamien Miller #include <sys/types.h>
18e3b60b52SDamien Miller #include <sys/socket.h>
19f17883e6SDamien Miller #include <sys/stat.h>
20d4a8b7e3SDamien Miller 
2172ef7c14SDamien Miller #ifdef WITH_OPENSSL
22eba71babSDamien Miller #include <openssl/evp.h>
23eba71babSDamien Miller #include <openssl/pem.h>
24bfaaf960SDarren Tucker #include "openbsd-compat/openssl-compat.h"
2572ef7c14SDamien Miller #endif
26eba71babSDamien Miller 
27cfc1897aSDamien Miller #ifdef HAVE_STDINT_H
28be02d7cbSdjm@openbsd.org # include <stdint.h>
29cfc1897aSDamien Miller #endif
3039972493SDarren Tucker #include <errno.h>
3157cf6385SDamien Miller #include <fcntl.h>
32be43ebf9SDamien Miller #include <netdb.h>
332ee50c5cSDarren Tucker #ifdef HAVE_PATHS_H
349f2abc47SDamien Miller # include <paths.h>
352ee50c5cSDarren Tucker #endif
369f2abc47SDamien Miller #include <pwd.h>
37ded319ccSDamien Miller #include <stdarg.h>
38a7a73ee3SDamien Miller #include <stdio.h>
39e7a1e5cfSDamien Miller #include <stdlib.h>
40e3476ed0SDamien Miller #include <string.h>
41e6b3b610SDamien Miller #include <unistd.h>
422ae4f337Sderaadt@openbsd.org #include <limits.h>
43a287c5adSdjm@openbsd.org #include <locale.h>
444d3b2f36SDamien Miller #include <time.h>
459f2abc47SDamien Miller 
46d4a8b7e3SDamien Miller #include "xmalloc.h"
471129dcfcSdjm@openbsd.org #include "sshkey.h"
48eba71babSDamien Miller #include "authfile.h"
491129dcfcSdjm@openbsd.org #include "sshbuf.h"
50226cfa03SBen Lindstrom #include "pathnames.h"
51226cfa03SBen Lindstrom #include "log.h"
52e608ca29SDarren Tucker #include "misc.h"
534b42d7f1SDamien Miller #include "match.h"
544b42d7f1SDamien Miller #include "hostfile.h"
5569996104SDamien Miller #include "dns.h"
56f3747bf4SDamien Miller #include "ssh.h"
570a80ca19SDamien Miller #include "ssh2.h"
581129dcfcSdjm@openbsd.org #include "ssherr.h"
597ea845e4SDamien Miller #include "ssh-pkcs11.h"
60f3747bf4SDamien Miller #include "atomicio.h"
61f3747bf4SDamien Miller #include "krl.h"
6256d1c83cSdjm@openbsd.org #include "digest.h"
63a287c5adSdjm@openbsd.org #include "utf8.h"
64a98339edSdjm@openbsd.org #include "authfd.h"
652a9c9f72Sdjm@openbsd.org #include "sshsig.h"
6623f38c2dSdjm@openbsd.org #include "ssh-sk.h"
6723f38c2dSdjm@openbsd.org #include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */
68b755264eSdtucker@openbsd.org #include "cipher.h"
69cd392284SBen Lindstrom 
70d1958793Sdjm@openbsd.org #ifdef WITH_OPENSSL
71d1958793Sdjm@openbsd.org # define DEFAULT_KEY_TYPE_NAME "rsa"
72d1958793Sdjm@openbsd.org #else
73d1958793Sdjm@openbsd.org # define DEFAULT_KEY_TYPE_NAME "ed25519"
74d1958793Sdjm@openbsd.org #endif
76f47269eaSdtucker@openbsd.org /*
7726e0cef0Sdtucker@openbsd.org  * Default number of bits in the RSA, DSA and ECDSA keys.  These value can be
7826e0cef0Sdtucker@openbsd.org  * overridden on the command line.
7926e0cef0Sdtucker@openbsd.org  *
8026e0cef0Sdtucker@openbsd.org  * These values, with the exception of DSA, provide security equivalent to at
8126e0cef0Sdtucker@openbsd.org  * least 128 bits of security according to NIST Special Publication 800-57:
8226e0cef0Sdtucker@openbsd.org  * Recommendation for Key Management Part 1 rev 4 section 5.6.1.
8326e0cef0Sdtucker@openbsd.org  * For DSA it (and FIPS-186-4 section 4.2) specifies that the only size for
8426e0cef0Sdtucker@openbsd.org  * which a 160bit hash is acceptable is 1kbit, and since ssh-dss specifies only
8526e0cef0Sdtucker@openbsd.org  * SHA1 we limit the DSA key size 1k bits.
86f47269eaSdtucker@openbsd.org  */
87f47269eaSdtucker@openbsd.org #define DEFAULT_BITS		3072
883f54a9f5SDamien Miller #define DEFAULT_BITS_DSA	1024
896e9f680cSDamien Miller #define DEFAULT_BITS_ECDSA	256
90d4a8b7e3SDamien Miller 
91851f8032Sdjm@openbsd.org static int quiet = 0;
92f2b70cadSDamien Miller 
9310f6f6baSDamien Miller /* Flag indicating that we just want to see the key fingerprint */
94851f8032Sdjm@openbsd.org static int print_fingerprint = 0;
95851f8032Sdjm@openbsd.org static int print_bubblebabble = 0;
9610f6f6baSDamien Miller 
9756d1c83cSdjm@openbsd.org /* Hash algorithm to use for fingerprints. */
98851f8032Sdjm@openbsd.org static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
100431f66b6SDamien Miller /* The identity file name, given on the command line or entered by the user. */
1013e53ef28Sderaadt@openbsd.org static char identity_file[PATH_MAX];
102851f8032Sdjm@openbsd.org static int have_identity = 0;
103d4a8b7e3SDamien Miller 
104d4a8b7e3SDamien Miller /* This is set to the passphrase if given on the command line. */
105851f8032Sdjm@openbsd.org static char *identity_passphrase = NULL;
106d4a8b7e3SDamien Miller 
107d4a8b7e3SDamien Miller /* This is set to the new passphrase if given on the command line. */
108851f8032Sdjm@openbsd.org static char *identity_new_passphrase = NULL;
1094e270b05SDamien Miller 
1100a80ca19SDamien Miller /* Key type when certifying */
111851f8032Sdjm@openbsd.org static u_int cert_key_type = SSH2_CERT_TYPE_USER;
1120a80ca19SDamien Miller 
1130a80ca19SDamien Miller /* "key ID" of signed key */
114851f8032Sdjm@openbsd.org static char *cert_key_id = NULL;
1150a80ca19SDamien Miller 
1160a80ca19SDamien Miller /* Comma-separated list of principal names for certifying keys */
117851f8032Sdjm@openbsd.org static char *cert_principals = NULL;
1180a80ca19SDamien Miller 
1190a80ca19SDamien Miller /* Validity period for certificates */
120851f8032Sdjm@openbsd.org static u_int64_t cert_valid_from = 0;
121851f8032Sdjm@openbsd.org static u_int64_t cert_valid_to = ~0ULL;
1220a80ca19SDamien Miller 
1234e270b05SDamien Miller /* Certificate options */
124d0e4a8e2SDamien Miller #define CERTOPT_X_FWD				(1)
125d0e4a8e2SDamien Miller #define CERTOPT_AGENT_FWD			(1<<1)
126d0e4a8e2SDamien Miller #define CERTOPT_PORT_FWD			(1<<2)
127d0e4a8e2SDamien Miller #define CERTOPT_PTY				(1<<3)
128d0e4a8e2SDamien Miller #define CERTOPT_USER_RC				(1<<4)
1292e71263bSdjm@openbsd.org #define CERTOPT_NO_REQUIRE_USER_PRESENCE	(1<<5)
132851f8032Sdjm@openbsd.org static u_int32_t certflags_flags = CERTOPT_DEFAULT;
133851f8032Sdjm@openbsd.org static char *certflags_command = NULL;
134851f8032Sdjm@openbsd.org static char *certflags_src_addr = NULL;
1350a80ca19SDamien Miller 
136249516e4Sdjm@openbsd.org /* Arbitrary extensions specified by user */
1372d8a3b7eSdjm@openbsd.org struct cert_ext {
138249516e4Sdjm@openbsd.org 	char *key;
139249516e4Sdjm@openbsd.org 	char *val;
140249516e4Sdjm@openbsd.org 	int crit;
141249516e4Sdjm@openbsd.org };
1422d8a3b7eSdjm@openbsd.org static struct cert_ext *cert_ext;
1432d8a3b7eSdjm@openbsd.org static size_t ncert_ext;
14544b25040SDamien Miller /* Conversion to/from various formats */
14644b25040SDamien Miller enum {
14744b25040SDamien Miller 	FMT_RFC4716,
14844b25040SDamien Miller 	FMT_PKCS8,
14944b25040SDamien Miller 	FMT_PEM
15044b25040SDamien Miller } convert_format = FMT_RFC4716;
1510bc1bd81SDamien Miller 
152851f8032Sdjm@openbsd.org static char *key_type_name = NULL;
153eba71babSDamien Miller 
154757f34e0SDamien Miller /* Load key from this PKCS#11 provider */
155851f8032Sdjm@openbsd.org static char *pkcs11provider = NULL;
15644b25040SDamien Miller 
15723f38c2dSdjm@openbsd.org /* FIDO/U2F provider to use */
15823f38c2dSdjm@openbsd.org static char *sk_provider = NULL;
160eb0d8e70Sdjm@openbsd.org /* Format for writing private keys */
161eb0d8e70Sdjm@openbsd.org static int private_key_format = SSHKEY_PRIVATE_OPENSSH;
162bcd00abdSDamien Miller 
163bcd00abdSDamien Miller /* Cipher for new-format private keys */
164eb0d8e70Sdjm@openbsd.org static char *openssh_format_cipher = NULL;
165bcd00abdSDamien Miller 
1663e60d18fSdjm@openbsd.org /* Number of KDF rounds to derive new format keys. */
167851f8032Sdjm@openbsd.org static int rounds = 0;
168bcd00abdSDamien Miller 
169431f66b6SDamien Miller /* argv0 */
170431f66b6SDamien Miller extern char *__progname;
171d4a8b7e3SDamien Miller 
172851f8032Sdjm@openbsd.org static char hostname[NI_MAXHOST];
173eba71babSDamien Miller 
1741d9a2e28Sdjm@openbsd.org #ifdef WITH_OPENSSL
175770fc010SDarren Tucker /* moduli.c */
176b089fb5fSDamien Miller int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
177dfceafe8SDamien Miller int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long,
178dfceafe8SDamien Miller     unsigned long);
1791d9a2e28Sdjm@openbsd.org #endif
180770fc010SDarren Tucker 
181bba81213SBen Lindstrom static void
type_bits_valid(int type,const char * name,u_int32_t * bitsp)1827efb4557Sdjm@openbsd.org type_bits_valid(int type, const char *name, u_int32_t *bitsp)
18358f1bafbSDamien Miller {
1843038a191Sdjm@openbsd.org 	if (type == KEY_UNSPEC)
1853038a191Sdjm@openbsd.org 		fatal("unknown key type %s", key_type_name);
186884b63a0SDamien Miller 	if (*bitsp == 0) {
187773dda25SDamien Miller #ifdef WITH_OPENSSL
188b12b835dSdjm@openbsd.org 		int nid;
190c31e4f5fSnaddy@openbsd.org 		switch(type) {
191c31e4f5fSnaddy@openbsd.org 		case KEY_DSA:
192884b63a0SDamien Miller 			*bitsp = DEFAULT_BITS_DSA;
193c31e4f5fSnaddy@openbsd.org 			break;
194c31e4f5fSnaddy@openbsd.org 		case KEY_ECDSA:
1957efb4557Sdjm@openbsd.org 			if (name != NULL &&
1967efb4557Sdjm@openbsd.org 			    (nid = sshkey_ecdsa_nid_from_name(name)) > 0)
1977efb4557Sdjm@openbsd.org 				*bitsp = sshkey_curve_nid_to_bits(nid);
1987efb4557Sdjm@openbsd.org 			if (*bitsp == 0)
199884b63a0SDamien Miller 				*bitsp = DEFAULT_BITS_ECDSA;
200c31e4f5fSnaddy@openbsd.org 			break;
201c31e4f5fSnaddy@openbsd.org 		case KEY_RSA:
202884b63a0SDamien Miller 			*bitsp = DEFAULT_BITS;
203c31e4f5fSnaddy@openbsd.org 			break;
204c31e4f5fSnaddy@openbsd.org 		}
205c31e4f5fSnaddy@openbsd.org #endif
20658f1bafbSDamien Miller 	}
20772ef7c14SDamien Miller #ifdef WITH_OPENSSL
208bd636f40Sdjm@openbsd.org 	switch (type) {
209bd636f40Sdjm@openbsd.org 	case KEY_DSA:
210bd636f40Sdjm@openbsd.org 		if (*bitsp != 1024)
211bd636f40Sdjm@openbsd.org 			fatal("Invalid DSA key length: must be 1024 bits");
212bd636f40Sdjm@openbsd.org 		break;
213bd636f40Sdjm@openbsd.org 	case KEY_RSA:
214bd636f40Sdjm@openbsd.org 		if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE)
215bd636f40Sdjm@openbsd.org 			fatal("Invalid RSA key length: minimum is %d bits",
216bd636f40Sdjm@openbsd.org 			    SSH_RSA_MINIMUM_MODULUS_SIZE);
217c31e4f5fSnaddy@openbsd.org 		else if (*bitsp > OPENSSL_RSA_MAX_MODULUS_BITS)
218c31e4f5fSnaddy@openbsd.org 			fatal("Invalid RSA key length: maximum is %d bits",
219c31e4f5fSnaddy@openbsd.org 			    OPENSSL_RSA_MAX_MODULUS_BITS);
220bd636f40Sdjm@openbsd.org 		break;
221bd636f40Sdjm@openbsd.org 	case KEY_ECDSA:
222bd636f40Sdjm@openbsd.org 		if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1)
2239b47b083SManoj Ampalam #ifdef OPENSSL_HAS_NISTP521
224292bcb24SDarren Tucker 			fatal("Invalid ECDSA key length: valid lengths are "
22558f1bafbSDamien Miller 			    "256, 384 or 521 bits");
2269b47b083SManoj Ampalam #else
227292bcb24SDarren Tucker 			fatal("Invalid ECDSA key length: valid lengths are "
2289b47b083SManoj Ampalam 			    "256 or 384 bits");
2299b47b083SManoj Ampalam #endif
230bd636f40Sdjm@openbsd.org 	}
2311f0311c7SDamien Miller #endif
23258f1bafbSDamien Miller }
23358f1bafbSDamien Miller 
23485443f16Sdjm@openbsd.org /*
23585443f16Sdjm@openbsd.org  * Checks whether a file exists and, if so, asks the user whether they wish
23685443f16Sdjm@openbsd.org  * to overwrite it.
23785443f16Sdjm@openbsd.org  * Returns nonzero if the file does not already exist or if the user agrees to
23885443f16Sdjm@openbsd.org  * overwrite, or zero otherwise.
23985443f16Sdjm@openbsd.org  */
24085443f16Sdjm@openbsd.org static int
confirm_overwrite(const char * filename)24185443f16Sdjm@openbsd.org confirm_overwrite(const char *filename)
24285443f16Sdjm@openbsd.org {
24385443f16Sdjm@openbsd.org 	char yesno[3];
24485443f16Sdjm@openbsd.org 	struct stat st;
24685443f16Sdjm@openbsd.org 	if (stat(filename, &st) != 0)
24785443f16Sdjm@openbsd.org 		return 1;
24885443f16Sdjm@openbsd.org 	printf("%s already exists.\n", filename);
24985443f16Sdjm@openbsd.org 	printf("Overwrite (y/n)? ");
25085443f16Sdjm@openbsd.org 	fflush(stdout);
25185443f16Sdjm@openbsd.org 	if (fgets(yesno, sizeof(yesno), stdin) == NULL)
25285443f16Sdjm@openbsd.org 		return 0;
25385443f16Sdjm@openbsd.org 	if (yesno[0] != 'y' && yesno[0] != 'Y')
25485443f16Sdjm@openbsd.org 		return 0;
25585443f16Sdjm@openbsd.org 	return 1;
25685443f16Sdjm@openbsd.org }
25858f1bafbSDamien Miller static void
ask_filename(struct passwd * pw,const char * prompt)259431f66b6SDamien Miller ask_filename(struct passwd *pw, const char *prompt)
260d4a8b7e3SDamien Miller {
261431f66b6SDamien Miller 	char buf[1024];
262e39cacc5SDamien Miller 	char *name = NULL;
263e39cacc5SDamien Miller 
264993dd550SDamien Miller 	if (key_type_name == NULL)
265993dd550SDamien Miller 		name = _PATH_SSH_CLIENT_ID_RSA;
2669096740fSDamien Miller 	else {
2671129dcfcSdjm@openbsd.org 		switch (sshkey_type_from_name(key_type_name)) {
2684e270b05SDamien Miller 		case KEY_DSA_CERT:
269e39cacc5SDamien Miller 		case KEY_DSA:
270226cfa03SBen Lindstrom 			name = _PATH_SSH_CLIENT_ID_DSA;
271e39cacc5SDamien Miller 			break;
272dd190ddfSDamien Miller #ifdef OPENSSL_HAS_ECC
273eb8b60e3SDamien Miller 		case KEY_ECDSA_CERT:
274eb8b60e3SDamien Miller 		case KEY_ECDSA:
275eb8b60e3SDamien Miller 			name = _PATH_SSH_CLIENT_ID_ECDSA;
276eb8b60e3SDamien Miller 			break;
27723f38c2dSdjm@openbsd.org 		case KEY_ECDSA_SK_CERT:
27823f38c2dSdjm@openbsd.org 		case KEY_ECDSA_SK:
27923f38c2dSdjm@openbsd.org 			name = _PATH_SSH_CLIENT_ID_ECDSA_SK;
28023f38c2dSdjm@openbsd.org 			break;
281dd190ddfSDamien Miller #endif
2824e270b05SDamien Miller 		case KEY_RSA_CERT:
283e39cacc5SDamien Miller 		case KEY_RSA:
284226cfa03SBen Lindstrom 			name = _PATH_SSH_CLIENT_ID_RSA;
285e39cacc5SDamien Miller 			break;
2865be9d9e3SDamien Miller 		case KEY_ED25519:
2875be9d9e3SDamien Miller 		case KEY_ED25519_CERT:
2885be9d9e3SDamien Miller 			name = _PATH_SSH_CLIENT_ID_ED25519;
2895be9d9e3SDamien Miller 			break;
2902c55744aSmarkus@openbsd.org 		case KEY_ED25519_SK:
2912c55744aSmarkus@openbsd.org 		case KEY_ED25519_SK_CERT:
2922c55744aSmarkus@openbsd.org 			name = _PATH_SSH_CLIENT_ID_ED25519_SK;
2932c55744aSmarkus@openbsd.org 			break;
2941b11ea7cSmarkus@openbsd.org 		case KEY_XMSS:
2951b11ea7cSmarkus@openbsd.org 		case KEY_XMSS_CERT:
2961b11ea7cSmarkus@openbsd.org 			name = _PATH_SSH_CLIENT_ID_XMSS;
2971b11ea7cSmarkus@openbsd.org 			break;
298e39cacc5SDamien Miller 		default:
2993038a191Sdjm@openbsd.org 			fatal("bad key type");
300e39cacc5SDamien Miller 		}
3019096740fSDamien Miller 	}
3023038a191Sdjm@openbsd.org 	snprintf(identity_file, sizeof(identity_file),
3033038a191Sdjm@openbsd.org 	    "%s/%s", pw->pw_dir, name);
3043038a191Sdjm@openbsd.org 	printf("%s (%s): ", prompt, identity_file);
3053038a191Sdjm@openbsd.org 	fflush(stdout);
306d4a8b7e3SDamien Miller 	if (fgets(buf, sizeof(buf), stdin) == NULL)
307d4a8b7e3SDamien Miller 		exit(1);
30814b017d6SDamien Miller 	buf[strcspn(buf, "\n")] = '\0';
309431f66b6SDamien Miller 	if (strcmp(buf, "") != 0)
310431f66b6SDamien Miller 		strlcpy(identity_file, buf, sizeof(identity_file));
311431f66b6SDamien Miller 	have_identity = 1;
312d4a8b7e3SDamien Miller }
313d4a8b7e3SDamien Miller 
3141129dcfcSdjm@openbsd.org static struct sshkey *
load_identity(const char * filename,char ** commentp)3159a396e33Sdjm@openbsd.org load_identity(const char *filename, char **commentp)
316eba71babSDamien Miller {
317d0fca423SBen Lindstrom 	char *pass;
3181129dcfcSdjm@openbsd.org 	struct sshkey *prv;
3191129dcfcSdjm@openbsd.org 	int r;
320d0fca423SBen Lindstrom 
3210713322eSdjm@openbsd.org 	if (commentp != NULL)
3220713322eSdjm@openbsd.org 		*commentp = NULL;
3230713322eSdjm@openbsd.org 	if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0)
3241129dcfcSdjm@openbsd.org 		return prv;
3251129dcfcSdjm@openbsd.org 	if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
326816036f1Sdjm@openbsd.org 		fatal_r(r, "Load key \"%s\"", filename);
327d78ae766SBen Lindstrom 	if (identity_passphrase)
328d78ae766SBen Lindstrom 		pass = xstrdup(identity_passphrase);
329d78ae766SBen Lindstrom 	else
3301129dcfcSdjm@openbsd.org 		pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN);
3310713322eSdjm@openbsd.org 	r = sshkey_load_private(filename, pass, &prv, commentp);
332d5ba1c03Sjsg@openbsd.org 	freezero(pass, strlen(pass));
3331129dcfcSdjm@openbsd.org 	if (r != 0)
334816036f1Sdjm@openbsd.org 		fatal_r(r, "Load key \"%s\"", filename);
335d0fca423SBen Lindstrom 	return prv;
336eba71babSDamien Miller }
337eba71babSDamien Miller 
338874d77bbSDamien Miller #define SSH_COM_PUBLIC_BEGIN		"---- BEGIN SSH2 PUBLIC KEY ----"
339874d77bbSDamien Miller #define SSH_COM_PUBLIC_END		"---- END SSH2 PUBLIC KEY ----"
340874d77bbSDamien Miller #define SSH_COM_PRIVATE_BEGIN		"---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
341874d77bbSDamien Miller #define	SSH_COM_PRIVATE_KEY_MAGIC	0x3f6ff9eb
342eba71babSDamien Miller 
3431f0311c7SDamien Miller #ifdef WITH_OPENSSL
344bba81213SBen Lindstrom static void
do_convert_to_ssh2(struct passwd * pw,struct sshkey * k)3451129dcfcSdjm@openbsd.org do_convert_to_ssh2(struct passwd *pw, struct sshkey *k)
346eba71babSDamien Miller {
34716dd8b2cSdjm@openbsd.org 	struct sshbuf *b;
34816dd8b2cSdjm@openbsd.org 	char comment[61], *b64;
3491129dcfcSdjm@openbsd.org 	int r;
350eba71babSDamien Miller 
35116dd8b2cSdjm@openbsd.org 	if ((b = sshbuf_new()) == NULL)
352816036f1Sdjm@openbsd.org 		fatal_f("sshbuf_new failed");
35316dd8b2cSdjm@openbsd.org 	if ((r = sshkey_putb(k, b)) != 0)
354816036f1Sdjm@openbsd.org 		fatal_fr(r, "put key");
35516dd8b2cSdjm@openbsd.org 	if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL)
356816036f1Sdjm@openbsd.org 		fatal_f("sshbuf_dtob64_string failed");
358d04758dcSDarren Tucker 	/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
359d04758dcSDarren Tucker 	snprintf(comment, sizeof(comment),
360d04758dcSDarren Tucker 	    "%u-bit %s, converted by %s@%s from OpenSSH",
3611129dcfcSdjm@openbsd.org 	    sshkey_size(k), sshkey_type(k),
362eba71babSDamien Miller 	    pw->pw_name, hostname);
363d04758dcSDarren Tucker 
3641129dcfcSdjm@openbsd.org 	sshkey_free(k);
36516dd8b2cSdjm@openbsd.org 	sshbuf_free(b);
36716dd8b2cSdjm@openbsd.org 	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
36816dd8b2cSdjm@openbsd.org 	fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64);
36916dd8b2cSdjm@openbsd.org 	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
37016dd8b2cSdjm@openbsd.org 	free(b64);
371eba71babSDamien Miller 	exit(0);
372eba71babSDamien Miller }
373eba71babSDamien Miller 
374bba81213SBen Lindstrom static void
do_convert_to_pkcs8(struct sshkey * k)3751129dcfcSdjm@openbsd.org do_convert_to_pkcs8(struct sshkey *k)
37644b25040SDamien Miller {
3771129dcfcSdjm@openbsd.org 	switch (sshkey_type_plain(k->type)) {
37844b25040SDamien Miller 	case KEY_RSA:
37944b25040SDamien Miller 		if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
38044b25040SDamien Miller 			fatal("PEM_write_RSA_PUBKEY failed");
38144b25040SDamien Miller 		break;
38244b25040SDamien Miller 	case KEY_DSA:
38344b25040SDamien Miller 		if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
38444b25040SDamien Miller 			fatal("PEM_write_DSA_PUBKEY failed");
38544b25040SDamien Miller 		break;
3866af914a1SDamien Miller #ifdef OPENSSL_HAS_ECC
387eb8b60e3SDamien Miller 	case KEY_ECDSA:
388eb8b60e3SDamien Miller 		if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
389eb8b60e3SDamien Miller 			fatal("PEM_write_EC_PUBKEY failed");
390eb8b60e3SDamien Miller 		break;
3916af914a1SDamien Miller #endif
39244b25040SDamien Miller 	default:
393816036f1Sdjm@openbsd.org 		fatal_f("unsupported key type %s", sshkey_type(k));
39444b25040SDamien Miller 	}
39544b25040SDamien Miller 	exit(0);
39644b25040SDamien Miller }
39744b25040SDamien Miller 
39844b25040SDamien Miller static void
do_convert_to_pem(struct sshkey * k)3991129dcfcSdjm@openbsd.org do_convert_to_pem(struct sshkey *k)
40044b25040SDamien Miller {
4011129dcfcSdjm@openbsd.org 	switch (sshkey_type_plain(k->type)) {
40244b25040SDamien Miller 	case KEY_RSA:
40344b25040SDamien Miller 		if (!PEM_write_RSAPublicKey(stdout, k->rsa))
40444b25040SDamien Miller 			fatal("PEM_write_RSAPublicKey failed");
40544b25040SDamien Miller 		break;
4068dfb6a20Sdjm@openbsd.org 	case KEY_DSA:
4078dfb6a20Sdjm@openbsd.org 		if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
4088dfb6a20Sdjm@openbsd.org 			fatal("PEM_write_DSA_PUBKEY failed");
4098dfb6a20Sdjm@openbsd.org 		break;
410e16dfa94SDarren Tucker #ifdef OPENSSL_HAS_ECC
4118dfb6a20Sdjm@openbsd.org 	case KEY_ECDSA:
4128dfb6a20Sdjm@openbsd.org 		if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
4138dfb6a20Sdjm@openbsd.org 			fatal("PEM_write_EC_PUBKEY failed");
4148dfb6a20Sdjm@openbsd.org 		break;
415e16dfa94SDarren Tucker #endif
41644b25040SDamien Miller 	default:
417816036f1Sdjm@openbsd.org 		fatal_f("unsupported key type %s", sshkey_type(k));
41844b25040SDamien Miller 	}
41944b25040SDamien Miller 	exit(0);
42044b25040SDamien Miller }
42144b25040SDamien Miller 
42244b25040SDamien Miller static void
do_convert_to(struct passwd * pw)42344b25040SDamien Miller do_convert_to(struct passwd *pw)
42444b25040SDamien Miller {
4251129dcfcSdjm@openbsd.org 	struct sshkey *k;
42644b25040SDamien Miller 	struct stat st;
4271129dcfcSdjm@openbsd.org 	int r;
42844b25040SDamien Miller 
42944b25040SDamien Miller 	if (!have_identity)
43044b25040SDamien Miller 		ask_filename(pw, "Enter file in which the key is");
4314d28fa78Sderaadt@openbsd.org 	if (stat(identity_file, &st) == -1)
43244b25040SDamien Miller 		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
4331129dcfcSdjm@openbsd.org 	if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0)
4340713322eSdjm@openbsd.org 		k = load_identity(identity_file, NULL);
43544b25040SDamien Miller 	switch (convert_format) {
43644b25040SDamien Miller 	case FMT_RFC4716:
43744b25040SDamien Miller 		do_convert_to_ssh2(pw, k);
43844b25040SDamien Miller 		break;
43944b25040SDamien Miller 	case FMT_PKCS8:
44044b25040SDamien Miller 		do_convert_to_pkcs8(k);
44144b25040SDamien Miller 		break;
44244b25040SDamien Miller 	case FMT_PEM:
44344b25040SDamien Miller 		do_convert_to_pem(k);
44444b25040SDamien Miller 		break;
44544b25040SDamien Miller 	default:
446816036f1Sdjm@openbsd.org 		fatal_f("unknown key format %d", convert_format);
44744b25040SDamien Miller 	}
44844b25040SDamien Miller 	exit(0);
44944b25040SDamien Miller }
45044b25040SDamien Miller 
4511129dcfcSdjm@openbsd.org /*
4521129dcfcSdjm@openbsd.org  * This is almost exactly the bignum1 encoding, but with 32 bit for length
4531129dcfcSdjm@openbsd.org  * instead of 16.
4541129dcfcSdjm@openbsd.org  */
45544b25040SDamien Miller static void
buffer_get_bignum_bits(struct sshbuf * b,BIGNUM * value)4561129dcfcSdjm@openbsd.org buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value)
457874d77bbSDamien Miller {
4581129dcfcSdjm@openbsd.org 	u_int bytes, bignum_bits;
4591129dcfcSdjm@openbsd.org 	int r;
460d09fcf5fSBen Lindstrom 
4611129dcfcSdjm@openbsd.org 	if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0)
462816036f1Sdjm@openbsd.org 		fatal_fr(r, "parse");
4631129dcfcSdjm@openbsd.org 	bytes = (bignum_bits + 7) / 8;
4641129dcfcSdjm@openbsd.org 	if (sshbuf_len(b) < bytes)
465816036f1Sdjm@openbsd.org 		fatal_f("input buffer too small: need %d have %zu",
466816036f1Sdjm@openbsd.org 		    bytes, sshbuf_len(b));
4671129dcfcSdjm@openbsd.org 	if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL)
468816036f1Sdjm@openbsd.org 		fatal_f("BN_bin2bn failed");
4691129dcfcSdjm@openbsd.org 	if ((r = sshbuf_consume(b, bytes)) != 0)
470816036f1Sdjm@openbsd.org 		fatal_fr(r, "consume");
471874d77bbSDamien Miller }
472874d77bbSDamien Miller 
4731129dcfcSdjm@openbsd.org static struct sshkey *
do_convert_private_ssh2(struct sshbuf * b)47416dd8b2cSdjm@openbsd.org do_convert_private_ssh2(struct sshbuf *b)
475874d77bbSDamien Miller {
4761129dcfcSdjm@openbsd.org 	struct sshkey *key = NULL;
477874d77bbSDamien Miller 	char *type, *cipher;
4781129dcfcSdjm@openbsd.org 	u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345";
4791129dcfcSdjm@openbsd.org 	int r, rlen, ktype;
4801129dcfcSdjm@openbsd.org 	u_int magic, i1, i2, i3, i4;
4811129dcfcSdjm@openbsd.org 	size_t slen;
482e586c4ceSBen Lindstrom 	u_long e;
483482d23bcSdjm@openbsd.org 	BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL;
484482d23bcSdjm@openbsd.org 	BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL;
485482d23bcSdjm@openbsd.org 	BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
486482d23bcSdjm@openbsd.org 	BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL;
4881129dcfcSdjm@openbsd.org 	if ((r = sshbuf_get_u32(b, &magic)) != 0)
489816036f1Sdjm@openbsd.org 		fatal_fr(r, "parse magic");
490874d77bbSDamien Miller 
491874d77bbSDamien Miller 	if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
4921129dcfcSdjm@openbsd.org 		error("bad magic 0x%x != 0x%x", magic,
4931129dcfcSdjm@openbsd.org 		    SSH_COM_PRIVATE_KEY_MAGIC);
494874d77bbSDamien Miller 		return NULL;
495874d77bbSDamien Miller 	}
4961129dcfcSdjm@openbsd.org 	if ((r = sshbuf_get_u32(b, &i1)) != 0 ||
4971129dcfcSdjm@openbsd.org 	    (r = sshbuf_get_cstring(b, &type, NULL)) != 0 ||
4981129dcfcSdjm@openbsd.org 	    (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 ||
4991129dcfcSdjm@openbsd.org 	    (r = sshbuf_get_u32(b, &i2)) != 0 ||
5001129dcfcSdjm@openbsd.org 	    (r = sshbuf_get_u32(b, &i3)) != 0 ||
5011129dcfcSdjm@openbsd.org 	    (r = sshbuf_get_u32(b, &i4)) != 0)
502816036f1Sdjm@openbsd.org 		fatal_fr(r, "parse");
50334f91883SBen Lindstrom 	debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
504874d77bbSDamien Miller 	if (strcmp(cipher, "none") != 0) {
505874d77bbSDamien Miller 		error("unsupported cipher %s", cipher);
506a627d42eSDarren Tucker 		free(cipher);
507a627d42eSDarren Tucker 		free(type);
508874d77bbSDamien Miller 		return NULL;
509874d77bbSDamien Miller 	}
510a627d42eSDarren Tucker 	free(cipher);
511874d77bbSDamien Miller 
512d09fcf5fSBen Lindstrom 	if (strstr(type, "dsa")) {
513d09fcf5fSBen Lindstrom 		ktype = KEY_DSA;
514d09fcf5fSBen Lindstrom 	} else if (strstr(type, "rsa")) {
515d09fcf5fSBen Lindstrom 		ktype = KEY_RSA;
516d09fcf5fSBen Lindstrom 	} else {
517a627d42eSDarren Tucker 		free(type);
518d09fcf5fSBen Lindstrom 		return NULL;
519d09fcf5fSBen Lindstrom 	}
5206da046f9Sdjm@openbsd.org 	if ((key = sshkey_new(ktype)) == NULL)
5216da046f9Sdjm@openbsd.org 		fatal("sshkey_new failed");
522a627d42eSDarren Tucker 	free(type);
523d09fcf5fSBen Lindstrom 
524d09fcf5fSBen Lindstrom 	switch (key->type) {
525d09fcf5fSBen Lindstrom 	case KEY_DSA:
526482d23bcSdjm@openbsd.org 		if ((dsa_p = BN_new()) == NULL ||
527482d23bcSdjm@openbsd.org 		    (dsa_q = BN_new()) == NULL ||
528482d23bcSdjm@openbsd.org 		    (dsa_g = BN_new()) == NULL ||
529482d23bcSdjm@openbsd.org 		    (dsa_pub_key = BN_new()) == NULL ||
530482d23bcSdjm@openbsd.org 		    (dsa_priv_key = BN_new()) == NULL)
531816036f1Sdjm@openbsd.org 			fatal_f("BN_new");
532482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, dsa_p);
533482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, dsa_g);
534482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, dsa_q);
535482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, dsa_pub_key);
536482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, dsa_priv_key);
537482d23bcSdjm@openbsd.org 		if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g))
538816036f1Sdjm@openbsd.org 			fatal_f("DSA_set0_pqg failed");
539482d23bcSdjm@openbsd.org 		dsa_p = dsa_q = dsa_g = NULL; /* transferred */
540482d23bcSdjm@openbsd.org 		if (!DSA_set0_key(key->dsa, dsa_pub_key, dsa_priv_key))
541816036f1Sdjm@openbsd.org 			fatal_f("DSA_set0_key failed");
542482d23bcSdjm@openbsd.org 		dsa_pub_key = dsa_priv_key = NULL; /* transferred */
543d09fcf5fSBen Lindstrom 		break;
544d09fcf5fSBen Lindstrom 	case KEY_RSA:
5451129dcfcSdjm@openbsd.org 		if ((r = sshbuf_get_u8(b, &e1)) != 0 ||
5461129dcfcSdjm@openbsd.org 		    (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) ||
5471129dcfcSdjm@openbsd.org 		    (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0))
548816036f1Sdjm@openbsd.org 			fatal_fr(r, "parse RSA");
5491129dcfcSdjm@openbsd.org 		e = e1;
55034f91883SBen Lindstrom 		debug("e %lx", e);
55134f91883SBen Lindstrom 		if (e < 30) {
55234f91883SBen Lindstrom 			e <<= 8;
5531129dcfcSdjm@openbsd.org 			e += e2;
55434f91883SBen Lindstrom 			debug("e %lx", e);
55534f91883SBen Lindstrom 			e <<= 8;
5561129dcfcSdjm@openbsd.org 			e += e3;
55734f91883SBen Lindstrom 			debug("e %lx", e);
55834f91883SBen Lindstrom 		}
559482d23bcSdjm@openbsd.org 		if ((rsa_e = BN_new()) == NULL)
560816036f1Sdjm@openbsd.org 			fatal_f("BN_new");
561482d23bcSdjm@openbsd.org 		if (!BN_set_word(rsa_e, e)) {
562482d23bcSdjm@openbsd.org 			BN_clear_free(rsa_e);
5631129dcfcSdjm@openbsd.org 			sshkey_free(key);
564874d77bbSDamien Miller 			return NULL;
565874d77bbSDamien Miller 		}
566482d23bcSdjm@openbsd.org 		if ((rsa_n = BN_new()) == NULL ||
567482d23bcSdjm@openbsd.org 		    (rsa_d = BN_new()) == NULL ||
568482d23bcSdjm@openbsd.org 		    (rsa_p = BN_new()) == NULL ||
569482d23bcSdjm@openbsd.org 		    (rsa_q = BN_new()) == NULL ||
570482d23bcSdjm@openbsd.org 		    (rsa_iqmp = BN_new()) == NULL)
571816036f1Sdjm@openbsd.org 			fatal_f("BN_new");
572482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, rsa_d);
573482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, rsa_n);
574482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, rsa_iqmp);
575482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, rsa_q);
576482d23bcSdjm@openbsd.org 		buffer_get_bignum_bits(b, rsa_p);
577482d23bcSdjm@openbsd.org 		if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, rsa_d))
578816036f1Sdjm@openbsd.org 			fatal_f("RSA_set0_key failed");
579482d23bcSdjm@openbsd.org 		rsa_n = rsa_e = rsa_d = NULL; /* transferred */
580482d23bcSdjm@openbsd.org 		if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q))
581816036f1Sdjm@openbsd.org 			fatal_f("RSA_set0_factors failed");
582482d23bcSdjm@openbsd.org 		rsa_p = rsa_q = NULL; /* transferred */
583482d23bcSdjm@openbsd.org 		if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0)
584816036f1Sdjm@openbsd.org 			fatal_fr(r, "generate RSA parameters");
585482d23bcSdjm@openbsd.org 		BN_clear_free(rsa_iqmp);
586d09fcf5fSBen Lindstrom 		break;