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