From: Harlan Stenn Date: Sat, 14 Dec 2002 20:24:21 +0000 (-0500) Subject: Cleanup and new stuff from Dave Mills. X-Git-Tag: NTP_4_1_73~20^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bb145f5230e3e18dfda98a80c1f2052e2b3914f5;p=thirdparty%2Fntp.git Cleanup and new stuff from Dave Mills. bk: 3dfb9375N18uELdPUMVHhJNdrq6CmQ --- diff --git a/include/ntp.h b/include/ntp.h index 14568486bf..55b2cec367 100644 --- a/include/ntp.h +++ b/include/ntp.h @@ -385,7 +385,7 @@ struct peer { #define FLAG_BURST 0x0100 /* burst mode */ #define FLAG_IBURST 0x0200 /* initial burst mode */ #define FLAG_NOSELECT 0x0400 /* this is a "noselect" peer */ -#define FLAG_ASSOC 0x0800 /* autokey reqeust */ +#define FLAG_ASSOC 0x0800 /* autokey request */ /* * Definitions for the clear() routine. We use memset() to clear diff --git a/include/ntpd.h b/include/ntpd.h index 12e6cc62ec..a5664540ae 100644 --- a/include/ntpd.h +++ b/include/ntpd.h @@ -146,6 +146,7 @@ extern void key_expire P((struct peer *)); extern void crypto_update P((void)); extern void crypto_config P((int, char *)); extern void crypto_setup P((void)); +extern u_int crypto_ident P((struct peer *)); extern struct exten *crypto_args P((struct peer *, u_int, u_char *)); extern int crypto_public P((struct peer *, u_char *, u_int)); extern void value_free P((struct value *)); diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c index 0ddd476793..5fc2b282db 100644 --- a/ntpd/ntp_crypto.c +++ b/ntpd/ntp_crypto.c @@ -1614,6 +1614,65 @@ crypto_encrypt( } +/* + * crypto_ident - construct extension field for identity scheme + * + * This routine determines which identity scheme is in use and + * constructs an extension field for that scheme. + */ +u_int +crypto_ident( + struct peer *peer /* peer structure pointer */ + ) +{ + char filename[MAXFILENAME + 1]; + tstamp_t fstamp; + + /* + * If the server identity has already been verified, no further + * action is necessary. Otherwise, try to load the identity file + * containing the scheme parameters. If the file does not exist, + * not to worry. Note we can't get here unless the trusted + * certificate has been found and the CRYPTO_FLAG_VALID bit is + * set, so the certificate issuer is valid. + */ + if (peer->crypto & CRYPTO_FLAG_VRFY) + return (0); + + if (peer->ident_pkey != NULL) + EVP_PKEY_free(peer->ident_pkey); + if (peer->crypto & CRYPTO_FLAG_GQ) { + snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", + peer->issuer); + peer->ident_pkey = crypto_key(filename, &fstamp); + if (peer->ident_pkey != NULL) + return (CRYPTO_GQ); + } + if (peer->crypto & CRYPTO_FLAG_IFF) { + snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", + peer->issuer); + peer->ident_pkey = crypto_key(filename, &fstamp); + if (peer->ident_pkey != NULL) + return (CRYPTO_IFF); + } + if (peer->crypto & CRYPTO_FLAG_MV) { + snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", + peer->issuer); + peer->ident_pkey = crypto_key(filename, &fstamp); + if (peer->ident_pkey != NULL) + return (CRYPTO_MV); + } + + /* + * No compatible identity scheme is available. Use the default + * TC scheme. + */ + msyslog(LOG_ERR, + "crypto_ident: no compatible identity scheme found"); + return (0); +} + + /* * crypto_args - construct extension field from arguments * @@ -1919,33 +1978,31 @@ bighash( * keys, sign keys and certificates. * * The IFF identity scheme is based on DSA cryptography and algorithms - * described in Stimson p. 285. The IFF values hide in a DSA cuckoo + * described in Stinson p. 285. The IFF values hide in a DSA cuckoo * structure, but only the primes and generator are used. The p is a * 512-bit prime, 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 random group key - * disguised as a DSA structure member, then computes public key g^(q - - * a). These values are shared only among group members and never - * revealed in messages. Alice challenges Bob to confirm identity using - * the protocol described below. + * of 1 mod p; that is, g^q = 1 mod p. The TA rolls primvate random + * group key b disguised as a DSA structure member, then computes public + * key g^(q - b). These values are shared only among group members and + * never revealed in messages. 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 prime p, - * prime q, generator g and some random a as the group key. They also - * have v = g^(q - a) mod p as the public key. These values are - * computed and distributed in advance via secret means, although only - * the group key a is truly secret. + * 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 = g^(q - a) mod p to Alice. * * Alice rolls new random challenge r and sends to Bob in the IFF - * request message. Bob rolls new random k, then computes y = k + a r + * request message. Bob rolls new random k, 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 a by observing + * 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 x, Alice knows that Bob has the group key a. The signed + * 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. * @@ -1954,6 +2011,7 @@ bighash( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key + * XEVNT_ID bad or missing identity parameters */ static int crypto_alice( @@ -1964,30 +2022,14 @@ crypto_alice( DSA *dsa; /* IFF parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ - char filename[MAXFILENAME + 1]; tstamp_t tstamp; - tstamp_t fstamp; u_int len; /* - * If the IFF parameters are not valid or there is no trusted - * host, something awful happened. Otherwise, load the identity - * file containing the scheme parameters. + * The identity parameters must have correct format and content. */ - if (!(crypto_flags & CRYPTO_FLAG_IFF) || peer->issuer == NULL) { - msyslog(LOG_ERR, "crypto_alice: IFF unavailable"); - return (XEVNT_PUB); - } - if (peer->ident_pkey != NULL) - EVP_PKEY_free(peer->ident_pkey); - snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", peer->issuer); - peer->ident_pkey = crypto_key(filename, &fstamp); - if (peer->ident_pkey == NULL) { - msyslog(LOG_ERR, - "crypto_alice: file %s not found or corrupt", - filename); - return (XEVNT_PUB); - } + if (peer->ident_pkey == NULL) + return (XEVNT_ID); if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_ERR, "crypto_alice: IFF defective key"); return (XEVNT_PUB); @@ -2072,15 +2114,15 @@ crypto_bob( } /* - * Bob rolls random k (0 < k < q), computes y = k + a r mod q - * and g = g^k mod p, then sends (y, hash(g)) to Alice. + * Bob rolls random k (0 < k < q), computes y = k + b r mod q + * and x = g^k mod p, then sends (y, hash(x)) to Alice. */ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new(); sdsa = DSA_SIG_new(); BN_rand(bk, len * 8, -1, 1); /* k */ - BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* a r mod q */ + BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* b r mod q */ BN_add(bn, bn, bk); - BN_mod(bn, bn, dsa->q, bctx); /* k + a r mod q */ + BN_mod(bn, bn, dsa->q, bctx); /* k + b r mod q */ sdsa->r = BN_dup(bn); BN_mod_exp(bk, dsa->g, bk, dsa->p, bctx); /* g^k mod p */ bighash(bk, bk); @@ -2126,7 +2168,7 @@ crypto_bob( * Returns * XEVNT_OK success * XEVNT_PUB bad or missint public key - * XEVNT_ID bad or missing identification + * XEVNT_ID bad or missing identity parameters */ int crypto_iff( @@ -2146,8 +2188,7 @@ crypto_iff( * If the IFF parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ - if (!(crypto_flags & CRYPTO_FLAG_IFF) || peer->ident_pkey == - NULL) { + if (peer->ident_pkey == NULL) { msyslog(LOG_ERR, "crypto_iff: IFF unavailable"); return (XEVNT_PUB); } @@ -2161,7 +2202,7 @@ crypto_iff( } /* - * Extract the k + a r and g^k values from the response. + * Extract the k + b r and g^k values from the response. */ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new(); len = ntohl(ep->vallen); @@ -2173,16 +2214,14 @@ crypto_iff( } /* - * Compute g^(k + a r) g^(q - a)r mod p. Remember, a is the - * private key known only to Bob and g^(q - a) is the public key - * needed only by Alice. + * Compute g^(k + b r) g^(q - b)r mod p. */ BN_mod_exp(bn, dsa->pub_key, peer->iffval, dsa->p, bctx); BN_mod_exp(bk, dsa->g, sdsa->r, dsa->p, bctx); BN_mod_mul(bn, bn, bk, dsa->p, bctx); /* - * The result should match the hash of g^k mod p. + * Verify the hash of the result matches hash(x). */ bighash(bn, bn); temp = BN_cmp(bn, sdsa->s); @@ -2212,11 +2251,11 @@ crypto_iff( * generations of host keys, sign keys and certificates. * * The GQ identity scheme is based on RSA cryptography and algorithms - * described in Stimson p. 300 (with errors). The GQ values hide in a + * described in Stinson p. 300 (with errors). The GQ values hide in a * RSA cuckoo structure, but only the modulus is used. The 512-bit * public modulus is n = p q, where p and q are secret large primes. The * TA rolls random group key b disguised as a RSA structure member. - * Except for the public key, these values are shared only among group + * Except for the public key, these values are shared only among group * members and never revealed in messages. * * When rolling new certificates, Bob recomputes the private and @@ -2246,7 +2285,7 @@ crypto_iff( * * 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 x, Alice knows that Bob has the group key b. The signed + * 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. * @@ -2255,6 +2294,7 @@ crypto_iff( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key + * XEVNT_ID bad or missing identity parameters */ static int crypto_alice2( @@ -2262,35 +2302,18 @@ crypto_alice2( struct value *vp /* value pointer */ ) { - RSA *rsapar; /* GQ parameters */ + RSA *rsa; /* GQ parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ - char filename[MAXFILENAME + 1]; tstamp_t tstamp; - tstamp_t fstamp; u_int len; /* - * If the GQ parameters are not valid or there is no trusted - * host, something awful happened. Otherwise, load the identity - * file containing the scheme parameters. + * The identity parameters must have correct format and content. */ - if (!(crypto_flags & CRYPTO_FLAG_GQ) || peer->issuer == NULL) { - msyslog(LOG_ERR, "crypto_alice2: GQ unavailable"); - return (XEVNT_PUB); - } - if (peer->ident_pkey != NULL) - EVP_PKEY_free(peer->ident_pkey); - snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", - peer->issuer); - peer->ident_pkey = crypto_key(filename, &fstamp); - if (peer->ident_pkey == NULL) { - msyslog(LOG_ERR, - "crypto_alice: file %s not found or corrupt", - filename); - return (XEVNT_PUB); - } - if ((rsapar = peer->ident_pkey->pkey.rsa) == NULL) { + if (peer->ident_pkey == NULL) + return (XEVNT_ID); + if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) { msyslog(LOG_ERR, "crypto_alice: GQ defective key"); return (XEVNT_PUB); } @@ -2300,12 +2323,12 @@ crypto_alice2( * omitting BN_rand_range, so we have to do it the hard way. */ bctx = BN_CTX_new(); - len = BN_num_bytes(rsapar->n); + len = BN_num_bytes(rsa->n); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = BN_new(); BN_rand(peer->iffval, len * 8, -1, 1); /* r mod n */ - BN_mod(peer->iffval, peer->iffval, rsapar->n, bctx); + BN_mod(peer->iffval, peer->iffval, rsa->n, bctx); BN_CTX_free(bctx); /* @@ -2375,7 +2398,7 @@ crypto_bob2( /* * Bob rolls random k (0 < k < n), computes y = k u^r mod n and - * g = k^b mod n, then sends (y, hash(g)) to Alice. + * x = k^b mod n, then sends (y, hash(x)) to Alice. */ bctx = BN_CTX_new(); k = BN_new(); g = BN_new(); y = BN_new(); sdsa = DSA_SIG_new(); @@ -2428,7 +2451,7 @@ crypto_bob2( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key - * XEVNT_ID bad or missing identification + * XEVNT_ID bad or missing identity parameters */ int crypto_gq( @@ -2448,8 +2471,7 @@ crypto_gq( * If the GQ parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ - if (!(crypto_flags & CRYPTO_FLAG_GQ) || peer->ident_pkey == - NULL) { + if (peer->ident_pkey == NULL) { msyslog(LOG_ERR, "crypto_gq: GQ unavailable"); return (XEVNT_PUB); } @@ -2463,7 +2485,8 @@ crypto_gq( } /* - * Extract the k u^r and k^b values from the response. + * Extract the y = k u^r and hash(x = k^b) values from the + * response. */ bctx = BN_CTX_new(); y = BN_new(); v = BN_new(); len = ntohl(ep->vallen); @@ -2475,7 +2498,7 @@ crypto_gq( } /* - * Alice computes v^r y^b mod n. + * Compute v^r y^b mod n. */ BN_mod_exp(v, peer->grpkey, peer->iffval, rsa->n, bctx); /* v^r mod n */ @@ -2483,7 +2506,7 @@ crypto_gq( BN_mod_mul(y, v, y, rsa->n, bctx); /* v^r y^b mod n */ /* - * The result should match the hash of g^k mod n. + * Verify the hash of the result matches hash(x). */ bighash(y, y); temp = BN_cmp(y, sdsa->s); @@ -2577,6 +2600,7 @@ crypto_gq( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key + * XEVNT_ID bad or missing identity parameters */ static int crypto_alice3( @@ -2587,31 +2611,14 @@ crypto_alice3( DSA *dsa; /* MV parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ - char filename[MAXFILENAME + 1]; tstamp_t tstamp; - tstamp_t fstamp; u_int len; /* - * If there is no trusted host, something awful happened. - * Otherwise, try to load the identity file containing the - * scheme parameters. If the file does not exist, not to worry; - * the client does not need identity confirmation. If it does - * exist, it must have correct format and content. + * The identity parameters must have correct format and content. */ - if (peer->issuer == NULL) { - msyslog(LOG_ERR, "crypto_alice: MV unavailable"); - return (XEVNT_PUB); - } - if (peer->ident_pkey != NULL) - EVP_PKEY_free(peer->ident_pkey); - snprintf(filename, MAXFILENAME, "ntpkey_mvkey_%s", - peer->issuer); - peer->ident_pkey = crypto_key(filename, &fstamp); - if (peer->ident_pkey == NULL) { - peer->crypto |= CRYPTO_FLAG_VRFY; - return (XEVNT_OK); - } + if (peer->ident_pkey == NULL) + return (XEVNT_ID); if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_ERR, "crypto_alice: MV defective key"); return (XEVNT_PUB); @@ -2755,7 +2762,7 @@ crypto_bob3( * Returns * XEVNT_OK success * XEVNT_PUB bad or missint public key - * XEVNT_ID bad or missing identification + * XEVNT_ID bad or missing identity parameters */ int crypto_mv( @@ -2775,8 +2782,7 @@ crypto_mv( * If the MV parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ - if (!(peer->crypto & CRYPTO_FLAG_MV) || peer->ident_pkey == - NULL) { + if (peer->ident_pkey == NULL) { msyslog(LOG_ERR, "crypto_mv: MV unavailable"); return (XEVNT_PUB); } @@ -3295,15 +3301,14 @@ cert_install( peer->crypto |= CRYPTO_FLAG_VALID; /* - * If this is the default identity scheme, the - * identity is confirmed valid. The next - * signature will set the server proventic. If - * this is an identity scheme, fetch the - * identity credentials. + * If the server has an an identity scheme, + * fetch the identity credentials. If not, the + * identity is verified only by the trusted + * certificate. The next signature will set the + * server proventic. */ - if ((peer->crypto & crypto_flags & - (CRYPTO_FLAG_IFF | CRYPTO_FLAG_GQ)) | - (peer->crypto & CRYPTO_FLAG_MV)) + if (peer->crypto & (CRYPTO_FLAG_GQ | + CRYPTO_FLAG_IFF | CRYPTO_FLAG_MV)) continue; peer->crypto |= CRYPTO_FLAG_VRFY; @@ -3697,7 +3702,7 @@ crypto_setup(void) } if (rand_file == NULL) { msyslog(LOG_ERR, - "crypto_setup random seed file not specified"); + "crypto_setup: random seed file not specified"); exit (-1); } if ((bytes = RAND_load_file(rand_file, -1)) == 0) { diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c index 6705bb0fa5..d4b1159c4e 100644 --- a/ntpd/ntp_proto.c +++ b/ntpd/ntp_proto.c @@ -92,6 +92,7 @@ static void peer_xmit P((struct peer *)); static void fast_xmit P((struct recvbuf *, int, keyid_t, int)); static void clock_update P((void)); int default_get_precision P((void)); +static int peer_unfit P((struct peer *)); /* * transmit - Transmit Procedure. See Section 3.4.2 of the @@ -215,25 +216,22 @@ transmit( * not been heard for three consecutive * polls, stuff the clock filter. Next, * determine the poll interval. If the - * peer is a synchronization candidate, - * use the system poll interval. If we - * cannot synchronize to the peer - * increase it by one. + * peer is unfit for synchronization, + * increase it by one; otherwise, use + * the system poll interval. */ if (!(peer->reach & 0x07)) { clock_filter(peer, 0., 0., MAXDISPERSE); clock_select(); } - if ((peer->stratum > 1 && - peer->refid == peer->dstadr->addr_refid) || - peer->stratum == STRATUM_UNSPEC) + if (peer_unfit(peer)) hpoll++; else hpoll = sys_poll; if (peer->flags & FLAG_BURST) peer->burst = NTP_BURST; - } + } } else { peer->burst--; if (peer->burst == 0) { @@ -1774,17 +1772,10 @@ clock_select(void) peer->status = CTL_PST_SEL_REJECT; /* - * A peer leaves the island immediately if - * unreachable, synchronized to us or suffers - * excessive root distance. Careful with the - * root distance, since the poll interval can - * increase to a day and a half. + * Leave the island immediately if the peer is + * unfit to synchronize. */ - if (!peer->reach || (peer->stratum > 1 && - peer->refid == peer->dstadr->addr_refid) || - peer->stratum >= STRATUM_UNSPEC || - (root_distance(peer) >= MAXDISTANCE + 2 * - clock_phi * ULOGTOD(sys_poll))) + if (peer_unfit(peer)) continue; /* @@ -2304,6 +2295,7 @@ peer_xmit( #ifdef OPENSSL if (crypto_flags && (peer->flags & FLAG_SKEY)) { struct exten *exten; /* extension field */ + u_int opcode; /* * The Public Key Dance (PKD): Cryptographic credentials @@ -2410,28 +2402,13 @@ peer_xmit( peer->issuer); /* - * Identity. We look first for GQ, then IFF. If - * the server has MV, then we look for that. If - * not found, we skip identity confirmation. - * Note we have to sign the certificate before - * the cookie to avoid a deadlock when the - * passive peer is walking the certificate - * trail. Awesome. + * Identity. Note we have to sign the + * certificate before the cookie to avoid a + * deadlock when the passive peer is walking the + * certificate trail. Awesome. */ - else if (!(peer->crypto & CRYPTO_FLAG_VRFY) && - crypto_flags & peer->crypto & - CRYPTO_FLAG_GQ) - exten = crypto_args(peer, CRYPTO_GQ, - NULL); - else if (!(peer->crypto & CRYPTO_FLAG_VRFY) && - crypto_flags & peer->crypto & - CRYPTO_FLAG_IFF) - exten = crypto_args(peer, CRYPTO_IFF, - NULL); - else if (!(peer->crypto & CRYPTO_FLAG_VRFY) && - peer->crypto & CRYPTO_FLAG_MV) - exten = crypto_args(peer, CRYPTO_MV, - NULL); + else if ((opcode = crypto_ident(peer)) != 0) + exten = crypto_args(peer, opcode, NULL); else if (sys_leap != LEAP_NOTINSYNC && !(peer->crypto & CRYPTO_FLAG_SIGN)) exten = crypto_args(peer, CRYPTO_SIGN, @@ -2513,24 +2490,10 @@ peer_xmit( peer->issuer); /* - * Identity. We look first for GQ, then IFF. If - * the server has MV, then we look for that. If - * not found, we skip identity confirmation. + * Identity. */ - else if (!(peer->crypto & CRYPTO_FLAG_VRFY) && - crypto_flags & peer->crypto & - CRYPTO_FLAG_GQ) - exten = crypto_args(peer, CRYPTO_GQ, - NULL); - else if (!(peer->crypto & CRYPTO_FLAG_VRFY) && - crypto_flags & peer->crypto & - CRYPTO_FLAG_IFF) - exten = crypto_args(peer, CRYPTO_IFF, - NULL); - else if (!(peer->crypto & CRYPTO_FLAG_VRFY) && - peer->crypto & CRYPTO_FLAG_MV) - exten = crypto_args(peer, CRYPTO_MV, - NULL); + else if ((opcode = crypto_ident(peer)) != 0) + exten = crypto_args(peer, opcode, NULL); /* * Autokey @@ -2837,6 +2800,29 @@ key_expire( } #endif /* OPENSSL */ + +/* + * Determine if the peer is unfit for synchronization + * + * A peer is unfit for synchronization if + * > not reachable + * > a synchronization loop would form + * > never been synchronized + * > stratum undefined or too high + * > too long without synchronization + */ +int /* 0 if no, 1 if yes */ +peer_unfit( + struct peer *peer /* peer structure pointer */ + ) +{ + return (!peer->reach || (peer->stratum > 1 && peer->refid == + peer->dstadr->addr_refid) || peer->leap == LEAP_NOTINSYNC || + peer->stratum >= STRATUM_UNSPEC || root_distance(peer) >= + MAXDISTANCE + 2. * clock_phi * ULOGTOD(sys_poll) ); +} + + /* * Find the precision of this particular machine */ diff --git a/util/ntp-keygen.c b/util/ntp-keygen.c index 9f169962f6..e447dc831f 100644 --- a/util/ntp-keygen.c +++ b/util/ntp-keygen.c @@ -5,12 +5,12 @@ * where is the file type, is the generating host and * is the NTP seconds in decimal format. The NTP programs * expect generic names such as "ntpkey__whimsy.udel.edu" with the - * connection being maintained by soft links. + * association maintained by soft links. * * 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. - *_assign + * * Note that private keys can be password encrypted as per OpenSSL * conventions. * @@ -21,25 +21,37 @@ * key cryptography * * ntpkey_RSAkey_. + * ntpkey_host_ (RSA) link * RSA private/public host key pair used for public key signatures * and data encryption * * ntpkey_DSAkey_. + * ntpkey_sign_ (RSA or DSA) link * DSA private/public sign key pair used for public key signatures, * but not data encryption * * ntpkey_IFFpar_. - * Schnorr (IFF) parameters used to verify trusted group membership + * ntpkey_iff_ (IFF server/client) link + * ntpkey_iffkey_ (IFF client) link + * Schnorr (IFF) server/client identity parameters + * + * ntpkey_IFFkey_. + * Schnorr (IFF) client identity parameters * * ntpkey_GQpar_., - * Guillou-Quisquater (GQ) parameters used to verify trusted group - * membership + * ntpkey_gq_ (GQ) link + * Guillou-Quisquater (GQ) identity parameters * * ntpkey_MVpar_., - * Mu-Varadharajan (MV) parameters used to verify trusted group - * membership + * Mu-Varadharajan (MV) server identity parameters + * + * ntpkey_MVkeyX_., + * ntpkey_mv_ (MV server) link + * ntpkey_mvkey_ (MV client) link + * Mu-Varadharajan (MV) client identity parameters * * ntpkey_XXXcert_. + * ntpkey_cert_ (RSA or DSA) link * X509v3 certificate using RSA or DSA public keys and signatures. * XXX is a code identifying the message digest and signature * encryption algorithm @@ -49,28 +61,6 @@ * RSA: RSA-MD2, RSA-MD5, RSA-SHA, RSA-SHA1, RSA-MDC2, EVP-RIPEMD160 * DSA: DSA-SHA, DSA-SHA1 * - * The links produced include - * - * ntpkey_key_ (RSA) - * Host public/private key pair used for cookie encryption and - * digital signatures if a sign key is not present. - * - * ntpkey_sign_ (RSA or DSA) - * Sign public/private key pair used for digital signatures. - * - * ntpkey_iffpar_ (IFF) - * Private IFF parameters used to securely confirm identity to - * other members of the group. - * - * ntpkey_gqpar_ (GQ) - * Private GQ parameters used to securely confirm identity to other - * members of the group. The public key value is disclosed in - * certificates. - * - * ntpkey_mvpar_ (MV) - * Private MV parameters used to securely confirm identity. to - * other members of the group. - * * 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 @@ -147,10 +137,10 @@ int gen_md5 P((char *)); #ifdef OPENSSL EVP_PKEY *gen_rsa P((char *)); EVP_PKEY *gen_dsa P((char *)); -EVP_PKEY *gen_iffpar 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_mvpar P((char *)); +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 *)); @@ -403,11 +393,11 @@ main( if (sign != NULL) pkey_sign = genkey(sign, "sign"); if (iffkey) - pkey_iff = gen_iffpar("iff"); + pkey_iff = gen_iff("iff"); if (gqpar) pkey_gq = gen_gqpar("gq"); if (mvpar) - pkey_mv = gen_mvpar("mv"); + pkey_mv = gen_mv("mv"); /* * If there is no new host key, look for an existing one. If not @@ -750,20 +740,25 @@ gen_dsa( * 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. + * 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. * * 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 + * 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 a less than q, then computes public g^(q - a). These - * values are shared among all group members but not revealed in - * certificate or message data. Alice challenges Bob to confirm identity - * using the protocol described below. + * 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. */ EVP_PKEY * /* DSA cuckoo nest */ -gen_iffpar( +gen_iff( char *id /* file name id */ ) { @@ -771,7 +766,7 @@ gen_iffpar( DSA *dsa; /* DSA parameters */ u_char seed[20]; /* seed for parameters */ BN_CTX *ctx; /* BN working space */ - BIGNUM *a, *k, *r, *bn, *bk; /* BN temp */ + BIGNUM *b, *r, *k, *u, *v, *w; /* BN temp */ FILE *str; u_int temp; @@ -796,59 +791,59 @@ gen_iffpar( * these keys are distributed to all members of the group. */ printf("Generating IFF keys (%d bits)...\n", modulus); - a = BN_new(); k = BN_new(); r = BN_new(); - bn = BN_new(); bk = BN_new(); ctx = BN_CTX_new(); - BN_rand(a, BN_num_bits(dsa->q), -1, 0); /* a */ - BN_mod(a, a, dsa->q, ctx); - BN_sub(bn, dsa->q, a); - BN_mod_exp(bn, dsa->g, bn, dsa->p, ctx); /* g^(q - a) mod p */ - BN_mod_exp(bk, dsa->g, a, dsa->p, ctx); /* g^a mod p */ - BN_mod_mul(bk, bk, bn, dsa->p, ctx); - temp = BN_is_one(bk); - printf("Confirm g^(q - a) g^a = 1 mod p: %s\n", temp == 1 ? + 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 */ + BN_mod(b, b, dsa->q, ctx); + BN_sub(v, dsa->q, b); + BN_mod_exp(v, dsa->g, v, dsa->p, ctx); /* g^(q - b) mod p */ + BN_mod_exp(u, dsa->g, b, dsa->p, ctx); /* g^b mod p */ + BN_mod_mul(u, u, v, dsa->p, ctx); + temp = BN_is_one(u); + printf("Confirm g^(q - b) g^b = 1 mod p: %s\n", temp == 1 ? "yes" : "no"); if (!temp) { - BN_free(a); BN_free(k); BN_free(r); - BN_free(bn); BN_free(bk); BN_CTX_free(ctx); + 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(a); /* private key */ - dsa->pub_key = BN_dup(bn); /* public key */ + dsa->priv_key = BN_dup(b); /* private key */ + dsa->pub_key = BN_dup(v); /* public key */ /* * Here is a trial round of the protocol. First, Alice rolls - * random r ( 0 < r < q) and sends it to Bob. She needs only + * random r (0 < r < q) and sends it to Bob. She needs only * modulus q. */ - BN_rand(r, BN_num_bits(dsa->q), -1, 0); /* r, 0 < r < q */ + 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 k + a r mod q and - * g^k, then sends (k, g) to Alice. He needs only modulus q and - * the private key. + * Bob rolls random k (0 < k < 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. */ BN_rand(k, BN_num_bits(dsa->q), -1, 0); /* k, 0 < k < q */ BN_mod(k, k, dsa->q, ctx); - BN_mod_mul(bn, a, r, dsa->q, ctx); /* a r mod q */ - BN_add(bn, bn, k); - BN_mod(bn, bn, dsa->q, ctx); /* k + a r mod q */ + BN_mod_mul(v, dsa->priv_key, r, dsa->q, ctx); /* b r mod q */ + BN_add(v, v, k); + BN_mod(v, v, dsa->q, ctx); /* y = k + b r mod q */ + BN_mod_exp(u, dsa->g, k, dsa->p, ctx); /* x = g^k mod p */ /* - * Alice computes g^(k + a r) g^(q - a) r and verifies the - * result is equal to g. She needs modulus p, generator g, and - * the public key, as well as her original r. + * 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. */ - BN_mod_exp(bn, dsa->g, bn, dsa->p, ctx); /* g^(k + a r) mod p */ - BN_mod_exp(bk, dsa->pub_key, r, dsa->p, ctx); /* g^(q - a) r */ - BN_mod_mul(bn, bk, bn, dsa->p, ctx); /* product mod p */ - BN_mod_exp(bk, dsa->g, k, dsa->p, ctx); /* g^k mod p */ - temp = BN_cmp(bk, bn); - printf("Confirm g^k = g^(k + a r) g^(q - a) r: %s\n", temp == + 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 */ + BN_mod_mul(v, w, v, dsa->p, ctx); /* product mod p */ + temp = BN_cmp(u, v); + printf("Confirm g^k = g^(k + b r) g^(q - b) r: %s\n", temp == 0 ? "yes" : "no"); - BN_free(a); BN_free(k); BN_free(r); - BN_free(bn); BN_free(bk); BN_CTX_free(ctx); + BN_free(b); BN_free(r); BN_free(k); + BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); if (temp != 0) { DSA_free(dsa); rval = -1; @@ -856,14 +851,14 @@ gen_iffpar( } /* - * Write the IFF parameters and keys as a DSA private key + * Write the IFF server parameters and keys as a DSA private key * encoded in PEM. * * p modulus p * q modulus q * g generator g - * priv_key a - * public_key g^(q - a) mod p + * priv_key b + * public_key v */ str = fheader("IFFpar", trustname); pkey = EVP_PKEY_new(); @@ -874,6 +869,26 @@ gen_iffpar( if (debug) DSA_print_fp(stdout, dsa, 0); fslink(id, trustname); + + /* + * Write the IFF client parameters and keys as a DSA private key + * encoded in PEM. Note the private key is obscured. + * + * p modulus p + * q modulus q + * g generator g + * priv_key 1 + * public_key v + */ + BN_copy(dsa->priv_key, BN_value_one()); + str = fheader("IFFkey", trustname); + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_DSA(pkey, dsa); + PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() : NULL, + NULL, 0, NULL, passwd); + fclose(str); + if (debug) + DSA_print_fp(stdout, dsa, 0); return (pkey); } @@ -886,15 +901,14 @@ gen_iffpar( * 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, sign keys and certificates. + * 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 * cryptography and described in Stimson p. 300 (with errors). The 512- * bit public modulus is n = p q, where p and q are secret large primes. - * The TA rolls random group key b as security parameter and RSA - * exponent. These values are shared among all group members but not - * revealed in certificate or message data. + * 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 * public keys. The private key u is a random roll, while the public key @@ -1086,13 +1100,13 @@ gen_gqkey( /* * Generate Mu-Varadharajan (MV) parameters and keys * - * The Mu-Varadharajan (MV) cryptosystem is intended when servers + * 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 * 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. + * tamperproof set-top box. We don't use it this way, but read on. * * The MV parameters and private encryption key hide in a DSA cuckoo * structure which uses the same parameters, but generated in a @@ -1105,16 +1119,15 @@ gen_gqkey( * 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 - * significant bits. The elements x mod q of Zq with the elements 2 and - * the primes removed form a field Zq* valid for polynomial arithetic. - * Let g be a generator of Zp; that is, gcd(g, p - 1) = 1 and g^q = 1 - * mod p. We expect M to be in the 500-bit range and n relatively small, - * like 25, so the likelihood of a randomly generated element of x mod q - * of Zq colliding with a factor of p - 1 is very small and can be - * avoided. 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. + * 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. * * 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 @@ -1140,7 +1153,7 @@ gen_gqkey( * thus unable to decrypt the block. */ EVP_PKEY * /* DSA cuckoo nest */ -gen_mvpar( +gen_mv( char *id /* file name id */ ) { @@ -1168,14 +1181,14 @@ gen_mvpar( /* * Generate MV parameters. * - * The object is to generate a multiplicative group Zp mod 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 and - * several minutes on a Blade 1000. + * 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 + * and several minutes on a Sun Blade 1000. */ n = nkeys; printf("Generating MV parameters for %d keys (%d bits)...\n", n, @@ -1214,7 +1227,10 @@ gen_mvpar( * one and try again. Note that q will hardly be a secret since * we have to reveal p to servers and clients. However, * factoring q to find the primes should be adequately hard, as - * this is the same problem considered hard in RSA. + * this is the same problem considered hard in RSA. Question: is + * it as hard to find n small prime factors totalling n bits as + * it is to find two large prime factors totalling n bits? + * Remember, the bad guy doesn't know n. */ temp = 0; while (1) { @@ -1246,7 +1262,8 @@ gen_mvpar( /* * Compute the generator g using a random roll such that - * gcd(g, p - 1) = 1 and g^q = 1. + * gcd(g, p - 1) = 1 and g^q = 1. This is a generator of p, not + * q. */ BN_copy(v, dsa->p); BN_sub_word(v, 1); @@ -1370,9 +1387,8 @@ gen_mvpar( /* * Roll private random group key b mod q (0 < b < q), where - * gcd(b, q) = 1 to guarantee the b^1 exists, then compute - * b^-1 mod q. If b is changed, the client keys must be - * recomputed. + * 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) { BN_rand(b, BN_num_bits(dsa->q), 0, 0); @@ -1445,18 +1461,13 @@ gen_mvpar( * 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. * - * The server reads the private file and rolls the session key - * k, then computes E^k, gbar^k and ghat^k. The E^k is the new - * symmetric key which is installed in the key cache. The gbar^k - * and ghat^k values 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 encryption key - * E^k as the inverse x^-1 of x and installs in the key cache. - * Once installed, the crypto computations don't have to be done - * again until the session key is refreshed, expected to be done - * once per day. + * 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. */ BN_copy(dsa->g, bige); dsa->priv_key = BN_dup(gbar); @@ -1503,10 +1514,12 @@ gen_mvpar( ctx); BN_mod_mul(u, u, v, dsa->p, ctx); BN_mod_mul(u, u, dsa->g, dsa->p, ctx); - if (!BN_is_one(u)) - printf("Revoke key %d\n", j); BN_free(xbar[j]); BN_free(xhat[j]); BN_free(x[j]); BN_free(s[j]); BN_free(s1[j]); + if (!BN_is_one(u)) { + printf("Revoke key %d\n", j); + continue; + } /* * Write the client parameters as a DSA private key @@ -1524,6 +1537,8 @@ gen_mvpar( PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd); fclose(str); + printf("ntpkey_%s_%s.%lu\n", ident, trustname, epoch + + JAN_1970); if (debug) DSA_print_fp(stdout, sdsa, 0); }