diff options
Diffstat (limited to 'util/ntp-keygen.c')
-rw-r--r-- | util/ntp-keygen.c | 1673 |
1 files changed, 933 insertions, 740 deletions
diff --git a/util/ntp-keygen.c b/util/ntp-keygen.c index 6c14518..66d074f 100644 --- a/util/ntp-keygen.c +++ b/util/ntp-keygen.c @@ -1,70 +1,65 @@ /* - * Program to generate cryptographic keys for NTP clients and servers + * Program to generate cryptographic keys for ntp clients and servers * - * This program generates files "ntpkey_<type>_<hostname>.<filestamp>", - * where <type> is the file type, <hostname> is the generating host and - * <filestamp> is the NTP seconds in decimal format. The NTP programs - * expect generic names such as "ntpkey_<type>_whimsy.udel.edu" with the - * association maintained by soft links. - * - * Files are prefixed with a header giving the name and date of creation + * This program generates password encrypted data files for use with the + * Autokey security protocol and Network Time Protocol Version 4. Files + * are prefixed with a header giving the name and date of creation * followed by a type-specific descriptive label and PEM-encoded data - * string compatible with programs of the OpenSSL library. + * structure compatible with programs of the OpenSSL library. * - * Note that private keys can be password encrypted as per OpenSSL - * conventions. - * - * The file types include + * All file names are like "ntpkey_<type>_<hostname>.<filestamp>", where + * <type> is the file type, <hostname> the generating host name and + * <filestamp> the generation time in NTP seconds. The NTP programs + * expect generic names such as "ntpkey_<type>_whimsy.udel.edu" with the + * association maintained by soft links. Following is a list of file + * types; the first line is the file name and the second link name. * * ntpkey_MD5key_<hostname>.<filestamp> * MD5 (128-bit) keys used to compute message digests in symmetric * key cryptography * - * ntpkey_RSAkey_<hostname>.<filestamp> - * ntpkey_host_<hostname> (RSA) link + * ntpkey_RSAhost_<hostname>.<filestamp> + * ntpkey_host_<hostname> * RSA private/public host key pair used for public key signatures - * and data encryption * - * ntpkey_DSAkey_<hostname>.<filestamp> - * ntpkey_sign_<hostname> (RSA or DSA) link - * DSA private/public sign key pair used for public key signatures, - * but not data encryption + * ntpkey_RSAsign_<hostname>.<filestamp> + * ntpkey_sign_<hostname> + * RSA private/public sign key pair used for public key signatures * - * ntpkey_IFFpar_<hostname>.<filestamp> - * ntpkey_iff_<hostname> (IFF server/client) link - * ntpkey_iffkey_<hostname> (IFF client) link - * Schnorr (IFF) server/client identity parameters + * ntpkey_DSAsign_<hostname>.<filestamp> + * ntpkey_sign_<hostname> + * DSA Private/public sign key pair used for public key signatures * - * ntpkey_IFFkey_<hostname>.<filestamp> - * Schnorr (IFF) client identity parameters - * - * ntpkey_GQpar_<hostname>.<filestamp>, - * ntpkey_gq_<hostname> (GQ) link - * Guillou-Quisquater (GQ) identity parameters - * - * ntpkey_MVpar_<hostname>.<filestamp>, - * Mu-Varadharajan (MV) server identity parameters + * Available digest/signature schemes * - * ntpkey_MVkeyX_<hostname>.<filestamp>, - * ntpkey_mv_<hostname> (MV server) link - * ntpkey_mvkey_<hostname> (MV client) link - * Mu-Varadharajan (MV) client identity parameters + * RSA: RSA-MD2, RSA-MD5, RSA-SHA, RSA-SHA1, RSA-MDC2, EVP-RIPEMD160 + * DSA: DSA-SHA, DSA-SHA1 * * ntpkey_XXXcert_<hostname>.<filestamp> - * ntpkey_cert_<hostname> (RSA or DSA) link + * ntpkey_cert_<hostname> * X509v3 certificate using RSA or DSA public keys and signatures. * XXX is a code identifying the message digest and signature * encryption algorithm * - * Available digest/signature schemes + * Identity schemes. The key type par is used for the challenge; the key + * type key is used for the response. * - * RSA: RSA-MD2, RSA-MD5, RSA-SHA, RSA-SHA1, RSA-MDC2, EVP-RIPEMD160 - * DSA: DSA-SHA, DSA-SHA1 + * ntpkey_IFFkey_<groupname>.<filestamp> + * ntpkey_iffkey_<groupname> + * Schnorr (IFF) identity parameters and keys + * + * ntpkey_GQkey_<groupname>.<filestamp>, + * ntpkey_gqkey_<groupname> + * Guillou-Quisquater (GQ) identity parameters and keys + * + * ntpkey_MVkeyX_<groupname>.<filestamp>, + * ntpkey_mvkey_<groupname> + * Mu-Varadharajan (MV) identity parameters and keys * * Note: Once in a while because of some statistical fluke this program * fails to generate and verify some cryptographic data, as indicated by * exit status -1. In this case simply run the program again. If the - * program does complete with return code 0, the data are correct as + * program does complete with exit code 0, the data are correct as * verified. * * These cryptographic routines are characterized by the prime modulus @@ -92,21 +87,15 @@ #include <unistd.h> #include <sys/stat.h> #include <sys/time.h> -#if HAVE_SYS_TYPES_H -# include <sys/types.h> -#endif +#include <sys/types.h> #include "ntp_types.h" #include "ntp_random.h" -#include "l_stdlib.h" +#include "ntp_stdlib.h" +#include "ntp_assert.h" +#include "ntp_libopts.h" #include "ntp-keygen-opts.h" -#ifdef SYS_WINNT -extern int ntp_getopt P((int, char **, const char *)); -#define getopt ntp_getopt -#define optarg ntp_optarg -#endif - #ifdef OPENSSL #include "openssl/bn.h" #include "openssl/evp.h" @@ -116,17 +105,21 @@ extern int ntp_getopt P((int, char **, const char *)); #include "openssl/x509v3.h" #include <openssl/objects.h> #endif /* OPENSSL */ +#include <ssl_applink.c> /* * Cryptodefines */ -#define MD5KEYS 16 /* number of MD5 keys generated */ -#define JAN_1970 ULONG_CONST(2208988800) /* NTP seconds */ +#define MD5KEYS 10 /* number of keys generated of each type */ +#define MD5SIZE 20 /* maximum key size */ +#define JAN_1970 2208988800UL /* NTP seconds */ #define YEAR ((long)60*60*24*365) /* one year in seconds */ #define MAXFILENAME 256 /* max file name length */ #define MAXHOSTNAME 256 /* max host name length */ #ifdef OPENSSL #define PLEN 512 /* default prime modulus size (bits) */ +#define ILEN 256 /* default identity modulus size (bits) */ +#define MVMAX 100 /* max MV parameters */ /* * Strings used in X509v3 extension fields @@ -140,35 +133,39 @@ extern int ntp_getopt P((int, char **, const char *)); /* * Prototypes */ -FILE *fheader P((const char *, const char *)); -void fslink P((const char *, const char *)); -int gen_md5 P((char *)); +FILE *fheader (const char *, const char *, const char *); +int gen_md5 (char *); #ifdef OPENSSL -EVP_PKEY *gen_rsa P((char *)); -EVP_PKEY *gen_dsa P((char *)); -EVP_PKEY *gen_iff P((char *)); -EVP_PKEY *gen_gqpar P((char *)); -EVP_PKEY *gen_gqkey P((char *, EVP_PKEY *)); -EVP_PKEY *gen_mv P((char *)); -int x509 P((EVP_PKEY *, const EVP_MD *, char *, char *)); -void cb P((int, int, void *)); -EVP_PKEY *genkey P((char *, char *)); -u_long asn2ntp P((ASN1_TIME *)); +EVP_PKEY *gen_rsa (char *); +EVP_PKEY *gen_dsa (char *); +EVP_PKEY *gen_iffkey (char *); +EVP_PKEY *gen_gqkey (char *); +EVP_PKEY *gen_mvkey (char *, EVP_PKEY **); +void gen_mvserv (char *, EVP_PKEY **); +int x509 (EVP_PKEY *, const EVP_MD *, char *, char *, + char *); +void cb (int, int, void *); +EVP_PKEY *genkey (char *, char *); +EVP_PKEY *readkey (char *, char *, u_int *, EVP_PKEY **); +void writekey (char *, char *, u_int *, EVP_PKEY **); +u_long asn2ntp (ASN1_TIME *); #endif /* OPENSSL */ /* * Program variables */ extern char *optarg; /* command line argument */ -int debug = 0; /* debug, not de bug */ -int rval; /* return status */ +char *progname; +volatile int debug = 0; /* debug, not de bug */ #ifdef OPENSSL u_int modulus = PLEN; /* prime modulus size (bits) */ +u_int modulus2 = ILEN; /* identity modulus size (bits) */ #endif -int nkeys = 0; /* MV keys */ +int nkeys; /* MV keys */ time_t epoch; /* Unix epoch (seconds) since 1970 */ -char *hostname; /* host name (subject name) */ -char *trustname; /* trusted host name (issuer name) */ +u_int fstamp; /* NTP filestamp */ +char *hostname = NULL; /* host name (subject name) */ +char *groupname = NULL; /* trusted host name (issuer name) */ char filename[MAXFILENAME + 1]; /* file name */ char *passwd1 = NULL; /* input private key password */ char *passwd2 = NULL; /* output private key password */ @@ -183,9 +180,11 @@ BOOL init_randfile(); * Don't try to follow symbolic links */ int -readlink(char * link, char * file, int len) { +readlink(char *link, char *file, int len) +{ return (-1); } + /* * Don't try to create a symbolic link for now. * Just move the file to the name you need. @@ -194,7 +193,7 @@ int symlink(char *filename, char *linkname) { DeleteFile(linkname); MoveFile(filename, linkname); - return 0; + return (0); } void InitWin32Sockets() { @@ -203,7 +202,7 @@ InitWin32Sockets() { wVersionRequested = MAKEWORD(2,0); if (WSAStartup(wVersionRequested, &wsaData)) { - fprintf(stderr, "No useable winsock.dll"); + fprintf(stderr, "No useable winsock.dll\n"); exit(1); } } @@ -222,17 +221,18 @@ main( int md5key = 0; /* generate MD5 keys */ #ifdef OPENSSL X509 *cert = NULL; /* X509 certificate */ + X509_EXTENSION *ext; /* X509v3 extension */ EVP_PKEY *pkey_host = NULL; /* host key */ EVP_PKEY *pkey_sign = NULL; /* sign key */ - EVP_PKEY *pkey_iff = NULL; /* IFF parameters */ - EVP_PKEY *pkey_gq = NULL; /* GQ parameters */ - EVP_PKEY *pkey_mv = NULL; /* MV parameters */ + EVP_PKEY *pkey_iffkey = NULL; /* IFF sever keys */ + EVP_PKEY *pkey_gqkey = NULL; /* GQ server keys */ + EVP_PKEY *pkey_mvkey = NULL; /* MV trusted agen keys */ + EVP_PKEY *pkey_mvpar[MVMAX]; /* MV cleient keys */ int hostkey = 0; /* generate RSA keys */ - int iffkey = 0; /* generate IFF parameters */ - int gqpar = 0; /* generate GQ parameters */ - int gqkey = 0; /* update GQ keys */ - int mvpar = 0; /* generate MV parameters */ + int iffkey = 0; /* generate IFF keys */ + int gqkey = 0; /* generate GQ keys */ int mvkey = 0; /* update MV keys */ + int mvpar = 0; /* generate MV parameters */ char *sign = NULL; /* sign key */ EVP_PKEY *pkey = NULL; /* temp key */ const EVP_MD *ectx; /* EVP digest */ @@ -242,33 +242,23 @@ main( char *grpkey = NULL; /* identity extension */ int nid; /* X509 digest/signature scheme */ FILE *fstr = NULL; /* file handle */ - u_int temp; #define iffsw HAVE_OPT(ID_KEY) #endif /* OPENSSL */ char hostbuf[MAXHOSTNAME + 1]; + char groupbuf[MAXHOSTNAME + 1]; + + progname = argv[0]; #ifdef SYS_WINNT /* Initialize before OpenSSL checks */ InitWin32Sockets(); - if(!init_randfile()) + if (!init_randfile()) fprintf(stderr, "Unable to initialize .rnd file\n"); + ssl_applink(); #endif #ifdef OPENSSL - /* - * OpenSSL version numbers: MNNFFPPS: major minor fix patch status - * We match major, minor, fix and status (not patch) - */ - if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) { - fprintf(stderr, - "OpenSSL version mismatch. Built against %lx, you have %lx\n", - OPENSSL_VERSION_NUMBER, SSLeay()); - return (-1); - - } else { - fprintf(stderr, - "Using OpenSSL version %lx\n", SSLeay()); - } + ssl_check_version(); #endif /* OPENSSL */ /* @@ -276,393 +266,585 @@ main( */ gethostname(hostbuf, MAXHOSTNAME); hostname = hostbuf; -#ifdef OPENSSL - trustname = hostbuf; - passwd1 = hostbuf; -#endif -#ifndef SYS_WINNT gettimeofday(&tv, 0); -#else - gettimeofday(&tv); -#endif + epoch = tv.tv_sec; - rval = 0; { - int optct = optionProcess(&ntp_keygenOptions, argc, argv); + int optct = ntpOptionProcess(&ntp_keygenOptions, + argc, argv); argc -= optct; argv += optct; } #ifdef OPENSSL - if (HAVE_OPT( CERTIFICATE )) - scheme = OPT_ARG( CERTIFICATE ); -#endif + if (SSLeay() == SSLEAY_VERSION_NUMBER) + fprintf(stderr, "Using OpenSSL version %s\n", + SSLeay_version(SSLEAY_VERSION)); + else + fprintf(stderr, "Built against OpenSSL %s, using version %s\n", + OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); +#endif /* OPENSSL */ debug = DESC(DEBUG_LEVEL).optOccCt; - -#ifdef OPENSSL - if (HAVE_OPT( GQ_PARAMS )) - gqpar++; - - if (HAVE_OPT( GQ_KEYS )) - gqkey++; - - if (HAVE_OPT( HOST_KEY )) - hostkey++; - - if (HAVE_OPT( IFFKEY )) - iffkey++; - - if (HAVE_OPT( ISSUER_NAME )) - trustname = OPT_ARG( ISSUER_NAME ); -#endif - if (HAVE_OPT( MD5KEY )) - md5key++; + md5key++; #ifdef OPENSSL - if (HAVE_OPT( MODULUS )) - modulus = OPT_VALUE_MODULUS; - - if (HAVE_OPT( PVT_CERT )) - exten = EXT_KEY_PRIVATE; - + passwd1 = hostbuf; if (HAVE_OPT( PVT_PASSWD )) - passwd2 = OPT_ARG( PVT_PASSWD ); + passwd1 = strdup(OPT_ARG( PVT_PASSWD )); if (HAVE_OPT( GET_PVT_PASSWD )) - passwd1 = OPT_ARG( GET_PVT_PASSWD ); + passwd2 = strdup(OPT_ARG( GET_PVT_PASSWD )); + + if (HAVE_OPT( HOST_KEY )) + hostkey++; if (HAVE_OPT( SIGN_KEY )) - sign = OPT_ARG( SIGN_KEY ); + sign = strdup(OPT_ARG( SIGN_KEY )); - if (HAVE_OPT( SUBJECT_NAME )) - hostname = OPT_ARG( SUBJECT_NAME ); + if (HAVE_OPT( GQ_PARAMS )) + gqkey++; - if (HAVE_OPT( TRUSTED_CERT )) - exten = EXT_KEY_TRUST; + if (HAVE_OPT( IFFKEY )) + iffkey++; if (HAVE_OPT( MV_PARAMS )) { - mvpar++; + mvkey++; nkeys = OPT_VALUE_MV_PARAMS; } - if (HAVE_OPT( MV_KEYS )) { - mvkey++; + mvpar++; nkeys = OPT_VALUE_MV_KEYS; } -#endif + if (HAVE_OPT( MODULUS )) + modulus = OPT_VALUE_MODULUS; + + if (HAVE_OPT( CERTIFICATE )) + scheme = OPT_ARG( CERTIFICATE ); + + if (HAVE_OPT( SUBJECT_NAME )) + hostname = strdup(OPT_ARG( SUBJECT_NAME )); + + if (HAVE_OPT( ISSUER_NAME )) + groupname = strdup(OPT_ARG( ISSUER_NAME )); + + if (HAVE_OPT( PVT_CERT )) + exten = EXT_KEY_PRIVATE; + + if (HAVE_OPT( TRUSTED_CERT )) + exten = EXT_KEY_TRUST; - if (passwd1 != NULL && passwd2 == NULL) - passwd2 = passwd1; -#ifdef OPENSSL /* * Seed random number generator and grow weeds. */ ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); - if (RAND_file_name(pathbuf, MAXFILENAME) == NULL) { - fprintf(stderr, "RAND_file_name %s\n", - ERR_error_string(ERR_get_error(), NULL)); - return (-1); - } - temp = RAND_load_file(pathbuf, -1); - if (temp == 0) { + if (!RAND_status()) { + u_int temp; + + if (RAND_file_name(pathbuf, MAXFILENAME) == NULL) { + fprintf(stderr, "RAND_file_name %s\n", + ERR_error_string(ERR_get_error(), NULL)); + exit (-1); + } + temp = RAND_load_file(pathbuf, -1); + if (temp == 0) { + fprintf(stderr, + "RAND_load_file %s not found or empty\n", + pathbuf); + exit (-1); + } fprintf(stderr, - "RAND_load_file %s not found or empty\n", pathbuf); - return (-1); + "Random seed file %s %u bytes\n", pathbuf, temp); + RAND_add(&epoch, sizeof(epoch), 4.0); } - fprintf(stderr, - "Random seed file %s %u bytes\n", pathbuf, temp); - RAND_add(&epoch, sizeof(epoch), 4.0); -#endif /* - * Generate new parameters and keys as requested. These replace - * any values already generated. + * Load previous certificate if available. */ - if (md5key) - gen_md5("MD5"); -#ifdef OPENSSL - if (hostkey) - pkey_host = genkey("RSA", "host"); - if (sign != NULL) - pkey_sign = genkey(sign, "sign"); - if (iffkey) - pkey_iff = gen_iff("iff"); - if (gqpar) - pkey_gq = gen_gqpar("gq"); - if (mvpar) - pkey_mv = gen_mv("mv"); + sprintf(filename, "ntpkey_cert_%s", hostname); + if ((fstr = fopen(filename, "r")) != NULL) { + cert = PEM_read_X509(fstr, NULL, NULL, NULL); + fclose(fstr); + } + if (cert != NULL) { + + /* + * Extract subject name. + */ + X509_NAME_oneline(X509_get_subject_name(cert), groupbuf, + MAXFILENAME); + + /* + * Extract digest/signature scheme. + */ + if (scheme == NULL) { + nid = OBJ_obj2nid(cert->cert_info-> + signature->algorithm); + scheme = OBJ_nid2sn(nid); + } + + /* + * If a key_usage extension field is present, determine + * whether this is a trusted or private certificate. + */ + if (exten == NULL) { + BIO *bp; + int i, cnt; + char *ptr; + + ptr = strstr(groupbuf, "CN="); + cnt = X509_get_ext_count(cert); + for (i = 0; i < cnt; i++) { + ext = X509_get_ext(cert, i); + if (OBJ_obj2nid(ext->object) == + NID_ext_key_usage) { + bp = BIO_new(BIO_s_mem()); + X509V3_EXT_print(bp, ext, 0, 0); + BIO_gets(bp, pathbuf, + MAXFILENAME); + BIO_free(bp); + if (strcmp(pathbuf, + "Trust Root") == 0) + exten = EXT_KEY_TRUST; + else if (strcmp(pathbuf, + "Private") == 0) + exten = EXT_KEY_PRIVATE; + if (groupname == NULL) + groupname = ptr + 3; + } + } + } + } + if (scheme == NULL) + scheme = "RSA-MD5"; + if (groupname == NULL) + groupname = hostname; + fprintf(stderr, "Using host %s group %s\n", hostname, + groupname); + if ((iffkey || gqkey || mvkey) && exten == NULL) + fprintf(stderr, + "Warning: identity files may not be useful with a nontrusted certificate.\n"); +#endif /* OPENSSL */ /* - * If there is no new host key, look for an existing one. If not - * found, create it. + * Create new unencrypted MD5 keys file if requested. If this + * option is selected, ignore all other options. */ - while (pkey_host == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { - sprintf(filename, "ntpkey_host_%s", hostname); - if ((fstr = fopen(filename, "r")) != NULL) { - pkey_host = PEM_read_PrivateKey(fstr, NULL, - NULL, passwd1); - fclose(fstr); - readlink(filename, filename, sizeof(filename)); - if (pkey_host == NULL) { - fprintf(stderr, "Host key\n%s\n", - ERR_error_string(ERR_get_error(), - NULL)); - rval = -1; - } else { - fprintf(stderr, - "Using host key %s\n", filename); - } - break; + if (md5key) { + gen_md5("md5"); + exit (0); + } - } else if ((pkey_host = genkey("RSA", "host")) == - NULL) { - rval = -1; - break; +#ifdef OPENSSL + /* + * Create a new encrypted RSA host key file if requested; + * otherwise, look for an existing host key file. If not found, + * create a new encrypted RSA host key file. If that fails, go + * no further. + */ + if (hostkey) + pkey_host = genkey("RSA", "host"); + if (pkey_host == NULL) { + sprintf(filename, "ntpkey_host_%s", hostname); + pkey_host = readkey(filename, passwd1, &fstamp, NULL); + if (pkey_host != NULL) { + readlink(filename, filename, sizeof(filename)); + fprintf(stderr, "Using host key %s\n", + filename); + } else { + pkey_host = genkey("RSA", "host"); } } + if (pkey_host == NULL) { + fprintf(stderr, "Generating host key fails\n"); + exit (-1); + } /* - * If there is no new sign key, look for an existing one. If not - * found, use the host key instead. + * Create new encrypted RSA or DSA sign keys file if requested; + * otherwise, look for an existing sign key file. If not found, + * use the host key instead. */ - pkey = pkey_sign; - while (pkey_sign == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { + if (sign != NULL) + pkey_sign = genkey(sign, "sign"); + if (pkey_sign == NULL) { sprintf(filename, "ntpkey_sign_%s", hostname); - if ((fstr = fopen(filename, "r")) != NULL) { - pkey_sign = PEM_read_PrivateKey(fstr, NULL, - NULL, passwd1); - fclose(fstr); + pkey_sign = readkey(filename, passwd1, &fstamp, NULL); + if (pkey_sign != NULL) { readlink(filename, filename, sizeof(filename)); - if (pkey_sign == NULL) { - fprintf(stderr, "Sign key\n%s\n", - ERR_error_string(ERR_get_error(), - NULL)); - rval = -1; - } else { - fprintf(stderr, "Using sign key %s\n", - filename); - } - break; - } else { - pkey = pkey_host; + fprintf(stderr, "Using sign key %s\n", + filename); + } else if (pkey_host != NULL) { + pkey_sign = pkey_host; fprintf(stderr, "Using host key as sign key\n"); - break; } } /* - * If there is no new IFF file, look for an existing one. + * Create new encrypted GQ server keys file if requested; + * otherwise, look for an exisiting file. If found, fetch the + * public key for the certificate. */ - if (pkey_iff == NULL && rval == 0) { - sprintf(filename, "ntpkey_iff_%s", hostname); - if ((fstr = fopen(filename, "r")) != NULL) { - pkey_iff = PEM_read_PrivateKey(fstr, NULL, - NULL, passwd1); - fclose(fstr); + if (gqkey) + pkey_gqkey = gen_gqkey("gqkey"); + if (pkey_gqkey == NULL) { + sprintf(filename, "ntpkey_gqkey_%s", groupname); + pkey_gqkey = readkey(filename, passwd1, &fstamp, NULL); + if (pkey_gqkey != NULL) { readlink(filename, filename, sizeof(filename)); - if (pkey_iff == NULL) { - fprintf(stderr, "IFF parameters\n%s\n", - ERR_error_string(ERR_get_error(), - NULL)); - rval = -1; - } else { - fprintf(stderr, - "Using IFF parameters %s\n", - filename); - } + fprintf(stderr, "Using GQ parameters %s\n", + filename); } } + if (pkey_gqkey != NULL) + grpkey = BN_bn2hex(pkey_gqkey->pkey.rsa->q); /* - * If there is no new GQ file, look for an existing one. + * Write the nonencrypted GQ client parameters to the stdout + * stream. The parameter file is the server key file with the + * private key obscured. */ - if (pkey_gq == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { - sprintf(filename, "ntpkey_gq_%s", hostname); - if ((fstr = fopen(filename, "r")) != NULL) { - pkey_gq = PEM_read_PrivateKey(fstr, NULL, NULL, - passwd1); - fclose(fstr); - readlink(filename, filename, sizeof(filename)); - if (pkey_gq == NULL) { - fprintf(stderr, "GQ parameters\n%s\n", - ERR_error_string(ERR_get_error(), - NULL)); - rval = -1; - } else { - fprintf(stderr, - "Using GQ parameters %s\n", - filename); - } - } + if (pkey_gqkey != NULL && HAVE_OPT(ID_KEY)) { + RSA *rsa; + + epoch = fstamp - JAN_1970; + sprintf(filename, "ntpkey_gqpar_%s.%u", groupname, + fstamp); + fprintf(stderr, "Writing GQ parameters %s to stdout\n", + filename); + fprintf(stdout, "# %s\n# %s\n", filename, + ctime(&epoch)); + rsa = pkey_gqkey->pkey.rsa; + BN_copy(rsa->p, BN_value_one()); + BN_copy(rsa->q, BN_value_one()); + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, rsa); + PEM_write_PrivateKey(stdout, pkey, NULL, NULL, 0, NULL, + NULL); + fclose(stdout); + if (debug) + RSA_print_fp(stderr, rsa, 0); } /* - * If there is a GQ parameter file, create GQ private/public - * keys and extract the public key for the certificate. + * Write the encrypted GQ server keys to the stdout stream. */ - if (pkey_gq != NULL && rval == 0) { - gen_gqkey("gq", pkey_gq); - grpkey = BN_bn2hex(pkey_gq->pkey.rsa->q); + if (pkey_gqkey != NULL && passwd2 != NULL) { + RSA *rsa; + + sprintf(filename, "ntpkey_gqkey_%s.%u", groupname, + fstamp); + fprintf(stderr, "Writing GQ keys %s to stdout\n", + filename); + fprintf(stdout, "# %s\n# %s\n", filename, + ctime(&epoch)); + rsa = pkey_gqkey->pkey.rsa; + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, rsa); + PEM_write_PrivateKey(stdout, pkey, + EVP_des_cbc(), NULL, 0, NULL, passwd2); + fclose(stdout); + if (debug) + RSA_print_fp(stderr, rsa, 0); } /* - * Generate a X509v3 certificate. + * Create new encrypted IFF server keys file if requested; + * otherwise, look for existing file. */ - while (scheme == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { - sprintf(filename, "ntpkey_cert_%s", hostname); - if ((fstr = fopen(filename, "r")) != NULL) { - cert = PEM_read_X509(fstr, NULL, NULL, NULL); - fclose(fstr); + if (iffkey) + pkey_iffkey = gen_iffkey("iffkey"); + if (pkey_iffkey == NULL) { + sprintf(filename, "ntpkey_iffkey_%s", groupname); + pkey_iffkey = readkey(filename, passwd1, &fstamp, NULL); + if (pkey_iffkey != NULL) { readlink(filename, filename, sizeof(filename)); - if (cert == NULL) { - fprintf(stderr, "Cert \n%s\n", - ERR_error_string(ERR_get_error(), - NULL)); - rval = -1; - } else { - nid = OBJ_obj2nid( - cert->cert_info->signature->algorithm); - scheme = OBJ_nid2sn(nid); - fprintf(stderr, - "Using scheme %s from %s\n", scheme, - filename); - break; - } - } - scheme = "RSA-MD5"; - } - if (pkey != NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { - ectx = EVP_get_digestbyname(scheme); - if (ectx == NULL) { - fprintf(stderr, - "Invalid digest/signature combination %s\n", - scheme); - rval = -1; - } else { - x509(pkey, ectx, grpkey, exten); + fprintf(stderr, "Using IFF keys %s\n", + filename); } } /* - * Write the IFF client parameters and keys as a DSA private key - * encoded in PEM. Note the private key is obscured. + * Write the nonencrypted IFF client parameters to the stdout + * stream. The parameter file is the server key file with the + * private key obscured. */ - if (pkey_iff != NULL && rval == 0 && HAVE_OPT(ID_KEY)) { + if (pkey_iffkey != NULL && HAVE_OPT(ID_KEY)) { DSA *dsa; - char *sptr; - char *tld; - - sptr = strrchr(filename, '.'); - tld = malloc(strlen(sptr)); /* we have an extra byte ... */ - strcpy(tld, 1+sptr); /* ... see? */ - sprintf(filename, "ntpkey_IFFkey_%s.%s", trustname, - tld); - free(tld); - fprintf(stderr, "Writing new IFF key %s\n", filename); - fprintf(stdout, "# %s\n# %s", filename, ctime(&epoch)); - dsa = pkey_iff->pkey.dsa; + + epoch = fstamp - JAN_1970; + sprintf(filename, "ntpkey_iffpar_%s.%u", groupname, + fstamp); + fprintf(stderr, "Writing IFF parameters %s to stdout\n", + filename); + fprintf(stdout, "# %s\n# %s\n", filename, + ctime(&epoch)); + dsa = pkey_iffkey->pkey.dsa; BN_copy(dsa->priv_key, BN_value_one()); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); - PEM_write_PrivateKey(stdout, pkey, passwd2 ? - EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); + PEM_write_PrivateKey(stdout, pkey, NULL, NULL, 0, NULL, + NULL); fclose(stdout); if (debug) - DSA_print_fp(stdout, dsa, 0); + DSA_print_fp(stderr, dsa, 0); } /* - * Return the marbles. - */ - if (grpkey != NULL) - OPENSSL_free(grpkey); - if (pkey_host != NULL) - EVP_PKEY_free(pkey_host); - if (pkey_sign != NULL) - EVP_PKEY_free(pkey_sign); - if (pkey_iff != NULL) - EVP_PKEY_free(pkey_iff); - if (pkey_gq != NULL) - EVP_PKEY_free(pkey_gq); - if (pkey_mv != NULL) - EVP_PKEY_free(pkey_mv); -#endif /* OPENSSL */ - return (rval); -} + * Write the encrypted IFF server keys to the stdout stream. + */ + if (pkey_iffkey != NULL && passwd2 != NULL) { + DSA *dsa; + epoch = fstamp - JAN_1970; + sprintf(filename, "ntpkey_iffkey_%s.%u", groupname, + fstamp); + fprintf(stderr, "Writing IFF keys %s to stdout\n", + filename); + fprintf(stdout, "# %s\n# %s\n", filename, + ctime(&epoch)); + dsa = pkey_iffkey->pkey.dsa; + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_DSA(pkey, dsa); + PEM_write_PrivateKey(stdout, pkey, EVP_des_cbc(), NULL, + 0, NULL, passwd2); + fclose(stdout); + if (debug) + DSA_print_fp(stderr, dsa, 0); + } -#if 0 -/* - * Generate random MD5 key with password. - */ -int -gen_md5( - char *id /* file name id */ - ) -{ - BIGNUM *key; - BIGNUM *keyid; - FILE *str; - u_char bin[16]; - - fprintf(stderr, "Generating MD5 keys...\n"); - str = fheader("MD5key", hostname); - keyid = BN_new(); key = BN_new(); - BN_rand(keyid, 16, -1, 0); - BN_rand(key, 128, -1, 0); - BN_bn2bin(key, bin); - PEM_write_fp(str, MD5, NULL, bin); - fclose(str); - fslink(id, hostname); - return (1); + /* + * Create new encrypted MV trusted-authority keys file if + * requested; otherwise, look for existing keys file. + */ + if (mvkey) + pkey_mvkey = gen_mvkey("mv", pkey_mvpar); + if (pkey_mvkey == NULL) { + sprintf(filename, "ntpkey_mvta_%s", groupname); + pkey_mvkey = readkey(filename, passwd1, &fstamp, + pkey_mvpar); + if (pkey_mvkey != NULL) { + readlink(filename, filename, sizeof(filename)); + fprintf(stderr, "Using MV keys %s\n", + filename); + } + } + + /* + * Write the nonencrypted MV client parameters to the stdout + * stream. For the moment, we always use the client parameters + * associated with client key 1. + */ + if (pkey_mvkey != NULL && HAVE_OPT(ID_KEY)) { + epoch = fstamp - JAN_1970; + sprintf(filename, "ntpkey_mvpar_%s.%u", groupname, + fstamp); + fprintf(stderr, "Writing MV parameters %s to stdout\n", + filename); + fprintf(stdout, "# %s\n# %s\n", filename, + ctime(&epoch)); + pkey = pkey_mvpar[2]; + PEM_write_PrivateKey(stdout, pkey, NULL, NULL, 0, NULL, + NULL); + fclose(stdout); + if (debug) + DSA_print_fp(stderr, pkey->pkey.dsa, 0); + } + + /* + * Write the encrypted MV server keys to the stdout stream. + */ + if (pkey_mvkey != NULL && passwd2 != NULL) { + epoch = fstamp - JAN_1970; + sprintf(filename, "ntpkey_mvkey_%s.%u", groupname, + fstamp); + fprintf(stderr, "Writing MV keys %s to stdout\n", + filename); + fprintf(stdout, "# %s\n# %s\n", filename, + ctime(&epoch)); + pkey = pkey_mvpar[1]; + PEM_write_PrivateKey(stdout, pkey, EVP_des_cbc(), NULL, + 0, NULL, passwd2); + fclose(stdout); + if (debug) + DSA_print_fp(stderr, pkey->pkey.dsa, 0); + } + + /* + * Don't generate a certificate if no host keys or extracting + * encrypted or nonencrypted keys to the standard output stream. + */ + if (pkey_host == NULL || HAVE_OPT(ID_KEY) || passwd2 != NULL) + exit (0); + + /* + * Decode the digest/signature scheme. If trusted, set the + * subject and issuer names to the group name; if not set both + * to the host name. + */ + ectx = EVP_get_digestbyname(scheme); + if (ectx == NULL) { + fprintf(stderr, + "Invalid digest/signature combination %s\n", + scheme); + exit (-1); + } + if (exten == NULL) + x509(pkey_sign, ectx, grpkey, exten, hostname); + else + x509(pkey_sign, ectx, grpkey, exten, groupname); +#endif /* OPENSSL */ + exit (0); } -#else /* - * Generate semi-random MD5 keys compatible with NTPv3 and NTPv4 + * Generate semi-random MD5 keys compatible with NTPv3 and NTPv4. Also, + * if OpenSSL is around, generate random SHA1 keys compatible with + * symmetric key cryptography. */ int gen_md5( char *id /* file name id */ ) { - u_char md5key[16]; /* MD5 key */ + u_char md5key[MD5SIZE + 1]; /* MD5 key */ FILE *str; - u_int temp = 0; /* Initialize to prevent warnings during compile */ int i, j; +#ifdef OPENSSL + u_char keystr[MD5SIZE]; + u_char hexstr[2 * MD5SIZE + 1]; + u_char hex[] = "0123456789abcdef"; +#endif /* OPENSSL */ - fprintf(stderr, "Generating MD5 keys...\n"); - str = fheader("MD5key", hostname); - ntp_srandom(epoch); + str = fheader("MD5key", id, groupname); + ntp_srandom((u_long)epoch); for (i = 1; i <= MD5KEYS; i++) { - for (j = 0; j < 16; j++) { + for (j = 0; j < MD5SIZE; j++) { + int temp; + while (1) { temp = ntp_random() & 0xff; if (temp == '#') continue; + if (temp > 0x20 && temp < 0x7f) break; } md5key[j] = (u_char)temp; } - md5key[15] = '\0'; - fprintf(str, "%2d MD5 %16s # MD5 key\n", i, + md5key[j] = '\0'; + fprintf(str, "%2d MD5 %s # MD5 key\n", i, md5key); } +#ifdef OPENSSL + for (i = 1; i <= MD5KEYS; i++) { + RAND_bytes(keystr, 20); + for (j = 0; j < MD5SIZE; j++) { + hexstr[2 * j] = hex[keystr[j] >> 4]; + hexstr[2 * j + 1] = hex[keystr[j] & 0xf]; + } + hexstr[2 * MD5SIZE] = '\0'; + fprintf(str, "%2d SHA1 %s # SHA1 key\n", i + MD5KEYS, + hexstr); + } +#endif /* OPENSSL */ fclose(str); - fslink(id, hostname); return (1); } -#endif /* OPENSSL */ #ifdef OPENSSL /* + * readkey - load cryptographic parameters and keys + * + * This routine loads a PEM-encoded file of given name and password and + * extracts the filestamp from the file name. It returns a pointer to + * the first key if valid, NULL if not. + */ +EVP_PKEY * /* public/private key pair */ +readkey( + char *cp, /* file name */ + char *passwd, /* password */ + u_int *estamp, /* file stamp */ + EVP_PKEY **evpars /* parameter list pointer */ + ) +{ + FILE *str; /* file handle */ + EVP_PKEY *pkey = NULL; /* public/private key */ + u_int gstamp; /* filestamp */ + char linkname[MAXFILENAME]; /* filestamp buffer) */ + EVP_PKEY *parkey; + char *ptr; + int i; + + /* + * Open the key file. + */ + str = fopen(cp, "r"); + if (str == NULL) + return (NULL); + + /* + * Read the filestamp, which is contained in the first line. + */ + if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) { + fprintf(stderr, "Empty key file %s\n", cp); + fclose(str); + return (NULL); + } + if ((ptr = strrchr(ptr, '.')) == NULL) { + fprintf(stderr, "No filestamp found in %s\n", cp); + fclose(str); + return (NULL); + } + if (sscanf(++ptr, "%u", &gstamp) != 1) { + fprintf(stderr, "Invalid filestamp found in %s\n", cp); + fclose(str); + return (NULL); + } + + /* + * Read and decrypt PEM-encoded private keys. The first one + * found is returned. If others are expected, add them to the + * parameter list. + */ + for (i = 0; i <= MVMAX - 1;) { + parkey = PEM_read_PrivateKey(str, NULL, NULL, passwd); + if (evpars != NULL) { + evpars[i++] = parkey; + evpars[i] = NULL; + } + if (parkey == NULL) + break; + + if (pkey == NULL) + pkey = parkey; + if (debug) { + if (parkey->type == EVP_PKEY_DSA) + DSA_print_fp(stderr, parkey->pkey.dsa, + 0); + else if (parkey->type == EVP_PKEY_RSA) + RSA_print_fp(stderr, parkey->pkey.rsa, + 0); + } + } + fclose(str); + if (pkey == NULL) { + fprintf(stderr, "Corrupt file %s or wrong key %s\n%s\n", + cp, passwd, ERR_error_string(ERR_get_error(), + NULL)); + exit (-1); + } + *estamp = gstamp; + return (pkey); +} + + +/* * Generate RSA public/private key pair */ EVP_PKEY * /* public/private key pair */ @@ -680,7 +862,6 @@ gen_rsa( if (rsa == NULL) { fprintf(stderr, "RSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); - rval = -1; return (NULL); } @@ -694,7 +875,6 @@ gen_rsa( fprintf(stderr, "Invalid RSA key\n%s\n", ERR_error_string(ERR_get_error(), NULL)); RSA_free(rsa); - rval = -1; return (NULL); } @@ -702,15 +882,17 @@ gen_rsa( * Write the RSA parameters and keys as a RSA private key * encoded in PEM. */ - str = fheader("RSAkey", hostname); + if (strcmp(id, "sign") == 0) + str = fheader("RSAsign", id, hostname); + else + str = fheader("RSAhost", id, hostname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); - PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, - NULL, 0, NULL, passwd2); + PEM_write_PrivateKey(str, pkey, EVP_des_cbc(), NULL, 0, NULL, + passwd1); fclose(str); if (debug) - RSA_print_fp(stdout, rsa, 0); - fslink(id, hostname); + RSA_print_fp(stderr, rsa, 0); return (pkey); } @@ -740,7 +922,6 @@ gen_dsa( if (dsa == NULL) { fprintf(stderr, "DSA generate parameters fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); - rval = -1; return (NULL); } @@ -752,7 +933,6 @@ gen_dsa( fprintf(stderr, "DSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_free(dsa); - rval = -1; return (NULL); } @@ -760,45 +940,70 @@ gen_dsa( * Write the DSA parameters and keys as a DSA private key * encoded in PEM. */ - str = fheader("DSAkey", hostname); + str = fheader("DSAsign", id, hostname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); - PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, - NULL, 0, NULL, passwd2); + PEM_write_PrivateKey(str, pkey, EVP_des_cbc(), NULL, 0, NULL, + passwd1); fclose(str); if (debug) - DSA_print_fp(stdout, dsa, 0); - fslink(id, hostname); + DSA_print_fp(stderr, dsa, 0); return (pkey); } /* - * Generate Schnorr (IFF) parameters and keys + *********************************************************************** + * * + * The following routines implement the Schnorr (IFF) identity scheme * + * * + *********************************************************************** * - * The Schnorr (IFF)identity scheme is intended for use when + * The Schnorr (IFF) identity scheme is intended for use when * certificates are generated by some other trusted certificate - * authority and the parameters cannot be conveyed in the certificate - * itself. For this purpose, new generations of IFF values must be - * securely transmitted to all members of the group before use. There - * are two kinds of files: server/client files that include private and - * public parameters and client files that include only public - * parameters. The scheme is self contained and independent of new - * generations of host keys, sign keys and certificates. + * authority and the certificate cannot be used to convey public + * parameters. There are two kinds of files: encrypted server files that + * contain private and public values and nonencrypted client files that + * contain only public values. New generations of server files must be + * securely transmitted to all servers of the group; client files can be + * distributed by any means. The scheme is self contained and + * independent of new generations of host keys, sign keys and + * certificates. * * The IFF values hide in a DSA cuckoo structure which uses the same * parameters. The values are used by an identity scheme based on DSA * cryptography and described in Stimson p. 285. The p is a 512-bit * prime, g a generator of Zp* and q a 160-bit prime that divides p - 1 * and is a qth root of 1 mod p; that is, g^q = 1 mod p. The TA rolls a - * private random group key b (0 < b < q), then computes public - * v = g^(q - a). All values except the group key are known to all group - * members; the group key is known to the group servers, but not the - * group clients. Alice challenges Bob to confirm identity using the - * protocol described below. + * private random group key b (0 < b < q) and public key v = g^b, then + * sends (p, q, g, b) to the servers and (p, q, g, v) to the clients. + * Alice challenges Bob to confirm identity using the protocol described + * below. + * + * How it works + * + * The scheme goes like this. Both Alice and Bob have the public primes + * p, q and generator g. The TA gives private key b to Bob and public + * key v to Alice. + * + * Alice rolls new random challenge r (o < r < q) and sends to Bob in + * the IFF request message. Bob rolls new random k (0 < k < q), then + * computes y = k + b r mod q and x = g^k mod p and sends (y, hash(x)) + * to Alice in the response message. Besides making the response + * shorter, the hash makes it effectivey impossible for an intruder to + * solve for b by observing a number of these messages. + * + * Alice receives the response and computes g^y v^r mod p. After a bit + * of algebra, this simplifies to g^k. If the hash of this result + * matches hash(x), Alice knows that Bob has the group key b. The signed + * response binds this knowledge to Bob's private key and the public key + * previously received in his certificate. + */ +/* + * Generate Schnorr (IFF) keys. */ EVP_PKEY * /* DSA cuckoo nest */ -gen_iff( +gen_iffkey( char *id /* file name id */ ) { @@ -813,24 +1018,23 @@ gen_iff( /* * Generate DSA parameters for use as IFF parameters. */ - fprintf(stderr, "Generating IFF parameters (%d bits)...\n", - modulus); + fprintf(stderr, "Generating IFF keys (%d bits)...\n", + modulus2); RAND_bytes(seed, sizeof(seed)); - dsa = DSA_generate_parameters(modulus, seed, sizeof(seed), NULL, + dsa = DSA_generate_parameters(modulus2, seed, sizeof(seed), NULL, NULL, cb, "IFF"); fprintf(stderr, "\n"); if (dsa == NULL) { fprintf(stderr, "DSA generate parameters fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); - rval = -1; return (NULL);; } /* * Generate the private and public keys. The DSA parameters and - * these keys are distributed to all members of the group. + * private key are distributed to the servers, while all except + * the private key are distributed to the clients. */ - fprintf(stderr, "Generating IFF keys (%d bits)...\n", modulus); b = BN_new(); r = BN_new(); k = BN_new(); u = BN_new(); v = BN_new(); w = BN_new(); ctx = BN_CTX_new(); BN_rand(b, BN_num_bits(dsa->q), -1, 0); /* a */ @@ -846,7 +1050,6 @@ gen_iff( if (!temp) { BN_free(b); BN_free(r); BN_free(k); BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); - rval = -1; return (NULL); } dsa->priv_key = BN_dup(b); /* private key */ @@ -854,16 +1057,16 @@ gen_iff( /* * Here is a trial round of the protocol. First, Alice rolls - * random r (0 < r < q) and sends it to Bob. She needs only - * modulus q. + * random nonce r mod q and sends it to Bob. She needs only + * q from parameters. */ BN_rand(r, BN_num_bits(dsa->q), -1, 0); /* r */ BN_mod(r, r, dsa->q, ctx); /* - * Bob rolls random k (0 < k < q), computes y = k + b r mod q + * Bob rolls random nonce k mod q, computes y = k + b r mod q * and x = g^k mod p, then sends (y, x) to Alice. He needs - * moduli p, q and the group key b. + * p, q and b from parameters and r from Alice. */ BN_rand(k, BN_num_bits(dsa->q), -1, 0); /* k, 0 < k < q */ BN_mod(k, k, dsa->q, ctx); @@ -873,9 +1076,10 @@ gen_iff( BN_mod_exp(u, dsa->g, k, dsa->p, ctx); /* x = g^k mod p */ /* - * Alice computes g^y v^r and verifies the result is equal to x. - * She needs modulus p, generator g, and the public key v, as - * well as her original r. + * Alice verifies x = g^y v^r to confirm that Bob has group key + * b. She needs p, q, g from parameters, (y, x) from Bob and the + * original r. We omit the detail here thatt only the hash of y + * is sent. */ BN_mod_exp(v, dsa->g, v, dsa->p, ctx); /* g^y mod p */ BN_mod_exp(w, dsa->pub_key, r, dsa->p, ctx); /* v^r */ @@ -888,42 +1092,52 @@ gen_iff( BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); if (temp != 0) { DSA_free(dsa); - rval = -1; return (NULL); } /* - * Write the IFF server parameters and keys as a DSA private key - * encoded in PEM. + * Write the IFF keys as an encrypted DSA private key encoded in + * PEM. * * p modulus p * q modulus q * g generator g * priv_key b * public_key v + * kinv not used + * r not used */ - str = fheader("IFFpar", trustname); + str = fheader("IFFkey", id, groupname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); - PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, - NULL, 0, NULL, passwd2); + PEM_write_PrivateKey(str, pkey, EVP_des_cbc(), NULL, 0, NULL, + passwd1); fclose(str); if (debug) - DSA_print_fp(stdout, dsa, 0); - fslink(id, trustname); + DSA_print_fp(stderr, dsa, 0); return (pkey); } /* - * Generate Guillou-Quisquater (GQ) parameters and keys + *********************************************************************** + * * + * The following routines implement the Guillou-Quisquater (GQ) * + * identity scheme * + * * + *********************************************************************** * * The Guillou-Quisquater (GQ) identity scheme is intended for use when - * the parameters, keys and certificates are generated by this program. - * The scheme uses a certificate extension field do convey the public - * key of a particular group identified by a group key known only to - * members of the group. The scheme is self contained and independent of - * new generations of host keys and sign keys. + * the certificate can be used to convey public parameters. The scheme + * uses a X509v3 certificate extension field do convey the public key of + * a private key known only to servers. There are two kinds of files: + * encrypted server files that contain private and public values and + * nonencrypted client files that contain only public values. New + * generations of server files must be securely transmitted to all + * servers of the group; client files can be distributed by any means. + * The scheme is self contained and independent of new generations of + * host keys and sign keys. The scheme is self contained and independent + * of new generations of host keys and sign keys. * * The GQ parameters hide in a RSA cuckoo structure which uses the same * parameters. The values are used by an identity scheme based on RSA @@ -932,105 +1146,81 @@ gen_iff( * The TA rolls private random group key b as RSA exponent. These values * are known to all group members. * - * When rolling new certificates, a member recomputes the private and + * When rolling new certificates, a server recomputes the private and * public keys. The private key u is a random roll, while the public key * is the inverse obscured by the group key v = (u^-1)^b. These values * replace the private and public keys normally generated by the RSA * scheme. Alice challenges Bob to confirm identity using the protocol * described below. + * + * How it works + * + * The scheme goes like this. Both Alice and Bob have the same modulus n + * and some random b as the group key. These values are computed and + * distributed in advance via secret means, although only the group key + * b is truly secret. Each has a private random private key u and public + * key (u^-1)^b, although not necessarily the same ones. Bob and Alice + * can regenerate the key pair from time to time without affecting + * operations. The public key is conveyed on the certificate in an + * extension field; the private key is never revealed. + * + * Alice rolls new random challenge r and sends to Bob in the GQ + * request message. Bob rolls new random k, then computes y = k u^r mod + * n and x = k^b mod n and sends (y, hash(x)) to Alice in the response + * message. Besides making the response shorter, the hash makes it + * effectivey impossible for an intruder to solve for b by observing + * a number of these messages. + * + * Alice receives the response and computes y^b v^r mod n. After a bit + * of algebra, this simplifies to k^b. If the hash of this result + * matches hash(x), Alice knows that Bob has the group key b. The signed + * response binds this knowledge to Bob's private key and the public key + * previously received in his certificate. + */ +/* + * Generate Guillou-Quisquater (GQ) parameters file. */ EVP_PKEY * /* RSA cuckoo nest */ -gen_gqpar( +gen_gqkey( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ - RSA *rsa; /* GQ parameters */ + RSA *rsa; /* RSA parameters */ BN_CTX *ctx; /* BN working space */ + BIGNUM *u, *v, *g, *k, *r, *y; /* BN temps */ FILE *str; + u_int temp; /* * Generate RSA parameters for use as GQ parameters. */ fprintf(stderr, - "Generating GQ parameters (%d bits)...\n", modulus); - rsa = RSA_generate_key(modulus, 3, cb, "GQ"); + "Generating GQ parameters (%d bits)...\n", + modulus2); + rsa = RSA_generate_key(modulus2, 3, cb, "GQ"); fprintf(stderr, "\n"); if (rsa == NULL) { fprintf(stderr, "RSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); - rval = -1; return (NULL); } + ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); + g = BN_new(); k = BN_new(); r = BN_new(); y = BN_new(); /* * Generate the group key b, which is saved in the e member of - * the RSA structure. These values are distributed to all - * members of the group, but shielded from all other groups. We - * don't use all the parameters, but set the unused ones to a - * small number to minimize the file size. + * the RSA structure. The group key is transmitted to each group + * member encrypted by the member private key. */ ctx = BN_CTX_new(); BN_rand(rsa->e, BN_num_bits(rsa->n), -1, 0); /* b */ BN_mod(rsa->e, rsa->e, rsa->n, ctx); - BN_copy(rsa->d, BN_value_one()); - BN_copy(rsa->p, BN_value_one()); - BN_copy(rsa->q, BN_value_one()); - BN_copy(rsa->dmp1, BN_value_one()); - BN_copy(rsa->dmq1, BN_value_one()); - BN_copy(rsa->iqmp, BN_value_one()); - - /* - * Write the GQ parameters as a RSA private key encoded in PEM. - * The public and private keys are filled in later. - * - * n modulus n - * e group key b - * (remaining values are not used) - */ - str = fheader("GQpar", trustname); - pkey = EVP_PKEY_new(); - EVP_PKEY_assign_RSA(pkey, rsa); - PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, - NULL, 0, NULL, passwd2); - fclose(str); - if (debug) - RSA_print_fp(stdout, rsa, 0); - fslink(id, trustname); - return (pkey); -} - - -/* - * Update Guillou-Quisquater (GQ) parameters - */ -EVP_PKEY * /* RSA cuckoo nest */ -gen_gqkey( - char *id, /* file name id */ - EVP_PKEY *gqpar /* GQ parameters */ - ) -{ - EVP_PKEY *pkey; /* private key */ - RSA *rsa; /* RSA parameters */ - BN_CTX *ctx; /* BN working space */ - BIGNUM *u, *v, *g, *k, *r, *y; /* BN temps */ - FILE *str; - u_int temp; - - /* - * Generate GQ keys. Note that the group key b is the e member - * of - * the GQ parameters. - */ - fprintf(stderr, "Updating GQ keys (%d bits)...\n", modulus); - ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); - g = BN_new(); k = BN_new(); r = BN_new(); y = BN_new(); /* * When generating his certificate, Bob rolls random private key - * u. + * u, then computes inverse v = u^-1. */ - rsa = gqpar->pkey.rsa; BN_rand(u, BN_num_bits(rsa->n), -1, 0); /* u */ BN_mod(u, u, rsa->n, ctx); BN_mod_inverse(v, u, rsa->n, ctx); /* u^-1 mod n */ @@ -1053,7 +1243,6 @@ gen_gqkey( BN_free(g); BN_free(k); BN_free(r); BN_free(y); BN_CTX_free(ctx); RSA_free(rsa); - rval = -1; return (NULL); } BN_copy(rsa->p, u); /* private key */ @@ -1061,28 +1250,29 @@ gen_gqkey( /* * Here is a trial run of the protocol. First, Alice rolls - * random r (0 < r < n) and sends it to Bob. She needs only - * modulus n from the parameters. + * random nonce r mod n and sends it to Bob. She needs only n + * from parameters. */ BN_rand(r, BN_num_bits(rsa->n), -1, 0); /* r */ BN_mod(r, r, rsa->n, ctx); /* - * Bob rolls random k (0 < k < n), computes y = k u^r mod n and - * g = k^b mod n, then sends (y, g) to Alice. He needs modulus n - * from the parameters and his private key u. + * Bob rolls random nonce k mod n, computes y = k u^r mod n and + * g = k^b mod n, then sends (y, g) to Alice. He needs n, u, b + * from parameters and r from Alice. */ BN_rand(k, BN_num_bits(rsa->n), -1, 0); /* k */ BN_mod(k, k, rsa->n, ctx); BN_mod_exp(y, rsa->p, r, rsa->n, ctx); /* u^r mod n */ BN_mod_mul(y, k, y, rsa->n, ctx); /* y = k u^r mod n */ - BN_mod_exp(g, k, rsa->e, rsa->n, ctx); /* g = k^b mod n */ + BN_mod_exp(g, k, rsa->e, rsa->n, ctx); /* g = k^b mod n */ /* - * Alice computes v^r y^b mod n and verifies the result is equal - * to g. She needs modulus n, generator g and group key b from - * the parameters and Bob's public key v = (u^-1)^b from his - * certificate. + * Alice verifies g = v^r y^b mod n to confirm that Bob has + * private key u. She needs n, g from parameters, public key v = + * (u^-1)^b from the certificate, (y, g) from Bob and the + * original r. We omit the detaul here that only the hash of g + * is sent. */ BN_mod_exp(v, rsa->q, r, rsa->n, ctx); /* v^r mod n */ BN_mod_exp(y, y, rsa->e, rsa->n, ctx); /* y^b mod n */ @@ -1094,43 +1284,53 @@ gen_gqkey( BN_free(g); BN_free(k); BN_free(r); BN_free(y); if (temp != 0) { RSA_free(rsa); - rval = -1; return (NULL); } /* - * Write the GQ parameters and keys as a RSA private key encoded - * in PEM. + * Write the GQ parameter file as an encrypted RSA private key + * encoded in PEM. * * n modulus n * e group key b + * d not used * p private key u * q public key (u^-1)^b - * (remaining values are not used) + * dmp1 not used + * dmq1 not used + * iqmp not used */ - str = fheader("GQpar", trustname); + BN_copy(rsa->d, BN_value_one()); + BN_copy(rsa->dmp1, BN_value_one()); + BN_copy(rsa->dmq1, BN_value_one()); + BN_copy(rsa->iqmp, BN_value_one()); + str = fheader("GQkey", id, groupname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); - PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, - NULL, 0, NULL, passwd2); + PEM_write_PrivateKey(str, pkey, EVP_des_cbc(), NULL, 0, NULL, + passwd1); fclose(str); if (debug) - RSA_print_fp(stdout, rsa, 0); - fslink(id, trustname); + RSA_print_fp(stderr, rsa, 0); return (pkey); } /* - * Generate Mu-Varadharajan (MV) parameters and keys + *********************************************************************** + * * + * The following routines implement the Mu-Varadharajan (MV) identity * + * scheme * + * * + *********************************************************************** * - * The Mu-Varadharajan (MV) cryptosystem is useful when servers - * broadcast messages to clients, but clients never send messages to - * servers. There is one encryption key for the server and a separate - * decryption key for each client. It operates something like a + * The Mu-Varadharajan (MV) cryptosystem was originally intended when + * servers broadcast messages to clients, but clients never send + * messages to servers. There is one encryption key for the server and a + * separate decryption key for each client. It operated something like a * pay-per-view satellite broadcasting system where the session key is * encrypted by the broadcaster and the decryption keys are held in a - * tamperproof set-top box. We don't use it this way, but read on. + * tamperproof set-top box. * * The MV parameters and private encryption key hide in a DSA cuckoo * structure which uses the same parameters, but generated in a @@ -1140,18 +1340,16 @@ gen_gqkey( * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001, * 223-231. The paper has significant errors and serious omissions. * - * Let q be the product of n distinct primes s'[j] (j = 1...n), where - * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so - * that q and each s'[j] divide p - 1 and p has M = n * m + 1 + * Let q be the product of n distinct primes s1[j] (j = 1...n), where + * each s1[j] has m significant bits. Let p be a prime p = 2 * q + 1, so + * that q and each s1[j] divide p - 1 and p has M = n * m + 1 * significant bits. Let g be a generator of Zp; that is, gcd(g, p - 1) * = 1 and g^q = 1 mod p. We do modular arithmetic over Zq and then * project into Zp* as exponents of g. Sometimes we have to compute an * inverse b^-1 of random b in Zq, but for that purpose we require * gcd(b, q) = 1. We expect M to be in the 500-bit range and n - * relatively small, like 30. Associated with each s'[j] is an element - * s[j] such that s[j] s'[j] = s'[j] mod q. We find s[j] as the quotient - * (q + s'[j]) / s'[j]. These are the parameters of the scheme and they - * are expensive to compute. + * relatively small, like 30. These are the parameters of the scheme and + * they are expensive to compute. * * We set up an instance of the scheme as follows. A set of random * values x[j] mod q (j = 1...n), are generated as the zeros of a @@ -1162,37 +1360,52 @@ gen_gqkey( * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used * to construct the decryption keys. The devil is in the details. * - * This routine generates a private encryption file including the - * private encryption key E and public key (gbar, ghat). It then - * generates decryption files including the private key (xbar[j], - * xhat[j]) for each client. E is a permutation that encrypts a block - * y = E x. The jth client computes the inverse permutation E^-1 = - * gbar^xhat[j] ghat^xbar[j] and decrypts the block x = E^-1 y. + * This routine generates a private server encryption file including the + * private encryption key E and partial decryption keys gbar and ghat. + * It then generates public client decryption files including the public + * keys xbar[j] and xhat[j] for each client j. The partial decryption + * files are used to compute the inverse of E. These values are suitably + * blinded so secrets are not revealed. * * The distinguishing characteristic of this scheme is the capability to * revoke keys. Included in the calculation of E, gbar and ghat is the - * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is + * product s = prod(s1[j]) (j = 1...n) above. If the factor s1[j] is * subsequently removed from the product and E, gbar and ghat * recomputed, the jth client will no longer be able to compute E^-1 and - * thus unable to decrypt the block. + * thus unable to decrypt the messageblock. + * + * How it works + * + * The scheme goes like this. Bob has the server values (p, E, q, gbar, + * ghat) and Alice has the client values (p, xbar, xhat). + * + * Alice rolls new random nonce r mod p and sends to Bob in the MV + * request message. Bob rolls random nonce k mod q, encrypts y = r E^k + * mod p and sends (y, gbar^k, ghat^k) to Alice. + * + * Alice receives the response and computes the inverse (E^k)^-1 from + * the partial decryption keys gbar^k, ghat^k, xbar and xhat. She then + * decrypts y and verifies it matches the original r. The signed + * response binds this knowledge to Bob's private key and the public key + * previously received in his certificate. */ EVP_PKEY * /* DSA cuckoo nest */ -gen_mv( - char *id /* file name id */ +gen_mvkey( + char *id, /* file name id */ + EVP_PKEY **evpars /* parameter list pointer */ ) { - EVP_PKEY *pkey, *pkey1; /* private key */ - DSA *dsa; /* DSA parameters */ - DSA *sdsa; /* DSA parameters */ + EVP_PKEY *pkey, *pkey1; /* private keys */ + DSA *dsa, *dsa2, *sdsa; /* DSA parameters */ BN_CTX *ctx; /* BN working space */ - BIGNUM **x; /* polynomial zeros vector */ - BIGNUM **a; /* polynomial coefficient vector */ - BIGNUM **g; /* public key vector */ - BIGNUM **s, **s1; /* private enabling keys */ - BIGNUM **xbar, **xhat; /* private keys vector */ + BIGNUM *a[MVMAX]; /* polynomial coefficient vector */ + BIGNUM *g[MVMAX]; /* public key vector */ + BIGNUM *s1[MVMAX]; /* private enabling keys */ + BIGNUM *x[MVMAX]; /* polynomial zeros vector */ + BIGNUM *xbar[MVMAX], *xhat[MVMAX]; /* private keys vector */ BIGNUM *b; /* group key */ BIGNUM *b1; /* inverse group key */ - BIGNUM *ss; /* enabling key */ + BIGNUM *s; /* enabling key */ BIGNUM *biga; /* master encryption key */ BIGNUM *bige; /* session encryption key */ BIGNUM *gbar, *ghat; /* public key */ @@ -1200,39 +1413,34 @@ gen_mv( int i, j, n; FILE *str; u_int temp; - char ident[20]; /* * Generate MV parameters. * * The object is to generate a multiplicative group Zp* modulo a * prime p and a subset Zq mod q, where q is the product of n - * distinct primes s'[j] (j = 1...n) and q divides p - 1. We - * first generate n distinct primes, which may have to be - * regenerated later. As a practical matter, it is tough to find - * more than 31 distinct primes for modulus 512 or 61 primes for - * modulus 1024. The latter can take several hundred iterations + * distinct primes s1[j] (j = 1...n) and q divides p - 1. We + * first generate n m-bit primes, where the product n m is in + * the order of 512 bits. One or more of these may have to be + * replaced later. As a practical matter, it is tough to find + * more than 31 distinct primes for 512 bits or 61 primes for + * 1024 bits. The latter can take several hundred iterations * and several minutes on a Sun Blade 1000. */ n = nkeys; fprintf(stderr, "Generating MV parameters for %d keys (%d bits)...\n", n, - modulus / n); + modulus2 / n); ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); w = BN_new(); b = BN_new(); b1 = BN_new(); dsa = DSA_new(); - dsa->p = BN_new(); - dsa->q = BN_new(); - dsa->g = BN_new(); - s = malloc((n + 1) * sizeof(BIGNUM)); - s1 = malloc((n + 1) * sizeof(BIGNUM)); - for (j = 1; j <= n; j++) - s1[j] = BN_new(); + dsa->p = BN_new(); dsa->q = BN_new(); dsa->g = BN_new(); + dsa->priv_key = BN_new(); dsa->pub_key = BN_new(); temp = 0; for (j = 1; j <= n; j++) { + s1[j] = BN_new(); while (1) { - fprintf(stderr, "Birthdays %d\r", temp); - BN_generate_prime(s1[j], modulus / n, 0, NULL, + BN_generate_prime(s1[j], modulus2 / n, 0, NULL, NULL, NULL, NULL); for (i = 1; i < j; i++) { if (BN_cmp(s1[i], s1[j]) == 0) @@ -1243,14 +1451,14 @@ gen_mv( temp++; } } - fprintf(stderr, "Birthday keys rejected %d\n", temp); + fprintf(stderr, "Birthday keys regenerated %d\n", temp); /* * Compute the modulus q as the product of the primes. Compute * the modulus p as 2 * q + 1 and test p for primality. If p * is composite, replace one of the primes with a new distinct * one and try again. Note that q will hardly be a secret since - * we have to reveal p to servers and clients. However, + * we have to reveal p to servers, but not clients. However, * factoring q to find the primes should be adequately hard, as * this is the same problem considered hard in RSA. Question: is * it as hard to find n small prime factors totalling n bits as @@ -1259,7 +1467,6 @@ gen_mv( */ temp = 0; while (1) { - fprintf(stderr, "Duplicate keys rejected %d\r", ++temp); BN_one(dsa->q); for (j = 1; j <= n; j++) BN_mul(dsa->q, dsa->q, s1[j], ctx); @@ -1270,9 +1477,10 @@ gen_mv( NULL)) break; + temp++; j = temp % n + 1; while (1) { - BN_generate_prime(u, modulus / n, 0, 0, NULL, + BN_generate_prime(u, modulus2 / n, 0, 0, NULL, NULL, NULL); for (i = 1; i <= n; i++) { if (BN_cmp(u, s1[i]) == 0) @@ -1283,12 +1491,12 @@ gen_mv( } BN_copy(s1[j], u); } - fprintf(stderr, "Duplicate keys rejected %d\n", temp); + fprintf(stderr, "Defective keys regenerated %d\n", temp); /* * Compute the generator g using a random roll such that * gcd(g, p - 1) = 1 and g^q = 1. This is a generator of p, not - * q. + * q. This may take several iterations. */ BN_copy(v, dsa->p); BN_sub_word(v, 1); @@ -1305,29 +1513,17 @@ gen_mv( } /* - * Compute s[j] such that s[j] * s'[j] = s'[j] for all j. The - * easy way to do this is to compute q + s'[j] and divide the - * result by s'[j]. Exercise for the student: prove the - * remainder is always zero. - */ - for (j = 1; j <= n; j++) { - s[j] = BN_new(); - BN_add(s[j], dsa->q, s1[j]); - BN_div(s[j], u, s[j], s1[j], ctx); - } - - /* * Setup is now complete. Roll random polynomial roots x[j] - * (0 < x[j] < q) for all j. While it may not be strictly + * (j = 1...n) for all j. While it may not be strictly * necessary, Make sure each root has no factors in common with * q. */ fprintf(stderr, "Generating polynomial coefficients for %d roots (%d bits)\n", n, BN_num_bits(dsa->q)); - x = malloc((n + 1) * sizeof(BIGNUM)); for (j = 1; j <= n; j++) { x[j] = BN_new(); + while (1) { BN_rand(x[j], BN_num_bits(dsa->q), 0, 0); BN_mod(x[j], x[j], dsa->q, ctx); @@ -1342,9 +1538,9 @@ gen_mv( * expansion of root products (x - x[j]) mod q for all j. The * method is a present from Charlie Boncelet. */ - a = malloc((n + 1) * sizeof(BIGNUM)); for (i = 0; i <= n; i++) { a[i] = BN_new(); + BN_one(a[i]); } for (j = 1; j <= n; j++) { @@ -1362,18 +1558,17 @@ gen_mv( /* * Generate g[i] = g^a[i] mod p for all i and the generator g. */ - fprintf(stderr, "Generating g[i] parameters\n"); - g = malloc((n + 1) * sizeof(BIGNUM)); for (i = 0; i <= n; i++) { g[i] = BN_new(); + BN_mod_exp(g[i], dsa->g, a[i], dsa->p, ctx); } /* - * Verify prod(g[i]^(a[i] x[j]^i)) = 1 for all i, j; otherwise, - * exit. Note the a[i] x[j]^i exponent is computed mod q, but - * the g[i] is computed mod p. also note the expression given in - * the paper is incorrect. + * Verify prod(g[i]^(a[i] x[j]^i)) = 1 for all i, j. Note the + * a[i] x[j]^i exponent is computed mod q, but the g[i] is + * computed mod p. also note the expression given in the paper + * is incorrect. */ temp = 1; for (j = 1; j <= n; j++) { @@ -1392,7 +1587,6 @@ gen_mv( "Confirm prod(g[i]^(x[j]^i)) = 1 for all i, j: %s\n", temp ? "yes" : "no"); if (!temp) { - rval = -1; return (NULL); } @@ -1401,6 +1595,7 @@ gen_mv( * since it is expensive to compute. */ biga = BN_new(); + BN_one(biga); for (j = 1; j <= n; j++) { for (i = 0; i < n; i++) { @@ -1413,7 +1608,7 @@ gen_mv( /* * Roll private random group key b mod q (0 < b < q), where - * gcd(b, q) = 1 to guarantee b^1 exists, then compute b^-1 + * gcd(b, q) = 1 to guarantee b^-1 exists, then compute b^-1 * mod q. If b is changed, the client keys must be recomputed. */ while (1) { @@ -1427,14 +1622,18 @@ gen_mv( /* * Make private client keys (xbar[j], xhat[j]) for all j. Note - * that the keys for the jth client involve s[j], but not s'[j] - * or the product s = prod(s'[j]) mod q, which is the enabling - * key. + * that the keys for the jth client do not s1[j] or the product + * s1[j]) (j = 1...n) which is q by construction. + * + * Compute the factor w such that w s1[j] = s1[j] for all j. The + * easy way to do this is to compute (q + s1[j]) / s1[j]. + * Exercise for the student: prove the remainder is always zero. */ - xbar = malloc((n + 1) * sizeof(BIGNUM)); - xhat = malloc((n + 1) * sizeof(BIGNUM)); for (j = 1; j <= n; j++) { xbar[j] = BN_new(); xhat[j] = BN_new(); + + BN_add(w, dsa->q, s1[j]); + BN_div(w, u, w, s1[j], ctx); BN_zero(xbar[j]); BN_set_word(v, n); for (i = 1; i <= n; i++) { @@ -1445,154 +1644,168 @@ gen_mv( } BN_mod_mul(xbar[j], xbar[j], b1, dsa->q, ctx); BN_mod_exp(xhat[j], x[j], v, dsa->q, ctx); - BN_mod_mul(xhat[j], xhat[j], s[j], dsa->q, ctx); + BN_mod_mul(xhat[j], xhat[j], w, dsa->q, ctx); } /* - * The enabling key is initially q by construction. We can - * revoke client j by dividing q by s'[j]. The quotient becomes - * the enabling key s. Note we always have to revoke one key; - * otherwise, the plaintext and cryptotext would be identical. + * We revoke client j by dividing q by s1[j]. The quotient + * becomes the enabling key s. Note we always have to revoke + * one key; otherwise, the plaintext and cryptotext would be + * identical. For the present there are no provisions to revoke + * additional keys, so we sail on with only token revocations. */ - ss = BN_new(); - BN_copy(ss, dsa->q); - BN_div(ss, u, dsa->q, s1[n], ctx); + s = BN_new(); + + BN_copy(s, dsa->q); + BN_div(s, u, s, s1[n], ctx); /* - * Make private server encryption key E = A^s and public server - * keys gbar = g^s mod p and ghat = g^(s b) mod p. The (gbar, - * ghat) is the public key provided to the server, which uses it - * to compute the session encryption key and public key included - * in its messages. These values must be regenerated if the - * enabling key is changed. + * For each combination of clients to be revoked, make private + * encryption key E = A^s and partial decryption keys gbar = g^s + * and ghat = g^(s b), all mod p. The servers use these keys to + * compute the session encryption key and partial decryption + * keys. These values must be regenerated if the enabling key is + * changed. */ bige = BN_new(); gbar = BN_new(); ghat = BN_new(); - BN_mod_exp(bige, biga, ss, dsa->p, ctx); - BN_mod_exp(gbar, dsa->g, ss, dsa->p, ctx); - BN_mod_mul(v, ss, b, dsa->q, ctx); - BN_mod_exp(ghat, dsa->g, v, dsa->p, ctx); + BN_mod_exp(bige, biga, s, dsa->p, ctx); + BN_mod_exp(gbar, dsa->g, s, dsa->p, ctx); + BN_mod_mul(v, s, b, dsa->q, ctx); + BN_mod_exp(ghat, dsa->g, v, dsa->p, ctx); + /* - * We produce the key media in three steps. The first step is to - * generate the private values that do not depend on the - * enabling key. These include the server values p, q, g, b, A - * and the client values s'[j], xbar[j] and xhat[j] for each j. - * The p, xbar[j] and xhat[j] values are encoded in private - * files which are distributed to respective clients. The p, q, - * g, A and s'[j] values (will be) written to a secret file to - * be read back later. - * - * The secret file (will be) read back at some later time to - * enable/disable individual keys and generate/regenerate the - * enabling key s. The p, q, E, gbar and ghat values are written - * to a secret file to be read back later by the server. + * Notes: We produce the key media in three steps. The first + * step is to generate the system parameters p, q, g, b, A and + * the enabling keys s1[j]. Associated with each s1[j] are + * parameters xbar[j] and xhat[j]. All of these parameters are + * retained in a data structure protecteted by the trusted-agent + * password. The p, xbar[j] and xhat[j] paremeters are + * distributed to the j clients. When the client keys are to be + * activated, the enabled keys are multipied together to form + * the master enabling key s. This and the other parameters are + * used to compute the server encryption key E and the partial + * decryption keys gbar and ghat. * - * The server reads the secret file and rolls the session key - * k, which is used only once, then computes E^k, gbar^k and - * ghat^k. The E^k is the session encryption key. The encrypted - * data, gbar^k and ghat^k are transmtted to clients in an - * extension field. The client receives the message and computes - * x = (gbar^k)^xbar[j] (ghat^k)^xhat[j], finds the session - * encryption key E^k as the inverse x^-1 and decrypts the data. + * In the identity exchange the client rolls random r and sends + * it to the server. The server rolls random k, which is used + * only once, then computes the session key E^k and partial + * decryption keys gbar^k and ghat^k. The server sends the + * encrypted r along with gbar^k and ghat^k to the client. The + * client completes the decryption and verifies it matches r. */ - BN_copy(dsa->g, bige); - dsa->priv_key = BN_dup(gbar); - dsa->pub_key = BN_dup(ghat); - /* - * Write the MV server parameters and keys as a DSA private key - * encoded in PEM. + * Write the MV trusted-agent parameters and keys as a DSA + * private key encoded in PEM. * * p modulus p - * q modulus q (used only to generate k) - * g E mod p - * priv_key gbar mod p - * pub_key ghat mod p + * q modulus q + * g generator g + * priv_key A mod p + * pub_key b mod q + * (remaining values are not used) */ - str = fheader("MVpar", trustname); + i = 0; + str = fheader("MVta", "mvta", groupname); + fprintf(stderr, "Generating MV trusted-authority keys\n"); + BN_copy(dsa->priv_key, biga); + BN_copy(dsa->pub_key, b); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); - PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, - NULL, 0, NULL, passwd2); - fclose(str); + PEM_write_PrivateKey(str, pkey, EVP_des_cbc(), NULL, 0, NULL, + passwd1); + evpars[i++] = pkey; if (debug) - DSA_print_fp(stdout, dsa, 0); - fslink(id, trustname); + DSA_print_fp(stderr, dsa, 0); /* - * Write the parameters and private key (xbar[j], xhat[j]) for - * all j as a DSA private key encoded in PEM. It is used only by - * the designated recipient(s) who pay a suitably outrageous fee - * for its use. + * Append the MV server parameters and keys as a DSA key encoded + * in PEM. + * + * p modulus p + * q modulus q (used only when generating k) + * g bige + * priv_key gbar + * pub_key ghat + * (remaining values are not used) + */ + fprintf(stderr, "Generating MV server keys\n"); + dsa2 = DSA_new(); + dsa2->p = BN_dup(dsa->p); + dsa2->q = BN_dup(dsa->q); + dsa2->g = BN_dup(bige); + dsa2->priv_key = BN_dup(gbar); + dsa2->pub_key = BN_dup(ghat); + pkey1 = EVP_PKEY_new(); + EVP_PKEY_assign_DSA(pkey1, dsa2); + PEM_write_PrivateKey(str, pkey1, EVP_des_cbc(), NULL, 0, NULL, + passwd1); + evpars[i++] = pkey1; + if (debug) + DSA_print_fp(stderr, dsa2, 0); + + /* + * Append the MV client parameters for each client j as DSA keys + * encoded in PEM. + * + * p modulus p + * priv_key xbar[j] mod q + * pub_key xhat[j] mod q + * (remaining values are not used) */ - sdsa = DSA_new(); - sdsa->p = BN_dup(dsa->p); - sdsa->q = BN_dup(BN_value_one()); - sdsa->g = BN_dup(BN_value_one()); - sdsa->priv_key = BN_new(); - sdsa->pub_key = BN_new(); + fprintf(stderr, "Generating %d MV client keys\n", n); for (j = 1; j <= n; j++) { - BN_copy(sdsa->priv_key, xbar[j]); - BN_copy(sdsa->pub_key, xhat[j]); - BN_mod_exp(v, dsa->priv_key, sdsa->pub_key, dsa->p, + sdsa = DSA_new(); + + sdsa->p = BN_dup(dsa->p); + sdsa->q = BN_dup(BN_value_one()); + sdsa->g = BN_dup(BN_value_one()); + sdsa->priv_key = BN_dup(xbar[j]); + sdsa->pub_key = BN_dup(xhat[j]); + pkey1 = EVP_PKEY_new(); + EVP_PKEY_set1_DSA(pkey1, sdsa); + PEM_write_PrivateKey(str, pkey1, EVP_des_cbc(), NULL, 0, + NULL, passwd1); + evpars[i++] = pkey1; + if (debug) + DSA_print_fp(stderr, sdsa, 0); + + /* + * The product gbar^k)^xbar[j] (ghat^k)^xhat[j] and E + * are inverses of each other. We check that the product + * is one for each client except the ones that have been + * revoked. + */ + BN_mod_exp(v, dsa2->priv_key, sdsa->pub_key, dsa->p, ctx); - BN_mod_exp(u, dsa->pub_key, sdsa->priv_key, dsa->p, + BN_mod_exp(u, dsa2->pub_key, sdsa->priv_key, dsa->p, ctx); BN_mod_mul(u, u, v, dsa->p, ctx); - BN_mod_mul(u, u, dsa->g, dsa->p, ctx); - BN_free(xbar[j]); BN_free(xhat[j]); - BN_free(x[j]); BN_free(s[j]); BN_free(s1[j]); + BN_mod_mul(u, u, bige, dsa->p, ctx); if (!BN_is_one(u)) { fprintf(stderr, "Revoke key %d\n", j); continue; } - - /* - * Write the client parameters as a DSA private key - * encoded in PEM. We don't make links for these. - * - * p modulus p - * priv_key xbar[j] mod q - * pub_key xhat[j] mod q - * (remaining values are not used) - */ - sprintf(ident, "MVkey%d", j); - str = fheader(ident, trustname); - pkey1 = EVP_PKEY_new(); - EVP_PKEY_set1_DSA(pkey1, sdsa); - PEM_write_PrivateKey(str, pkey1, passwd2 ? - EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); - fclose(str); - fprintf(stderr, "ntpkey_%s_%s.%lu\n", ident, trustname, - epoch + JAN_1970); - if (debug) - DSA_print_fp(stdout, sdsa, 0); - EVP_PKEY_free(pkey1); } + evpars[i++] = NULL; + fclose(str); /* * Free the countries. */ for (i = 0; i <= n; i++) { - BN_free(a[i]); - BN_free(g[i]); + BN_free(a[i]); BN_free(g[i]); + } + for (j = 1; j <= n; j++) { + BN_free(x[j]); BN_free(xbar[j]); BN_free(xhat[j]); + BN_free(s1[j]); } - BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); - BN_free(b); BN_free(b1); BN_free(biga); BN_free(bige); - BN_free(ss); BN_free(gbar); BN_free(ghat); - DSA_free(sdsa); - - /* - * Free the world. - */ - free(x); free(a); free(g); free(s); free(s1); - free(xbar); free(xhat); return (pkey); } /* - * Generate X509v3 scertificate. + * Generate X509v3 certificate. * * The certificate consists of the version number, serial number, * validity interval, issuer name, subject name and public key. For a @@ -1607,7 +1820,8 @@ x509 ( EVP_PKEY *pkey, /* generic signature algorithm */ const EVP_MD *md, /* generic digest algorithm */ char *gqpub, /* identity extension (hex string) */ - char *exten /* private cert extension */ + char *exten, /* private cert extension */ + char *name /* subject/issuer namd */ ) { X509 *cert; /* X509 certificate */ @@ -1622,31 +1836,29 @@ x509 ( * Generate X509 self-signed certificate. * * Set the certificate serial to the NTP seconds for grins. Set - * the version to 3. Set the subject name and issuer name to the - * subject name in the request. Set the initial validity to the - * current time and the final validity one year hence. + * the version to 3. Set the initial validity to the current + * time and the finalvalidity one year hence. */ - id = OBJ_nid2sn(md->pkey_type); - fprintf(stderr, "Generating certificate %s\n", id); + id = OBJ_nid2sn(md->pkey_type); + fprintf(stderr, "Generating new certificate %s %s\n", name, id); cert = X509_new(); X509_set_version(cert, 2L); serial = ASN1_INTEGER_new(); - ASN1_INTEGER_set(serial, epoch + JAN_1970); + ASN1_INTEGER_set(serial, (long)epoch + JAN_1970); X509_set_serialNumber(cert, serial); ASN1_INTEGER_free(serial); X509_time_adj(X509_get_notBefore(cert), 0L, &epoch); X509_time_adj(X509_get_notAfter(cert), YEAR, &epoch); subj = X509_get_subject_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, - (unsigned char *) hostname, strlen(hostname), -1, 0); + (unsigned char *) name, strlen(name), -1, 0); subj = X509_get_issuer_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, - (unsigned char *) trustname, strlen(trustname), -1, 0); + (unsigned char *) name, strlen(name), -1, 0); if (!X509_set_pubkey(cert, pkey)) { fprintf(stderr, "Assign key fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); - rval = -1; return (0); } @@ -1666,7 +1878,6 @@ x509 ( if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); - rval = -1; return (0); } X509_EXTENSION_free(ex); @@ -1680,7 +1891,6 @@ x509 ( if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); - rval = -1; return (0); } X509_EXTENSION_free(ex); @@ -1696,7 +1906,6 @@ x509 ( fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); - rval = -1; return (0); } X509_EXTENSION_free(ex); @@ -1718,7 +1927,6 @@ x509 ( fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); - rval = -1; return (0); } X509_EXTENSION_free(ex); @@ -1728,11 +1936,10 @@ x509 ( * Sign and verify. */ X509_sign(cert, pkey, md); - if (!X509_verify(cert, pkey)) { + if (X509_verify(cert, pkey) <= 0) { fprintf(stderr, "Verify %s certificate fails\n%s\n", id, ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); - rval = -1; return (0); } @@ -1740,17 +1947,16 @@ x509 ( * Write the certificate encoded in PEM. */ sprintf(pathbuf, "%scert", id); - str = fheader(pathbuf, hostname); + str = fheader(pathbuf, "cert", hostname); PEM_write_X509(str, cert); fclose(str); if (debug) - X509_print_fp(stdout, cert); + X509_print_fp(stderr, cert); X509_free(cert); - fslink("cert", hostname); return (1); } -#if 0 /* asn2ntp is not used */ +#if 0 /* asn2ntp is used only with commercial certificates */ /* * asn2ntp - convert ASN1_TIME time structure to NTP time */ @@ -1840,51 +2046,38 @@ genkey( return (gen_dsa(id)); fprintf(stderr, "Invalid %s key type %s\n", id, type); - rval = -1; return (NULL); } #endif /* OPENSSL */ /* - * Generate file header + * Generate file header and link */ FILE * fheader ( - const char *id, /* file name id */ - const char *name /* owner name */ + const char *file, /* file name id */ + const char *ulink, /* linkname */ + const char *owner /* owner name */ ) { FILE *str; /* file handle */ + char linkname[MAXFILENAME]; /* link name */ + int temp; - sprintf(filename, "ntpkey_%s_%s.%lu", id, name, epoch + + sprintf(filename, "ntpkey_%s_%s.%lu", file, owner, epoch + JAN_1970); if ((str = fopen(filename, "w")) == NULL) { perror("Write"); exit (-1); } - fprintf(str, "# %s\n# %s", filename, ctime(&epoch)); - return (str); -} - - -/* - * Generate symbolic links - */ -void -fslink( - const char *id, /* file name id */ - const char *name /* owner name */ - ) -{ - char linkname[MAXFILENAME]; /* link name */ - int temp; - - sprintf(linkname, "ntpkey_%s_%s", id, name); + sprintf(linkname, "ntpkey_%s_%s", ulink, owner); remove(linkname); temp = symlink(filename, linkname); if (temp < 0) - perror(id); - fprintf(stderr, "Generating new %s file and link\n", id); + perror(file); + fprintf(stderr, "Generating new %s file and link\n", ulink); fprintf(stderr, "%s->%s\n", linkname, filename); + fprintf(str, "# %s\n# %s\n", filename, ctime(&epoch)); + return (str); } |