📄 ntp_crypto.c
字号:
* hostval host name (not signed) * pubkey public key * cinfo certificate info/value list * tai_leap leapseconds file * * Filestamps are proventicated data, so this routine is run only when * the host has been synchronized to a proventicated source. Thus, the * timestamp is proventicated, too, and can be used to deflect * clogging attacks and even cook breakfast. * * Returns void (no errors) */voidcrypto_update(void){ EVP_MD_CTX ctx; /* message digest context */ struct cert_info *cp, *cpn; /* certificate info/value */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t tstamp; /* NTP timestamp */ u_int len; if ((tstamp = crypto_time()) == 0) return; hostval.tstamp = htonl(tstamp); /* * Sign public key and timestamps. The filestamp is derived from * the host key file extension from wherever the file was * generated. */ if (pubkey.vallen != 0) { pubkey.tstamp = hostval.tstamp; pubkey.siglen = 0; if (pubkey.sig == NULL) pubkey.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&pubkey, 12); EVP_SignUpdate(&ctx, pubkey.ptr, ntohl(pubkey.vallen)); if (EVP_SignFinal(&ctx, pubkey.sig, &len, sign_pkey)) pubkey.siglen = htonl(len); } /* * Sign certificates and timestamps. The filestamp is derived * from the certificate file extension from wherever the file * was generated. Note we do not throw expired certificates * away; they may have signed younger ones. */ for (cp = cinfo; cp != NULL; cp = cpn) { cpn = cp->link; cp->cert.tstamp = hostval.tstamp; cp->cert.siglen = 0; if (cp->cert.sig == NULL) cp->cert.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&cp->cert, 12); EVP_SignUpdate(&ctx, cp->cert.ptr, ntohl(cp->cert.vallen)); if (EVP_SignFinal(&ctx, cp->cert.sig, &len, sign_pkey)) cp->cert.siglen = htonl(len); } /* * Sign leapseconds table and timestamps. The filestamp is * derived from the leapsecond file extension from wherever the * file was generated. */ if (tai_leap.vallen != 0) { tai_leap.tstamp = hostval.tstamp; tai_leap.siglen = 0; if (tai_leap.sig == NULL) tai_leap.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&tai_leap, 12); EVP_SignUpdate(&ctx, tai_leap.ptr, ntohl(tai_leap.vallen)); if (EVP_SignFinal(&ctx, tai_leap.sig, &len, sign_pkey)) tai_leap.siglen = htonl(len); } sprintf(statstr, "update ts %u", ntohl(hostval.tstamp)); record_crypto_stats(NULL, statstr);#ifdef DEBUG if (debug) printf("crypto_update: %s\n", statstr);#endif}/* * value_free - free value structure components. * * Returns void (no errors) */voidvalue_free( struct value *vp /* value structure */ ){ if (vp->ptr != NULL) free(vp->ptr); if (vp->sig != NULL) free(vp->sig); memset(vp, 0, sizeof(struct value));}/* * crypto_time - returns current NTP time in seconds. */tstamp_tcrypto_time(){ l_fp tstamp; /* NTP time */ L_CLR(&tstamp); L_CLR(&tstamp); if (sys_leap != LEAP_NOTINSYNC) get_systime(&tstamp); return (tstamp.l_ui);}/* * asn2ntp - convert ASN1_TIME time structure to NTP time in seconds. */u_longasn2ntp ( ASN1_TIME *asn1time /* pointer to ASN1_TIME structure */ ){ char *v; /* pointer to ASN1_TIME string */ struct tm tm; /* used to convert to NTP time */ /* * Extract time string YYMMDDHHMMSSZ from ASN1 time structure. * Note that the YY, MM, DD fields start with one, the HH, MM, * SS fiels start with zero and the Z character should be 'Z' * for UTC. Also note that years less than 50 map to years * greater than 100. Dontcha love ASN.1? Better than MIL-188. */ if (asn1time->length > 13) return ((u_long)(~0)); /* We can't use -1 here. It's invalid */ v = (char *)asn1time->data; tm.tm_year = (v[0] - '0') * 10 + v[1] - '0'; if (tm.tm_year < 50) tm.tm_year += 100; tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1; tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0'; tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0'; tm.tm_min = (v[8] - '0') * 10 + v[9] - '0'; tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0'; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; return (timegm(&tm) + JAN_1970);}/* * bigdig() - compute a BIGNUM MD5 hash of a BIGNUM number. */static intbighash( BIGNUM *bn, /* BIGNUM * from */ BIGNUM *bk /* BIGNUM * to */ ){ EVP_MD_CTX ctx; /* message digest context */ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */ u_char *ptr; /* a BIGNUM as binary string */ u_int len; len = BN_num_bytes(bn); ptr = emalloc(len); BN_bn2bin(bn, ptr); EVP_DigestInit(&ctx, EVP_md5()); EVP_DigestUpdate(&ctx, ptr, len); EVP_DigestFinal(&ctx, dgst, &len); BN_bin2bn(dgst, len, bk); return (1);}/* *********************************************************************** * * * The following routines implement the Schnorr (IFF) identity scheme * * * *********************************************************************** * * The Schnorr (IFF) identity scheme is intended for use when * the ntp-genkeys program does not generate the certificates used in * the protocol and the group key 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. The * scheme is self contained and independent of new generations of host * keys, sign keys and certificates. * * The IFF identity scheme is based on DSA cryptography and algorithms * 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 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 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 + 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. * * crypto_alice - construct Alice's challenge in IFF scheme * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key */static intcrypto_alice( struct peer *peer, /* peer pointer */ struct value *vp /* value pointer */ ){ DSA *dsa; /* IFF parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; u_int len; /* * The identity parameters must have correct format and content. */ if (peer->ident_pkey == NULL) return (XEVNT_ID); if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_alice: defective key"); return (XEVNT_PUB); } /* * Roll new random r (0 < r < q). The OpenSSL library has a bug * omitting BN_rand_range, so we have to do it the hard way. */ bctx = BN_CTX_new(); len = BN_num_bytes(dsa->q); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = BN_new(); BN_rand(peer->iffval, len * 8, -1, 1); /* r */ BN_mod(peer->iffval, peer->iffval, dsa->q, bctx); BN_CTX_free(bctx); /* * Sign and send to Bob. The filestamp is from the local file. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(peer->fstamp); vp->vallen = htonl(len); vp->ptr = emalloc(len); BN_bn2bin(peer->iffval, vp->ptr); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK);}/* * crypto_bob - construct Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_ID bad or missing group key * XEVNT_ERR protocol error * XEVNT_PER host expired certificate */static intcrypto_bob( struct exten *ep, /* extension pointer */ struct value *vp /* value pointer */ ){ DSA *dsa; /* IFF parameters */ DSA_SIG *sdsa; /* DSA signature context fake */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ BIGNUM *bn, *bk, *r; u_char *ptr; u_int len; /* * If the IFF parameters are not valid, something awful * happened or we are being tormented. */ if (iffpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob: scheme unavailable"); return (XEVNT_ID); } dsa = iffpar_pkey->pkey.dsa; /* * Extract r from the challenge. */ len = ntohl(ep->vallen); if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * 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); /* b r mod q */ BN_add(bn, bn, bk); 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); sdsa->s = BN_dup(bk); BN_CTX_free(bctx); BN_free(r); BN_free(bn); BN_free(bk); /* * Encode the values in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(if_fstamp); len = i2d_DSA_SIG(sdsa, NULL); if (len <= 0) { msyslog(LOG_ERR, "crypto_bob %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_SIG_free(sdsa); return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); vp->ptr = ptr; i2d_DSA_SIG(sdsa, &ptr); DSA_SIG_free(sdsa); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK);}/* * crypto_iff - verify Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_FSP bad filestamp */intcrypto_iff( struct exten *ep, /* extension pointer */ struct peer *peer /* peer structure pointer */ ){ DSA *dsa; /* IFF parameters */ BN_CTX *bctx; /* BIGNUM context */ DSA_SIG *sdsa; /* DSA parameters */ BIGNUM *bn, *bk; u_int len; const u_char *ptr; int temp; /* * If the IFF parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_iff: scheme unavailable"); return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_iff: invalid filestamp %u", ntohl(ep->fstamp)); return (XEVNT_FSP); } if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_iff: defective key"); return (XEVNT_PUB); } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_iff: missing challenge"); return (XEVNT_ID); } /* * 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); ptr = (const u_char *)ep->pkt; if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_iff %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * 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); /* * Verify the hash of the result matches hash(x). */ bighash(bn, bn); temp = B
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -