int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) { if (!ctx || !packet) { return KNOT_EINVAL; } if (ctx->key == NULL) { return KNOT_EOK; } int ret = KNOT_ERROR; if (ctx->digest_size == 0) { ctx->digest_size = knot_tsig_digest_length(ctx->key->algorithm); ret = knot_tsig_sign(packet->wire, &packet->size, packet->max_size, NULL, 0, ctx->digest, &ctx->digest_size, ctx->key, 0, 0); } else { uint8_t previous_digest[ctx->digest_size]; memcpy(previous_digest, ctx->digest, ctx->digest_size); ret = knot_tsig_sign_next(packet->wire, &packet->size, packet->max_size, previous_digest, ctx->digest_size, ctx->digest, &ctx->digest_size, ctx->key, packet->wire, packet->size); } return ret; }
static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr, const uint8_t *wire, size_t size, const uint8_t *request_mac, size_t request_mac_len, const knot_tsig_key_t *tsig_key, uint64_t prev_time_signed, int use_times) { if (!wire || !tsig_key) { return KNOT_EINVAL; } /* No TSIG record means verification failure. */ if (tsig_rr == NULL) { return KNOT_TSIG_EBADKEY; } /* Check time signed. */ int ret = knot_tsig_check_time_signed(tsig_rr, prev_time_signed); if (ret != KNOT_EOK) { return ret; } dbg_tsig_verb("TSIG: time checked.\n"); /* Check that libknot knows the algorithm. */ ret = knot_tsig_check_algorithm(tsig_rr); if (ret != KNOT_EOK) { return ret; } dbg_tsig_verb("TSIG: algorithm checked.\n"); /* Check that key is valid, ie. the same as given in args. */ ret = knot_tsig_check_key(tsig_rr, tsig_key); if (ret != KNOT_EOK) { return ret; } dbg_tsig_verb("TSIG: key validity checked.\n"); uint8_t *wire_to_sign = malloc(sizeof(uint8_t) * size); if (!wire_to_sign) { ERR_ALLOC_FAILED; return KNOT_ENOMEM; } memset(wire_to_sign, 0, sizeof(uint8_t) * size); memcpy(wire_to_sign, wire, size); uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; size_t digest_tmp_len = 0; assert(tsig_rr->rrs.rr_count > 0); if (use_times) { /* Wire is not a single packet, TSIG RRs must be stripped already. */ ret = knot_tsig_create_sign_wire_next(wire_to_sign, size, request_mac, request_mac_len, digest_tmp, &digest_tmp_len, tsig_rr, tsig_key); } else { ret = knot_tsig_create_sign_wire(wire_to_sign, size, request_mac, request_mac_len, digest_tmp, &digest_tmp_len, tsig_rr, tsig_key); } assert(tsig_rr->rrs.rr_count > 0); free(wire_to_sign); if (ret != KNOT_EOK) { dbg_tsig("Failed to create wire format for checking: %s.\n", knot_strerror(ret)); return ret; } dbg_tsig_verb("TSIG: digest calculated\n"); /* Compare MAC from TSIG RR RDATA with just computed digest. */ /*!< \todo move to function. */ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr); knot_tsig_algorithm_t alg = tsig_alg_from_name(alg_name); /*! \todo [TSIG] TRUNCATION */ uint16_t mac_length = tsig_rdata_mac_length(tsig_rr); const uint8_t *tsig_mac = tsig_rdata_mac(tsig_rr); if (mac_length != knot_tsig_digest_length(alg)) { dbg_tsig("TSIG: calculated digest length and given length do " "not match!\n"); return KNOT_TSIG_EBADSIG; } dbg_tsig_verb("TSIG: calc digest :\n"); dbg_tsig_hex_verb((char *)digest_tmp, digest_tmp_len); dbg_tsig_verb("TSIG: given digest:\n"); dbg_tsig_hex_verb((char *)tsig_mac, mac_length); if (memcmp(tsig_mac, digest_tmp, mac_length) != 0) { return KNOT_TSIG_EBADSIG; } return KNOT_EOK; }
int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, const uint8_t *prev_digest, size_t prev_digest_len, uint8_t *digest, size_t *digest_len, const knot_tsig_key_t *key, uint8_t *to_sign, size_t to_sign_len) { if (!msg || !msg_len || !key || !digest || !digest_len) { return KNOT_EINVAL; } uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; size_t digest_tmp_len = 0; knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, NULL); if (!tmp_tsig) { return KNOT_ENOMEM; } /* Create rdata for TSIG RR. */ tsig_create_rdata(tmp_tsig, tsig_alg_to_dname(key->algorithm), knot_tsig_digest_length(key->algorithm), 0); tsig_rdata_store_current_time(tmp_tsig); tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); /* Create wire to be signed. */ size_t wire_len = prev_digest_len + to_sign_len + KNOT_TSIG_TIMERS_LENGTH + 2; uint8_t *wire = malloc(wire_len); if (!wire) { ERR_ALLOC_FAILED; knot_rrset_free(&tmp_tsig, NULL); return KNOT_ENOMEM; } memset(wire, 0, wire_len); /* Write previous digest length. */ knot_wire_write_u16(wire, prev_digest_len); /* Write previous digest. */ memcpy(wire + 2, prev_digest, sizeof(uint8_t) * prev_digest_len); /* Write original message. */ memcpy(wire + prev_digest_len + 2, to_sign, to_sign_len); /* Write timers. */ knot_tsig_wire_write_timers(wire + prev_digest_len + to_sign_len + 2, tmp_tsig); dbg_tsig_detail("Previous digest: \n"); dbg_tsig_hex_detail((char *)prev_digest, prev_digest_len); dbg_tsig_detail("Timers: \n"); dbg_tsig_hex_detail((char *)(wire + prev_digest_len + *msg_len), KNOT_TSIG_TIMERS_LENGTH); int ret = KNOT_ERROR; ret = knot_tsig_compute_digest(wire, wire_len, digest_tmp, &digest_tmp_len, key); /* No matter how the function did, this data is no longer needed. */ free(wire); if (ret != KNOT_EOK) { knot_rrset_free(&tmp_tsig, NULL); *digest_len = 0; return ret; } if (digest_tmp_len > *digest_len) { knot_rrset_free(&tmp_tsig, NULL); *digest_len = 0; return KNOT_ESPACE; } /* Set the MAC. */ tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); /* Set original id. */ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); /* Set other data. */ tsig_rdata_set_other_data(tmp_tsig, 0, NULL); dbg_tsig_verb("Message max length: %zu, message length: %zu\n", msg_max_len, *msg_len); size_t tsig_wire_size = 0; uint16_t rr_count = 0; ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, &tsig_wire_size, msg_max_len - *msg_len, &rr_count, NULL); if (ret != KNOT_EOK) { knot_rrset_free(&tmp_tsig, NULL); *digest_len = 0; return ret; } /* This should not happen, at least one rr has to be converted. */ if (rr_count == 0) { knot_rrset_free(&tmp_tsig, NULL); return KNOT_EINVAL; } knot_rrset_free(&tmp_tsig, NULL); *msg_len += tsig_wire_size; uint16_t arcount = knot_wire_get_arcount(msg); knot_wire_set_arcount(msg, ++arcount); memcpy(digest, digest_tmp, digest_tmp_len); *digest_len = digest_tmp_len; return KNOT_EOK; }
int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len, const uint8_t *request_mac, size_t request_mac_len, uint8_t *digest, size_t *digest_len, const knot_tsig_key_t *key, uint16_t tsig_rcode, uint64_t request_time_signed) { if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) { return KNOT_EINVAL; } knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, NULL); if (!tmp_tsig) { dbg_tsig("TSIG: tmp_tsig = NULL\n"); return KNOT_ENOMEM; } /* Create rdata for TSIG RR. */ uint16_t rdata_rcode = 0; if (tsig_rcode == KNOT_RCODE_BADTIME) rdata_rcode = tsig_rcode; tsig_create_rdata(tmp_tsig, tsig_alg_to_dname(key->algorithm), knot_tsig_digest_length(key->algorithm), rdata_rcode); /* Distinguish BADTIME response. */ if (tsig_rcode == KNOT_RCODE_BADTIME) { /* Set client's time signed into the time signed field. */ tsig_rdata_set_time_signed(tmp_tsig, request_time_signed); /* Store current time into Other data. */ uint8_t time_signed[6]; time_t curr_time = time(NULL); uint64_t time64 = curr_time; knot_wire_write_u48(time_signed, time64); tsig_rdata_set_other_data(tmp_tsig, 6, time_signed); } else { tsig_rdata_store_current_time(tmp_tsig); /* Set other len. */ tsig_rdata_set_other_data(tmp_tsig, 0, 0); } tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); /* Set original ID */ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; size_t digest_tmp_len = 0; int ret = KNOT_ERROR; ret = knot_tsig_create_sign_wire(msg, *msg_len, /*msg_max_len,*/ request_mac, request_mac_len, digest_tmp, &digest_tmp_len, tmp_tsig, key); if (ret != KNOT_EOK) { dbg_tsig("TSIG: could not create wire or sign wire: %s\n", knot_strerror(ret)); knot_rrset_free(&tmp_tsig, NULL); return ret; } /* Set the digest. */ size_t tsig_wire_len = 0; dbg_tsig("TSIG: msg_len=%zu, msg_max_len=%zu, tsig_max_len=%zu\n", *msg_len, msg_max_len, tsig_wire_len); uint16_t rr_count = 0; tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); /* Write RRSet to wire */ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, &tsig_wire_len, msg_max_len - *msg_len, &rr_count, NULL); if (ret != KNOT_EOK) { dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); *digest_len = 0; knot_rrset_free(&tmp_tsig, NULL); return ret; } knot_rrset_free(&tmp_tsig, NULL); dbg_tsig("TSIG: written TSIG RR (wire len %zu)\n", tsig_wire_len); *msg_len += tsig_wire_len; uint16_t arcount = knot_wire_get_arcount(msg); knot_wire_set_arcount(msg, ++arcount); // everything went ok, save the digest to the output parameter memcpy(digest, digest_tmp, digest_tmp_len); *digest_len = digest_tmp_len; return KNOT_EOK; }