/** * Sets the fields in a EXT_ENC_INFO extension for transmission. * Returns the number of bytes set, or 0 on error. */ int set_enc_info(const struct finfo_t *finfo, struct enc_info_he *encinfo) { unsigned char *keyblob; uint16_t bloblen; int extlen; keyblob = ((uint8_t *)encinfo + sizeof(struct enc_info_he)); encinfo->exttype = EXT_ENC_INFO; encinfo->keyextype_sigtype = (keyextype & 0x0F) << 4; encinfo->keyextype_sigtype |= (sigtype & 0x0F); encinfo->keytype = keytype; encinfo->hashtype = hashtype; if (client_auth) { encinfo->flags |= FLAG_CLIENT_AUTH; } memcpy(encinfo->rand1, rand1, sizeof(rand1)); if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) { if (!export_RSA_key(privkey.rsa, keyblob, &bloblen)) { glog0(finfo, "Error exporting server public key"); return 0; } } else { if (!export_EC_key(privkey.ec, keyblob, &bloblen)) { glog0(finfo, "Error exporting server public key"); return 0; } } encinfo->keylen = htons(bloblen); if ((keyextype == KEYEX_ECDH_RSA) || (keyextype == KEYEX_ECDH_ECDSA)) { uint16_t dhlen; uint8_t *dhblob = ((uint8_t *)encinfo + sizeof(struct enc_info_he) + ntohs(encinfo->keylen)); if (!export_EC_key(dhkey.ec, dhblob, &dhlen)) { glog0(finfo, "Error exporting server ECDH public key"); return 0; } encinfo->dhlen = htons(dhlen); if (keyextype == KEYEX_ECDH_RSA) { encinfo->siglen = htons(RSA_keylen(privkey.rsa)); } else { encinfo->siglen = htons(ECDSA_siglen(privkey.ec)); } } else { encinfo->dhlen = 0; encinfo->siglen = 0; } extlen = sizeof(struct enc_info_he) + ntohs(encinfo->keylen) + ntohs(encinfo->dhlen) + ntohs(encinfo->siglen); encinfo->extlen = extlen / 4; return extlen; }
/** * Initialize crypto library, generate keys */ void key_init(void) { unsigned char *prf_buf; time_t t; uint32_t t2; int explen, len; if (keytype == KEY_NONE) { return; } set_sys_keys(sys_keys); get_key_info(keytype, &keylen, &ivlen); hmaclen = get_hash_len(hashtype); memset(groupkey, 0, sizeof(groupkey)); memset(groupsalt, 0, sizeof(groupsalt)); memset(grouphmackey, 0, sizeof(grouphmackey)); if (!get_random_bytes(groupmaster, sizeof(groupmaster))) { log0(0, 0, 0, "Failed to generate group master"); exit(ERR_CRYPTO); } groupmaster[0] = UFTP_VER_NUM; if (!get_random_bytes(rand1, sizeof(rand1))) { log0(0, 0, 0, "Failed to generate rand1"); exit(ERR_CRYPTO); } // Sets the first 4 bytes of rand1 to the current time t = time(NULL); t2 = (uint32_t)(t & 0xFFFFFFFF); *(uint32_t *)rand1 = t2; explen = hmaclen + keylen + SALT_LEN; prf_buf = safe_calloc(explen + hmaclen, 1); PRF(hashtype, explen, groupmaster, sizeof(groupmaster), "key expansion", rand1, sizeof(rand1), prf_buf, &len); memcpy(grouphmackey, prf_buf, hmaclen); memcpy(groupkey, prf_buf + hmaclen, keylen); memcpy(groupsalt, prf_buf + hmaclen + keylen, SALT_LEN); ivctr = 0; free(prf_buf); if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) { if ((!strcmp(keyfile, "")) || (newkeylen != 0)) { privkey.rsa = gen_RSA_key(newkeylen, RSA_EXP, keyfile); } else { privkey.rsa = read_RSA_key(keyfile); } if (!privkey.key) { log0(0, 0, 0, "Failed to read/generate private key"); exit(ERR_CRYPTO); } privkeylen = RSA_keylen(privkey.rsa); } else { if ((!strcmp(keyfile, "")) || (ecdsa_curve != 0)) { privkey.ec = gen_EC_key(ecdsa_curve, 0, keyfile); } else { privkey.ec = read_EC_key(keyfile); } if (!privkey.key) { log0(0, 0, 0, "Failed to read/generate private key"); exit(ERR_CRYPTO); } privkeylen = ECDSA_siglen(privkey.ec); } if ((keyextype == KEYEX_ECDH_RSA) || (keyextype == KEYEX_ECDH_ECDSA)) { dhkey.ec = gen_EC_key(ecdh_curve, 1, NULL); if (!dhkey.key) { log0(0, 0, 0, "Failed to generate DH key"); exit(ERR_CRYPTO); } } }
/** * Read encryption related fields from an ANNOUNCE */ int read_announce_encryption(struct group_list_t *group, struct enc_info_he *encinfo, const unsigned char *packet, int packetlen) { int keyextype, sigtype, keytype, i; unsigned char *keys; keys = (unsigned char *)encinfo + sizeof(struct enc_info_he); // Sanity check the selected encryption parameters if (!cipher_supported(encinfo->keytype)) { glog1(group, "Keytype invalid or not supported here"); send_abort(group, "Keytype invalid or not supported here"); return 0; } if (!hash_supported(encinfo->hashtype)) { glog1(group, "Hashtype invalid or not supported here"); send_abort(group, "Hashtype invalid or not supported here"); return 0; } keyextype = (encinfo->keyextype_sigtype & 0xF0) >> 4; sigtype = encinfo->keyextype_sigtype & 0x0F; if (((sigtype != SIG_HMAC) && (sigtype != SIG_KEYEX) && (sigtype != SIG_AUTHENC)) || ((sigtype == SIG_AUTHENC) && (!is_auth_enc(encinfo->keytype)))) { glog1(group, "Invalid sigtype specified"); send_abort(group, "Invalid sigtype specified"); return 0; } if ((keyextype != KEYEX_RSA) && (keyextype != KEYEX_ECDH_RSA) && (keyextype != KEYEX_ECDH_ECDSA)) { glog1(group, "Invalid keyextype specified"); send_abort(group, "Invalid keyextype specified"); return 0; } group->keyextype = keyextype; group->keytype = encinfo->keytype; group->hashtype = encinfo->hashtype; group->sigtype = sigtype; group->client_auth = ((encinfo->flags & FLAG_CLIENT_AUTH) != 0); if (!verify_server_fingerprint(keys, ntohs(encinfo->keylen), group)) { glog1(group, "Failed to verify server key fingerprint"); send_abort(group, "Failed to verify server key fingerprint"); return 0; } if ((group->keyextype == KEYEX_RSA) || (group->keyextype == KEYEX_ECDH_RSA)) { keytype = KEYBLOB_RSA; } else { keytype = KEYBLOB_EC; } // Load server key and select a matching client key if (keytype == KEYBLOB_RSA) { if (!import_RSA_key(&group->server_pubkey.rsa, keys, ntohs(encinfo->keylen))) { glog0(group, "Failed to load server public key"); send_abort(group, "Failed to load server public key"); return 0; } group->server_pubkeylen = RSA_keylen(group->server_pubkey.rsa); for (i = 0; i < key_count; i++) { if ((privkey_type[i] == KEYBLOB_RSA) && (group->server_pubkeylen == RSA_keylen(privkey[i].rsa))) { group->client_privkey = privkey[i]; group->client_privkeylen = RSA_keylen(privkey[i].rsa); break; } } } else { if (!import_EC_key(&group->server_pubkey.ec, keys, ntohs(encinfo->keylen), 0)) { glog0(group, "Failed to load server public key"); send_abort(group, "Failed to load server public key"); return 0; } group->server_pubkeylen = ECDSA_siglen(group->server_pubkey.ec); for (i = 0; i < key_count; i++) { if ((privkey_type[i] == KEYBLOB_EC) && (get_EC_curve(group->server_pubkey.ec) == get_EC_curve(privkey[i].ec))) { group->client_privkey = privkey[i]; group->client_privkeylen = ECDSA_siglen(privkey[i].ec); break; } } } if (!group->client_privkey.key) { glog1(group, "No client key compatible with server key"); send_abort(group, "No client key compatible with server key"); return 0; } if (has_proxy) { if (!proxy_pubkey.key) { glog1(group, "Response proxy set but haven't gotten key yet"); send_abort(group,"Response proxy set but haven't gotten key yet"); return 0; } if (!(((keytype == KEYBLOB_RSA) && (proxy_pubkeytype == KEYBLOB_RSA)) && (RSA_keylen(group->server_pubkey.rsa) == RSA_keylen(proxy_pubkey.rsa))) && !(((keytype == KEYBLOB_EC) && (proxy_pubkeytype==KEYBLOB_EC)) && (get_EC_curve(group->server_pubkey.ec) == get_EC_curve(proxy_pubkey.ec)))) { glog1(group, "Response proxy key not compatible with server key"); send_abort(group, "Response proxy key not compatible with server key"); return 0; } } if ((group->keyextype == KEYEX_ECDH_ECDSA) || (group->keyextype == KEYEX_ECDH_RSA)) { unsigned char *sigcopy; int siglen; unsigned char *dhblob = keys + ntohs(encinfo->keylen); unsigned char *sig = dhblob + ntohs(encinfo->dhlen); if (!import_EC_key(&group->server_dhkey.ec, dhblob, ntohs(encinfo->dhlen), 1)) { glog0(group, "Failed to load server public ECDH key"); send_abort(group, "Failed to load server public ECDH key"); return 0; } group->client_dhkey.ec = gen_EC_key(get_EC_curve(group->server_dhkey.ec), 1, NULL); if (!group->client_dhkey.key) { glog0(group, "Failed to generate client ECDH key"); send_abort(group, "Failed to generate client ECDH key"); return 0; } if (has_proxy) { // We already checked if the proxy key exists, so no need to repeat if (get_EC_curve(group->server_dhkey.ec) != get_EC_curve(proxy_dhkey.ec)) { glog1(group, "Response proxy ECDH key " "not compatible with server ECDH key"); send_abort(group, "Response proxy ECDH key " "not compatible with server ECDH key"); return 0; } } siglen = ntohs(encinfo->siglen); sigcopy = safe_calloc(siglen, 1); memcpy(sigcopy, sig, siglen); memset(sig, 0, siglen); if (keytype == KEYBLOB_RSA) { if (!verify_RSA_sig(group->server_pubkey.rsa, group->hashtype, packet, packetlen, sigcopy, siglen)) { glog1(group, "Signature verification failed"); send_abort(group, "Signature verification failed"); free(sigcopy); return 0; } } else { if (!verify_ECDSA_sig(group->server_pubkey.ec, group->hashtype, packet, packetlen, sigcopy, siglen)) { glog1(group, "Signature verification failed"); send_abort(group, "Signature verification failed"); free(sigcopy); return 0; } } free(sigcopy); } // Calculate keys if (!calculate_server_keys(group, encinfo)) { return 0; } return 1; }
/** * Handles an incoming CLIENT_KEY message from a client. */ void handle_clientkey(struct pr_group_list_t *group, int hostidx, const unsigned char *message, unsigned meslen, uint32_t src) { const struct client_key_h *clientkey; const unsigned char *keyblob, *verify; struct pr_destinfo_t *dest; int dupmsg; clientkey = (const struct client_key_h *)message; keyblob = (const unsigned char *)clientkey + sizeof(struct client_key_h); verify = keyblob + ntohs(clientkey->bloblen); if (group->destcount == MAXPROXYDEST) { glog1(group, "Rejecting CLIENT_KEY from %08X: " "max destinations exceeded", ntohl(src)); send_downstream_abort(group, src, "Max destinations exceeded", 0); return; } if ((meslen < (clientkey->hlen * 4U)) || ((clientkey->hlen * 4U) < sizeof(struct client_key_h) + ntohs(clientkey->bloblen) + ntohs(clientkey->siglen))) { glog1(group, "Rejecting CLIENT_KEY from %08X: invalid message size", ntohl(src)); send_downstream_abort(group, src, "Invalid message size", 0); return; } if ((((group->keyextype == KEYEX_RSA) || (group->keyextype == KEYEX_ECDH_RSA)) && (keyblob[0] != KEYBLOB_RSA)) || ((group->keyextype == KEYEX_ECDH_ECDSA) && (keyblob[0] != KEYBLOB_EC))) { glog1(group, "Rejecting CLIENT_KEY from %08X: invalid keyblob type", ntohl(src)); send_downstream_abort(group, src, "Invalid keyblob type", 0); return; } if (hostidx == -1) { hostidx = add_client(src, group); } dest = &group->destinfo[hostidx]; dupmsg = (dest->pubkey.key != 0); if (!dest->verified) { if (keyblob[0] == KEYBLOB_RSA) { if (!import_RSA_key(&dest->pubkey.rsa, keyblob, ntohs(clientkey->bloblen))) { glog1(group, "Failed to load client public key"); send_downstream_abort(group, src, "Failed to load client public key", 0); return; } dest->pubkeylen = RSA_keylen(dest->pubkey.rsa); } else { if (!import_EC_key(&dest->pubkey.ec, keyblob, ntohs(clientkey->bloblen), 0)) { glog1(group, "Failed to load client public key"); send_downstream_abort(group, src, "Failed to load client public key", 0); return; } dest->pubkeylen = ECDSA_siglen(dest->pubkey.ec); } if (!verify_fingerprint(client_fp, client_fp_count, keyblob, ntohs(clientkey->bloblen), group, src)) { glog1(group, "Failed to verify client key fingerprint"); send_downstream_abort(group, src, "Failed to verify client key fingerprint", 0); return; } dest->verified = 1; } memcpy(dest->verifydata, verify, ntohs(clientkey->siglen)); dest->verifylen = ntohs(clientkey->siglen); if (dest->registered) { if (!verify_client_key(group, hostidx)) { return; } dest->state = PR_CLIENT_REGISTERED; } glog2(group,"Received CLIENT_KEY%s from %s", dupmsg ? "+" : "", dest->name); if (dest->state == PR_CLIENT_REGISTERED) { // Pass in a dummy REGISTER message to check_pending, since // CLIENT_KEY is basically an extension of REGISTER. struct register_h reg; reg.func = REGISTER; check_pending(group, hostidx, (unsigned char *)®); } }