1132451Sroberto/*
2290000Sglebius * Program to generate cryptographic keys for ntp clients and servers
3132451Sroberto *
4290000Sglebius * This program generates password encrypted data files for use with the
5290000Sglebius * Autokey security protocol and Network Time Protocol Version 4. Files
6290000Sglebius * are prefixed with a header giving the name and date of creation
7132451Sroberto * followed by a type-specific descriptive label and PEM-encoded data
8290000Sglebius * structure compatible with programs of the OpenSSL library.
9132451Sroberto *
10290000Sglebius * All file names are like "ntpkey_<type>_<hostname>.<filestamp>", where
11290000Sglebius * <type> is the file type, <hostname> the generating host name and
12290000Sglebius * <filestamp> the generation time in NTP seconds. The NTP programs
13290000Sglebius * expect generic names such as "ntpkey_<type>_whimsy.udel.edu" with the
14290000Sglebius * association maintained by soft links. Following is a list of file
15290000Sglebius * 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 *
21290000Sglebius * ntpkey_RSAhost_<hostname>.<filestamp>
22290000Sglebius * ntpkey_host_<hostname>
23132451Sroberto *	RSA private/public host key pair used for public key signatures
24132451Sroberto *
25290000Sglebius * ntpkey_RSAsign_<hostname>.<filestamp>
26290000Sglebius * ntpkey_sign_<hostname>
27290000Sglebius *	RSA private/public sign key pair used for public key signatures
28132451Sroberto *
29290000Sglebius * ntpkey_DSAsign_<hostname>.<filestamp>
30290000Sglebius * ntpkey_sign_<hostname>
31290000Sglebius *	DSA Private/public sign key pair used for public key signatures
32132451Sroberto *
33290000Sglebius * Available digest/signature schemes
34132451Sroberto *
35290000Sglebius * RSA:	RSA-MD2, RSA-MD5, RSA-SHA, RSA-SHA1, RSA-MDC2, EVP-RIPEMD160
36290000Sglebius * DSA:	DSA-SHA, DSA-SHA1
37132451Sroberto *
38132451Sroberto * ntpkey_XXXcert_<hostname>.<filestamp>
39290000Sglebius * 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 *
44290000Sglebius * Identity schemes. The key type par is used for the challenge; the key
45290000Sglebius * type key is used for the response.
46132451Sroberto *
47290000Sglebius * ntpkey_IFFkey_<groupname>.<filestamp>
48290000Sglebius * ntpkey_iffkey_<groupname>
49290000Sglebius *	Schnorr (IFF) identity parameters and keys
50132451Sroberto *
51290000Sglebius * ntpkey_GQkey_<groupname>.<filestamp>,
52290000Sglebius * ntpkey_gqkey_<groupname>
53290000Sglebius *	Guillou-Quisquater (GQ) identity parameters and keys
54290000Sglebius *
55290000Sglebius * ntpkey_MVkeyX_<groupname>.<filestamp>,
56290000Sglebius * ntpkey_mvkey_<groupname>
57290000Sglebius *	Mu-Varadharajan (MV) identity parameters and keys
58290000Sglebius *
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
62290000Sglebius * 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>
90290000Sglebius#include <sys/types.h>
91290000Sglebius
92290000Sglebius#include "ntp.h"
93182007Sroberto#include "ntp_random.h"
94290000Sglebius#include "ntp_stdlib.h"
95290000Sglebius#include "ntp_assert.h"
96290000Sglebius#include "ntp_libopts.h"
97290000Sglebius#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"
109290000Sglebius#endif	/* OPENSSL */
110290000Sglebius#include <ssl_applink.c>
111132451Sroberto
112290000Sglebius#define _UC(str)	((char *)(intptr_t)(str))
113132451Sroberto/*
114132451Sroberto * Cryptodefines
115132451Sroberto */
116290000Sglebius#define	MD5KEYS		10	/* number of keys generated of each type */
117290000Sglebius#define	MD5SIZE		20	/* maximum key size */
118290000Sglebius#ifdef AUTOKEY
119132451Sroberto#define	PLEN		512	/* default prime modulus size (bits) */
120290000Sglebius#define	ILEN		256	/* default identity modulus size (bits) */
121290000Sglebius#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"
130290000Sglebius#endif	/* AUTOKEY */
131132451Sroberto
132132451Sroberto/*
133132451Sroberto * Prototypes
134132451Sroberto */
135290000SglebiusFILE	*fheader	(const char *, const char *, const char *);
136290000Sglebiusint	gen_md5		(const char *);
137290000Sglebiusvoid	followlink	(char *, size_t);
138290000Sglebius#ifdef AUTOKEY
139290000SglebiusEVP_PKEY *gen_rsa	(const char *);
140290000SglebiusEVP_PKEY *gen_dsa	(const char *);
141290000SglebiusEVP_PKEY *gen_iffkey	(const char *);
142290000SglebiusEVP_PKEY *gen_gqkey	(const char *);
143290000SglebiusEVP_PKEY *gen_mvkey	(const char *, EVP_PKEY **);
144290000Sglebiusvoid	gen_mvserv	(char *, EVP_PKEY **);
145290000Sglebiusint	x509		(EVP_PKEY *, const EVP_MD *, char *, const char *,
146290000Sglebius			    char *);
147290000Sglebiusvoid	cb		(int, int, void *);
148290000SglebiusEVP_PKEY *genkey	(const char *, const char *);
149290000SglebiusEVP_PKEY *readkey	(char *, char *, u_int *, EVP_PKEY **);
150290000Sglebiusvoid	writekey	(char *, char *, u_int *, EVP_PKEY **);
151290000Sglebiusu_long	asn2ntp		(ASN1_TIME *);
152310419Sdelphij
153310419Sdelphijstatic DSA* genDsaParams(int, char*);
154310419Sdelphijstatic RSA* genRsaKeyPair(int, char*);
155310419Sdelphij
156290000Sglebius#endif	/* AUTOKEY */
157132451Sroberto
158132451Sroberto/*
159132451Sroberto * Program variables
160132451Sroberto */
161132451Srobertoextern char *optarg;		/* command line argument */
162290000Sglebiuschar	const *progname;
163290000Sglebiusu_int	lifetime = DAYSPERYEAR;	/* certificate lifetime (days) */
164290000Sglebiusint	nkeys;			/* MV keys */
165132451Srobertotime_t	epoch;			/* Unix epoch (seconds) since 1970 */
166290000Sglebiusu_int	fstamp;			/* NTP filestamp */
167290000Sglebiuschar	hostbuf[MAXHOSTNAME + 1];
168290000Sglebiuschar	*hostname = NULL;	/* host, used in cert filenames */
169290000Sglebiuschar	*groupname = NULL;	/* group name */
170290000Sglebiuschar	certnamebuf[2 * sizeof(hostbuf)];
171290000Sglebiuschar	*certname = NULL;	/* certificate subject/issuer name */
172132451Srobertochar	*passwd1 = NULL;	/* input private key password */
173132451Srobertochar	*passwd2 = NULL;	/* output private key password */
174290000Sglebiuschar	filename[MAXFILENAME + 1]; /* file name */
175290000Sglebius#ifdef AUTOKEY
176290000Sglebiusu_int	modulus = PLEN;		/* prime modulus size (bits) */
177290000Sglebiusu_int	modulus2 = ILEN;	/* identity modulus size (bits) */
178132451Srobertolong	d0, d1, d2, d3;		/* callback counters */
179290000Sglebiusconst EVP_CIPHER * cipher = NULL;
180290000Sglebius#endif	/* AUTOKEY */
181132451Sroberto
182132451Sroberto#ifdef SYS_WINNT
183132451SrobertoBOOL init_randfile();
184132451Sroberto
185132451Sroberto/*
186290000Sglebius * Don't try to follow symbolic links on Windows.  Assume link == file.
187132451Sroberto */
188132451Srobertoint
189290000Sglebiusreadlink(
190290000Sglebius	char *	link,
191290000Sglebius	char *	file,
192290000Sglebius	int	len
193290000Sglebius	)
194290000Sglebius{
195293894Sglebius	return (int)strlen(file); /* assume no overflow possible */
196132451Sroberto}
197290000Sglebius
198132451Sroberto/*
199290000Sglebius * Don't try to create symbolic links on Windows, that is supported on
200290000Sglebius * Vista and later only.  Instead, if CreateHardLink is available (XP
201290000Sglebius * and later), hardlink the linkname to the original filename.  On
202290000Sglebius * earlier systems, user must rename file to match expected link for
203290000Sglebius * ntpd to find it.  To allow building a ntp-keygen.exe which loads on
204290000Sglebius * Windows pre-XP, runtime link to CreateHardLinkA().
205132451Sroberto */
206132451Srobertoint
207290000Sglebiussymlink(
208290000Sglebius	char *	filename,
209290000Sglebius	char*	linkname
210290000Sglebius	)
211290000Sglebius{
212290000Sglebius	typedef BOOL (WINAPI *PCREATEHARDLINKA)(
213290000Sglebius		__in LPCSTR	lpFileName,
214290000Sglebius		__in LPCSTR	lpExistingFileName,
215290000Sglebius		__reserved LPSECURITY_ATTRIBUTES lpSA
216290000Sglebius		);
217290000Sglebius	static PCREATEHARDLINKA pCreateHardLinkA;
218290000Sglebius	static int		tried;
219290000Sglebius	HMODULE			hDll;
220290000Sglebius	FARPROC			pfn;
221290000Sglebius	int			link_created;
222290000Sglebius	int			saved_errno;
223290000Sglebius
224290000Sglebius	if (!tried) {
225290000Sglebius		tried = TRUE;
226290000Sglebius		hDll = LoadLibrary("kernel32");
227290000Sglebius		pfn = GetProcAddress(hDll, "CreateHardLinkA");
228290000Sglebius		pCreateHardLinkA = (PCREATEHARDLINKA)pfn;
229290000Sglebius	}
230290000Sglebius
231290000Sglebius	if (NULL == pCreateHardLinkA) {
232290000Sglebius		errno = ENOSYS;
233290000Sglebius		return -1;
234290000Sglebius	}
235290000Sglebius
236290000Sglebius	link_created = (*pCreateHardLinkA)(linkname, filename, NULL);
237290000Sglebius
238290000Sglebius	if (link_created)
239290000Sglebius		return 0;
240290000Sglebius
241290000Sglebius	saved_errno = GetLastError();	/* yes we play loose */
242290000Sglebius	mfprintf(stderr, "Create hard link %s to %s failed: %m\n",
243290000Sglebius		 linkname, filename);
244290000Sglebius	errno = saved_errno;
245290000Sglebius	return -1;
246132451Sroberto}
247290000Sglebius
248132451Srobertovoid
249132451SrobertoInitWin32Sockets() {
250132451Sroberto	WORD wVersionRequested;
251132451Sroberto	WSADATA wsaData;
252132451Sroberto	wVersionRequested = MAKEWORD(2,0);
253132451Sroberto	if (WSAStartup(wVersionRequested, &wsaData))
254132451Sroberto	{
255290000Sglebius		fprintf(stderr, "No useable winsock.dll\n");
256132451Sroberto		exit(1);
257132451Sroberto	}
258132451Sroberto}
259132451Sroberto#endif /* SYS_WINNT */
260132451Sroberto
261290000Sglebius
262132451Sroberto/*
263290000Sglebius * followlink() - replace filename with its target if symlink.
264290000Sglebius *
265290000Sglebius * Some readlink() implementations do not null-terminate the result.
266290000Sglebius */
267290000Sglebiusvoid
268290000Sglebiusfollowlink(
269290000Sglebius	char *	fname,
270290000Sglebius	size_t	bufsiz
271290000Sglebius	)
272290000Sglebius{
273290000Sglebius	int len;
274290000Sglebius
275290000Sglebius	REQUIRE(bufsiz > 0);
276290000Sglebius
277290000Sglebius	len = readlink(fname, fname, (int)bufsiz);
278290000Sglebius	if (len < 0 ) {
279290000Sglebius		fname[0] = '\0';
280290000Sglebius		return;
281290000Sglebius	}
282290000Sglebius	if (len > (int)bufsiz - 1)
283290000Sglebius		len = (int)bufsiz - 1;
284290000Sglebius	fname[len] = '\0';
285290000Sglebius}
286290000Sglebius
287290000Sglebius
288290000Sglebius/*
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 */
299290000Sglebius	int	optct;		/* option count */
300290000Sglebius#ifdef AUTOKEY
301132451Sroberto	X509	*cert = NULL;	/* X509 certificate */
302132451Sroberto	EVP_PKEY *pkey_host = NULL; /* host key */
303132451Sroberto	EVP_PKEY *pkey_sign = NULL; /* sign key */
304290000Sglebius	EVP_PKEY *pkey_iffkey = NULL; /* IFF sever keys */
305290000Sglebius	EVP_PKEY *pkey_gqkey = NULL; /* GQ server keys */
306290000Sglebius	EVP_PKEY *pkey_mvkey = NULL; /* MV trusted agen keys */
307290000Sglebius	EVP_PKEY *pkey_mvpar[MVMAX]; /* MV cleient keys */
308132451Sroberto	int	hostkey = 0;	/* generate RSA keys */
309290000Sglebius	int	iffkey = 0;	/* generate IFF keys */
310290000Sglebius	int	gqkey = 0;	/* generate GQ keys */
311290000Sglebius	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 */
318290000Sglebius	const char *ciphername = NULL; /* to encrypt priv. key */
319290000Sglebius	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 */
323290000Sglebius	char	groupbuf[MAXHOSTNAME + 1];
324182007Sroberto	u_int	temp;
325290000Sglebius	BIO *	bp;
326290000Sglebius	int	i, cnt;
327290000Sglebius	char *	ptr;
328290000Sglebius#endif	/* AUTOKEY */
329132451Sroberto
330290000Sglebius	progname = argv[0];
331290000Sglebius
332132451Sroberto#ifdef SYS_WINNT
333132451Sroberto	/* Initialize before OpenSSL checks */
334132451Sroberto	InitWin32Sockets();
335290000Sglebius	if (!init_randfile())
336132451Sroberto		fprintf(stderr, "Unable to initialize .rnd file\n");
337290000Sglebius	ssl_applink();
338132451Sroberto#endif
339132451Sroberto
340132451Sroberto#ifdef OPENSSL
341290000Sglebius	ssl_check_version();
342290000Sglebius#endif	/* OPENSSL */
343132451Sroberto
344290000Sglebius	ntp_crypto_srandom();
345132451Sroberto
346132451Sroberto	/*
347132451Sroberto	 * Process options, initialize host name and timestamp.
348290000Sglebius	 * gethostname() won't null-terminate if hostname is exactly the
349290000Sglebius	 * length provided for the buffer.
350132451Sroberto	 */
351290000Sglebius	gethostname(hostbuf, sizeof(hostbuf) - 1);
352290000Sglebius	hostbuf[COUNTOF(hostbuf) - 1] = '\0';
353132451Sroberto	hostname = hostbuf;
354290000Sglebius	groupname = hostbuf;
355132451Sroberto	passwd1 = hostbuf;
356290000Sglebius	passwd2 = NULL;
357290000Sglebius	GETTIMEOFDAY(&tv, NULL);
358132451Sroberto	epoch = tv.tv_sec;
359290000Sglebius	fstamp = (u_int)(epoch + JAN_1970);
360132451Sroberto
361290000Sglebius	optct = ntpOptionProcess(&ntp_keygenOptions, argc, argv);
362290000Sglebius	argc -= optct;	// Just in case we care later.
363290000Sglebius	argv += optct;	// Just in case we care later.
364182007Sroberto
365132536Sroberto#ifdef OPENSSL
366290000Sglebius	if (SSLeay() == SSLEAY_VERSION_NUMBER)
367290000Sglebius		fprintf(stderr, "Using OpenSSL version %s\n",
368290000Sglebius			SSLeay_version(SSLEAY_VERSION));
369290000Sglebius	else
370290000Sglebius		fprintf(stderr, "Built against OpenSSL %s, using version %s\n",
371290000Sglebius			OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
372290000Sglebius#endif /* OPENSSL */
373132451Sroberto
374290000Sglebius	debug = OPT_VALUE_SET_DEBUG_LEVEL;
375132451Sroberto
376290000Sglebius	if (HAVE_OPT( MD5KEY ))
377290000Sglebius		md5key++;
378290000Sglebius#ifdef AUTOKEY
379290000Sglebius	if (HAVE_OPT( PASSWORD ))
380290000Sglebius		passwd1 = estrdup(OPT_ARG( PASSWORD ));
381132451Sroberto
382290000Sglebius	if (HAVE_OPT( EXPORT_PASSWD ))
383290000Sglebius		passwd2 = estrdup(OPT_ARG( EXPORT_PASSWD ));
384132451Sroberto
385182007Sroberto	if (HAVE_OPT( HOST_KEY ))
386290000Sglebius		hostkey++;
387132451Sroberto
388290000Sglebius	if (HAVE_OPT( SIGN_KEY ))
389290000Sglebius		sign = estrdup(OPT_ARG( SIGN_KEY ));
390290000Sglebius
391290000Sglebius	if (HAVE_OPT( GQ_PARAMS ))
392290000Sglebius		gqkey++;
393290000Sglebius
394182007Sroberto	if (HAVE_OPT( IFFKEY ))
395290000Sglebius		iffkey++;
396132451Sroberto
397290000Sglebius	if (HAVE_OPT( MV_PARAMS )) {
398290000Sglebius		mvkey++;
399290000Sglebius		nkeys = OPT_VALUE_MV_PARAMS;
400290000Sglebius	}
401290000Sglebius	if (HAVE_OPT( MV_KEYS )) {
402290000Sglebius		mvpar++;
403290000Sglebius		nkeys = OPT_VALUE_MV_KEYS;
404290000Sglebius	}
405132451Sroberto
406290000Sglebius	if (HAVE_OPT( IMBITS ))
407290000Sglebius		modulus2 = OPT_VALUE_IMBITS;
408132451Sroberto
409182007Sroberto	if (HAVE_OPT( MODULUS ))
410290000Sglebius		modulus = OPT_VALUE_MODULUS;
411132536Sroberto
412290000Sglebius	if (HAVE_OPT( CERTIFICATE ))
413290000Sglebius		scheme = OPT_ARG( CERTIFICATE );
414132451Sroberto
415290000Sglebius	if (HAVE_OPT( CIPHER ))
416290000Sglebius		ciphername = OPT_ARG( CIPHER );
417132451Sroberto
418290000Sglebius	if (HAVE_OPT( SUBJECT_NAME ))
419290000Sglebius		hostname = estrdup(OPT_ARG( SUBJECT_NAME ));
420132451Sroberto
421290000Sglebius	if (HAVE_OPT( IDENT ))
422290000Sglebius		groupname = estrdup(OPT_ARG( IDENT ));
423132451Sroberto
424290000Sglebius	if (HAVE_OPT( LIFETIME ))
425290000Sglebius		lifetime = OPT_VALUE_LIFETIME;
426132536Sroberto
427290000Sglebius	if (HAVE_OPT( PVT_CERT ))
428290000Sglebius		exten = EXT_KEY_PRIVATE;
429290000Sglebius
430182007Sroberto	if (HAVE_OPT( TRUSTED_CERT ))
431290000Sglebius		exten = EXT_KEY_TRUST;
432132451Sroberto
433290000Sglebius	/*
434290000Sglebius	 * Remove the group name from the hostname variable used
435290000Sglebius	 * in host and sign certificate file names.
436290000Sglebius	 */
437290000Sglebius	if (hostname != hostbuf)
438290000Sglebius		ptr = strchr(hostname, '@');
439290000Sglebius	else
440290000Sglebius		ptr = NULL;
441290000Sglebius	if (ptr != NULL) {
442290000Sglebius		*ptr = '\0';
443290000Sglebius		groupname = estrdup(ptr + 1);
444290000Sglebius		/* -s @group is equivalent to -i group, host unch. */
445290000Sglebius		if (ptr == hostname)
446290000Sglebius			hostname = hostbuf;
447182007Sroberto	}
448132451Sroberto
449290000Sglebius	/*
450290000Sglebius	 * Derive host certificate issuer/subject names from host name
451290000Sglebius	 * and optional group.  If no groupname is provided, the issuer
452290000Sglebius	 * and subject is the hostname with no '@group', and the
453290000Sglebius	 * groupname variable is pointed to hostname for use in IFF, GQ,
454290000Sglebius	 * and MV parameters file names.
455290000Sglebius	 */
456290000Sglebius	if (groupname == hostbuf) {
457290000Sglebius		certname = hostname;
458290000Sglebius	} else {
459290000Sglebius		snprintf(certnamebuf, sizeof(certnamebuf), "%s@%s",
460290000Sglebius			 hostname, groupname);
461290000Sglebius		certname = certnamebuf;
462182007Sroberto	}
463132451Sroberto
464132451Sroberto	/*
465132451Sroberto	 * Seed random number generator and grow weeds.
466132451Sroberto	 */
467132451Sroberto	ERR_load_crypto_strings();
468132451Sroberto	OpenSSL_add_all_algorithms();
469290000Sglebius	if (!RAND_status()) {
470290000Sglebius		if (RAND_file_name(pathbuf, sizeof(pathbuf)) == NULL) {
471290000Sglebius			fprintf(stderr, "RAND_file_name %s\n",
472290000Sglebius			    ERR_error_string(ERR_get_error(), NULL));
473290000Sglebius			exit (-1);
474290000Sglebius		}
475290000Sglebius		temp = RAND_load_file(pathbuf, -1);
476290000Sglebius		if (temp == 0) {
477290000Sglebius			fprintf(stderr,
478290000Sglebius			    "RAND_load_file %s not found or empty\n",
479290000Sglebius			    pathbuf);
480290000Sglebius			exit (-1);
481290000Sglebius		}
482132451Sroberto		fprintf(stderr,
483290000Sglebius		    "Random seed file %s %u bytes\n", pathbuf, temp);
484290000Sglebius		RAND_add(&epoch, sizeof(epoch), 4.0);
485132451Sroberto	}
486290000Sglebius#endif	/* AUTOKEY */
487132451Sroberto
488132451Sroberto	/*
489290000Sglebius	 * Create new unencrypted MD5 keys file if requested. If this
490290000Sglebius	 * option is selected, ignore all other options.
491132451Sroberto	 */
492290000Sglebius	if (md5key) {
493290000Sglebius		gen_md5("md5");
494290000Sglebius		exit (0);
495290000Sglebius	}
496132451Sroberto
497290000Sglebius#ifdef AUTOKEY
498132451Sroberto	/*
499290000Sglebius	 * Load previous certificate if available.
500132451Sroberto	 */
501290000Sglebius	snprintf(filename, sizeof(filename), "ntpkey_cert_%s", hostname);
502290000Sglebius	if ((fstr = fopen(filename, "r")) != NULL) {
503290000Sglebius		cert = PEM_read_X509(fstr, NULL, NULL, NULL);
504290000Sglebius		fclose(fstr);
505290000Sglebius	}
506290000Sglebius	if (cert != NULL) {
507290000Sglebius
508290000Sglebius		/*
509290000Sglebius		 * Extract subject name.
510290000Sglebius		 */
511290000Sglebius		X509_NAME_oneline(X509_get_subject_name(cert), groupbuf,
512290000Sglebius		    MAXFILENAME);
513290000Sglebius
514290000Sglebius		/*
515290000Sglebius		 * Extract digest/signature scheme.
516290000Sglebius		 */
517290000Sglebius		if (scheme == NULL) {
518310419Sdelphij			nid = X509_get_signature_nid(cert);
519290000Sglebius			scheme = OBJ_nid2sn(nid);
520290000Sglebius		}
521290000Sglebius
522290000Sglebius		/*
523290000Sglebius		 * If a key_usage extension field is present, determine
524290000Sglebius		 * whether this is a trusted or private certificate.
525290000Sglebius		 */
526290000Sglebius		if (exten == NULL) {
527290000Sglebius			ptr = strstr(groupbuf, "CN=");
528290000Sglebius			cnt = X509_get_ext_count(cert);
529290000Sglebius			for (i = 0; i < cnt; i++) {
530310419Sdelphij				X509_EXTENSION *ext;
531310419Sdelphij				ASN1_OBJECT *obj;
532310419Sdelphij
533290000Sglebius				ext = X509_get_ext(cert, i);
534310419Sdelphij				obj = X509_EXTENSION_get_object(ext);
535310419Sdelphij
536310419Sdelphij				if (OBJ_obj2nid(obj) ==
537290000Sglebius				    NID_ext_key_usage) {
538290000Sglebius					bp = BIO_new(BIO_s_mem());
539290000Sglebius					X509V3_EXT_print(bp, ext, 0, 0);
540290000Sglebius					BIO_gets(bp, pathbuf,
541290000Sglebius					    MAXFILENAME);
542290000Sglebius					BIO_free(bp);
543290000Sglebius					if (strcmp(pathbuf,
544290000Sglebius					    "Trust Root") == 0)
545290000Sglebius						exten = EXT_KEY_TRUST;
546290000Sglebius					else if (strcmp(pathbuf,
547290000Sglebius					    "Private") == 0)
548290000Sglebius						exten = EXT_KEY_PRIVATE;
549290000Sglebius					certname = estrdup(ptr + 3);
550290000Sglebius				}
551132451Sroberto			}
552290000Sglebius		}
553290000Sglebius	}
554290000Sglebius	if (scheme == NULL)
555290000Sglebius		scheme = "RSA-MD5";
556290000Sglebius	if (ciphername == NULL)
557290000Sglebius		ciphername = "des-ede3-cbc";
558290000Sglebius	cipher = EVP_get_cipherbyname(ciphername);
559290000Sglebius	if (cipher == NULL) {
560290000Sglebius		fprintf(stderr, "Unknown cipher %s\n", ciphername);
561290000Sglebius		exit(-1);
562290000Sglebius	}
563290000Sglebius	fprintf(stderr, "Using host %s group %s\n", hostname,
564290000Sglebius	    groupname);
565132451Sroberto
566290000Sglebius	/*
567290000Sglebius	 * Create a new encrypted RSA host key file if requested;
568290000Sglebius	 * otherwise, look for an existing host key file. If not found,
569290000Sglebius	 * create a new encrypted RSA host key file. If that fails, go
570290000Sglebius	 * no further.
571290000Sglebius	 */
572290000Sglebius	if (hostkey)
573290000Sglebius		pkey_host = genkey("RSA", "host");
574290000Sglebius	if (pkey_host == NULL) {
575290000Sglebius		snprintf(filename, sizeof(filename), "ntpkey_host_%s", hostname);
576290000Sglebius		pkey_host = readkey(filename, passwd1, &fstamp, NULL);
577290000Sglebius		if (pkey_host != NULL) {
578290000Sglebius			followlink(filename, sizeof(filename));
579290000Sglebius			fprintf(stderr, "Using host key %s\n",
580290000Sglebius			    filename);
581290000Sglebius		} else {
582290000Sglebius			pkey_host = genkey("RSA", "host");
583132451Sroberto		}
584132451Sroberto	}
585290000Sglebius	if (pkey_host == NULL) {
586290000Sglebius		fprintf(stderr, "Generating host key fails\n");
587290000Sglebius		exit(-1);
588290000Sglebius	}
589132451Sroberto
590132451Sroberto	/*
591290000Sglebius	 * Create new encrypted RSA or DSA sign keys file if requested;
592290000Sglebius	 * otherwise, look for an existing sign key file. If not found,
593290000Sglebius	 * use the host key instead.
594132451Sroberto	 */
595290000Sglebius	if (sign != NULL)
596290000Sglebius		pkey_sign = genkey(sign, "sign");
597290000Sglebius	if (pkey_sign == NULL) {
598290000Sglebius		snprintf(filename, sizeof(filename), "ntpkey_sign_%s",
599290000Sglebius			 hostname);
600290000Sglebius		pkey_sign = readkey(filename, passwd1, &fstamp, NULL);
601290000Sglebius		if (pkey_sign != NULL) {
602290000Sglebius			followlink(filename, sizeof(filename));
603290000Sglebius			fprintf(stderr, "Using sign key %s\n",
604290000Sglebius			    filename);
605132451Sroberto		} else {
606290000Sglebius			pkey_sign = pkey_host;
607132451Sroberto			fprintf(stderr, "Using host key as sign key\n");
608132451Sroberto		}
609132451Sroberto	}
610132451Sroberto
611132451Sroberto	/*
612290000Sglebius	 * Create new encrypted GQ server keys file if requested;
613290000Sglebius	 * otherwise, look for an exisiting file. If found, fetch the
614290000Sglebius	 * public key for the certificate.
615132451Sroberto	 */
616290000Sglebius	if (gqkey)
617290000Sglebius		pkey_gqkey = gen_gqkey("gqkey");
618290000Sglebius	if (pkey_gqkey == NULL) {
619290000Sglebius		snprintf(filename, sizeof(filename), "ntpkey_gqkey_%s",
620290000Sglebius		    groupname);
621290000Sglebius		pkey_gqkey = readkey(filename, passwd1, &fstamp, NULL);
622290000Sglebius		if (pkey_gqkey != NULL) {
623290000Sglebius			followlink(filename, sizeof(filename));
624290000Sglebius			fprintf(stderr, "Using GQ parameters %s\n",
625290000Sglebius			    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	/*
638290000Sglebius	 * Write the nonencrypted GQ client parameters to the stdout
639290000Sglebius	 * stream. The parameter file is the server key file with the
640290000Sglebius	 * private key obscured.
641132451Sroberto	 */
642290000Sglebius	if (pkey_gqkey != NULL && HAVE_OPT(ID_KEY)) {
643290000Sglebius		RSA	*rsa;
644290000Sglebius
645290000Sglebius		snprintf(filename, sizeof(filename),
646290000Sglebius		    "ntpkey_gqpar_%s.%u", groupname, fstamp);
647290000Sglebius		fprintf(stderr, "Writing GQ parameters %s to stdout\n",
648290000Sglebius		    filename);
649290000Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
650290000Sglebius		    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()));
655290000Sglebius		pkey = EVP_PKEY_new();
656290000Sglebius		EVP_PKEY_assign_RSA(pkey, rsa);
657290000Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, NULL, NULL, 0,
658290000Sglebius		    NULL, NULL);
659290000Sglebius		fflush(stdout);
660290000Sglebius		if (debug)
661290000Sglebius			RSA_print_fp(stderr, rsa, 0);
662132451Sroberto	}
663132451Sroberto
664132451Sroberto	/*
665290000Sglebius	 * Write the encrypted GQ server keys to the stdout stream.
666132451Sroberto	 */
667290000Sglebius	if (pkey_gqkey != NULL && passwd2 != NULL) {
668290000Sglebius		RSA	*rsa;
669290000Sglebius
670290000Sglebius		snprintf(filename, sizeof(filename),
671290000Sglebius		    "ntpkey_gqkey_%s.%u", groupname, fstamp);
672290000Sglebius		fprintf(stderr, "Writing GQ keys %s to stdout\n",
673290000Sglebius		    filename);
674290000Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
675290000Sglebius		    ctime(&epoch));
676310419Sdelphij		rsa = EVP_PKEY_get0_RSA(pkey_gqkey);
677290000Sglebius		pkey = EVP_PKEY_new();
678290000Sglebius		EVP_PKEY_assign_RSA(pkey, rsa);
679290000Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, cipher, NULL, 0,
680290000Sglebius		    NULL, passwd2);
681290000Sglebius		fflush(stdout);
682290000Sglebius		if (debug)
683290000Sglebius			RSA_print_fp(stderr, rsa, 0);
684132451Sroberto	}
685132451Sroberto
686132451Sroberto	/*
687290000Sglebius	 * Create new encrypted IFF server keys file if requested;
688290000Sglebius	 * otherwise, look for existing file.
689132451Sroberto	 */
690290000Sglebius	if (iffkey)
691290000Sglebius		pkey_iffkey = gen_iffkey("iffkey");
692290000Sglebius	if (pkey_iffkey == NULL) {
693290000Sglebius		snprintf(filename, sizeof(filename), "ntpkey_iffkey_%s",
694290000Sglebius		    groupname);
695290000Sglebius		pkey_iffkey = readkey(filename, passwd1, &fstamp, NULL);
696290000Sglebius		if (pkey_iffkey != NULL) {
697290000Sglebius			followlink(filename, sizeof(filename));
698290000Sglebius			fprintf(stderr, "Using IFF keys %s\n",
699290000Sglebius			    filename);
700132451Sroberto		}
701132451Sroberto	}
702132451Sroberto
703132451Sroberto	/*
704290000Sglebius	 * Write the nonencrypted IFF client parameters to the stdout
705290000Sglebius	 * stream. The parameter file is the server key file with the
706290000Sglebius	 * private key obscured.
707132451Sroberto	 */
708290000Sglebius	if (pkey_iffkey != NULL && HAVE_OPT(ID_KEY)) {
709132451Sroberto		DSA	*dsa;
710132451Sroberto
711290000Sglebius		snprintf(filename, sizeof(filename),
712290000Sglebius		    "ntpkey_iffpar_%s.%u", groupname, fstamp);
713290000Sglebius		fprintf(stderr, "Writing IFF parameters %s to stdout\n",
714290000Sglebius		    filename);
715290000Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
716290000Sglebius		    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);
723290000Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, NULL, NULL, 0,
724290000Sglebius		    NULL, NULL);
725290000Sglebius		fflush(stdout);
726132451Sroberto		if (debug)
727290000Sglebius			DSA_print_fp(stderr, dsa, 0);
728132451Sroberto	}
729132451Sroberto
730132451Sroberto	/*
731290000Sglebius	 * Write the encrypted IFF server keys to the stdout stream.
732132451Sroberto	 */
733290000Sglebius	if (pkey_iffkey != NULL && passwd2 != NULL) {
734290000Sglebius		DSA	*dsa;
735132451Sroberto
736290000Sglebius		snprintf(filename, sizeof(filename),
737290000Sglebius		    "ntpkey_iffkey_%s.%u", groupname, fstamp);
738290000Sglebius		fprintf(stderr, "Writing IFF keys %s to stdout\n",
739290000Sglebius		    filename);
740290000Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
741290000Sglebius		    ctime(&epoch));
742310419Sdelphij		dsa = EVP_PKEY_get0_DSA(pkey_iffkey);
743290000Sglebius		pkey = EVP_PKEY_new();
744290000Sglebius		EVP_PKEY_assign_DSA(pkey, dsa);
745290000Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, cipher, NULL, 0,
746290000Sglebius		    NULL, passwd2);
747290000Sglebius		fflush(stdout);
748290000Sglebius		if (debug)
749290000Sglebius			DSA_print_fp(stderr, dsa, 0);
750290000Sglebius	}
751132451Sroberto
752290000Sglebius	/*
753290000Sglebius	 * Create new encrypted MV trusted-authority keys file if
754290000Sglebius	 * requested; otherwise, look for existing keys file.
755290000Sglebius	 */
756290000Sglebius	if (mvkey)
757290000Sglebius		pkey_mvkey = gen_mvkey("mv", pkey_mvpar);
758290000Sglebius	if (pkey_mvkey == NULL) {
759290000Sglebius		snprintf(filename, sizeof(filename), "ntpkey_mvta_%s",
760290000Sglebius		    groupname);
761290000Sglebius		pkey_mvkey = readkey(filename, passwd1, &fstamp,
762290000Sglebius		    pkey_mvpar);
763290000Sglebius		if (pkey_mvkey != NULL) {
764290000Sglebius			followlink(filename, sizeof(filename));
765290000Sglebius			fprintf(stderr, "Using MV keys %s\n",
766290000Sglebius			    filename);
767290000Sglebius		}
768290000Sglebius	}
769132451Sroberto
770290000Sglebius	/*
771290000Sglebius	 * Write the nonencrypted MV client parameters to the stdout
772290000Sglebius	 * stream. For the moment, we always use the client parameters
773290000Sglebius	 * associated with client key 1.
774290000Sglebius	 */
775290000Sglebius	if (pkey_mvkey != NULL && HAVE_OPT(ID_KEY)) {
776290000Sglebius		snprintf(filename, sizeof(filename),
777290000Sglebius		    "ntpkey_mvpar_%s.%u", groupname, fstamp);
778290000Sglebius		fprintf(stderr, "Writing MV parameters %s to stdout\n",
779290000Sglebius		    filename);
780290000Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
781290000Sglebius		    ctime(&epoch));
782290000Sglebius		pkey = pkey_mvpar[2];
783290000Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, NULL, NULL, 0,
784290000Sglebius		    NULL, NULL);
785290000Sglebius		fflush(stdout);
786290000Sglebius		if (debug)
787310419Sdelphij			DSA_print_fp(stderr, EVP_PKEY_get0_DSA(pkey), 0);
788290000Sglebius	}
789290000Sglebius
790290000Sglebius	/*
791290000Sglebius	 * Write the encrypted MV server keys to the stdout stream.
792290000Sglebius	 */
793290000Sglebius	if (pkey_mvkey != NULL && passwd2 != NULL) {
794290000Sglebius		snprintf(filename, sizeof(filename),
795290000Sglebius		    "ntpkey_mvkey_%s.%u", groupname, fstamp);
796290000Sglebius		fprintf(stderr, "Writing MV keys %s to stdout\n",
797290000Sglebius		    filename);
798290000Sglebius		fprintf(stdout, "# %s\n# %s\n", filename,
799290000Sglebius		    ctime(&epoch));
800290000Sglebius		pkey = pkey_mvpar[1];
801290000Sglebius		PEM_write_PKCS8PrivateKey(stdout, pkey, cipher, NULL, 0,
802290000Sglebius		    NULL, passwd2);
803290000Sglebius		fflush(stdout);
804290000Sglebius		if (debug)
805310419Sdelphij			DSA_print_fp(stderr, EVP_PKEY_get0_DSA(pkey), 0);
806290000Sglebius	}
807290000Sglebius
808290000Sglebius	/*
809290000Sglebius	 * Decode the digest/signature scheme and create the
810290000Sglebius	 * certificate. Do this every time we run the program.
811290000Sglebius	 */
812290000Sglebius	ectx = EVP_get_digestbyname(scheme);
813290000Sglebius	if (ectx == NULL) {
814290000Sglebius		fprintf(stderr,
815290000Sglebius		    "Invalid digest/signature combination %s\n",
816290000Sglebius		    scheme);
817290000Sglebius			exit (-1);
818290000Sglebius	}
819290000Sglebius	x509(pkey_sign, ectx, grpkey, exten, certname);
820290000Sglebius#endif	/* AUTOKEY */
821290000Sglebius	exit(0);
822132451Sroberto}
823132451Sroberto
824132451Sroberto
825132451Sroberto/*
826290000Sglebius * Generate semi-random MD5 keys compatible with NTPv3 and NTPv4. Also,
827290000Sglebius * if OpenSSL is around, generate random SHA1 keys compatible with
828290000Sglebius * symmetric key cryptography.
829132451Sroberto */
830132451Srobertoint
831132451Srobertogen_md5(
832290000Sglebius	const char *id		/* file name id */
833132451Sroberto	)
834132451Sroberto{
835290000Sglebius	u_char	md5key[MD5SIZE + 1];	/* MD5 key */
836132451Sroberto	FILE	*str;
837132451Sroberto	int	i, j;
838290000Sglebius#ifdef OPENSSL
839290000Sglebius	u_char	keystr[MD5SIZE];
840290000Sglebius	u_char	hexstr[2 * MD5SIZE + 1];
841290000Sglebius	u_char	hex[] = "0123456789abcdef";
842290000Sglebius#endif	/* OPENSSL */
843132451Sroberto
844290000Sglebius	str = fheader("MD5key", id, groupname);
845132451Sroberto	for (i = 1; i <= MD5KEYS; i++) {
846290000Sglebius		for (j = 0; j < MD5SIZE; j++) {
847290000Sglebius			u_char temp;
848290000Sglebius
849132451Sroberto			while (1) {
850290000Sglebius				int rc;
851290000Sglebius
852290000Sglebius				rc = ntp_crypto_random_buf(
853290000Sglebius				    &temp, sizeof(temp));
854290000Sglebius				if (-1 == rc) {
855290000Sglebius					fprintf(stderr, "ntp_crypto_random_buf() failed.\n");
856290000Sglebius					exit (-1);
857290000Sglebius				}
858132451Sroberto				if (temp == '#')
859132451Sroberto					continue;
860290000Sglebius
861132451Sroberto				if (temp > 0x20 && temp < 0x7f)
862132451Sroberto					break;
863132451Sroberto			}
864290000Sglebius			md5key[j] = temp;
865132451Sroberto		}
866290000Sglebius		md5key[j] = '\0';
867290000Sglebius		fprintf(str, "%2d MD5 %s  # MD5 key\n", i,
868132451Sroberto		    md5key);
869132451Sroberto	}
870290000Sglebius#ifdef OPENSSL
871290000Sglebius	for (i = 1; i <= MD5KEYS; i++) {
872290000Sglebius		RAND_bytes(keystr, 20);
873290000Sglebius		for (j = 0; j < MD5SIZE; j++) {
874290000Sglebius			hexstr[2 * j] = hex[keystr[j] >> 4];
875290000Sglebius			hexstr[2 * j + 1] = hex[keystr[j] & 0xf];
876290000Sglebius		}
877290000Sglebius		hexstr[2 * MD5SIZE] = '\0';
878290000Sglebius		fprintf(str, "%2d SHA1 %s  # SHA1 key\n", i + MD5KEYS,
879290000Sglebius		    hexstr);
880290000Sglebius	}
881290000Sglebius#endif	/* OPENSSL */
882132451Sroberto	fclose(str);
883132451Sroberto	return (1);
884132451Sroberto}
885132451Sroberto
886132451Sroberto
887290000Sglebius#ifdef AUTOKEY
888132451Sroberto/*
889290000Sglebius * readkey - load cryptographic parameters and keys
890290000Sglebius *
891290000Sglebius * This routine loads a PEM-encoded file of given name and password and
892290000Sglebius * extracts the filestamp from the file name. It returns a pointer to
893290000Sglebius * the first key if valid, NULL if not.
894290000Sglebius */
895290000SglebiusEVP_PKEY *			/* public/private key pair */
896290000Sglebiusreadkey(
897290000Sglebius	char	*cp,		/* file name */
898290000Sglebius	char	*passwd,	/* password */
899290000Sglebius	u_int	*estamp,	/* file stamp */
900290000Sglebius	EVP_PKEY **evpars	/* parameter list pointer */
901290000Sglebius	)
902290000Sglebius{
903290000Sglebius	FILE	*str;		/* file handle */
904290000Sglebius	EVP_PKEY *pkey = NULL;	/* public/private key */
905290000Sglebius	u_int	gstamp;		/* filestamp */
906290000Sglebius	char	linkname[MAXFILENAME]; /* filestamp buffer) */
907290000Sglebius	EVP_PKEY *parkey;
908290000Sglebius	char	*ptr;
909290000Sglebius	int	i;
910290000Sglebius
911290000Sglebius	/*
912290000Sglebius	 * Open the key file.
913290000Sglebius	 */
914290000Sglebius	str = fopen(cp, "r");
915290000Sglebius	if (str == NULL)
916290000Sglebius		return (NULL);
917290000Sglebius
918290000Sglebius	/*
919290000Sglebius	 * Read the filestamp, which is contained in the first line.
920290000Sglebius	 */
921290000Sglebius	if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) {
922290000Sglebius		fprintf(stderr, "Empty key file %s\n", cp);
923290000Sglebius		fclose(str);
924290000Sglebius		return (NULL);
925290000Sglebius	}
926290000Sglebius	if ((ptr = strrchr(ptr, '.')) == NULL) {
927290000Sglebius		fprintf(stderr, "No filestamp found in %s\n", cp);
928290000Sglebius		fclose(str);
929290000Sglebius		return (NULL);
930290000Sglebius	}
931290000Sglebius	if (sscanf(++ptr, "%u", &gstamp) != 1) {
932290000Sglebius		fprintf(stderr, "Invalid filestamp found in %s\n", cp);
933290000Sglebius		fclose(str);
934290000Sglebius		return (NULL);
935290000Sglebius	}
936290000Sglebius
937290000Sglebius	/*
938290000Sglebius	 * Read and decrypt PEM-encoded private keys. The first one
939290000Sglebius	 * found is returned. If others are expected, add them to the
940290000Sglebius	 * parameter list.
941290000Sglebius	 */
942290000Sglebius	for (i = 0; i <= MVMAX - 1;) {
943290000Sglebius		parkey = PEM_read_PrivateKey(str, NULL, NULL, passwd);
944290000Sglebius		if (evpars != NULL) {
945290000Sglebius			evpars[i++] = parkey;
946290000Sglebius			evpars[i] = NULL;
947290000Sglebius		}
948290000Sglebius		if (parkey == NULL)
949290000Sglebius			break;
950290000Sglebius
951290000Sglebius		if (pkey == NULL)
952290000Sglebius			pkey = parkey;
953290000Sglebius		if (debug) {
954310419Sdelphij			if (EVP_PKEY_base_id(parkey) == EVP_PKEY_DSA)
955310419Sdelphij				DSA_print_fp(stderr, EVP_PKEY_get0_DSA(parkey),
956290000Sglebius				    0);
957310419Sdelphij			else if (EVP_PKEY_base_id(parkey) == EVP_PKEY_RSA)
958310419Sdelphij				RSA_print_fp(stderr, EVP_PKEY_get0_RSA(parkey),
959290000Sglebius				    0);
960290000Sglebius		}
961290000Sglebius	}
962290000Sglebius	fclose(str);
963290000Sglebius	if (pkey == NULL) {
964290000Sglebius		fprintf(stderr, "Corrupt file %s or wrong key %s\n%s\n",
965290000Sglebius		    cp, passwd, ERR_error_string(ERR_get_error(),
966290000Sglebius		    NULL));
967290000Sglebius		exit (-1);
968290000Sglebius	}
969290000Sglebius	*estamp = gstamp;
970290000Sglebius	return (pkey);
971290000Sglebius}
972290000Sglebius
973290000Sglebius
974290000Sglebius/*
975132451Sroberto * Generate RSA public/private key pair
976132451Sroberto */
977132451SrobertoEVP_PKEY *			/* public/private key pair */
978132451Srobertogen_rsa(
979290000Sglebius	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	 */
1012290000Sglebius	if (strcmp(id, "sign") == 0)
1013290000Sglebius		str = fheader("RSAsign", id, hostname);
1014290000Sglebius	else
1015290000Sglebius		str = fheader("RSAhost", id, hostname);
1016132451Sroberto	pkey = EVP_PKEY_new();
1017132451Sroberto	EVP_PKEY_assign_RSA(pkey, rsa);
1018290000Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1019290000Sglebius	    passwd1);
1020132451Sroberto	fclose(str);
1021132451Sroberto	if (debug)
1022290000Sglebius		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(
1032290000Sglebius	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	 */
1067290000Sglebius	str = fheader("DSAsign", id, hostname);
1068132451Sroberto	pkey = EVP_PKEY_new();
1069132451Sroberto	EVP_PKEY_assign_DSA(pkey, dsa);
1070290000Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1071290000Sglebius	    passwd1);
1072132451Sroberto	fclose(str);
1073132451Sroberto	if (debug)
1074290000Sglebius		DSA_print_fp(stderr, dsa, 0);
1075132451Sroberto	return (pkey);
1076132451Sroberto}
1077132451Sroberto
1078132451Sroberto
1079132451Sroberto/*
1080290000Sglebius ***********************************************************************
1081290000Sglebius *								       *
1082290000Sglebius * The following routines implement the Schnorr (IFF) identity scheme  *
1083290000Sglebius *								       *
1084290000Sglebius ***********************************************************************
1085132451Sroberto *
1086290000Sglebius * The Schnorr (IFF) identity scheme is intended for use when
1087132451Sroberto * certificates are generated by some other trusted certificate
1088290000Sglebius * authority and the certificate cannot be used to convey public
1089290000Sglebius * parameters. There are two kinds of files: encrypted server files that
1090290000Sglebius * contain private and public values and nonencrypted client files that
1091290000Sglebius * contain only public values. New generations of server files must be
1092290000Sglebius * securely transmitted to all servers of the group; client files can be
1093290000Sglebius * distributed by any means. The scheme is self contained and
1094290000Sglebius * independent of new generations of host keys, sign keys and
1095290000Sglebius * 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
1102290000Sglebius * private random group key b (0 < b < q) and public key v = g^b, then
1103290000Sglebius * sends (p, q, g, b) to the servers and (p, q, g, v) to the clients.
1104290000Sglebius * Alice challenges Bob to confirm identity using the protocol described
1105290000Sglebius * below.
1106290000Sglebius *
1107290000Sglebius * How it works
1108290000Sglebius *
1109290000Sglebius * The scheme goes like this. Both Alice and Bob have the public primes
1110290000Sglebius * p, q and generator g. The TA gives private key b to Bob and public
1111290000Sglebius * key v to Alice.
1112290000Sglebius *
1113290000Sglebius * Alice rolls new random challenge r (o < r < q) and sends to Bob in
1114290000Sglebius * the IFF request message. Bob rolls new random k (0 < k < q), then
1115290000Sglebius * computes y = k + b r mod q and x = g^k mod p and sends (y, hash(x))
1116290000Sglebius * to Alice in the response message. Besides making the response
1117290000Sglebius * shorter, the hash makes it effectivey impossible for an intruder to
1118290000Sglebius * solve for b by observing a number of these messages.
1119290000Sglebius *
1120290000Sglebius * Alice receives the response and computes g^y v^r mod p. After a bit
1121290000Sglebius * of algebra, this simplifies to g^k. If the hash of this result
1122290000Sglebius * matches hash(x), Alice knows that Bob has the group key b. The signed
1123290000Sglebius * response binds this knowledge to Bob's private key and the public key
1124290000Sglebius * previously received in his certificate.
1125132451Sroberto */
1126290000Sglebius/*
1127290000Sglebius * Generate Schnorr (IFF) keys.
1128290000Sglebius */
1129132451SrobertoEVP_PKEY *			/* DSA cuckoo nest */
1130290000Sglebiusgen_iffkey(
1131290000Sglebius	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	 */
1146290000Sglebius	fprintf(stderr, "Generating IFF keys (%d bits)...\n",
1147290000Sglebius	    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
1159290000Sglebius	 * private key are distributed to the servers, while all except
1160290000Sglebius	 * 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
1185290000Sglebius	 * random nonce r mod q and sends it to Bob. She needs only
1186290000Sglebius	 * q from parameters.
1187132451Sroberto	 */
1188310419Sdelphij	BN_rand(r, BN_num_bits(q), -1, 0);	/* r */
1189310419Sdelphij	BN_mod(r, r, q, ctx);
1190132451Sroberto
1191132451Sroberto	/*
1192290000Sglebius	 * 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
1194290000Sglebius	 * 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	/*
1204290000Sglebius	 * Alice verifies x = g^y v^r to confirm that Bob has group key
1205290000Sglebius	 * b. She needs p, q, g from parameters, (y, x) from Bob and the
1206290000Sglebius	 * original r. We omit the detail here thatt only the hash of y
1207290000Sglebius	 * 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	/*
1224290000Sglebius	 * Write the IFF keys as an encrypted DSA private key encoded in
1225290000Sglebius	 * PEM.
1226132451Sroberto	 *
1227132451Sroberto	 * p	modulus p
1228132451Sroberto	 * q	modulus q
1229132451Sroberto	 * g	generator g
1230132451Sroberto	 * priv_key b
1231132451Sroberto	 * public_key v
1232290000Sglebius	 * kinv	not used
1233290000Sglebius	 * r	not used
1234132451Sroberto	 */
1235290000Sglebius	str = fheader("IFFkey", id, groupname);
1236132451Sroberto	pkey = EVP_PKEY_new();
1237132451Sroberto	EVP_PKEY_assign_DSA(pkey, dsa);
1238290000Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1239290000Sglebius	    passwd1);
1240132451Sroberto	fclose(str);
1241132451Sroberto	if (debug)
1242290000Sglebius		DSA_print_fp(stderr, dsa, 0);
1243132451Sroberto	return (pkey);
1244132451Sroberto}
1245132451Sroberto
1246132451Sroberto
1247132451Sroberto/*
1248290000Sglebius ***********************************************************************
1249290000Sglebius *								       *
1250290000Sglebius * The following routines implement the Guillou-Quisquater (GQ)        *
1251290000Sglebius * identity scheme                                                     *
1252290000Sglebius *								       *
1253290000Sglebius ***********************************************************************
1254132451Sroberto *
1255132451Sroberto * The Guillou-Quisquater (GQ) identity scheme is intended for use when
1256290000Sglebius * the certificate can be used to convey public parameters. The scheme
1257290000Sglebius * uses a X509v3 certificate extension field do convey the public key of
1258290000Sglebius * a private key known only to servers. There are two kinds of files:
1259290000Sglebius * encrypted server files that contain private and public values and
1260290000Sglebius * nonencrypted client files that contain only public values. New
1261290000Sglebius * generations of server files must be securely transmitted to all
1262290000Sglebius * servers of the group; client files can be distributed by any means.
1263290000Sglebius * The scheme is self contained and independent of new generations of
1264290000Sglebius * host keys and sign keys. The scheme is self contained and independent
1265290000Sglebius * 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 *
1274290000Sglebius * 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.
1280290000Sglebius *
1281290000Sglebius * How it works
1282290000Sglebius *
1283290000Sglebius * The scheme goes like this. Both Alice and Bob have the same modulus n
1284290000Sglebius * and some random b as the group key. These values are computed and
1285290000Sglebius * distributed in advance via secret means, although only the group key
1286290000Sglebius * b is truly secret. Each has a private random private key u and public
1287290000Sglebius * key (u^-1)^b, although not necessarily the same ones. Bob and Alice
1288290000Sglebius * can regenerate the key pair from time to time without affecting
1289290000Sglebius * operations. The public key is conveyed on the certificate in an
1290290000Sglebius * extension field; the private key is never revealed.
1291290000Sglebius *
1292290000Sglebius * Alice rolls new random challenge r and sends to Bob in the GQ
1293290000Sglebius * request message. Bob rolls new random k, then computes y = k u^r mod
1294290000Sglebius * n and x = k^b mod n and sends (y, hash(x)) to Alice in the response
1295290000Sglebius * message. Besides making the response shorter, the hash makes it
1296290000Sglebius * effectivey impossible for an intruder to solve for b by observing
1297290000Sglebius * a number of these messages.
1298290000Sglebius *
1299290000Sglebius * Alice receives the response and computes y^b v^r mod n. After a bit
1300290000Sglebius * of algebra, this simplifies to k^b. If the hash of this result
1301290000Sglebius * matches hash(x), Alice knows that Bob has the group key b. The signed
1302290000Sglebius * response binds this knowledge to Bob's private key and the public key
1303290000Sglebius * previously received in his certificate.
1304132451Sroberto */
1305290000Sglebius/*
1306290000Sglebius * Generate Guillou-Quisquater (GQ) parameters file.
1307290000Sglebius */
1308132451SrobertoEVP_PKEY *			/* RSA cuckoo nest */
1309290000Sglebiusgen_gqkey(
1310290000Sglebius	const char *id		/* file name id */
1311132451Sroberto	)
1312132451Sroberto{
1313132451Sroberto	EVP_PKEY *pkey;		/* private key */
1314290000Sglebius	RSA	*rsa;		/* RSA parameters */
1315132451Sroberto	BN_CTX	*ctx;		/* BN working space */
1316290000Sglebius	BIGNUM	*u, *v, *g, *k, *r, *y; /* BN temps */
1317132451Sroberto	FILE	*str;
1318290000Sglebius	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,
1326290000Sglebius	    "Generating GQ parameters (%d bits)...\n",
1327290000Sglebius	     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);
1336290000Sglebius	u = BN_new(); v = BN_new(); g = BN_new();
1337290000Sglebius	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
1342290000Sglebius	 * the RSA structure. The group key is transmitted to each group
1343290000Sglebius	 * 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
1351290000Sglebius	 * 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
1385290000Sglebius	 * random nonce r mod n and sends it to Bob. She needs only n
1386290000Sglebius	 * from parameters.
1387132451Sroberto	 */
1388310419Sdelphij	BN_rand(r, BN_num_bits(n), -1, 0);	/* r */
1389310419Sdelphij	BN_mod(r, r, n, ctx);
1390132451Sroberto
1391132451Sroberto	/*
1392290000Sglebius	 * Bob rolls random nonce k mod n, computes y = k u^r mod n and
1393290000Sglebius	 * g = k^b mod n, then sends (y, g) to Alice. He needs n, u, b
1394290000Sglebius	 * 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	/*
1403290000Sglebius	 * Alice verifies g = v^r y^b mod n to confirm that Bob has
1404290000Sglebius	 * private key u. She needs n, g from parameters, public key v =
1405290000Sglebius	 * (u^-1)^b from the certificate, (y, g) from Bob and the
1406290000Sglebius	 * original r. We omit the detaul here that only the hash of g
1407290000Sglebius	 * 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	/*
1423290000Sglebius	 * Write the GQ parameter file as an encrypted RSA private key
1424290000Sglebius	 * encoded in PEM.
1425132451Sroberto	 *
1426132451Sroberto	 * n	modulus n
1427132451Sroberto	 * e	group key b
1428290000Sglebius	 * d	not used
1429132451Sroberto	 * p	private key u
1430132451Sroberto	 * q	public key (u^-1)^b
1431290000Sglebius	 * dmp1	not used
1432290000Sglebius	 * dmq1	not used
1433290000Sglebius	 * 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()));
1438290000Sglebius	str = fheader("GQkey", id, groupname);
1439132451Sroberto	pkey = EVP_PKEY_new();
1440132451Sroberto	EVP_PKEY_assign_RSA(pkey, rsa);
1441290000Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1442290000Sglebius	    passwd1);
1443132451Sroberto	fclose(str);
1444132451Sroberto	if (debug)
1445290000Sglebius		RSA_print_fp(stderr, rsa, 0);
1446132451Sroberto	return (pkey);
1447132451Sroberto}
1448132451Sroberto
1449132451Sroberto
1450132451Sroberto/*
1451290000Sglebius ***********************************************************************
1452290000Sglebius *								       *
1453290000Sglebius * The following routines implement the Mu-Varadharajan (MV) identity  *
1454290000Sglebius * scheme                                                              *
1455290000Sglebius *								       *
1456290000Sglebius ***********************************************************************
1457132451Sroberto *
1458290000Sglebius * The Mu-Varadharajan (MV) cryptosystem was originally intended when
1459290000Sglebius * servers broadcast messages to clients, but clients never send
1460290000Sglebius * messages to servers. There is one encryption key for the server and a
1461290000Sglebius * 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
1464290000Sglebius * 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 *
1474290000Sglebius * Let q be the product of n distinct primes s1[j] (j = 1...n), where
1475290000Sglebius * each s1[j] has m significant bits. Let p be a prime p = 2 * q + 1, so
1476290000Sglebius * 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
1482290000Sglebius * relatively small, like 30. These are the parameters of the scheme and
1483290000Sglebius * 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 *
1494290000Sglebius * This routine generates a private server encryption file including the
1495290000Sglebius * private encryption key E and partial decryption keys gbar and ghat.
1496290000Sglebius * It then generates public client decryption files including the public
1497290000Sglebius * keys xbar[j] and xhat[j] for each client j. The partial decryption
1498290000Sglebius * files are used to compute the inverse of E. These values are suitably
1499290000Sglebius * 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
1503290000Sglebius * 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
1506290000Sglebius * thus unable to decrypt the messageblock.
1507290000Sglebius *
1508290000Sglebius * How it works
1509290000Sglebius *
1510290000Sglebius * The scheme goes like this. Bob has the server values (p, E, q,
1511290000Sglebius * gbar, ghat) and Alice has the client values (p, xbar, xhat).
1512290000Sglebius *
1513290000Sglebius * Alice rolls new random nonce r mod p and sends to Bob in the MV
1514290000Sglebius * request message. Bob rolls random nonce k mod q, encrypts y = r E^k
1515290000Sglebius * mod p and sends (y, gbar^k, ghat^k) to Alice.
1516290000Sglebius *
1517290000Sglebius * Alice receives the response and computes the inverse (E^k)^-1 from
1518290000Sglebius * the partial decryption keys gbar^k, ghat^k, xbar and xhat. She then
1519290000Sglebius * decrypts y and verifies it matches the original r. The signed
1520290000Sglebius * response binds this knowledge to Bob's private key and the public key
1521290000Sglebius * previously received in his certificate.
1522132451Sroberto */
1523132451SrobertoEVP_PKEY *			/* DSA cuckoo nest */
1524290000Sglebiusgen_mvkey(
1525290000Sglebius	const char *id,		/* file name id */
1526290000Sglebius	EVP_PKEY **evpars	/* parameter list pointer */
1527132451Sroberto	)
1528132451Sroberto{
1529290000Sglebius	EVP_PKEY *pkey, *pkey1;	/* private keys */
1530290000Sglebius	DSA	*dsa, *dsa2, *sdsa; /* DSA parameters */
1531132451Sroberto	BN_CTX	*ctx;		/* BN working space */
1532290000Sglebius	BIGNUM	*a[MVMAX];	/* polynomial coefficient vector */
1533310419Sdelphij	BIGNUM	*gs[MVMAX];	/* public key vector */
1534290000Sglebius	BIGNUM	*s1[MVMAX];	/* private enabling keys */
1535290000Sglebius	BIGNUM	*x[MVMAX];	/* polynomial zeros vector */
1536290000Sglebius	BIGNUM	*xbar[MVMAX], *xhat[MVMAX]; /* private keys vector */
1537132451Sroberto	BIGNUM	*b;		/* group key */
1538132451Sroberto	BIGNUM	*b1;		/* inverse group key */
1539290000Sglebius	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
1554290000Sglebius	 * distinct primes s1[j] (j = 1...n) and q divides p - 1. We
1555290000Sglebius	 * first generate n m-bit primes, where the product n m is in
1556290000Sglebius	 * the order of 512 bits. One or more of these may have to be
1557290000Sglebius	 * replaced later. As a practical matter, it is tough to find
1558290000Sglebius	 * more than 31 distinct primes for 512 bits or 61 primes for
1559290000Sglebius	 * 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,
1565290000Sglebius	    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++) {
1573290000Sglebius		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	}
1586290000Sglebius	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
1593290000Sglebius	 * 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
1611290000Sglebius		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	}
1625290000Sglebius	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
1630290000Sglebius	 * 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]
1650290000Sglebius	 * (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();
1659290000Sglebius
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
1701290000Sglebius	 * computed mod p. also note the expression given in the paper
1702290000Sglebius	 * 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();
1729290000Sglebius
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
1742290000Sglebius	 * 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
1756290000Sglebius	 * that the keys for the jth client do not s1[j] or the product
1757290000Sglebius	 * s1[j]) (j = 1...n) which is q by construction.
1758290000Sglebius	 *
1759290000Sglebius	 * Compute the factor w such that w s1[j] = s1[j] for all j. The
1760290000Sglebius	 * easy way to do this is to compute (q + s1[j]) / s1[j].
1761290000Sglebius	 * 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();
1765290000Sglebius
1766310419Sdelphij		BN_add(w, q, s1[j]);
1767290000Sglebius		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;
1773290000Sglebius
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	/*
1783290000Sglebius	 * We revoke client j by dividing q by s1[j]. The quotient
1784290000Sglebius	 * becomes the enabling key s. Note we always have to revoke
1785290000Sglebius	 * one key; otherwise, the plaintext and cryptotext would be
1786290000Sglebius	 * identical. For the present there are no provisions to revoke
1787290000Sglebius	 * additional keys, so we sail on with only token revocations.
1788132451Sroberto	 */
1789290000Sglebius	s = BN_new();
1790310419Sdelphij	BN_copy(s, q);
1791290000Sglebius	BN_div(s, u, s, s1[n], ctx);
1792132451Sroberto
1793132451Sroberto	/*
1794290000Sglebius	 * For each combination of clients to be revoked, make private
1795290000Sglebius	 * encryption key E = A^s and partial decryption keys gbar = g^s
1796290000Sglebius	 * and ghat = g^(s b), all mod p. The servers use these keys to
1797290000Sglebius	 * compute the session encryption key and partial decryption
1798290000Sglebius	 * keys. These values must be regenerated if the enabling key is
1799290000Sglebius	 * 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);
1806290000Sglebius
1807132451Sroberto	/*
1808290000Sglebius	 * Notes: We produce the key media in three steps. The first
1809290000Sglebius	 * step is to generate the system parameters p, q, g, b, A and
1810290000Sglebius	 * the enabling keys s1[j]. Associated with each s1[j] are
1811290000Sglebius	 * parameters xbar[j] and xhat[j]. All of these parameters are
1812290000Sglebius	 * retained in a data structure protecteted by the trusted-agent
1813290000Sglebius	 * password. The p, xbar[j] and xhat[j] paremeters are
1814290000Sglebius	 * distributed to the j clients. When the client keys are to be
1815290000Sglebius	 * activated, the enabled keys are multipied together to form
1816290000Sglebius	 * the master enabling key s. This and the other parameters are
1817290000Sglebius	 * used to compute the server encryption key E and the partial
1818290000Sglebius	 * decryption keys gbar and ghat.
1819132451Sroberto	 *
1820290000Sglebius	 * In the identity exchange the client rolls random r and sends
1821290000Sglebius	 * it to the server. The server rolls random k, which is used
1822290000Sglebius	 * only once, then computes the session key E^k and partial
1823290000Sglebius	 * decryption keys gbar^k and ghat^k. The server sends the
1824290000Sglebius	 * encrypted r along with gbar^k and ghat^k to the client. The
1825290000Sglebius	 * client completes the decryption and verifies it matches r.
1826132451Sroberto	 */
1827132451Sroberto	/*
1828290000Sglebius	 * Write the MV trusted-agent parameters and keys as a DSA
1829290000Sglebius	 * private key encoded in PEM.
1830132451Sroberto	 *
1831132451Sroberto	 * p	modulus p
1832290000Sglebius	 * q	modulus q
1833290000Sglebius	 * g	generator g
1834290000Sglebius	 * priv_key A mod p
1835290000Sglebius	 * pub_key b mod q
1836290000Sglebius	 * (remaining values are not used)
1837132451Sroberto	 */
1838290000Sglebius	i = 0;
1839290000Sglebius	str = fheader("MVta", "mvta", groupname);
1840290000Sglebius	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);
1846290000Sglebius	PEM_write_PKCS8PrivateKey(str, pkey, cipher, NULL, 0, NULL,
1847290000Sglebius	    passwd1);
1848290000Sglebius	evpars[i++] = pkey;
1849132451Sroberto	if (debug)
1850290000Sglebius		DSA_print_fp(stderr, dsa, 0);
1851132451Sroberto
1852132451Sroberto	/*
1853290000Sglebius	 * Append the MV server parameters and keys as a DSA key encoded
1854290000Sglebius	 * in PEM.
1855290000Sglebius	 *
1856290000Sglebius	 * p	modulus p
1857290000Sglebius	 * q	modulus q (used only when generating k)
1858290000Sglebius	 * g	bige
1859290000Sglebius	 * priv_key gbar
1860290000Sglebius	 * pub_key ghat
1861290000Sglebius	 * (remaining values are not used)
1862132451Sroberto	 */
1863290000Sglebius	fprintf(stderr, "Generating MV server keys\n");
1864290000Sglebius	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));
1867290000Sglebius	pkey1 = EVP_PKEY_new();
1868290000Sglebius	EVP_PKEY_assign_DSA(pkey1, dsa2);
1869290000Sglebius	PEM_write_PKCS8PrivateKey(str, pkey1, cipher, NULL, 0, NULL,
1870290000Sglebius	    passwd1);
1871290000Sglebius	evpars[i++] = pkey1;
1872290000Sglebius	if (debug)
1873290000Sglebius		DSA_print_fp(stderr, dsa2, 0);
1874290000Sglebius
1875290000Sglebius	/*
1876290000Sglebius	 * Append the MV client parameters for each client j as DSA keys
1877290000Sglebius	 * encoded in PEM.
1878290000Sglebius	 *
1879290000Sglebius	 * p	modulus p
1880290000Sglebius	 * priv_key xbar[j] mod q
1881290000Sglebius	 * pub_key xhat[j] mod q
1882290000Sglebius	 * (remaining values are not used)
1883290000Sglebius	 */
1884290000Sglebius	fprintf(stderr, "Generating %d MV client keys\n", n);
1885132451Sroberto	for (j = 1; j <= n; j++) {
1886290000Sglebius		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]));
1890290000Sglebius		pkey1 = EVP_PKEY_new();
1891290000Sglebius		EVP_PKEY_set1_DSA(pkey1, sdsa);
1892290000Sglebius		PEM_write_PKCS8PrivateKey(str, pkey1, cipher, NULL, 0,
1893290000Sglebius		    NULL, passwd1);
1894290000Sglebius		evpars[i++] = pkey1;
1895290000Sglebius		if (debug)
1896290000Sglebius			DSA_print_fp(stderr, sdsa, 0);
1897290000Sglebius
1898290000Sglebius		/*
1899310419Sdelphij		 * The product (gbar^k)^xbar[j] (ghat^k)^xhat[j] and E
1900290000Sglebius		 * are inverses of each other. We check that the product
1901290000Sglebius		 * is one for each client except the ones that have been
1902290000Sglebius		 * revoked.
1903290000Sglebius		 */
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	}
1913290000Sglebius	evpars[i++] = NULL;
1914290000Sglebius	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	}
1922290000Sglebius	for (j = 1; j <= n; j++) {
1923290000Sglebius		BN_free(x[j]); BN_free(xbar[j]); BN_free(xhat[j]);
1924290000Sglebius		BN_free(s1[j]);
1925290000Sglebius	}
1926132451Sroberto	return (pkey);
1927132451Sroberto}
1928132451Sroberto
1929132451Sroberto
1930132451Sroberto/*
1931290000Sglebius * 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	(
1943290000Sglebius	EVP_PKEY *pkey,		/* signing key */
1944290000Sglebius	const EVP_MD *md,	/* signature/digest scheme */
1945132451Sroberto	char	*gqpub,		/* identity extension (hex string) */
1946290000Sglebius	const char *exten,	/* private cert extension */
1947290000Sglebius	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
1962290000Sglebius	 * the version to 3. Set the initial validity to the current
1963290000Sglebius	 * time and the finalvalidity one year hence.
1964132451Sroberto	 */
1965310419Sdelphij 	id = OBJ_nid2sn(EVP_MD_pkey_type(md));
1966290000Sglebius	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();
1970290000Sglebius	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);
1974290000Sglebius	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,
1977293894Sglebius	    (u_char *)name, -1, -1, 0);
1978132451Sroberto	subj = X509_get_issuer_name(cert);
1979132451Sroberto	X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC,
1980293894Sglebius	    (u_char *)name, -1, -1, 0);
1981132451Sroberto	if (!X509_set_pubkey(cert, pkey)) {
1982290000Sglebius		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,
2000290000Sglebius	    _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);
2013290000Sglebius	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,
2048290000Sglebius		    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);
2062290000Sglebius	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	 */
2072290000Sglebius	snprintf(pathbuf, sizeof(pathbuf), "%scert", id);
2073290000Sglebius	str = fheader(pathbuf, "cert", hostname);
2074132451Sroberto	PEM_write_X509(str, cert);
2075132451Sroberto	fclose(str);
2076132451Sroberto	if (debug)
2077290000Sglebius		X509_print_fp(stderr, cert);
2078132451Sroberto	X509_free(cert);
2079132451Sroberto	return (1);
2080132451Sroberto}
2081132451Sroberto
2082290000Sglebius#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(
2159290000Sglebius	const char *type,	/* key type (RSA or DSA) */
2160290000Sglebius	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
2224290000Sglebius#endif	/* AUTOKEY */
2225132451Sroberto
2226132451Sroberto
2227132451Sroberto/*
2228290000Sglebius * Generate file header and link
2229132451Sroberto */
2230132451SrobertoFILE *
2231132451Srobertofheader	(
2232290000Sglebius	const char *file,	/* file name id */
2233290000Sglebius	const char *ulink,	/* linkname */
2234290000Sglebius	const char *owner	/* owner name */
2235132451Sroberto	)
2236132451Sroberto{
2237132451Sroberto	FILE	*str;		/* file handle */
2238290000Sglebius	char	linkname[MAXFILENAME]; /* link name */
2239290000Sglebius	int	temp;
2240290000Sglebius#ifdef HAVE_UMASK
2241290000Sglebius        mode_t  orig_umask;
2242290000Sglebius#endif
2243290000Sglebius
2244290000Sglebius	snprintf(filename, sizeof(filename), "ntpkey_%s_%s.%u", file,
2245290000Sglebius	    owner, fstamp);
2246290000Sglebius#ifdef HAVE_UMASK
2247290000Sglebius        orig_umask = umask( S_IWGRP | S_IRWXO );
2248290000Sglebius        str = fopen(filename, "w");
2249290000Sglebius        (void) umask(orig_umask);
2250290000Sglebius#else
2251290000Sglebius        str = fopen(filename, "w");
2252290000Sglebius#endif
2253290000Sglebius	if (str == NULL) {
2254132451Sroberto		perror("Write");
2255132451Sroberto		exit (-1);
2256132451Sroberto	}
2257290000Sglebius        if (strcmp(ulink, "md5") == 0) {
2258290000Sglebius          strcpy(linkname,"ntp.keys");
2259290000Sglebius        } else {
2260290000Sglebius          snprintf(linkname, sizeof(linkname), "ntpkey_%s_%s", ulink,
2261290000Sglebius                   hostname);
2262290000Sglebius        }
2263290000Sglebius	(void)remove(linkname);		/* The symlink() line below matters */
2264132451Sroberto	temp = symlink(filename, linkname);
2265132451Sroberto	if (temp < 0)
2266290000Sglebius		perror(file);
2267290000Sglebius	fprintf(stderr, "Generating new %s file and link\n", ulink);
2268132451Sroberto	fprintf(stderr, "%s->%s\n", linkname, filename);
2269290000Sglebius	fprintf(str, "# %s\n# %s\n", filename, ctime(&epoch));
2270290000Sglebius	return (str);
2271132451Sroberto}
2272