1132451Sroberto/*
2290001Sglebius * Program to generate cryptographic keys for ntp clients and servers
3132451Sroberto *
4290001Sglebius * This program generates password encrypted data files for use with the
5290001Sglebius * Autokey security protocol and Network Time Protocol Version 4. Files
6290001Sglebius * are prefixed with a header giving the name and date of creation
7132451Sroberto * followed by a type-specific descriptive label and PEM-encoded data
8290001Sglebius * structure compatible with programs of the OpenSSL library.
9132451Sroberto *
10290001Sglebius * All file names are like "ntpkey_<type>_<hostname>.<filestamp>", where
11290001Sglebius * <type> is the file type, <hostname> the generating host name and
12290001Sglebius * <filestamp> the generation time in NTP seconds. The NTP programs
13290001Sglebius * expect generic names such as "ntpkey_<type>_whimsy.udel.edu" with the
14290001Sglebius * association maintained by soft links. Following is a list of file
15290001Sglebius * types; the first line is the file name and the second link name.
16132451Sroberto *
17132451Sroberto * ntpkey_MD5key_<hostname>.<filestamp>
18132451Sroberto * 	MD5 (128-bit) keys used to compute message digests in symmetric
19132451Sroberto *	key cryptography
20132451Sroberto *
21290001Sglebius * ntpkey_RSAhost_<hostname>.<filestamp>
22290001Sglebius * ntpkey_host_<hostname>
23132451Sroberto *	RSA private/public host key pair used for public key signatures
24132451Sroberto *
25290001Sglebius * ntpkey_RSAsign_<hostname>.<filestamp>
26290001Sglebius * ntpkey_sign_<hostname>
27290001Sglebius *	RSA private/public sign key pair used for public key signatures
28132451Sroberto *
29290001Sglebius * ntpkey_DSAsign_<hostname>.<filestamp>
30290001Sglebius * ntpkey_sign_<hostname>
31290001Sglebius *	DSA Private/public sign key pair used for public key signatures
32132451Sroberto *
33290001Sglebius * Available digest/signature schemes
34132451Sroberto *
35290001Sglebius * RSA:	RSA-MD2, RSA-MD5, RSA-SHA, RSA-SHA1, RSA-MDC2, EVP-RIPEMD160
36290001Sglebius * DSA:	DSA-SHA, DSA-SHA1
37132451Sroberto *
38132451Sroberto * ntpkey_XXXcert_<hostname>.<filestamp>
39290001Sglebius * ntpkey_cert_<hostname>
40132451Sroberto *	X509v3 certificate using RSA or DSA public keys and signatures.
41132451Sroberto *	XXX is a code identifying the message digest and signature
42132451Sroberto *	encryption algorithm
43132451Sroberto *
44290001Sglebius * Identity schemes. The key type par is used for the challenge; the key
45290001Sglebius * type key is used for the response.
46132451Sroberto *
47290001Sglebius * ntpkey_IFFkey_<groupname>.<filestamp>
48290001Sglebius * ntpkey_iffkey_<groupname>
49290001Sglebius *	Schnorr (IFF) identity parameters and keys
50132451Sroberto *
51290001Sglebius * ntpkey_GQkey_<groupname>.<filestamp>,
52290001Sglebius * ntpkey_gqkey_<groupname>
53290001Sglebius *	Guillou-Quisquater (GQ) identity parameters and keys
54290001Sglebius *
55290001Sglebius * ntpkey_MVkeyX_<groupname>.<filestamp>,
56290001Sglebius * ntpkey_mvkey_<groupname>
57290001Sglebius *	Mu-Varadharajan (MV) identity parameters and keys
58290001Sglebius *
59132451Sroberto * Note: Once in a while because of some statistical fluke this program
60132451Sroberto * fails to generate and verify some cryptographic data, as indicated by
61132451Sroberto * exit status -1. In this case simply run the program again. If the
62290001Sglebius * program does complete with exit code 0, the data are correct as
63132451Sroberto * verified.
64132451Sroberto *
65132451Sroberto * These cryptographic routines are characterized by the prime modulus
66132451Sroberto * size in bits. The default value of 512 bits is a compromise between
67132451Sroberto * cryptographic strength and computing time and is ordinarily
68132451Sroberto * considered adequate for this application. The routines have been
69132451Sroberto * tested with sizes of 256, 512, 1024 and 2048 bits. Not all message
70132451Sroberto * digest and signature encryption schemes work with sizes less than 512
71132451Sroberto * bits. The computing time for sizes greater than 2048 bits is
72132451Sroberto * prohibitive on all but the fastest processors. An UltraSPARC Blade
73132451Sroberto * 1000 took something over nine minutes to generate and verify the
74132451Sroberto * values with size 2048. An old SPARC IPC would take a week.
75132451Sroberto *
76132451Sroberto * The OpenSSL library used by this program expects a random seed file.
77132451Sroberto * As described in the OpenSSL documentation, the file name defaults to
78132451Sroberto * first the RANDFILE environment variable in the user's home directory
79132451Sroberto * and then .rnd in the user's home directory.
80132451Sroberto */
81132451Sroberto#ifdef HAVE_CONFIG_H
82132451Sroberto# include <config.h>
83132451Sroberto#endif
84132451Sroberto#include <string.h>
85132451Sroberto#include <stdio.h>
86132451Sroberto#include <stdlib.h>
87132451Sroberto#include <unistd.h>
88132451Sroberto#include <sys/stat.h>
89132451Sroberto#include <sys/time.h>
90290001Sglebius#include <sys/types.h>
91290001Sglebius
92290001Sglebius#include "ntp.h"
93182007Sroberto#include "ntp_random.h"
94290001Sglebius#include "ntp_stdlib.h"
95290001Sglebius#include "ntp_assert.h"
96290001Sglebius#include "ntp_libopts.h"
97290001Sglebius#include "ntp_unixtime.h"
98182007Sroberto#include "ntp-keygen-opts.h"
99182007Sroberto
100132451Sroberto#ifdef OPENSSL
101132451Sroberto#include "openssl/bn.h"
102132451Sroberto#include "openssl/evp.h"
103132451Sroberto#include "openssl/err.h"
104132451Sroberto#include "openssl/rand.h"
105132451Sroberto#include "openssl/pem.h"
106132451Sroberto#include "openssl/x509v3.h"
107132451Sroberto#include <openssl/objects.h>
108310419Sdelphij#include "libssl_compat.h"
109290001Sglebius#endif	/* OPENSSL */
110290001Sglebius#include <ssl_applink.c>
111132451Sroberto
112290001Sglebius#define _UC(str)	((char *)(intptr_t)(str))
113132451Sroberto/*
114132451Sroberto * Cryptodefines
115132451Sroberto */
116290001Sglebius#define	MD5KEYS		10	/* number of keys generated of each type */
117290001Sglebius#define	MD5SIZE		20	/* maximum key size */
118290001Sglebius#ifdef AUTOKEY
119132451Sroberto#define	PLEN		512	/* default prime modulus size (bits) */
120290001Sglebius#define	ILEN		256	/* default identity modulus size (bits) */
121290001Sglebius#define	MVMAX		100	/* max MV parameters */
122132451Sroberto
123132451Sroberto/*
124132451Sroberto * Strings used in X509v3 extension fields
125132451Sroberto */
126132451Sroberto#define KEY_USAGE		"digitalSignature,keyCertSign"
127132451Sroberto#define BASIC_CONSTRAINTS	"critical,CA:TRUE"
128132451Sroberto#define EXT_KEY_PRIVATE		"private"
129132451Sroberto#define EXT_KEY_TRUST		"trustRoot"
130290001Sglebius#endif	/* AUTOKEY */
131132451Sroberto
132132451Sroberto/*
133132451Sroberto * Prototypes
134132451Sroberto */
135290001SglebiusFILE	*fheader	(const char *, const char *, const char *);
136290001Sglebiusint	gen_md5		(const char *);
137290001Sglebiusvoid	followlink	(char *, size_t);
138290001Sglebius#ifdef AUTOKEY
139290001SglebiusEVP_PKEY *gen_rsa	(const char *);
140290001SglebiusEVP_PKEY *gen_dsa	(const char *);
141290001SglebiusEVP_PKEY *gen_iffkey	(const char *);
142290001SglebiusEVP_PKEY *gen_gqkey	(const char *);
143290001SglebiusEVP_PKEY *gen_mvkey	(const char *, EVP_PKEY **);
144290001Sglebiusvoid	gen_mvserv	(char *, EVP_PKEY **);
145290001Sglebiusint	x509		(EVP_PKEY *, const EVP_MD *, char *, const char *,
146290001Sglebius			    char *);
147290001Sglebiusvoid	cb		(int, int, void *);
148290001SglebiusEVP_PKEY *genkey	(const char *, const char *);
149290001SglebiusEVP_PKEY *readkey	(char *, char *, u_int *, EVP_PKEY **);
150290001Sglebiusvoid	writekey	(char *, char *, u_int *, EVP_PKEY **);
151290001Sglebiusu_long	asn2ntp		(ASN1_TIME *);
152310419Sdelphij
153310419Sdelphijstatic DSA* genDsaParams(int, char*);
154310419Sdelphijstatic RSA* genRsaKeyPair(int, char*);
155310419Sdelphij
156290001Sglebius#endif	/* AUTOKEY */
157132451Sroberto
158132451Sroberto/*
159132451Sroberto * Program variables
160132451Sroberto */
161132451Srobertoextern char *optarg;		/* command line argument */
162290001Sglebiuschar	const *progname;
163290001Sglebiusu_int	lifetime = DAYSPERYEAR;	/* certificate lifetime (days) */
164290001Sglebiusint	nkeys;			/* MV keys */
165132451Srobertotime_t	epoch;			/* Unix epoch (seconds) since 1970 */
166290001Sglebiusu_int	fstamp;			/* NTP filestamp */
167290001Sglebiuschar	hostbuf[MAXHOSTNAME + 1];
168290001Sglebiuschar	*hostname = NULL;	/* host, used in cert filenames */
169290001Sglebiuschar	*groupname = NULL;	/* group name */
170290001Sglebiuschar	certnamebuf[2 * sizeof(hostbuf)];
171290001Sglebiuschar	*certname = NULL;	/* certificate subject/issuer name */
172132451Srobertochar	*passwd1 = NULL;	/* input private key password */
173132451Srobertochar	*passwd2 = NULL;	/* output private key password */
174290001Sglebiuschar	filename[MAXFILENAME + 1]; /* file name */
175290001Sglebius#ifdef AUTOKEY
176290001Sglebiusu_int	modulus = PLEN;		/* prime modulus size (bits) */
177290001Sglebiusu_int	modulus2 = ILEN;	/* identity modulus size (bits) */
178132451Srobertolong	d0, d1, d2, d3;		/* callback counters */
179290001Sglebiusconst EVP_CIPHER * cipher = NULL;
180290001Sglebius#endif	/* AUTOKEY */
181132451Sroberto
182132451Sroberto#ifdef SYS_WINNT
183132451SrobertoBOOL init_randfile();
184132451Sroberto
185132451Sroberto/*
186290001Sglebius * Don't try to follow symbolic links on Windows.  Assume link == file.
187132451Sroberto */
188132451Srobertoint
189290001Sglebiusreadlink(
190290001Sglebius	char *	link,
191290001Sglebius	char *	file,
192290001Sglebius	int	len
193290001Sglebius	)
194290001Sglebius{
195293896Sglebius	return (int)strlen(file); /* assume no overflow possible */
196132451Sroberto}
197290001Sglebius
198132451Sroberto/*
199290001Sglebius * Don't try to create symbolic links on Windows, that is supported on
200290001Sglebius * Vista and later only.  Instead, if CreateHardLink is available (XP
201290001Sglebius * and later), hardlink the linkname to the original filename.  On
202290001Sglebius * earlier systems, user must rename file to match expected link for
203290001Sglebius * ntpd to find it.  To allow building a ntp-keygen.exe which loads on
204290001Sglebius * Windows pre-XP, runtime link to CreateHardLinkA().
205132451Sroberto */
206132451Srobertoint
207290001Sglebiussymlink(
208290001Sglebius	char *	filename,
209290001Sglebius	char*	linkname
210290001Sglebius	)
211290001Sglebius{
212290001Sglebius	typedef BOOL (WINAPI *PCREATEHARDLINKA)(
213290001Sglebius		__in LPCSTR	lpFileName,
214290001Sglebius		__in LPCSTR	lpExistingFileName,
215290001Sglebius		__reserved LPSECURITY_ATTRIBUTES lpSA
216290001Sglebius		);
217290001Sglebius	static PCREATEHARDLINKA pCreateHardLinkA;
218290001Sglebius	static int		tried;
219290001Sglebius	HMODULE			hDll;
220290001Sglebius	FARPROC			pfn;
221290001Sglebius	int			link_created;
222290001Sglebius	int			saved_errno;
223290001Sglebius
224290001Sglebius	if (!tried) {
225290001Sglebius		tried = TRUE;
226290001Sglebius		hDll = LoadLibrary("kernel32");
227290001Sglebius		pfn = GetProcAddress(hDll, "CreateHardLinkA");
228290001Sglebius		pCreateHardLinkA = (PCREATEHARDLINKA)pfn;
229290001Sglebius	}
230290001Sglebius
231290001Sglebius	if (NULL == pCreateHardLinkA) {
232290001Sglebius		errno = ENOSYS;
233290001Sglebius		return -1;
234290001Sglebius	}
235290001Sglebius
236290001Sglebius	link_created = (*pCreateHardLinkA)(linkname, filename, NULL);
237290001Sglebius
238290001Sglebius	if (link_created)
239290001Sglebius		return 0;
240290001Sglebius
241290001Sglebius	saved_errno = GetLastError();	/* yes we play loose */
242290001Sglebius	mfprintf(stderr, "Create hard link %s to %s failed: %m\n",
243290001Sglebius		 linkname, filename);
244290001Sglebius	errno = saved_errno;
245290001Sglebius	return -1;
246132451Sroberto}
247290001Sglebius
248132451Srobertovoid
249132451SrobertoInitWin32Sockets() {
250132451Sroberto	WORD wVersionRequested;
251132451Sroberto	WSADATA wsaData;
252132451Sroberto	wVersionRequested = MAKEWORD(2,0);
253132451Sroberto	if (WSAStartup(wVersionRequested, &wsaData))
254132451Sroberto	{
255290001Sglebius		fprintf(stderr, "No useable winsock.dll\n");
256132451Sroberto		exit(1);
257132451Sroberto	}
258132451Sroberto}
259132451Sroberto#endif /* SYS_WINNT */
260132451Sroberto
261290001Sglebius
262132451Sroberto/*
263290001Sglebius * followlink() - replace filename with its target if symlink.
264290001Sglebius *
265290001Sglebius * Some readlink() implementations do not null-terminate the result.
266290001Sglebius */
267290001Sglebiusvoid
268290001Sglebiusfollowlink(
269290001Sglebius	char *	fname,
270290001Sglebius	size_t	bufsiz
271290001Sglebius	)
272290001Sglebius{
273290001Sglebius	int len;
274290001Sglebius
275290001Sglebius	REQUIRE(bufsiz > 0);
276290001Sglebius
277290001Sglebius	len = readlink(fname, fname, (int)bufsiz);
278290001Sglebius	if (len < 0 ) {
279290001Sglebius		fname[0] = '\0';
280290001Sglebius		return;
281290001Sglebius	}
282290001Sglebius	if (len > (int)bufsiz - 1)
283290001Sglebius		len = (int)bufsiz - 1;
284290001Sglebius	fname[len] = '\0';
285290001Sglebius}
286290001Sglebius
287290001Sglebius
288290001Sglebius/*
289132451Sroberto * Main program
290132451Sroberto */
291132451Srobertoint
292132451Srobertomain(
293132451Sroberto	int	argc,		/* command line options */
294132451Sroberto	char	**argv
295132451Sroberto	)
296132451Sroberto{
297132451Sroberto	struct timeval tv;	/* initialization vector */
298182007Sroberto	int	md5key = 0;	/* generate MD5 keys */
299290001Sglebius	int	optct;		/* option count */
300290001Sglebius#ifdef AUTOKEY
301132451Sroberto	X509	*cert = NULL;	/* X509 certificate */
302132451Sroberto	EVP_PKEY *pkey_host = NULL; /* host key */
303132451Sroberto	EVP_PKEY *pkey_sign = NULL; /* sign key */
304290001Sglebius	EVP_PKEY *pkey_iffkey = NULL; /* IFF sever keys */
305290001Sglebius	EVP_PKEY *pkey_gqkey = NULL; /* GQ server keys */
306290001Sglebius	EVP_PKEY *pkey_mvkey = NULL; /* MV trusted agen keys */
307290001Sglebius	EVP_PKEY *pkey_mvpar[MVMAX]; /* MV cleient keys */
308132451Sroberto	int	hostkey = 0;	/* generate RSA keys */
309290001Sglebius	int	iffkey = 0;	/* generate IFF keys */
310290001Sglebius	int	gqkey = 0;	/* generate GQ keys */
311290001Sglebius	int	mvkey = 0;	/* update MV keys */
312132451Sroberto	int	mvpar = 0;	/* generate MV parameters */
313132451Sroberto	char	*sign = NULL;	/* sign key */
314132451Sroberto	EVP_PKEY *pkey = NULL;	/* temp key */
315132451Sroberto	const EVP_MD *ectx;	/* EVP digest */
316132451Sroberto	char	pathbuf[MAXFILENAME + 1];
317132451Sroberto	const char *scheme = NULL; /* digest/signature scheme */
318290001Sglebius	const char *ciphername = NULL; /* to encrypt priv. key */
319290001Sglebius	const char *exten = NULL;	/* private extension */
320132451Sroberto	char	*grpkey = NULL;	/* identity extension */
321132451Sroberto	int	nid;		/* X509 digest/signature scheme */
322132451Sroberto	FILE	*fstr = NULL;	/* file handle */
323290001Sglebius	char	groupbuf[MAXHOSTNAME + 1];
324182007Sroberto	u_int	temp;
325290001Sglebius	BIO *	bp;
326290001Sglebius	int	i, cnt;
327290001Sglebius	char *	ptr;
328290001Sglebius#endif	/* AUTOKEY */
329132451Sroberto
330290001Sglebius	progname = argv[0];
331290001Sglebius
332132451Sroberto#ifdef SYS_WINNT
333132451Sroberto	/* Initialize before OpenSSL checks */
334132451Sroberto	InitWin32Sockets();
335290001Sglebius	if (!init_randfile())
336132451Sroberto		fprintf(stderr, "Unable to initialize .rnd file\n");
337290001Sglebius	ssl_applink();
338132451Sroberto#endif
339132451Sroberto
340132451Sroberto#ifdef OPENSSL
341290001Sglebius	ssl_check_version();
342290001Sglebius#endif	/* OPENSSL */
343132451Sroberto
344290001Sglebius	ntp_crypto_srandom();
345132451Sroberto
346132451Sroberto	/*
347132451Sroberto	 * Process options, initialize host name and timestamp.
348290001Sglebius	 * gethostname() won't null-terminate if hostname is exactly the
349290001Sglebius	 * length provided for the buffer.
350132451Sroberto	 */
351290001Sglebius	gethostname(hostbuf, sizeof(hostbuf) - 1);
352290001Sglebius	hostbuf[COUNTOF(hostbuf) - 1] = '\0';
353132451Sroberto	hostname = hostbuf;
354290001Sglebius	groupname = hostbuf;
355132451Sroberto	passwd1 = hostbuf;
356290001Sglebius	passwd2 = NULL;
357290001Sglebius	GETTIMEOFDAY(&tv, NULL);
358132451Sroberto	epoch = tv.tv_sec;
359290001Sglebius	fstamp = (u_int)(epoch + JAN_1970);
360132451Sroberto
361290001Sglebius	optct = ntpOptionProcess(&ntp_keygenOptions, argc, argv);
362290001Sglebius	argc -= optct;	// Just in case we care later.
363290001Sglebius	argv += optct;	// Just in case we care later.
364182007Sroberto
365132536Sroberto#ifdef OPENSSL
366290001Sglebius	if (SSLeay() == SSLEAY_VERSION_NUMBER)
367290001Sglebius		fprintf(stderr, "Using OpenSSL version %s\n",
368290001Sglebius			SSLeay_version(SSLEAY_VERSION));
369290001Sglebius	else
370290001Sglebius		fprintf(stderr, "Built against OpenSSL %s, using version %s\n",
371290001Sglebius			OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
372290001Sglebius#endif /* OPENSSL */
373132451Sroberto
374290001Sglebius	debug = OPT_VALUE_SET_DEBUG_LEVEL;
375132451Sroberto
376290001Sglebius	if (HAVE_OPT( MD5KEY ))
377290001Sglebius		md5key++;
378290001Sglebius#ifdef AUTOKEY
379290001Sglebius	if (HAVE_OPT( PASSWORD ))
380290001Sglebius		passwd1 = estrdup(OPT_ARG( PASSWORD ));
381132451Sroberto
382290001Sglebius	if (HAVE_OPT( EXPORT_PASSWD ))
383290001Sglebius		passwd2 = estrdup(OPT_ARG( EXPORT_PASSWD ));
384132451Sroberto
385182007Sroberto	if (HAVE_OPT( HOST_KEY ))
386290001Sglebius		hostkey++;
387132451Sroberto
388290001Sglebius	if (HAVE_OPT( SIGN_KEY ))
389290001Sglebius		sign = estrdup(OPT_ARG( SIGN_KEY ));
390290001Sglebius
391290001Sglebius	if (HAVE_OPT( GQ_PARAMS ))
392290001Sglebius		gqkey++;
393290001Sglebius
394182007Sroberto	if (HAVE_OPT( IFFKEY ))
395290001Sglebius		iffkey++;
396132451Sroberto
397290001Sglebius	if (HAVE_OPT( MV_PARAMS )) {
398290001Sglebius		mvkey++;
399290001Sglebius		nkeys = OPT_VALUE_MV_PARAMS;
400290001Sglebius	}
401290001Sglebius	if (HAVE_OPT( MV_KEYS )) {
402290001Sglebius		mvpar++;
403290001Sglebius		nkeys = OPT_VALUE_MV_KEYS;
404290001Sglebius	}
405132451Sroberto
406290001Sglebius	if (HAVE_OPT( IMBITS ))
407290001Sglebius		modulus2 = OPT_VALUE_IMBITS;
408132451Sroberto
409182007Sroberto	if (HAVE_OPT( MODULUS ))
410290001Sglebius		modulus = OPT_VALUE_MODULUS;
411132536Sroberto
412290001Sglebius	if (HAVE_OPT( CERTIFICATE ))
413290001Sglebius		scheme = OPT_ARG( CERTIFICATE );
414132451Sroberto
415290001Sglebius	if (HAVE_OPT( CIPHER ))
416290001Sglebius		ciphername = OPT_ARG( CIPHER );
417132451Sroberto
418290001Sglebius	if (HAVE_OPT( SUBJECT_NAME ))
419290001Sglebius		hostname = estrdup(OPT_ARG( SUBJECT_NAME ));
420132451Sroberto
421290001Sglebius	if (HAVE_OPT( IDENT ))
422290001Sglebius		groupname = estrdup(OPT_ARG( IDENT ));
423132451Sroberto
424290001Sglebius	if (HAVE_OPT( LIFETIME ))
425290001Sglebius		lifetime = OPT_VALUE_LIFETIME;
426132536Sroberto
427290001Sglebius	if (HAVE_OPT( PVT_CERT ))
428290001Sglebius		exten = EXT_KEY_PRIVATE;
429290001Sglebius
430182007Sroberto	if (HAVE_OPT( TRUSTED_CERT ))
431290001Sglebius		exten = EXT_KEY_TRUST;
432132451Sroberto
433290001Sglebius	/*
434290001Sglebius	 * Remove the group name from the hostname variable used
435290001Sglebius	 * in host and sign certificate file names.
436290001Sglebius	 */
437290001Sglebius	if (hostname != hostbuf)
438290001Sglebius		ptr = strchr(hostname, '@');
439290001Sglebius	else
440290001Sglebius		ptr = NULL;
441290001Sglebius	if (ptr != NULL) {
442290001Sglebius		*ptr = '\0';
443290001Sglebius		groupname = estrdup(ptr + 1);
444290001Sglebius		/* -s @group is equivalent to -i group, host unch. */
445290001Sglebius		if (ptr == hostname)
446290001Sglebius			hostname = hostbuf;
447182007Sroberto	}
448132451Sroberto
449290001Sglebius	/*
450290001Sglebius	 * Derive host certificate issuer/subject names from host name
451290001Sglebius	 * and optional group.  If no groupname is provided, the issuer
452290001Sglebius	 * and subject is the hostname with no '@group', and the
453290001Sglebius	 * groupname variable is pointed to hostname for use in IFF, GQ,
454290001Sglebius	 * and MV parameters file names.
455290001Sglebius	 */
456290001Sglebius	if (groupname == hostbuf) {
457290001Sglebius		certname = hostname;
458290001Sglebius	} else {
459290001Sglebius		snprintf(certnamebuf, sizeof(certnamebuf), "%s@%s",
460290001Sglebius			 hostname, groupname);
461290001Sglebius		certname = certnamebuf;
462182007Sroberto	}
463132451Sroberto
464132451Sroberto	/*
465132451Sroberto	 * Seed random number generator and grow weeds.
466132451Sroberto	 */
467132451Sroberto	ERR_load_crypto_strings();
468132451Sroberto	OpenSSL_add_all_algorithms();
469290001Sglebius	if (!RAND_status()) {
470290001Sglebius		if (RAND_file_name(pathbuf, sizeof(pathbuf)) == NULL) {
471290001Sglebius			fprintf(stderr, "RAND_file_name %s\n",
472290001Sglebius			    ERR_error_string(ERR_get_error(), NULL));
473290001Sglebius			exit (-1);
474290001Sglebius		}
475290001Sglebius		temp = RAND_load_file(pathbuf, -1);
476290001Sglebius		if (temp == 0) {
477290001Sglebius			fprintf(stderr,
478290001Sglebius			    "RAND_load_file %s not found or empty\n",
479290001Sglebius			    pathbuf);
480290001Sglebius			exit (-1);
481290001Sglebius		}
482132451Sroberto		fprintf(stderr,
483290001Sglebius		    "Random seed file %s %u bytes\n", pathbuf, temp);
484290001Sglebius		RAND_add(&epoch, sizeof(epoch), 4.0);
485132451Sroberto	}
486290001Sglebius#endif	/* AUTOKEY */
487132451Sroberto
488132451Sroberto	/*
489290001Sglebius	 * Create new unencrypted MD5 keys file if requested. If this
490290001Sglebius	 * option is selected, ignore all other options.
491132451Sroberto	 */
492290001Sglebius	if (md5key) {
493290001Sglebius		gen_md5("md5");
494290001Sglebius		exit (0);
495290001Sglebius	}
496132451Sroberto
497290001Sglebius#ifdef AUTOKEY
498132451Sroberto	/*
499290001Sglebius	 * Load previous certificate if available.
500132451Sroberto	 */
501290001Sglebius	snprintf(filename, sizeof(filename), "ntpkey_cert_%s", hostname);
502290001Sglebius	if ((fstr = fopen(filename, "r")) != NULL) {
503290001Sglebius		cert = PEM_read_X509(fstr, NULL, NULL, NULL);
504290001Sglebius		fclose(fstr);
505290001Sglebius	}
506290001Sglebius	if (cert != NULL) {
507290001Sglebius
508290001Sglebius		/*
509290001Sglebius		 * Extract subject name.
510290001Sglebius		 */
511290001Sglebius		X509_NAME_oneline(X509_get_subject_name(cert), groupbuf,
512290001Sglebius		    MAXFILENAME);
513290001Sglebius
514290001Sglebius		/*
515290001Sglebius		 * Extract digest/signature scheme.
516290001Sglebius		 */
517290001Sglebius		if (scheme == NULL) {
518310419Sdelphij			nid = X509_get_signature_nid(cert);
519290001Sglebius			scheme = OBJ_nid2sn(nid);
520290001Sglebius		}
521290001Sglebius
522290001Sglebius		/*
523290001Sglebius		 * If a key_usage extension field is present, determine
524290001Sglebius		 * whether this is a trusted or private certificate.
525290001Sglebius		 */
526290001Sglebius		if (exten == NULL) {
527290001Sglebius			ptr = strstr(groupbuf, "CN=");
528290001Sglebius			cnt = X509_get_ext_count(cert);
529290001Sglebius			for (i = 0; i < cnt; i++) {
530310419Sdelphij				X509_EXTENSION *ext;
531310419Sdelphij				ASN1_OBJECT *obj;
532310419Sdelphij
533290001Sglebius				ext = X509_get_ext(cert, i);
534310419Sdelphij				obj = X509_EXTENSION_get_object(ext);
535310419Sdelphij
536310419Sdelphij				if (OBJ_obj2nid(obj) ==
537290001Sglebius				    NID_ext_key_usage) {
538290001Sglebius					bp = BIO_new(BIO_s_mem());
539290001Sglebius					X509V3_EXT_print(bp, ext, 0, 0);
540290001Sglebius					BIO_gets(bp, pathbuf,
541290001Sglebius					    MAXFILENAME);
542290001Sglebius					BIO_free(bp);
543290001Sglebius					if (strcmp(pathbuf,
544290001Sglebius					    "Trust Root") == 0)
545290001Sglebius						exten = EXT_KEY_TRUST;
546290001Sglebius					else if (strcmp(pathbuf,
547290001Sglebius					    "Private") == 0)
548290001Sglebius						exten = EXT_KEY_PRIVATE;
549290001Sglebius					certname = estrdup(ptr + 3);
550290001Sglebius				}
551132451Sroberto			}
552290001Sglebius		}
553290001Sglebius	}
554290001Sglebius	if (scheme == NULL)
555290001Sglebius		scheme = "RSA-MD5";
556290001Sglebius	if (ciphername == NULL)
557290001Sglebius		ciphername = "des-ede3-cbc";
558290001Sglebius	cipher = EVP_get_cipherbyname(ciphername);
559290001Sglebius	if (cipher == NULL) {
560290001Sglebius		fprintf(stderr, "Unknown cipher %s\n", ciphername);
561290001Sglebius		exit(-1);
562290001Sglebius	}
563290001Sglebius	fprintf(stderr, "Using host %s group %s\n", hostname,
564290001Sglebius	    groupname);
565132451Sroberto
566290001Sglebius	/*
567290001Sglebius	 * Create a new encrypted RSA host key file if requested;
568290001Sglebius	 * otherwise, look for an existing host key file. If not found,
569290001Sglebius	 * create a new encrypted RSA host key file. If that fails, go
570290001Sglebius	 * no further.
571290001Sglebius	 */
572290001Sglebius	if (hostkey)
573290001Sglebius		pkey_host = genkey("RSA", "host");
574290001Sglebius	if (pkey_host == NULL) {
575290001Sglebius		snprintf(filename, sizeof(filename), "ntpkey_host_%s", hostname);
576290001Sglebius		pkey_host = readkey(filename, passwd1, &fstamp, NULL);
577290001Sglebius		if (pkey_host != NULL) {
578290001Sglebius			followlink(filename, sizeof(filename));
579290001Sglebius			fprintf(stderr, "Using host key %s\n",
580290001Sglebius			    filename);
581290001Sglebius		} else {
582290001Sglebius			pkey_host = genkey("RSA", "host");
583132451Sroberto		}
584132451Sroberto	}
585290001Sglebius	if (pkey_host == NULL) {
586290001Sglebius		fprintf(stderr, "Generating host key fails\n");
587290001Sglebius		exit(-1);
588290001Sglebius	}
589132451Sroberto
590132451Sroberto	/*
591290001Sglebius	 * Create new encrypted RSA or DSA sign keys file if requested;
592290001Sglebius	 * otherwise, look for an existing sign key file. If not found,
593290001Sglebius	 * use the host key instead.
594132451Sroberto	 */
595290001Sglebius	if (sign != NULL)
596290001Sglebius		pkey_sign = genkey(sign, "sign");
597290001Sglebius	if (pkey_sign == NULL) {
598290001Sglebius		snprintf(filename, sizeof(filename), "ntpkey_sign_%s",
599290001Sglebius			 hostname);
600290001Sglebius		pkey_sign = readkey(filename, passwd1, &fstamp, NULL);
601290001Sglebius		if (pkey_sign != NULL) {
602290001Sglebius			followlink(filename, sizeof(filename));
603290001Sglebius			fprintf(stderr, "Using sign key %s\n",
604290001Sglebius			    filename);
605132451Sroberto		} else {
606290001Sglebius			pkey_sign = pkey_host;
607132451Sroberto			fprintf(stderr, "Using host key as sign key\n");
608132451Sroberto		}
609132451Sroberto	}
610132451Sroberto
611132451Sroberto	/*
612290001Sglebius	 * Create new encrypted GQ server keys file if requested;
613290001Sglebius	 * otherwise, look for an exisiting file. If found, fetch the
614290001Sglebius	 * public key for the certificate.
615132451Sroberto	 */
616290001Sglebius	if (gqkey)
617290001Sglebius		pkey_gqkey = gen_gqkey("gqkey");
618290001Sglebius	if (pkey_gqkey == NULL) {
619290001Sglebius		snprintf(filename, sizeof(filename), "ntpkey_gqkey_%s",
620290001Sglebius		    groupname);
621290001Sglebius		pkey_gqkey = readkey(filename, passwd1, &fstamp, NULL);
622290001Sglebius		if (pkey_gqkey != NULL) {
623290001Sglebius			followlink(filename, sizeof(filename));
624290001Sglebius			fprintf(stderr, "Using GQ parameters %s\n",
625290001Sglebius			    filename);
626132451Sroberto		}
627132451Sroberto	}
628310419Sdelphij	if (pkey_gqkey != NULL) {
629310419Sdelphij		RSA	*rsa;
630310419Sdelphij		const BIGNUM *q;
631132451Sroberto
632310419Sdelphij		rsa = EVP_PKEY_get0_RSA(pkey_gqkey);
633310419Sdelphij		RSA_get0_factors(rsa, NULL, &q);
634310419Sdelphij		grpkey = BN_bn2hex(q);
635310419Sdelphij	}
636310419Sdelphij
637132451Sroberto	/*
638290001Sglebius	 * Write the nonencrypted GQ client parameters to the stdout
639290001Sglebius	 * stream. The parameter file is the server key file with the
640290001Sglebius	 * private key obscured.
641132451Sroberto	 */
642290001Sglebius	if (pkey_gqkey != NULL && HAVE_OPT(ID_KEY)) {
643290001Sglebius		RSA	*rsa;
644290001Sglebius
645290001Sglebius		snprintf(filename, sizeof(filename),
646290001Sglebius		    "ntpkey_gqpar_%s.%u", groupname, fstamp);
647290001Sglebius		fprintf(stderr, "Writing GQ parameters %s to stdout\n",
648290001Sglebius		    filename);
649290001Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
650290001Sglebius		    ctime(&epoch));
651310419Sdelphij		/* XXX: This modifies the private key and should probably use a
652310419Sdelphij		 * copy of it instead. */
653310419Sdelphij		rsa = EVP_PKEY_get0_RSA(pkey_gqkey);
654310419Sdelphij		RSA_set0_factors(rsa, BN_dup(BN_value_one()), BN_dup(BN_value_one()));
655290001Sglebius		pkey = EVP_PKEY_new();
656290001Sglebius		EVP_PKEY_assign_RSA(pkey, rsa);
657290001Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, NULL, NULL, 0,
658290001Sglebius		    NULL, NULL);
659290001Sglebius		fflush(stdout);
660290001Sglebius		if (debug)
661290001Sglebius			RSA_print_fp(stderr, rsa, 0);
662132451Sroberto	}
663132451Sroberto
664132451Sroberto	/*
665290001Sglebius	 * Write the encrypted GQ server keys to the stdout stream.
666132451Sroberto	 */
667290001Sglebius	if (pkey_gqkey != NULL && passwd2 != NULL) {
668290001Sglebius		RSA	*rsa;
669290001Sglebius
670290001Sglebius		snprintf(filename, sizeof(filename),
671290001Sglebius		    "ntpkey_gqkey_%s.%u", groupname, fstamp);
672290001Sglebius		fprintf(stderr, "Writing GQ keys %s to stdout\n",
673290001Sglebius		    filename);
674290001Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
675290001Sglebius		    ctime(&epoch));
676310419Sdelphij		rsa = EVP_PKEY_get0_RSA(pkey_gqkey);
677290001Sglebius		pkey = EVP_PKEY_new();
678290001Sglebius		EVP_PKEY_assign_RSA(pkey, rsa);
679290001Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, cipher, NULL, 0,
680290001Sglebius		    NULL, passwd2);
681290001Sglebius		fflush(stdout);
682290001Sglebius		if (debug)
683290001Sglebius			RSA_print_fp(stderr, rsa, 0);
684132451Sroberto	}
685132451Sroberto
686132451Sroberto	/*
687290001Sglebius	 * Create new encrypted IFF server keys file if requested;
688290001Sglebius	 * otherwise, look for existing file.
689132451Sroberto	 */
690290001Sglebius	if (iffkey)
691290001Sglebius		pkey_iffkey = gen_iffkey("iffkey");
692290001Sglebius	if (pkey_iffkey == NULL) {
693290001Sglebius		snprintf(filename, sizeof(filename), "ntpkey_iffkey_%s",
694290001Sglebius		    groupname);
695290001Sglebius		pkey_iffkey = readkey(filename, passwd1, &fstamp, NULL);
696290001Sglebius		if (pkey_iffkey != NULL) {
697290001Sglebius			followlink(filename, sizeof(filename));
698290001Sglebius			fprintf(stderr, "Using IFF keys %s\n",
699290001Sglebius			    filename);
700132451Sroberto		}
701132451Sroberto	}
702132451Sroberto
703132451Sroberto	/*
704290001Sglebius	 * Write the nonencrypted IFF client parameters to the stdout
705290001Sglebius	 * stream. The parameter file is the server key file with the
706290001Sglebius	 * private key obscured.
707132451Sroberto	 */
708290001Sglebius	if (pkey_iffkey != NULL && HAVE_OPT(ID_KEY)) {
709132451Sroberto		DSA	*dsa;
710132451Sroberto
711290001Sglebius		snprintf(filename, sizeof(filename),
712290001Sglebius		    "ntpkey_iffpar_%s.%u", groupname, fstamp);
713290001Sglebius		fprintf(stderr, "Writing IFF parameters %s to stdout\n",
714290001Sglebius		    filename);
715290001Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
716290001Sglebius		    ctime(&epoch));
717310419Sdelphij		/* XXX: This modifies the private key and should probably use a
718310419Sdelphij		 * copy of it instead. */
719310419Sdelphij		dsa = EVP_PKEY_get0_DSA(pkey_iffkey);
720310419Sdelphij		DSA_set0_key(dsa, NULL, BN_dup(BN_value_one()));
721132451Sroberto		pkey = EVP_PKEY_new();
722132451Sroberto		EVP_PKEY_assign_DSA(pkey, dsa);
723290001Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, NULL, NULL, 0,
724290001Sglebius		    NULL, NULL);
725290001Sglebius		fflush(stdout);
726132451Sroberto		if (debug)
727290001Sglebius			DSA_print_fp(stderr, dsa, 0);
728132451Sroberto	}
729132451Sroberto
730132451Sroberto	/*
731290001Sglebius	 * Write the encrypted IFF server keys to the stdout stream.
732132451Sroberto	 */
733290001Sglebius	if (pkey_iffkey != NULL && passwd2 != NULL) {
734290001Sglebius		DSA	*dsa;
735132451Sroberto
736290001Sglebius		snprintf(filename, sizeof(filename),
737290001Sglebius		    "ntpkey_iffkey_%s.%u", groupname, fstamp);
738290001Sglebius		fprintf(stderr, "Writing IFF keys %s to stdout\n",
739290001Sglebius		    filename);
740290001Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
741290001Sglebius		    ctime(&epoch));
742310419Sdelphij		dsa = EVP_PKEY_get0_DSA(pkey_iffkey);
743290001Sglebius		pkey = EVP_PKEY_new();
744290001Sglebius		EVP_PKEY_assign_DSA(pkey, dsa);
745290001Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, cipher, NULL, 0,
746290001Sglebius		    NULL, passwd2);
747290001Sglebius		fflush(stdout);
748290001Sglebius		if (debug)
749290001Sglebius			DSA_print_fp(stderr, dsa, 0);
750290001Sglebius	}
751132451Sroberto
752290001Sglebius	/*
753290001Sglebius	 * Create new encrypted MV trusted-authority keys file if
754290001Sglebius	 * requested; otherwise, look for existing keys file.
755290001Sglebius	 */
756290001Sglebius	if (mvkey)
757290001Sglebius		pkey_mvkey = gen_mvkey("mv", pkey_mvpar);
758290001Sglebius	if (pkey_mvkey == NULL) {
759290001Sglebius		snprintf(filename, sizeof(filename), "ntpkey_mvta_%s",
760290001Sglebius		    groupname);
761290001Sglebius		pkey_mvkey = readkey(filename, passwd1, &fstamp,
762290001Sglebius		    pkey_mvpar);
763290001Sglebius		if (pkey_mvkey != NULL) {
764290001Sglebius			followlink(filename, sizeof(filename));
765290001Sglebius			fprintf(stderr, "Using MV keys %s\n",
766290001Sglebius			    filename);
767290001Sglebius		}
768290001Sglebius	}
769132451Sroberto
770290001Sglebius	/*
771290001Sglebius	 * Write the nonencrypted MV client parameters to the stdout
772290001Sglebius	 * stream. For the moment, we always use the client parameters
773290001Sglebius	 * associated with client key 1.
774290001Sglebius	 */
775290001Sglebius	if (pkey_mvkey != NULL && HAVE_OPT(ID_KEY)) {
776290001Sglebius		snprintf(filename, sizeof(filename),
777290001Sglebius		    "ntpkey_mvpar_%s.%u", groupname, fstamp);
778290001Sglebius		fprintf(stderr, "Writing MV parameters %s to stdout\n",
779290001Sglebius		    filename);
780290001Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
781290001Sglebius		    ctime(&epoch));
782290001Sglebius		pkey = pkey_mvpar[2];
783290001Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, NULL, NULL, 0,
784290001Sglebius		    NULL, NULL);
785290001Sglebius		fflush(stdout);
786290001Sglebius		if (debug)
787310419Sdelphij			DSA_print_fp(stderr, EVP_PKEY_get0_DSA(pkey), 0);
788290001Sglebius	}
789290001Sglebius
790290001Sglebius	/*
791290001Sglebius	 * Write the encrypted MV server keys to the stdout stream.
792290001Sglebius	 */
793290001Sglebius	if (pkey_mvkey != NULL && passwd2 != NULL) {
794290001Sglebius		snprintf(filename, sizeof(filename),
795290001Sglebius		    "ntpkey_mvkey_%s.%u", groupname, fstamp);
796290001Sglebius		fprintf(stderr, "Writing MV keys %s to stdout\n",
797290001Sglebius		    filename);
798290001Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
799290001Sglebius		    ctime(&epoch));
800290001Sglebius		pkey = pkey_mvpar[1];
801290001Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, cipher, NULL, 0,
802290001Sglebius		    NULL, passwd2);
803290001Sglebius		fflush(stdout);
804290001Sglebius		if (debug)
805310419Sdelphij			DSA_print_fp(stderr, EVP_PKEY_get0_DSA(pkey), 0);
806290001Sglebius	}
807290001Sglebius
808290001Sglebius	/*
809290001Sglebius	 * Decode the digest/signature scheme and create the
810290001Sglebius	 * certificate. Do this every time we run the program.
811290001Sglebius	 */
812290001Sglebius	ectx = EVP_get_digestbyname(scheme);
813290001Sglebius	if (ectx == NULL) {
814290001Sglebius		fprintf(stderr,
815290001Sglebius		    "Invalid digest/signature combination %s\n",
816290001Sglebius		    scheme);
817290001Sglebius			exit (-1);
818290001Sglebius	}
819290001Sglebius	x509(pkey_sign, ectx, grpkey, exten, certname);
820290001Sglebius#endif	/* AUTOKEY */
821290001Sglebius	exit(0);
822132451Sroberto}
823132451Sroberto
824132451Sroberto
825132451Sroberto/*
826290001Sglebius * Generate semi-random MD5 keys compatible with NTPv3 and NTPv4. Also,
827290001Sglebius * if OpenSSL is around, generate random SHA1 keys compatible with
828290001Sglebius * symmetric key cryptography.
829132451Sroberto */
830132451Srobertoint
831132451Srobertogen_md5(
832290001Sglebius	const char *id		/* file name id */
833132451Sroberto	)
834132451Sroberto{
835290001Sglebius	u_char	md5key[MD5SIZE + 1];	/* MD5 key */
836132451Sroberto	FILE	*str;
837132451Sroberto	int	i, j;
838290001Sglebius#ifdef OPENSSL
839290001Sglebius	u_char	keystr[MD5SIZE];
840290001Sglebius	u_char	hexstr[2 * MD5SIZE + 1];
841290001Sglebius	u_char	hex[] = "0123456789abcdef";
842290001Sglebius#endif	/* OPENSSL */
843132451Sroberto
844290001Sglebius	str = fheader("MD5key", id, groupname);
845132451Sroberto	for (i = 1; i <= MD5KEYS; i++) {
846290001Sglebius		for (j = 0; j < MD5SIZE; j++) {
847290001Sglebius			u_char temp;
848290001Sglebius
849132451Sroberto			while (1) {
850290001Sglebius				int rc;
851290001Sglebius
852290001Sglebius				rc = ntp_crypto_random_buf(
853290001Sglebius				    &temp, sizeof(temp));
854290001Sglebius				if (-1 == rc) {
855290001Sglebius					fprintf(stderr, "ntp_crypto_random_buf() failed.\n");
856290001Sglebius					exit (-1);
857290001Sglebius				}
858132451Sroberto				if (temp == '#')
859132451Sroberto					continue;
860290001Sglebius
861132451Sroberto				if (temp > 0x20 && temp < 0x7f)
862132451Sroberto					break;
863132451Sroberto			}
864290001Sglebius			md5key[j] = temp;
865132451Sroberto		}
866290001Sglebius		md5key[j] = '\0';
867290001Sglebius		fprintf(str, "%2d MD5 %s  # MD5 key\n", i,
868132451Sroberto		    md5key);
869132451Sroberto	}
870290001Sglebius#ifdef OPENSSL
871290001Sglebius	for (i = 1; i <= MD5KEYS; i++) {
872290001Sglebius		RAND_bytes(keystr, 20);
873290001Sglebius		for (j = 0; j < MD5SIZE; j++) {
874290001Sglebius			hexstr[2 * j] = hex[keystr[j] >> 4];
875290001Sglebius			hexstr[2 * j + 1] = hex[keystr[j] & 0xf];
876290001Sglebius		}
877290001Sglebius		hexstr[2 * MD5SIZE] = '\0';
878290001Sglebius		fprintf(str, "%2d SHA1 %s  # SHA1 key\n", i + MD5KEYS,
879290001Sglebius		    hexstr);
880290001Sglebius	}
881290001Sglebius#endif	/* OPENSSL */
882132451Sroberto	fclose(str);
883132451Sroberto	return (1);
884132451Sroberto}
885132451Sroberto
886132451Sroberto
887290001Sglebius#ifdef AUTOKEY
888132451Sroberto/*
889290001Sglebius * readkey - load cryptographic parameters and keys
890290001Sglebius *
891290001Sglebius * This routine loads a PEM-encoded file of given name and password and
892290001Sglebius * extracts the filestamp from the file name. It returns a pointer to
893290001Sglebius * the first key if valid, NULL if not.
894290001Sglebius */
895290001SglebiusEVP_PKEY *			/* public/private key pair */
896290001Sglebiusreadkey(
897290001Sglebius	char	*cp,		/* file name */
898290001Sglebius	char	*passwd,	/* password */
899290001Sglebius	u_int	*estamp,	/* file stamp */
900290001Sglebius	EVP_PKEY **evpars	/* parameter list pointer */
901290001Sglebius	)
902290001Sglebius{
903290001Sglebius	FILE	*str;		/* file handle */
904290001Sglebius	EVP_PKEY *pkey = NULL;	/* public/private key */
905290001Sglebius	u_int	gstamp;		/* filestamp */
906290001Sglebius	char	linkname[MAXFILENAME]; /* filestamp buffer) */
907290001Sglebius	EVP_PKEY *parkey;
908290001Sglebius	char	*ptr;
909290001Sglebius	int	i;
910290001Sglebius
911290001Sglebius	/*
912290001Sglebius	 * Open the key file.
913290001Sglebius	 */
914290001Sglebius	str = fopen(cp, "r");
915290001Sglebius	if (str == NULL)
916290001Sglebius		return (NULL);
917290001Sglebius
918290001Sglebius	/*
919290001Sglebius	 * Read the filestamp, which is contained in the first line.
920290001Sglebius	 */
921290001Sglebius	if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) {
922290001Sglebius		fprintf(stderr, "Empty key file %s\n", cp);
923290001Sglebius		fclose(str);
924290001Sglebius		return (NULL);
925290001Sglebius	}
926290001Sglebius	if ((ptr = strrchr(ptr, '.')) == NULL) {
927290001Sglebius		fprintf(stderr, "No filestamp found in %s\n", cp);
928290001Sglebius		fclose(str);
929290001Sglebius		return (NULL);
930290001Sglebius	}
931290001Sglebius	if (sscanf(++ptr, "%u", &gstamp) != 1) {
932290001Sglebius		fprintf(stderr, "Invalid filestamp found in %s\n", cp);
933290001Sglebius		fclose(str);
934290001Sglebius		return (NULL);
935290001Sglebius	}
936290001Sglebius
937290001Sglebius	/*
938290001Sglebius	 * Read and decrypt PEM-encoded private keys. The first one
939290001Sglebius	 * found is returned. If others are expected, add them to the
940290001Sglebius	 * parameter list.
941290001Sglebius	 */
942290001Sglebius	for (i = 0; i <= MVMAX - 1;) {
943290001Sglebius		parkey = PEM_read_PrivateKey(str, NULL, NULL, passwd);
944290001Sglebius		if (evpars != NULL) {
945290001Sglebius			evpars[i++] = parkey;
946290001Sglebius			evpars[i] = NULL;
947290001Sglebius		}
948290001Sglebius		if (parkey == NULL)
949290001Sglebius			break;
950290001Sglebius
951290001Sglebius		if (pkey == NULL)
952290001Sglebius			pkey = parkey;
953290001Sglebius		if (debug) {
954310419Sdelphij			if (EVP_PKEY_base_id(parkey) == EVP_PKEY_DSA)
955310419Sdelphij				DSA_print_fp(stderr, EVP_PKEY_get0_DSA(parkey),
956290001Sglebius				    0);
957310419Sdelphij			else if (EVP_PKEY_base_id(parkey) == EVP_PKEY_RSA)
958310419Sdelphij				RSA_print_fp(stderr, EVP_PKEY_get0_RSA(parkey),
959290001Sglebius				    0);
960290001Sglebius		}
961290001Sglebius	}
962290001Sglebius	fclose(str);
963290001Sglebius	if (pkey == NULL) {
964290001Sglebius		fprintf(stderr, "Corrupt file %s or wrong key %s\n%s\n",
965290001Sglebius		    cp, passwd, ERR_error_string(ERR_get_error(),
966290001Sglebius		    NULL));
967290001Sglebius		exit (-1);
968290001Sglebius	}
969290001Sglebius	*estamp = gstamp;
970290001Sglebius	return (pkey);
971290001Sglebius}
972290001Sglebius
973290001Sglebius
974290001Sglebius/*
975132451Sroberto * Generate RSA public/private key pair
976132451Sroberto */
977132451SrobertoEVP_PKEY *			/* public/private key pair */
978132451Srobertogen_rsa(
979290001Sglebius	const char *id		/* file name id */
980132451Sroberto	)
981132451Sroberto{
982132451Sroberto	EVP_PKEY *pkey;		/* private key */
983132451Sroberto	RSA	*rsa;		/* RSA parameters and key pair */
984132451Sroberto	FILE	*str;
985132451Sroberto
986132451Sroberto	fprintf(stderr, "Generating RSA keys (%d bits)...\n", modulus);
987310419Sdelphij	rsa = genRsaKeyPair(modulus, _UC("RSA"));
988132451Sroberto	fprintf(stderr, "\n");
989132451Sroberto	if (rsa == NULL) {
990132451Sroberto		fprintf(stderr, "RSA generate keys fails\n%s\n",
991132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
992132451Sroberto		return (NULL);
993132451Sroberto	}
994132451Sroberto
995132451Sroberto	/*
996132451Sroberto	 * For signature encryption it is not necessary that the RSA
997132451Sroberto	 * parameters be strictly groomed and once in a while the
998132451Sroberto	 * modulus turns out to be non-prime. Just for grins, we check
999132451Sroberto	 * the primality.
1000132451Sroberto	 */
1001132451Sroberto	if (!RSA_check_key(rsa)) {
1002132451Sroberto		fprintf(stderr, "Invalid RSA key\n%s\n",
1003132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
1004132451Sroberto		RSA_free(rsa);
1005132451Sroberto		return (NULL);
1006132451Sroberto	}
1007132451Sroberto
1008132451Sroberto	/*
1009132451Sroberto	 * Write the RSA parameters and keys as a RSA private key
1010132451Sroberto	 * encoded in PEM.
1011132451Sroberto	 */
1012290001Sglebius	if (strcmp(id, "sign") == 0)
1013290001Sglebius		str = fheader("RSAsign", id, hostname);
1014290001Sglebius	else
1015290001Sglebius		str = fheader("RSAhost", id, hostname);
1016132451Sroberto	pkey = EVP_PKEY_new();
1017132451Sroberto	EVP_PKEY_assign_RSA(pkey, rsa);
1018290001Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1019290001Sglebius	    passwd1);
1020132451Sroberto	fclose(str);
1021132451Sroberto	if (debug)
1022290001Sglebius		RSA_print_fp(stderr, rsa, 0);
1023132451Sroberto	return (pkey);
1024132451Sroberto}
1025132451Sroberto
1026310419Sdelphij
1027132451Sroberto/*
1028132451Sroberto * Generate DSA public/private key pair
1029132451Sroberto */
1030132451SrobertoEVP_PKEY *			/* public/private key pair */
1031132451Srobertogen_dsa(
1032290001Sglebius	const char *id		/* file name id */
1033132451Sroberto	)
1034132451Sroberto{
1035132451Sroberto	EVP_PKEY *pkey;		/* private key */
1036132451Sroberto	DSA	*dsa;		/* DSA parameters */
1037132451Sroberto	FILE	*str;
1038132451Sroberto
1039132451Sroberto	/*
1040132451Sroberto	 * Generate DSA parameters.
1041132451Sroberto	 */
1042132451Sroberto	fprintf(stderr,
1043132451Sroberto	    "Generating DSA parameters (%d bits)...\n", modulus);
1044310419Sdelphij	dsa = genDsaParams(modulus, _UC("DSA"));
1045132451Sroberto	fprintf(stderr, "\n");
1046132451Sroberto	if (dsa == NULL) {
1047132451Sroberto		fprintf(stderr, "DSA generate parameters fails\n%s\n",
1048132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
1049132451Sroberto		return (NULL);
1050132451Sroberto	}
1051132451Sroberto
1052132451Sroberto	/*
1053132451Sroberto	 * Generate DSA keys.
1054132451Sroberto	 */
1055132451Sroberto	fprintf(stderr, "Generating DSA keys (%d bits)...\n", modulus);
1056132451Sroberto	if (!DSA_generate_key(dsa)) {
1057132451Sroberto		fprintf(stderr, "DSA generate keys fails\n%s\n",
1058132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
1059132451Sroberto		DSA_free(dsa);
1060132451Sroberto		return (NULL);
1061132451Sroberto	}
1062132451Sroberto
1063132451Sroberto	/*
1064132451Sroberto	 * Write the DSA parameters and keys as a DSA private key
1065132451Sroberto	 * encoded in PEM.
1066132451Sroberto	 */
1067290001Sglebius	str = fheader("DSAsign", id, hostname);
1068132451Sroberto	pkey = EVP_PKEY_new();
1069132451Sroberto	EVP_PKEY_assign_DSA(pkey, dsa);
1070290001Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1071290001Sglebius	    passwd1);
1072132451Sroberto	fclose(str);
1073132451Sroberto	if (debug)
1074290001Sglebius		DSA_print_fp(stderr, dsa, 0);
1075132451Sroberto	return (pkey);
1076132451Sroberto}
1077132451Sroberto
1078132451Sroberto
1079132451Sroberto/*
1080290001Sglebius ***********************************************************************
1081290001Sglebius *								       *
1082290001Sglebius * The following routines implement the Schnorr (IFF) identity scheme  *
1083290001Sglebius *								       *
1084290001Sglebius ***********************************************************************
1085132451Sroberto *
1086290001Sglebius * The Schnorr (IFF) identity scheme is intended for use when
1087132451Sroberto * certificates are generated by some other trusted certificate
1088290001Sglebius * authority and the certificate cannot be used to convey public
1089290001Sglebius * parameters. There are two kinds of files: encrypted server files that
1090290001Sglebius * contain private and public values and nonencrypted client files that
1091290001Sglebius * contain only public values. New generations of server files must be
1092290001Sglebius * securely transmitted to all servers of the group; client files can be
1093290001Sglebius * distributed by any means. The scheme is self contained and
1094290001Sglebius * independent of new generations of host keys, sign keys and
1095290001Sglebius * certificates.
1096132451Sroberto *
1097132451Sroberto * The IFF values hide in a DSA cuckoo structure which uses the same
1098132451Sroberto * parameters. The values are used by an identity scheme based on DSA
1099132451Sroberto * cryptography and described in Stimson p. 285. The p is a 512-bit
1100132451Sroberto * prime, g a generator of Zp* and q a 160-bit prime that divides p - 1
1101132451Sroberto * and is a qth root of 1 mod p; that is, g^q = 1 mod p. The TA rolls a
1102290001Sglebius * private random group key b (0 < b < q) and public key v = g^b, then
1103290001Sglebius * sends (p, q, g, b) to the servers and (p, q, g, v) to the clients.
1104290001Sglebius * Alice challenges Bob to confirm identity using the protocol described
1105290001Sglebius * below.
1106290001Sglebius *
1107290001Sglebius * How it works
1108290001Sglebius *
1109290001Sglebius * The scheme goes like this. Both Alice and Bob have the public primes
1110290001Sglebius * p, q and generator g. The TA gives private key b to Bob and public
1111290001Sglebius * key v to Alice.
1112290001Sglebius *
1113290001Sglebius * Alice rolls new random challenge r (o < r < q) and sends to Bob in
1114290001Sglebius * the IFF request message. Bob rolls new random k (0 < k < q), then
1115290001Sglebius * computes y = k + b r mod q and x = g^k mod p and sends (y, hash(x))
1116290001Sglebius * to Alice in the response message. Besides making the response
1117290001Sglebius * shorter, the hash makes it effectivey impossible for an intruder to
1118290001Sglebius * solve for b by observing a number of these messages.
1119290001Sglebius *
1120290001Sglebius * Alice receives the response and computes g^y v^r mod p. After a bit
1121290001Sglebius * of algebra, this simplifies to g^k. If the hash of this result
1122290001Sglebius * matches hash(x), Alice knows that Bob has the group key b. The signed
1123290001Sglebius * response binds this knowledge to Bob's private key and the public key
1124290001Sglebius * previously received in his certificate.
1125132451Sroberto */
1126290001Sglebius/*
1127290001Sglebius * Generate Schnorr (IFF) keys.
1128290001Sglebius */
1129132451SrobertoEVP_PKEY *			/* DSA cuckoo nest */
1130290001Sglebiusgen_iffkey(
1131290001Sglebius	const char *id		/* file name id */
1132132451Sroberto	)
1133132451Sroberto{
1134132451Sroberto	EVP_PKEY *pkey;		/* private key */
1135132451Sroberto	DSA	*dsa;		/* DSA parameters */
1136132451Sroberto	BN_CTX	*ctx;		/* BN working space */
1137132451Sroberto	BIGNUM	*b, *r, *k, *u, *v, *w; /* BN temp */
1138132451Sroberto	FILE	*str;
1139132451Sroberto	u_int	temp;
1140310419Sdelphij	const BIGNUM *p, *q, *g;
1141310419Sdelphij	BIGNUM *pub_key, *priv_key;
1142310419Sdelphij
1143132451Sroberto	/*
1144132451Sroberto	 * Generate DSA parameters for use as IFF parameters.
1145132451Sroberto	 */
1146290001Sglebius	fprintf(stderr, "Generating IFF keys (%d bits)...\n",
1147290001Sglebius	    modulus2);
1148310419Sdelphij	dsa = genDsaParams(modulus2, _UC("IFF"));
1149132451Sroberto	fprintf(stderr, "\n");
1150132451Sroberto	if (dsa == NULL) {
1151132451Sroberto		fprintf(stderr, "DSA generate parameters fails\n%s\n",
1152132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
1153310419Sdelphij		return (NULL);
1154132451Sroberto	}
1155310419Sdelphij	DSA_get0_pqg(dsa, &p, &q, &g);
1156132451Sroberto
1157132451Sroberto	/*
1158132451Sroberto	 * Generate the private and public keys. The DSA parameters and
1159290001Sglebius	 * private key are distributed to the servers, while all except
1160290001Sglebius	 * the private key are distributed to the clients.
1161132451Sroberto	 */
1162132451Sroberto	b = BN_new(); r = BN_new(); k = BN_new();
1163132451Sroberto	u = BN_new(); v = BN_new(); w = BN_new(); ctx = BN_CTX_new();
1164310419Sdelphij	BN_rand(b, BN_num_bits(q), -1, 0);	/* a */
1165310419Sdelphij	BN_mod(b, b, q, ctx);
1166310419Sdelphij	BN_sub(v, q, b);
1167310419Sdelphij	BN_mod_exp(v, g, v, p, ctx); /* g^(q - b) mod p */
1168310419Sdelphij	BN_mod_exp(u, g, b, p, ctx);	/* g^b mod p */
1169310419Sdelphij	BN_mod_mul(u, u, v, p, ctx);
1170132451Sroberto	temp = BN_is_one(u);
1171132451Sroberto	fprintf(stderr,
1172132451Sroberto	    "Confirm g^(q - b) g^b = 1 mod p: %s\n", temp == 1 ?
1173132451Sroberto	    "yes" : "no");
1174132451Sroberto	if (!temp) {
1175132451Sroberto		BN_free(b); BN_free(r); BN_free(k);
1176132451Sroberto		BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx);
1177132451Sroberto		return (NULL);
1178132451Sroberto	}
1179310419Sdelphij	pub_key = BN_dup(v);
1180310419Sdelphij	priv_key = BN_dup(b);
1181310419Sdelphij	DSA_set0_key(dsa, pub_key, priv_key);
1182132451Sroberto
1183132451Sroberto	/*
1184132451Sroberto	 * Here is a trial round of the protocol. First, Alice rolls
1185290001Sglebius	 * random nonce r mod q and sends it to Bob. She needs only
1186290001Sglebius	 * q from parameters.
1187132451Sroberto	 */
1188310419Sdelphij	BN_rand(r, BN_num_bits(q), -1, 0);	/* r */
1189310419Sdelphij	BN_mod(r, r, q, ctx);
1190132451Sroberto
1191132451Sroberto	/*
1192290001Sglebius	 * Bob rolls random nonce k mod q, computes y = k + b r mod q
1193132451Sroberto	 * and x = g^k mod p, then sends (y, x) to Alice. He needs
1194290001Sglebius	 * p, q and b from parameters and r from Alice.
1195132451Sroberto	 */
1196310419Sdelphij	BN_rand(k, BN_num_bits(q), -1, 0);	/* k, 0 < k < q  */
1197310419Sdelphij	BN_mod(k, k, q, ctx);
1198310419Sdelphij	BN_mod_mul(v, priv_key, r, q, ctx); /* b r mod q */
1199132451Sroberto	BN_add(v, v, k);
1200310419Sdelphij	BN_mod(v, v, q, ctx);		/* y = k + b r mod q */
1201310419Sdelphij	BN_mod_exp(u, g, k, p, ctx);	/* x = g^k mod p */
1202132451Sroberto
1203132451Sroberto	/*
1204290001Sglebius	 * Alice verifies x = g^y v^r to confirm that Bob has group key
1205290001Sglebius	 * b. She needs p, q, g from parameters, (y, x) from Bob and the
1206290001Sglebius	 * original r. We omit the detail here thatt only the hash of y
1207290001Sglebius	 * is sent.
1208132451Sroberto	 */
1209310419Sdelphij	BN_mod_exp(v, g, v, p, ctx); /* g^y mod p */
1210310419Sdelphij	BN_mod_exp(w, pub_key, r, p, ctx); /* v^r */
1211310419Sdelphij	BN_mod_mul(v, w, v, p, ctx);	/* product mod p */
1212132451Sroberto	temp = BN_cmp(u, v);
1213132451Sroberto	fprintf(stderr,
1214132451Sroberto	    "Confirm g^k = g^(k + b r) g^(q - b) r: %s\n", temp ==
1215132451Sroberto	    0 ? "yes" : "no");
1216132451Sroberto	BN_free(b); BN_free(r);	BN_free(k);
1217132451Sroberto	BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx);
1218132451Sroberto	if (temp != 0) {
1219132451Sroberto		DSA_free(dsa);
1220132451Sroberto		return (NULL);
1221132451Sroberto	}
1222132451Sroberto
1223132451Sroberto	/*
1224290001Sglebius	 * Write the IFF keys as an encrypted DSA private key encoded in
1225290001Sglebius	 * PEM.
1226132451Sroberto	 *
1227132451Sroberto	 * p	modulus p
1228132451Sroberto	 * q	modulus q
1229132451Sroberto	 * g	generator g
1230132451Sroberto	 * priv_key b
1231132451Sroberto	 * public_key v
1232290001Sglebius	 * kinv	not used
1233290001Sglebius	 * r	not used
1234132451Sroberto	 */
1235290001Sglebius	str = fheader("IFFkey", id, groupname);
1236132451Sroberto	pkey = EVP_PKEY_new();
1237132451Sroberto	EVP_PKEY_assign_DSA(pkey, dsa);
1238290001Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1239290001Sglebius	    passwd1);
1240132451Sroberto	fclose(str);
1241132451Sroberto	if (debug)
1242290001Sglebius		DSA_print_fp(stderr, dsa, 0);
1243132451Sroberto	return (pkey);
1244132451Sroberto}
1245132451Sroberto
1246132451Sroberto
1247132451Sroberto/*
1248290001Sglebius ***********************************************************************
1249290001Sglebius *								       *
1250290001Sglebius * The following routines implement the Guillou-Quisquater (GQ)        *
1251290001Sglebius * identity scheme                                                     *
1252290001Sglebius *								       *
1253290001Sglebius ***********************************************************************
1254132451Sroberto *
1255132451Sroberto * The Guillou-Quisquater (GQ) identity scheme is intended for use when
1256290001Sglebius * the certificate can be used to convey public parameters. The scheme
1257290001Sglebius * uses a X509v3 certificate extension field do convey the public key of
1258290001Sglebius * a private key known only to servers. There are two kinds of files:
1259290001Sglebius * encrypted server files that contain private and public values and
1260290001Sglebius * nonencrypted client files that contain only public values. New
1261290001Sglebius * generations of server files must be securely transmitted to all
1262290001Sglebius * servers of the group; client files can be distributed by any means.
1263290001Sglebius * The scheme is self contained and independent of new generations of
1264290001Sglebius * host keys and sign keys. The scheme is self contained and independent
1265290001Sglebius * of new generations of host keys and sign keys.
1266132451Sroberto *
1267132451Sroberto * The GQ parameters hide in a RSA cuckoo structure which uses the same
1268132451Sroberto * parameters. The values are used by an identity scheme based on RSA
1269132451Sroberto * cryptography and described in Stimson p. 300 (with errors). The 512-
1270132451Sroberto * bit public modulus is n = p q, where p and q are secret large primes.
1271132451Sroberto * The TA rolls private random group key b as RSA exponent. These values
1272132451Sroberto * are known to all group members.
1273132451Sroberto *
1274290001Sglebius * When rolling new certificates, a server recomputes the private and
1275132451Sroberto * public keys. The private key u is a random roll, while the public key
1276132451Sroberto * is the inverse obscured by the group key v = (u^-1)^b. These values
1277132451Sroberto * replace the private and public keys normally generated by the RSA
1278132451Sroberto * scheme. Alice challenges Bob to confirm identity using the protocol
1279132451Sroberto * described below.
1280290001Sglebius *
1281290001Sglebius * How it works
1282290001Sglebius *
1283290001Sglebius * The scheme goes like this. Both Alice and Bob have the same modulus n
1284290001Sglebius * and some random b as the group key. These values are computed and
1285290001Sglebius * distributed in advance via secret means, although only the group key
1286290001Sglebius * b is truly secret. Each has a private random private key u and public
1287290001Sglebius * key (u^-1)^b, although not necessarily the same ones. Bob and Alice
1288290001Sglebius * can regenerate the key pair from time to time without affecting
1289290001Sglebius * operations. The public key is conveyed on the certificate in an
1290290001Sglebius * extension field; the private key is never revealed.
1291290001Sglebius *
1292290001Sglebius * Alice rolls new random challenge r and sends to Bob in the GQ
1293290001Sglebius * request message. Bob rolls new random k, then computes y = k u^r mod
1294290001Sglebius * n and x = k^b mod n and sends (y, hash(x)) to Alice in the response
1295290001Sglebius * message. Besides making the response shorter, the hash makes it
1296290001Sglebius * effectivey impossible for an intruder to solve for b by observing
1297290001Sglebius * a number of these messages.
1298290001Sglebius *
1299290001Sglebius * Alice receives the response and computes y^b v^r mod n. After a bit
1300290001Sglebius * of algebra, this simplifies to k^b. If the hash of this result
1301290001Sglebius * matches hash(x), Alice knows that Bob has the group key b. The signed
1302290001Sglebius * response binds this knowledge to Bob's private key and the public key
1303290001Sglebius * previously received in his certificate.
1304132451Sroberto */
1305290001Sglebius/*
1306290001Sglebius * Generate Guillou-Quisquater (GQ) parameters file.
1307290001Sglebius */
1308132451SrobertoEVP_PKEY *			/* RSA cuckoo nest */
1309290001Sglebiusgen_gqkey(
1310290001Sglebius	const char *id		/* file name id */
1311132451Sroberto	)
1312132451Sroberto{
1313132451Sroberto	EVP_PKEY *pkey;		/* private key */
1314290001Sglebius	RSA	*rsa;		/* RSA parameters */
1315132451Sroberto	BN_CTX	*ctx;		/* BN working space */
1316290001Sglebius	BIGNUM	*u, *v, *g, *k, *r, *y; /* BN temps */
1317132451Sroberto	FILE	*str;
1318290001Sglebius	u_int	temp;
1319310419Sdelphij	BIGNUM	*b;
1320310419Sdelphij	const BIGNUM	*n;
1321310419Sdelphij
1322132451Sroberto	/*
1323132451Sroberto	 * Generate RSA parameters for use as GQ parameters.
1324132451Sroberto	 */
1325132451Sroberto	fprintf(stderr,
1326290001Sglebius	    "Generating GQ parameters (%d bits)...\n",
1327290001Sglebius	     modulus2);
1328310419Sdelphij	rsa = genRsaKeyPair(modulus2, _UC("GQ"));
1329132451Sroberto	fprintf(stderr, "\n");
1330132451Sroberto	if (rsa == NULL) {
1331132451Sroberto		fprintf(stderr, "RSA generate keys fails\n%s\n",
1332132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
1333132451Sroberto		return (NULL);
1334132451Sroberto	}
1335310419Sdelphij	RSA_get0_key(rsa, &n, NULL, NULL);
1336290001Sglebius	u = BN_new(); v = BN_new(); g = BN_new();
1337290001Sglebius	k = BN_new(); r = BN_new(); y = BN_new();
1338310419Sdelphij	b = BN_new();
1339132451Sroberto
1340132451Sroberto	/*
1341132451Sroberto	 * Generate the group key b, which is saved in the e member of
1342290001Sglebius	 * the RSA structure. The group key is transmitted to each group
1343290001Sglebius	 * member encrypted by the member private key.
1344132451Sroberto	 */
1345132451Sroberto	ctx = BN_CTX_new();
1346310419Sdelphij	BN_rand(b, BN_num_bits(n), -1, 0); /* b */
1347310419Sdelphij	BN_mod(b, b, n, ctx);
1348132451Sroberto
1349132451Sroberto	/*
1350132451Sroberto	 * When generating his certificate, Bob rolls random private key
1351290001Sglebius	 * u, then computes inverse v = u^-1.
1352132451Sroberto	 */
1353310419Sdelphij	BN_rand(u, BN_num_bits(n), -1, 0); /* u */
1354310419Sdelphij	BN_mod(u, u, n, ctx);
1355310419Sdelphij	BN_mod_inverse(v, u, n, ctx);	/* u^-1 mod n */
1356310419Sdelphij	BN_mod_mul(k, v, u, n, ctx);
1357132451Sroberto
1358132451Sroberto	/*
1359132451Sroberto	 * Bob computes public key v = (u^-1)^b, which is saved in an
1360132451Sroberto	 * extension field on his certificate. We check that u^b v =
1361132451Sroberto	 * 1 mod n.
1362132451Sroberto	 */
1363310419Sdelphij	BN_mod_exp(v, v, b, n, ctx);
1364310419Sdelphij	BN_mod_exp(g, u, b, n, ctx); /* u^b */
1365310419Sdelphij	BN_mod_mul(g, g, v, n, ctx); /* u^b (u^-1)^b */
1366132451Sroberto	temp = BN_is_one(g);
1367132451Sroberto	fprintf(stderr,
1368132451Sroberto	    "Confirm u^b (u^-1)^b = 1 mod n: %s\n", temp ? "yes" :
1369132451Sroberto	    "no");
1370132451Sroberto	if (!temp) {
1371132451Sroberto		BN_free(u); BN_free(v);
1372132451Sroberto		BN_free(g); BN_free(k); BN_free(r); BN_free(y);
1373132451Sroberto		BN_CTX_free(ctx);
1374132451Sroberto		RSA_free(rsa);
1375132451Sroberto		return (NULL);
1376132451Sroberto	}
1377310419Sdelphij	/* setting 'u' and 'v' into a RSA object takes over ownership.
1378310419Sdelphij	 * Since we use these values again, we have to pass in dupes,
1379310419Sdelphij	 * or we'll corrupt the program!
1380310419Sdelphij	 */
1381310419Sdelphij	RSA_set0_factors(rsa, BN_dup(u), BN_dup(v));
1382132451Sroberto
1383132451Sroberto	/*
1384132451Sroberto	 * Here is a trial run of the protocol. First, Alice rolls
1385290001Sglebius	 * random nonce r mod n and sends it to Bob. She needs only n
1386290001Sglebius	 * from parameters.
1387132451Sroberto	 */
1388310419Sdelphij	BN_rand(r, BN_num_bits(n), -1, 0);	/* r */
1389310419Sdelphij	BN_mod(r, r, n, ctx);
1390132451Sroberto
1391132451Sroberto	/*
1392290001Sglebius	 * Bob rolls random nonce k mod n, computes y = k u^r mod n and
1393290001Sglebius	 * g = k^b mod n, then sends (y, g) to Alice. He needs n, u, b
1394290001Sglebius	 * from parameters and r from Alice.
1395132451Sroberto	 */
1396310419Sdelphij	BN_rand(k, BN_num_bits(n), -1, 0);	/* k */
1397310419Sdelphij	BN_mod(k, k, n, ctx);
1398310419Sdelphij	BN_mod_exp(y, u, r, n, ctx);	/* u^r mod n */
1399310419Sdelphij	BN_mod_mul(y, k, y, n, ctx);	/* y = k u^r mod n */
1400310419Sdelphij	BN_mod_exp(g, k, b, n, ctx);	/* g = k^b mod n */
1401132451Sroberto
1402132451Sroberto	/*
1403290001Sglebius	 * Alice verifies g = v^r y^b mod n to confirm that Bob has
1404290001Sglebius	 * private key u. She needs n, g from parameters, public key v =
1405290001Sglebius	 * (u^-1)^b from the certificate, (y, g) from Bob and the
1406290001Sglebius	 * original r. We omit the detaul here that only the hash of g
1407290001Sglebius	 * is sent.
1408132451Sroberto	 */
1409310419Sdelphij	BN_mod_exp(v, v, r, n, ctx);	/* v^r mod n */
1410310419Sdelphij	BN_mod_exp(y, y, b, n, ctx);	/* y^b mod n */
1411310419Sdelphij	BN_mod_mul(y, v, y, n, ctx);	/* v^r y^b mod n */
1412132451Sroberto	temp = BN_cmp(y, g);
1413132451Sroberto	fprintf(stderr, "Confirm g^k = v^r y^b mod n: %s\n", temp == 0 ?
1414132451Sroberto	    "yes" : "no");
1415132451Sroberto	BN_CTX_free(ctx); BN_free(u); BN_free(v);
1416132451Sroberto	BN_free(g); BN_free(k); BN_free(r); BN_free(y);
1417132451Sroberto	if (temp != 0) {
1418132451Sroberto		RSA_free(rsa);
1419132451Sroberto		return (NULL);
1420132451Sroberto	}
1421132451Sroberto
1422132451Sroberto	/*
1423290001Sglebius	 * Write the GQ parameter file as an encrypted RSA private key
1424290001Sglebius	 * encoded in PEM.
1425132451Sroberto	 *
1426132451Sroberto	 * n	modulus n
1427132451Sroberto	 * e	group key b
1428290001Sglebius	 * d	not used
1429132451Sroberto	 * p	private key u
1430132451Sroberto	 * q	public key (u^-1)^b
1431290001Sglebius	 * dmp1	not used
1432290001Sglebius	 * dmq1	not used
1433290001Sglebius	 * iqmp	not used
1434132451Sroberto	 */
1435310419Sdelphij	RSA_set0_key(rsa, NULL, b, BN_dup(BN_value_one()));
1436310419Sdelphij	RSA_set0_crt_params(rsa, BN_dup(BN_value_one()), BN_dup(BN_value_one()),
1437310419Sdelphij		BN_dup(BN_value_one()));
1438290001Sglebius	str = fheader("GQkey", id, groupname);
1439132451Sroberto	pkey = EVP_PKEY_new();
1440132451Sroberto	EVP_PKEY_assign_RSA(pkey, rsa);
1441290001Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1442290001Sglebius	    passwd1);
1443132451Sroberto	fclose(str);
1444132451Sroberto	if (debug)
1445290001Sglebius		RSA_print_fp(stderr, rsa, 0);
1446132451Sroberto	return (pkey);
1447132451Sroberto}
1448132451Sroberto
1449132451Sroberto
1450132451Sroberto/*
1451290001Sglebius ***********************************************************************
1452290001Sglebius *								       *
1453290001Sglebius * The following routines implement the Mu-Varadharajan (MV) identity  *
1454290001Sglebius * scheme                                                              *
1455290001Sglebius *								       *
1456290001Sglebius ***********************************************************************
1457132451Sroberto *
1458290001Sglebius * The Mu-Varadharajan (MV) cryptosystem was originally intended when
1459290001Sglebius * servers broadcast messages to clients, but clients never send
1460290001Sglebius * messages to servers. There is one encryption key for the server and a
1461290001Sglebius * separate decryption key for each client. It operated something like a
1462132451Sroberto * pay-per-view satellite broadcasting system where the session key is
1463132451Sroberto * encrypted by the broadcaster and the decryption keys are held in a
1464290001Sglebius * tamperproof set-top box.
1465132451Sroberto *
1466132451Sroberto * The MV parameters and private encryption key hide in a DSA cuckoo
1467132451Sroberto * structure which uses the same parameters, but generated in a
1468132451Sroberto * different way. The values are used in an encryption scheme similar to
1469132451Sroberto * El Gamal cryptography and a polynomial formed from the expansion of
1470132451Sroberto * product terms (x - x[j]), as described in Mu, Y., and V.
1471132451Sroberto * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001,
1472132451Sroberto * 223-231. The paper has significant errors and serious omissions.
1473132451Sroberto *
1474290001Sglebius * Let q be the product of n distinct primes s1[j] (j = 1...n), where
1475290001Sglebius * each s1[j] has m significant bits. Let p be a prime p = 2 * q + 1, so
1476290001Sglebius * that q and each s1[j] divide p - 1 and p has M = n * m + 1
1477132451Sroberto * significant bits. Let g be a generator of Zp; that is, gcd(g, p - 1)
1478132451Sroberto * = 1 and g^q = 1 mod p. We do modular arithmetic over Zq and then
1479132451Sroberto * project into Zp* as exponents of g. Sometimes we have to compute an
1480132451Sroberto * inverse b^-1 of random b in Zq, but for that purpose we require
1481132451Sroberto * gcd(b, q) = 1. We expect M to be in the 500-bit range and n
1482290001Sglebius * relatively small, like 30. These are the parameters of the scheme and
1483290001Sglebius * they are expensive to compute.
1484132451Sroberto *
1485132451Sroberto * We set up an instance of the scheme as follows. A set of random
1486132451Sroberto * values x[j] mod q (j = 1...n), are generated as the zeros of a
1487132451Sroberto * polynomial of order n. The product terms (x - x[j]) are expanded to
1488132451Sroberto * form coefficients a[i] mod q (i = 0...n) in powers of x. These are
1489132451Sroberto * used as exponents of the generator g mod p to generate the private
1490132451Sroberto * encryption key A. The pair (gbar, ghat) of public server keys and the
1491132451Sroberto * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used
1492132451Sroberto * to construct the decryption keys. The devil is in the details.
1493132451Sroberto *
1494290001Sglebius * This routine generates a private server encryption file including the
1495290001Sglebius * private encryption key E and partial decryption keys gbar and ghat.
1496290001Sglebius * It then generates public client decryption files including the public
1497290001Sglebius * keys xbar[j] and xhat[j] for each client j. The partial decryption
1498290001Sglebius * files are used to compute the inverse of E. These values are suitably
1499290001Sglebius * blinded so secrets are not revealed.
1500132451Sroberto *
1501132451Sroberto * The distinguishing characteristic of this scheme is the capability to
1502132451Sroberto * revoke keys. Included in the calculation of E, gbar and ghat is the
1503290001Sglebius * product s = prod(s1[j]) (j = 1...n) above. If the factor s1[j] is
1504132451Sroberto * subsequently removed from the product and E, gbar and ghat
1505132451Sroberto * recomputed, the jth client will no longer be able to compute E^-1 and
1506290001Sglebius * thus unable to decrypt the messageblock.
1507290001Sglebius *
1508290001Sglebius * How it works
1509290001Sglebius *
1510290001Sglebius * The scheme goes like this. Bob has the server values (p, E, q,
1511290001Sglebius * gbar, ghat) and Alice has the client values (p, xbar, xhat).
1512290001Sglebius *
1513290001Sglebius * Alice rolls new random nonce r mod p and sends to Bob in the MV
1514290001Sglebius * request message. Bob rolls random nonce k mod q, encrypts y = r E^k
1515290001Sglebius * mod p and sends (y, gbar^k, ghat^k) to Alice.
1516290001Sglebius *
1517290001Sglebius * Alice receives the response and computes the inverse (E^k)^-1 from
1518290001Sglebius * the partial decryption keys gbar^k, ghat^k, xbar and xhat. She then
1519290001Sglebius * decrypts y and verifies it matches the original r. The signed
1520290001Sglebius * response binds this knowledge to Bob's private key and the public key
1521290001Sglebius * previously received in his certificate.
1522132451Sroberto */
1523132451SrobertoEVP_PKEY *			/* DSA cuckoo nest */
1524290001Sglebiusgen_mvkey(
1525290001Sglebius	const char *id,		/* file name id */
1526290001Sglebius	EVP_PKEY **evpars	/* parameter list pointer */
1527132451Sroberto	)
1528132451Sroberto{
1529290001Sglebius	EVP_PKEY *pkey, *pkey1;	/* private keys */
1530290001Sglebius	DSA	*dsa, *dsa2, *sdsa; /* DSA parameters */
1531132451Sroberto	BN_CTX	*ctx;		/* BN working space */
1532290001Sglebius	BIGNUM	*a[MVMAX];	/* polynomial coefficient vector */
1533310419Sdelphij	BIGNUM	*gs[MVMAX];	/* public key vector */
1534290001Sglebius	BIGNUM	*s1[MVMAX];	/* private enabling keys */
1535290001Sglebius	BIGNUM	*x[MVMAX];	/* polynomial zeros vector */
1536290001Sglebius	BIGNUM	*xbar[MVMAX], *xhat[MVMAX]; /* private keys vector */
1537132451Sroberto	BIGNUM	*b;		/* group key */
1538132451Sroberto	BIGNUM	*b1;		/* inverse group key */
1539290001Sglebius	BIGNUM	*s;		/* enabling key */
1540132451Sroberto	BIGNUM	*biga;		/* master encryption key */
1541132451Sroberto	BIGNUM	*bige;		/* session encryption key */
1542132451Sroberto	BIGNUM	*gbar, *ghat;	/* public key */
1543132451Sroberto	BIGNUM	*u, *v, *w;	/* BN scratch */
1544310419Sdelphij	BIGNUM	*p, *q, *g, *priv_key, *pub_key;
1545132451Sroberto	int	i, j, n;
1546132451Sroberto	FILE	*str;
1547132451Sroberto	u_int	temp;
1548132451Sroberto
1549132451Sroberto	/*
1550132451Sroberto	 * Generate MV parameters.
1551132451Sroberto	 *
1552132451Sroberto	 * The object is to generate a multiplicative group Zp* modulo a
1553132451Sroberto	 * prime p and a subset Zq mod q, where q is the product of n
1554290001Sglebius	 * distinct primes s1[j] (j = 1...n) and q divides p - 1. We
1555290001Sglebius	 * first generate n m-bit primes, where the product n m is in
1556290001Sglebius	 * the order of 512 bits. One or more of these may have to be
1557290001Sglebius	 * replaced later. As a practical matter, it is tough to find
1558290001Sglebius	 * more than 31 distinct primes for 512 bits or 61 primes for
1559290001Sglebius	 * 1024 bits. The latter can take several hundred iterations
1560132451Sroberto	 * and several minutes on a Sun Blade 1000.
1561132451Sroberto	 */
1562132451Sroberto	n = nkeys;
1563132451Sroberto	fprintf(stderr,
1564132451Sroberto	    "Generating MV parameters for %d keys (%d bits)...\n", n,
1565290001Sglebius	    modulus2 / n);
1566132451Sroberto	ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); w = BN_new();
1567132451Sroberto	b = BN_new(); b1 = BN_new();
1568132536Sroberto	dsa = DSA_new();
1569310419Sdelphij	p = BN_new(); q = BN_new(); g = BN_new();
1570310419Sdelphij	priv_key = BN_new(); pub_key = BN_new();
1571132451Sroberto	temp = 0;
1572132451Sroberto	for (j = 1; j <= n; j++) {
1573290001Sglebius		s1[j] = BN_new();
1574132451Sroberto		while (1) {
1575310419Sdelphij			BN_generate_prime_ex(s1[j], modulus2 / n, 0,
1576310419Sdelphij					     NULL, NULL, NULL);
1577132451Sroberto			for (i = 1; i < j; i++) {
1578132451Sroberto				if (BN_cmp(s1[i], s1[j]) == 0)
1579132451Sroberto					break;
1580132451Sroberto			}
1581132451Sroberto			if (i == j)
1582132451Sroberto				break;
1583132451Sroberto			temp++;
1584132451Sroberto		}
1585132451Sroberto	}
1586290001Sglebius	fprintf(stderr, "Birthday keys regenerated %d\n", temp);
1587132451Sroberto
1588132451Sroberto	/*
1589132451Sroberto	 * Compute the modulus q as the product of the primes. Compute
1590132451Sroberto	 * the modulus p as 2 * q + 1 and test p for primality. If p
1591132451Sroberto	 * is composite, replace one of the primes with a new distinct
1592132451Sroberto	 * one and try again. Note that q will hardly be a secret since
1593290001Sglebius	 * we have to reveal p to servers, but not clients. However,
1594132451Sroberto	 * factoring q to find the primes should be adequately hard, as
1595132451Sroberto	 * this is the same problem considered hard in RSA. Question: is
1596132451Sroberto	 * it as hard to find n small prime factors totalling n bits as
1597132451Sroberto	 * it is to find two large prime factors totalling n bits?
1598132451Sroberto	 * Remember, the bad guy doesn't know n.
1599132451Sroberto	 */
1600132451Sroberto	temp = 0;
1601132451Sroberto	while (1) {
1602310419Sdelphij		BN_one(q);
1603132451Sroberto		for (j = 1; j <= n; j++)
1604310419Sdelphij			BN_mul(q, q, s1[j], ctx);
1605310419Sdelphij		BN_copy(p, q);
1606310419Sdelphij		BN_add(p, p, p);
1607310419Sdelphij		BN_add_word(p, 1);
1608310419Sdelphij		if (BN_is_prime_ex(p, BN_prime_checks, ctx, NULL))
1609132451Sroberto			break;
1610132451Sroberto
1611290001Sglebius		temp++;
1612132451Sroberto		j = temp % n + 1;
1613132451Sroberto		while (1) {
1614310419Sdelphij			BN_generate_prime_ex(u, modulus2 / n, 0,
1615310419Sdelphij					     NULL, NULL, NULL);
1616132451Sroberto			for (i = 1; i <= n; i++) {
1617132451Sroberto				if (BN_cmp(u, s1[i]) == 0)
1618132451Sroberto					break;
1619132451Sroberto			}
1620132451Sroberto			if (i > n)
1621132451Sroberto				break;
1622132451Sroberto		}
1623132451Sroberto		BN_copy(s1[j], u);
1624132451Sroberto	}
1625290001Sglebius	fprintf(stderr, "Defective keys regenerated %d\n", temp);
1626132451Sroberto
1627132451Sroberto	/*
1628132451Sroberto	 * Compute the generator g using a random roll such that
1629132451Sroberto	 * gcd(g, p - 1) = 1 and g^q = 1. This is a generator of p, not
1630290001Sglebius	 * q. This may take several iterations.
1631132451Sroberto	 */
1632310419Sdelphij	BN_copy(v, p);
1633132451Sroberto	BN_sub_word(v, 1);
1634132451Sroberto	while (1) {
1635310419Sdelphij		BN_rand(g, BN_num_bits(p) - 1, 0, 0);
1636310419Sdelphij		BN_mod(g, g, p, ctx);
1637310419Sdelphij		BN_gcd(u, g, v, ctx);
1638132451Sroberto		if (!BN_is_one(u))
1639132451Sroberto			continue;
1640132451Sroberto
1641310419Sdelphij		BN_mod_exp(u, g, q, p, ctx);
1642132451Sroberto		if (BN_is_one(u))
1643132451Sroberto			break;
1644132451Sroberto	}
1645132451Sroberto
1646310419Sdelphij	DSA_set0_pqg(dsa, p, q, g);
1647310419Sdelphij
1648132451Sroberto	/*
1649132451Sroberto	 * Setup is now complete. Roll random polynomial roots x[j]
1650290001Sglebius	 * (j = 1...n) for all j. While it may not be strictly
1651132451Sroberto	 * necessary, Make sure each root has no factors in common with
1652132451Sroberto	 * q.
1653132451Sroberto	 */
1654132451Sroberto	fprintf(stderr,
1655132451Sroberto	    "Generating polynomial coefficients for %d roots (%d bits)\n",
1656310419Sdelphij	    n, BN_num_bits(q));
1657132451Sroberto	for (j = 1; j <= n; j++) {
1658132451Sroberto		x[j] = BN_new();
1659290001Sglebius
1660132451Sroberto		while (1) {
1661310419Sdelphij			BN_rand(x[j], BN_num_bits(q), 0, 0);
1662310419Sdelphij			BN_mod(x[j], x[j], q, ctx);
1663310419Sdelphij			BN_gcd(u, x[j], q, ctx);
1664132451Sroberto			if (BN_is_one(u))
1665132451Sroberto				break;
1666132451Sroberto		}
1667132451Sroberto	}
1668132451Sroberto
1669132451Sroberto	/*
1670132451Sroberto	 * Generate polynomial coefficients a[i] (i = 0...n) from the
1671132451Sroberto	 * expansion of root products (x - x[j]) mod q for all j. The
1672132451Sroberto	 * method is a present from Charlie Boncelet.
1673132451Sroberto	 */
1674132451Sroberto	for (i = 0; i <= n; i++) {
1675132451Sroberto		a[i] = BN_new();
1676132451Sroberto		BN_one(a[i]);
1677132451Sroberto	}
1678132451Sroberto	for (j = 1; j <= n; j++) {
1679132451Sroberto		BN_zero(w);
1680132451Sroberto		for (i = 0; i < j; i++) {
1681310419Sdelphij			BN_copy(u, q);
1682310419Sdelphij			BN_mod_mul(v, a[i], x[j], q, ctx);
1683132451Sroberto			BN_sub(u, u, v);
1684132451Sroberto			BN_add(u, u, w);
1685132451Sroberto			BN_copy(w, a[i]);
1686310419Sdelphij			BN_mod(a[i], u, q, ctx);
1687132451Sroberto		}
1688132451Sroberto	}
1689132451Sroberto
1690132451Sroberto	/*
1691310419Sdelphij	 * Generate gs[i] = g^a[i] mod p for all i and the generator g.
1692132451Sroberto	 */
1693132451Sroberto	for (i = 0; i <= n; i++) {
1694310419Sdelphij		gs[i] = BN_new();
1695310419Sdelphij		BN_mod_exp(gs[i], g, a[i], p, ctx);
1696132451Sroberto	}
1697132451Sroberto
1698132451Sroberto	/*
1699310419Sdelphij	 * Verify prod(gs[i]^(a[i] x[j]^i)) = 1 for all i, j. Note the
1700310419Sdelphij	 * a[i] x[j]^i exponent is computed mod q, but the gs[i] is
1701290001Sglebius	 * computed mod p. also note the expression given in the paper
1702290001Sglebius	 * is incorrect.
1703132451Sroberto	 */
1704132451Sroberto	temp = 1;
1705132451Sroberto	for (j = 1; j <= n; j++) {
1706132451Sroberto		BN_one(u);
1707132451Sroberto		for (i = 0; i <= n; i++) {
1708132451Sroberto			BN_set_word(v, i);
1709310419Sdelphij			BN_mod_exp(v, x[j], v, q, ctx);
1710310419Sdelphij			BN_mod_mul(v, v, a[i], q, ctx);
1711310419Sdelphij			BN_mod_exp(v, g, v, p, ctx);
1712310419Sdelphij			BN_mod_mul(u, u, v, p, ctx);
1713132451Sroberto		}
1714132451Sroberto		if (!BN_is_one(u))
1715132451Sroberto			temp = 0;
1716132451Sroberto	}
1717132451Sroberto	fprintf(stderr,
1718310419Sdelphij	    "Confirm prod(gs[i]^(x[j]^i)) = 1 for all i, j: %s\n", temp ?
1719132451Sroberto	    "yes" : "no");
1720132451Sroberto	if (!temp) {
1721132451Sroberto		return (NULL);
1722132451Sroberto	}
1723132451Sroberto
1724132451Sroberto	/*
1725132451Sroberto	 * Make private encryption key A. Keep it around for awhile,
1726132451Sroberto	 * since it is expensive to compute.
1727132451Sroberto	 */
1728132451Sroberto	biga = BN_new();
1729290001Sglebius
1730132451Sroberto	BN_one(biga);
1731132451Sroberto	for (j = 1; j <= n; j++) {
1732132451Sroberto		for (i = 0; i < n; i++) {
1733132451Sroberto			BN_set_word(v, i);
1734310419Sdelphij			BN_mod_exp(v, x[j], v, q, ctx);
1735310419Sdelphij			BN_mod_exp(v, gs[i], v, p, ctx);
1736310419Sdelphij			BN_mod_mul(biga, biga, v, p, ctx);
1737132451Sroberto		}
1738132451Sroberto	}
1739132451Sroberto
1740132451Sroberto	/*
1741132451Sroberto	 * Roll private random group key b mod q (0 < b < q), where
1742290001Sglebius	 * gcd(b, q) = 1 to guarantee b^-1 exists, then compute b^-1
1743132451Sroberto	 * mod q. If b is changed, the client keys must be recomputed.
1744132451Sroberto	 */
1745132451Sroberto	while (1) {
1746310419Sdelphij		BN_rand(b, BN_num_bits(q), 0, 0);
1747310419Sdelphij		BN_mod(b, b, q, ctx);
1748310419Sdelphij		BN_gcd(u, b, q, ctx);
1749132451Sroberto		if (BN_is_one(u))
1750132451Sroberto			break;
1751132451Sroberto	}
1752310419Sdelphij	BN_mod_inverse(b1, b, q, ctx);
1753132451Sroberto
1754132451Sroberto	/*
1755132451Sroberto	 * Make private client keys (xbar[j], xhat[j]) for all j. Note
1756290001Sglebius	 * that the keys for the jth client do not s1[j] or the product
1757290001Sglebius	 * s1[j]) (j = 1...n) which is q by construction.
1758290001Sglebius	 *
1759290001Sglebius	 * Compute the factor w such that w s1[j] = s1[j] for all j. The
1760290001Sglebius	 * easy way to do this is to compute (q + s1[j]) / s1[j].
1761290001Sglebius	 * Exercise for the student: prove the remainder is always zero.
1762132451Sroberto	 */
1763132451Sroberto	for (j = 1; j <= n; j++) {
1764132451Sroberto		xbar[j] = BN_new(); xhat[j] = BN_new();
1765290001Sglebius
1766310419Sdelphij		BN_add(w, q, s1[j]);
1767290001Sglebius		BN_div(w, u, w, s1[j], ctx);
1768132451Sroberto		BN_zero(xbar[j]);
1769132451Sroberto		BN_set_word(v, n);
1770132451Sroberto		for (i = 1; i <= n; i++) {
1771132451Sroberto			if (i == j)
1772132451Sroberto				continue;
1773290001Sglebius
1774310419Sdelphij			BN_mod_exp(u, x[i], v, q, ctx);
1775132451Sroberto			BN_add(xbar[j], xbar[j], u);
1776132451Sroberto		}
1777310419Sdelphij		BN_mod_mul(xbar[j], xbar[j], b1, q, ctx);
1778310419Sdelphij		BN_mod_exp(xhat[j], x[j], v, q, ctx);
1779310419Sdelphij		BN_mod_mul(xhat[j], xhat[j], w, q, ctx);
1780132451Sroberto	}
1781132451Sroberto
1782132451Sroberto	/*
1783290001Sglebius	 * We revoke client j by dividing q by s1[j]. The quotient
1784290001Sglebius	 * becomes the enabling key s. Note we always have to revoke
1785290001Sglebius	 * one key; otherwise, the plaintext and cryptotext would be
1786290001Sglebius	 * identical. For the present there are no provisions to revoke
1787290001Sglebius	 * additional keys, so we sail on with only token revocations.
1788132451Sroberto	 */
1789290001Sglebius	s = BN_new();
1790310419Sdelphij	BN_copy(s, q);
1791290001Sglebius	BN_div(s, u, s, s1[n], ctx);
1792132451Sroberto
1793132451Sroberto	/*
1794290001Sglebius	 * For each combination of clients to be revoked, make private
1795290001Sglebius	 * encryption key E = A^s and partial decryption keys gbar = g^s
1796290001Sglebius	 * and ghat = g^(s b), all mod p. The servers use these keys to
1797290001Sglebius	 * compute the session encryption key and partial decryption
1798290001Sglebius	 * keys. These values must be regenerated if the enabling key is
1799290001Sglebius	 * changed.
1800132451Sroberto	 */
1801132451Sroberto	bige = BN_new(); gbar = BN_new(); ghat = BN_new();
1802310419Sdelphij	BN_mod_exp(bige, biga, s, p, ctx);
1803310419Sdelphij	BN_mod_exp(gbar, g, s, p, ctx);
1804310419Sdelphij	BN_mod_mul(v, s, b, q, ctx);
1805310419Sdelphij	BN_mod_exp(ghat, g, v, p, ctx);
1806290001Sglebius
1807132451Sroberto	/*
1808290001Sglebius	 * Notes: We produce the key media in three steps. The first
1809290001Sglebius	 * step is to generate the system parameters p, q, g, b, A and
1810290001Sglebius	 * the enabling keys s1[j]. Associated with each s1[j] are
1811290001Sglebius	 * parameters xbar[j] and xhat[j]. All of these parameters are
1812290001Sglebius	 * retained in a data structure protecteted by the trusted-agent
1813290001Sglebius	 * password. The p, xbar[j] and xhat[j] paremeters are
1814290001Sglebius	 * distributed to the j clients. When the client keys are to be
1815290001Sglebius	 * activated, the enabled keys are multipied together to form
1816290001Sglebius	 * the master enabling key s. This and the other parameters are
1817290001Sglebius	 * used to compute the server encryption key E and the partial
1818290001Sglebius	 * decryption keys gbar and ghat.
1819132451Sroberto	 *
1820290001Sglebius	 * In the identity exchange the client rolls random r and sends
1821290001Sglebius	 * it to the server. The server rolls random k, which is used
1822290001Sglebius	 * only once, then computes the session key E^k and partial
1823290001Sglebius	 * decryption keys gbar^k and ghat^k. The server sends the
1824290001Sglebius	 * encrypted r along with gbar^k and ghat^k to the client. The
1825290001Sglebius	 * client completes the decryption and verifies it matches r.
1826132451Sroberto	 */
1827132451Sroberto	/*
1828290001Sglebius	 * Write the MV trusted-agent parameters and keys as a DSA
1829290001Sglebius	 * private key encoded in PEM.
1830132451Sroberto	 *
1831132451Sroberto	 * p	modulus p
1832290001Sglebius	 * q	modulus q
1833290001Sglebius	 * g	generator g
1834290001Sglebius	 * priv_key A mod p
1835290001Sglebius	 * pub_key b mod q
1836290001Sglebius	 * (remaining values are not used)
1837132451Sroberto	 */
1838290001Sglebius	i = 0;
1839290001Sglebius	str = fheader("MVta", "mvta", groupname);
1840290001Sglebius	fprintf(stderr, "Generating MV trusted-authority keys\n");
1841310419Sdelphij	BN_copy(priv_key, biga);
1842310419Sdelphij	BN_copy(pub_key, b);
1843310419Sdelphij	DSA_set0_key(dsa, pub_key, priv_key);
1844132451Sroberto	pkey = EVP_PKEY_new();
1845132451Sroberto	EVP_PKEY_assign_DSA(pkey, dsa);
1846290001Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1847290001Sglebius	    passwd1);
1848290001Sglebius	evpars[i++] = pkey;
1849132451Sroberto	if (debug)
1850290001Sglebius		DSA_print_fp(stderr, dsa, 0);
1851132451Sroberto
1852132451Sroberto	/*
1853290001Sglebius	 * Append the MV server parameters and keys as a DSA key encoded
1854290001Sglebius	 * in PEM.
1855290001Sglebius	 *
1856290001Sglebius	 * p	modulus p
1857290001Sglebius	 * q	modulus q (used only when generating k)
1858290001Sglebius	 * g	bige
1859290001Sglebius	 * priv_key gbar
1860290001Sglebius	 * pub_key ghat
1861290001Sglebius	 * (remaining values are not used)
1862132451Sroberto	 */
1863290001Sglebius	fprintf(stderr, "Generating MV server keys\n");
1864290001Sglebius	dsa2 = DSA_new();
1865310419Sdelphij	DSA_set0_pqg(dsa2, BN_dup(p), BN_dup(q), BN_dup(bige));
1866310419Sdelphij	DSA_set0_key(dsa2, BN_dup(ghat), BN_dup(gbar));
1867290001Sglebius	pkey1 = EVP_PKEY_new();
1868290001Sglebius	EVP_PKEY_assign_DSA(pkey1, dsa2);
1869290001Sglebius	PEM_write_PKCS8PrivateKey(str, pkey1, cipher, NULL, 0, NULL,
1870290001Sglebius	    passwd1);
1871290001Sglebius	evpars[i++] = pkey1;
1872290001Sglebius	if (debug)
1873290001Sglebius		DSA_print_fp(stderr, dsa2, 0);
1874290001Sglebius
1875290001Sglebius	/*
1876290001Sglebius	 * Append the MV client parameters for each client j as DSA keys
1877290001Sglebius	 * encoded in PEM.
1878290001Sglebius	 *
1879290001Sglebius	 * p	modulus p
1880290001Sglebius	 * priv_key xbar[j] mod q
1881290001Sglebius	 * pub_key xhat[j] mod q
1882290001Sglebius	 * (remaining values are not used)
1883290001Sglebius	 */
1884290001Sglebius	fprintf(stderr, "Generating %d MV client keys\n", n);
1885132451Sroberto	for (j = 1; j <= n; j++) {
1886290001Sglebius		sdsa = DSA_new();
1887310419Sdelphij		DSA_set0_pqg(sdsa, BN_dup(p), BN_dup(BN_value_one()),
1888310419Sdelphij			BN_dup(BN_value_one()));
1889310419Sdelphij		DSA_set0_key(sdsa, BN_dup(xhat[j]), BN_dup(xbar[j]));
1890290001Sglebius		pkey1 = EVP_PKEY_new();
1891290001Sglebius		EVP_PKEY_set1_DSA(pkey1, sdsa);
1892290001Sglebius		PEM_write_PKCS8PrivateKey(str, pkey1, cipher, NULL, 0,
1893290001Sglebius		    NULL, passwd1);
1894290001Sglebius		evpars[i++] = pkey1;
1895290001Sglebius		if (debug)
1896290001Sglebius			DSA_print_fp(stderr, sdsa, 0);
1897290001Sglebius
1898290001Sglebius		/*
1899310419Sdelphij		 * The product (gbar^k)^xbar[j] (ghat^k)^xhat[j] and E
1900290001Sglebius		 * are inverses of each other. We check that the product
1901290001Sglebius		 * is one for each client except the ones that have been
1902290001Sglebius		 * revoked.
1903290001Sglebius		 */
1904310419Sdelphij		BN_mod_exp(v, gbar, xhat[j], p, ctx);
1905310419Sdelphij		BN_mod_exp(u, ghat, xbar[j], p, ctx);
1906310419Sdelphij		BN_mod_mul(u, u, v, p, ctx);
1907310419Sdelphij		BN_mod_mul(u, u, bige, p, ctx);
1908132451Sroberto		if (!BN_is_one(u)) {
1909132451Sroberto			fprintf(stderr, "Revoke key %d\n", j);
1910132451Sroberto			continue;
1911132451Sroberto		}
1912132451Sroberto	}
1913290001Sglebius	evpars[i++] = NULL;
1914290001Sglebius	fclose(str);
1915132451Sroberto
1916132451Sroberto	/*
1917132451Sroberto	 * Free the countries.
1918132451Sroberto	 */
1919132451Sroberto	for (i = 0; i <= n; i++) {
1920310419Sdelphij		BN_free(a[i]); BN_free(gs[i]);
1921132451Sroberto	}
1922290001Sglebius	for (j = 1; j <= n; j++) {
1923290001Sglebius		BN_free(x[j]); BN_free(xbar[j]); BN_free(xhat[j]);
1924290001Sglebius		BN_free(s1[j]);
1925290001Sglebius	}
1926132451Sroberto	return (pkey);
1927132451Sroberto}
1928132451Sroberto
1929132451Sroberto
1930132451Sroberto/*
1931290001Sglebius * Generate X509v3 certificate.
1932132451Sroberto *
1933132451Sroberto * The certificate consists of the version number, serial number,
1934132451Sroberto * validity interval, issuer name, subject name and public key. For a
1935132451Sroberto * self-signed certificate, the issuer name is the same as the subject
1936132451Sroberto * name and these items are signed using the subject private key. The
1937132451Sroberto * validity interval extends from the current time to the same time one
1938132451Sroberto * year hence. For NTP purposes, it is convenient to use the NTP seconds
1939132451Sroberto * of the current time as the serial number.
1940132451Sroberto */
1941132451Srobertoint
1942132451Srobertox509	(
1943290001Sglebius	EVP_PKEY *pkey,		/* signing key */
1944290001Sglebius	const EVP_MD *md,	/* signature/digest scheme */
1945132451Sroberto	char	*gqpub,		/* identity extension (hex string) */
1946290001Sglebius	const char *exten,	/* private cert extension */
1947290001Sglebius	char	*name		/* subject/issuer name */
1948132451Sroberto	)
1949132451Sroberto{
1950132451Sroberto	X509	*cert;		/* X509 certificate */
1951132451Sroberto	X509_NAME *subj;	/* distinguished (common) name */
1952132451Sroberto	X509_EXTENSION *ex;	/* X509v3 extension */
1953132451Sroberto	FILE	*str;		/* file handle */
1954132451Sroberto	ASN1_INTEGER *serial;	/* serial number */
1955132451Sroberto	const char *id;		/* digest/signature scheme name */
1956132451Sroberto	char	pathbuf[MAXFILENAME + 1];
1957132451Sroberto
1958132451Sroberto	/*
1959132451Sroberto	 * Generate X509 self-signed certificate.
1960132451Sroberto	 *
1961132451Sroberto	 * Set the certificate serial to the NTP seconds for grins. Set
1962290001Sglebius	 * the version to 3. Set the initial validity to the current
1963290001Sglebius	 * time and the finalvalidity one year hence.
1964132451Sroberto	 */
1965310419Sdelphij 	id = OBJ_nid2sn(EVP_MD_pkey_type(md));
1966290001Sglebius	fprintf(stderr, "Generating new certificate %s %s\n", name, id);
1967132451Sroberto	cert = X509_new();
1968132451Sroberto	X509_set_version(cert, 2L);
1969132451Sroberto	serial = ASN1_INTEGER_new();
1970290001Sglebius	ASN1_INTEGER_set(serial, (long)epoch + JAN_1970);
1971132451Sroberto	X509_set_serialNumber(cert, serial);
1972132451Sroberto	ASN1_INTEGER_free(serial);
1973182007Sroberto	X509_time_adj(X509_get_notBefore(cert), 0L, &epoch);
1974290001Sglebius	X509_time_adj(X509_get_notAfter(cert), lifetime * SECSPERDAY, &epoch);
1975132451Sroberto	subj = X509_get_subject_name(cert);
1976132451Sroberto	X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC,
1977293896Sglebius	    (u_char *)name, -1, -1, 0);
1978132451Sroberto	subj = X509_get_issuer_name(cert);
1979132451Sroberto	X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC,
1980293896Sglebius	    (u_char *)name, -1, -1, 0);
1981132451Sroberto	if (!X509_set_pubkey(cert, pkey)) {
1982290001Sglebius		fprintf(stderr, "Assign certificate signing key fails\n%s\n",
1983132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
1984132451Sroberto		X509_free(cert);
1985132451Sroberto		return (0);
1986132451Sroberto	}
1987132451Sroberto
1988132451Sroberto	/*
1989132451Sroberto	 * Add X509v3 extensions if present. These represent the minimum
1990132451Sroberto	 * set defined in RFC3280 less the certificate_policy extension,
1991132451Sroberto	 * which is seriously obfuscated in OpenSSL.
1992132451Sroberto	 */
1993132451Sroberto	/*
1994132451Sroberto	 * The basic_constraints extension CA:TRUE allows servers to
1995132451Sroberto	 * sign client certficitates.
1996132451Sroberto	 */
1997132451Sroberto	fprintf(stderr, "%s: %s\n", LN_basic_constraints,
1998132451Sroberto	    BASIC_CONSTRAINTS);
1999132451Sroberto	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
2000290001Sglebius	    _UC(BASIC_CONSTRAINTS));
2001132451Sroberto	if (!X509_add_ext(cert, ex, -1)) {
2002132451Sroberto		fprintf(stderr, "Add extension field fails\n%s\n",
2003132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
2004132451Sroberto		return (0);
2005132451Sroberto	}
2006132451Sroberto	X509_EXTENSION_free(ex);
2007132451Sroberto
2008132451Sroberto	/*
2009132451Sroberto	 * The key_usage extension designates the purposes the key can
2010132451Sroberto	 * be used for.
2011132451Sroberto	 */
2012132451Sroberto	fprintf(stderr, "%s: %s\n", LN_key_usage, KEY_USAGE);
2013290001Sglebius	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, _UC(KEY_USAGE));
2014132451Sroberto	if (!X509_add_ext(cert, ex, -1)) {
2015132451Sroberto		fprintf(stderr, "Add extension field fails\n%s\n",
2016132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
2017132451Sroberto		return (0);
2018132451Sroberto	}
2019132451Sroberto	X509_EXTENSION_free(ex);
2020132451Sroberto	/*
2021132451Sroberto	 * The subject_key_identifier is used for the GQ public key.
2022132451Sroberto	 * This should not be controversial.
2023132451Sroberto	 */
2024132451Sroberto	if (gqpub != NULL) {
2025132451Sroberto		fprintf(stderr, "%s\n", LN_subject_key_identifier);
2026132451Sroberto		ex = X509V3_EXT_conf_nid(NULL, NULL,
2027132451Sroberto		    NID_subject_key_identifier, gqpub);
2028132451Sroberto		if (!X509_add_ext(cert, ex, -1)) {
2029132451Sroberto			fprintf(stderr,
2030132451Sroberto			    "Add extension field fails\n%s\n",
2031132451Sroberto			    ERR_error_string(ERR_get_error(), NULL));
2032132451Sroberto			return (0);
2033132451Sroberto		}
2034132451Sroberto		X509_EXTENSION_free(ex);
2035132451Sroberto	}
2036132451Sroberto
2037132451Sroberto	/*
2038132451Sroberto	 * The extended key usage extension is used for special purpose
2039132451Sroberto	 * here. The semantics probably do not conform to the designer's
2040132451Sroberto	 * intent and will likely change in future.
2041132451Sroberto	 *
2042132451Sroberto	 * "trustRoot" designates a root authority
2043132451Sroberto	 * "private" designates a private certificate
2044132451Sroberto	 */
2045132451Sroberto	if (exten != NULL) {
2046132451Sroberto		fprintf(stderr, "%s: %s\n", LN_ext_key_usage, exten);
2047132451Sroberto		ex = X509V3_EXT_conf_nid(NULL, NULL,
2048290001Sglebius		    NID_ext_key_usage, _UC(exten));
2049132451Sroberto		if (!X509_add_ext(cert, ex, -1)) {
2050132451Sroberto			fprintf(stderr,
2051132451Sroberto			    "Add extension field fails\n%s\n",
2052132451Sroberto			    ERR_error_string(ERR_get_error(), NULL));
2053132451Sroberto			return (0);
2054132451Sroberto		}
2055132451Sroberto		X509_EXTENSION_free(ex);
2056132451Sroberto	}
2057132451Sroberto
2058132451Sroberto	/*
2059132451Sroberto	 * Sign and verify.
2060132451Sroberto	 */
2061132451Sroberto	X509_sign(cert, pkey, md);
2062290001Sglebius	if (X509_verify(cert, pkey) <= 0) {
2063132451Sroberto		fprintf(stderr, "Verify %s certificate fails\n%s\n", id,
2064132451Sroberto		    ERR_error_string(ERR_get_error(), NULL));
2065132451Sroberto		X509_free(cert);
2066132451Sroberto		return (0);
2067132451Sroberto	}
2068132451Sroberto
2069132451Sroberto	/*
2070132451Sroberto	 * Write the certificate encoded in PEM.
2071132451Sroberto	 */
2072290001Sglebius	snprintf(pathbuf, sizeof(pathbuf), "%scert", id);
2073290001Sglebius	str = fheader(pathbuf, "cert", hostname);
2074132451Sroberto	PEM_write_X509(str, cert);
2075132451Sroberto	fclose(str);
2076132451Sroberto	if (debug)
2077290001Sglebius		X509_print_fp(stderr, cert);
2078132451Sroberto	X509_free(cert);
2079132451Sroberto	return (1);
2080132451Sroberto}
2081132451Sroberto
2082290001Sglebius#if 0	/* asn2ntp is used only with commercial certificates */
2083132451Sroberto/*
2084132451Sroberto * asn2ntp - convert ASN1_TIME time structure to NTP time
2085132451Sroberto */
2086132451Srobertou_long
2087132451Srobertoasn2ntp	(
2088132451Sroberto	ASN1_TIME *asn1time	/* pointer to ASN1_TIME structure */
2089132451Sroberto	)
2090132451Sroberto{
2091132451Sroberto	char	*v;		/* pointer to ASN1_TIME string */
2092132451Sroberto	struct	tm tm;		/* time decode structure time */
2093132451Sroberto
2094132451Sroberto	/*
2095132451Sroberto	 * Extract time string YYMMDDHHMMSSZ from ASN.1 time structure.
2096132451Sroberto	 * Note that the YY, MM, DD fields start with one, the HH, MM,
2097132451Sroberto	 * SS fiels start with zero and the Z character should be 'Z'
2098132451Sroberto	 * for UTC. Also note that years less than 50 map to years
2099132451Sroberto	 * greater than 100. Dontcha love ASN.1?
2100132451Sroberto	 */
2101132451Sroberto	if (asn1time->length > 13)
2102132451Sroberto		return (-1);
2103132451Sroberto	v = (char *)asn1time->data;
2104132451Sroberto	tm.tm_year = (v[0] - '0') * 10 + v[1] - '0';
2105132451Sroberto	if (tm.tm_year < 50)
2106132451Sroberto		tm.tm_year += 100;
2107132451Sroberto	tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1;
2108132451Sroberto	tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0';
2109132451Sroberto	tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0';
2110132451Sroberto	tm.tm_min = (v[8] - '0') * 10 + v[9] - '0';
2111132451Sroberto	tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0';
2112132451Sroberto	tm.tm_wday = 0;
2113132451Sroberto	tm.tm_yday = 0;
2114132451Sroberto	tm.tm_isdst = 0;
2115132451Sroberto	return (mktime(&tm) + JAN_1970);
2116132451Sroberto}
2117132451Sroberto#endif
2118132451Sroberto
2119132451Sroberto/*
2120132451Sroberto * Callback routine
2121132451Sroberto */
2122132451Srobertovoid
2123132451Srobertocb	(
2124132451Sroberto	int	n1,		/* arg 1 */
2125132451Sroberto	int	n2,		/* arg 2 */
2126132451Sroberto	void	*chr		/* arg 3 */
2127132451Sroberto	)
2128132451Sroberto{
2129132451Sroberto	switch (n1) {
2130132451Sroberto	case 0:
2131132451Sroberto		d0++;
2132132451Sroberto		fprintf(stderr, "%s %d %d %lu\r", (char *)chr, n1, n2,
2133132451Sroberto		    d0);
2134132451Sroberto		break;
2135132451Sroberto	case 1:
2136132451Sroberto		d1++;
2137132451Sroberto		fprintf(stderr, "%s\t\t%d %d %lu\r", (char *)chr, n1,
2138132451Sroberto		    n2, d1);
2139132451Sroberto		break;
2140132451Sroberto	case 2:
2141132451Sroberto		d2++;
2142132451Sroberto		fprintf(stderr, "%s\t\t\t\t%d %d %lu\r", (char *)chr,
2143132451Sroberto		    n1, n2, d2);
2144132451Sroberto		break;
2145132451Sroberto	case 3:
2146132451Sroberto		d3++;
2147132451Sroberto		fprintf(stderr, "%s\t\t\t\t\t\t%d %d %lu\r",
2148132451Sroberto		    (char *)chr, n1, n2, d3);
2149132451Sroberto		break;
2150132451Sroberto	}
2151132451Sroberto}
2152132451Sroberto
2153132451Sroberto
2154132451Sroberto/*
2155132451Sroberto * Generate key
2156132451Sroberto */
2157132451SrobertoEVP_PKEY *			/* public/private key pair */
2158132451Srobertogenkey(
2159290001Sglebius	const char *type,	/* key type (RSA or DSA) */
2160290001Sglebius	const char *id		/* file name id */
2161132451Sroberto	)
2162132451Sroberto{
2163132451Sroberto	if (type == NULL)
2164132451Sroberto		return (NULL);
2165132451Sroberto	if (strcmp(type, "RSA") == 0)
2166132451Sroberto		return (gen_rsa(id));
2167132451Sroberto
2168132451Sroberto	else if (strcmp(type, "DSA") == 0)
2169132451Sroberto		return (gen_dsa(id));
2170132451Sroberto
2171132451Sroberto	fprintf(stderr, "Invalid %s key type %s\n", id, type);
2172132451Sroberto	return (NULL);
2173132451Sroberto}
2174310419Sdelphij
2175310419Sdelphijstatic RSA*
2176310419SdelphijgenRsaKeyPair(
2177310419Sdelphij	int	bits,
2178310419Sdelphij	char *	what
2179310419Sdelphij	)
2180310419Sdelphij{
2181310419Sdelphij	RSA *		rsa = RSA_new();
2182310419Sdelphij	BN_GENCB *	gcb = BN_GENCB_new();
2183310419Sdelphij	BIGNUM *	bne = BN_new();
2184310419Sdelphij
2185310419Sdelphij	if (gcb)
2186310419Sdelphij		BN_GENCB_set_old(gcb, cb, what);
2187310419Sdelphij	if (bne)
2188310419Sdelphij		BN_set_word(bne, 65537);
2189310419Sdelphij	if (!(rsa && gcb && bne && RSA_generate_key_ex(
2190310419Sdelphij		      rsa, bits, bne, gcb)))
2191310419Sdelphij	{
2192310419Sdelphij		RSA_free(rsa);
2193310419Sdelphij		rsa = NULL;
2194310419Sdelphij	}
2195310419Sdelphij	BN_GENCB_free(gcb);
2196310419Sdelphij	BN_free(bne);
2197310419Sdelphij	return rsa;
2198310419Sdelphij}
2199310419Sdelphij
2200310419Sdelphijstatic DSA*
2201310419SdelphijgenDsaParams(
2202310419Sdelphij	int	bits,
2203310419Sdelphij	char *	what
2204310419Sdelphij	)
2205310419Sdelphij{
2206310419Sdelphij
2207310419Sdelphij	DSA *		dsa = DSA_new();
2208310419Sdelphij	BN_GENCB *	gcb = BN_GENCB_new();
2209310419Sdelphij	u_char		seed[20];
2210310419Sdelphij
2211310419Sdelphij	if (gcb)
2212310419Sdelphij		BN_GENCB_set_old(gcb, cb, what);
2213310419Sdelphij	RAND_bytes(seed, sizeof(seed));
2214310419Sdelphij	if (!(dsa && gcb && DSA_generate_parameters_ex(
2215310419Sdelphij		      dsa, bits, seed, sizeof(seed), NULL, NULL, gcb)))
2216310419Sdelphij	{
2217310419Sdelphij		DSA_free(dsa);
2218310419Sdelphij		dsa = NULL;
2219310419Sdelphij	}
2220310419Sdelphij	BN_GENCB_free(gcb);
2221310419Sdelphij	return dsa;
2222310419Sdelphij}
2223310419Sdelphij
2224290001Sglebius#endif	/* AUTOKEY */
2225132451Sroberto
2226132451Sroberto
2227132451Sroberto/*
2228290001Sglebius * Generate file header and link
2229132451Sroberto */
2230132451SrobertoFILE *
2231132451Srobertofheader	(
2232290001Sglebius	const char *file,	/* file name id */
2233290001Sglebius	const char *ulink,	/* linkname */
2234290001Sglebius	const char *owner	/* owner name */
2235132451Sroberto	)
2236132451Sroberto{
2237132451Sroberto	FILE	*str;		/* file handle */
2238290001Sglebius	char	linkname[MAXFILENAME]; /* link name */
2239290001Sglebius	int	temp;
2240290001Sglebius#ifdef HAVE_UMASK
2241290001Sglebius        mode_t  orig_umask;
2242290001Sglebius#endif
2243290001Sglebius
2244290001Sglebius	snprintf(filename, sizeof(filename), "ntpkey_%s_%s.%u", file,
2245290001Sglebius	    owner, fstamp);
2246290001Sglebius#ifdef HAVE_UMASK
2247290001Sglebius        orig_umask = umask( S_IWGRP | S_IRWXO );
2248290001Sglebius        str = fopen(filename, "w");
2249290001Sglebius        (void) umask(orig_umask);
2250290001Sglebius#else
2251290001Sglebius        str = fopen(filename, "w");
2252290001Sglebius#endif
2253290001Sglebius	if (str == NULL) {
2254132451Sroberto		perror("Write");
2255132451Sroberto		exit (-1);
2256132451Sroberto	}
2257290001Sglebius        if (strcmp(ulink, "md5") == 0) {
2258290001Sglebius          strcpy(linkname,"ntp.keys");
2259290001Sglebius        } else {
2260290001Sglebius          snprintf(linkname, sizeof(linkname), "ntpkey_%s_%s", ulink,
2261290001Sglebius                   hostname);
2262290001Sglebius        }
2263290001Sglebius	(void)remove(linkname);		/* The symlink() line below matters */
2264132451Sroberto	temp = symlink(filename, linkname);
2265132451Sroberto	if (temp < 0)
2266290001Sglebius		perror(file);
2267290001Sglebius	fprintf(stderr, "Generating new %s file and link\n", ulink);
2268132451Sroberto	fprintf(stderr, "%s->%s\n", linkname, filename);
2269290001Sglebius	fprintf(str, "# %s\n# %s\n", filename, ctime(&epoch));
2270290001Sglebius	return (str);
2271132451Sroberto}
2272