]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- xfr-tsig, unit test for tsig_sign_shared and tsig_verify_shared.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 27 Jun 2025 07:24:51 +0000 (09:24 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 27 Jun 2025 07:24:51 +0000 (09:24 +0200)
testcode/unittsig.c
testdata/tsig_test.4
util/tsig.c
util/tsig.h

index 59d3cf3d7ce9d52ac4756dd7f80c68396600d186..52f9fdcbe2acb9dc54bdc5c4c3f302ae3ef1a499 100644 (file)
@@ -44,6 +44,7 @@
 #include "testcode/unitmain.h"
 #include "sldns/parseutil.h"
 #include "sldns/sbuffer.h"
+#include "sldns/str2wire.h"
 #include "sldns/wire2str.h"
 #include <ctype.h>
 
@@ -89,6 +90,17 @@ static int vtest = 0;
  *     If no tsig data is returned, keyname '.', and 0 and 0 are the
  *     tsigerr and tsigothertime values that are checked.
  *
+ * tsig-sign-shared <key> <time> <expected result>
+ *     Looks up key in key_table and signs a reply with it.
+ *     For a shared packet without prior hash of previous packet, since
+ *     there is no previous packet. If the result is not the expected
+ *     result the test fails.
+ * tsig-verify-shared <key> <time> <expected result>
+ *     Looks up key in key_table and verifies a reply with it.
+ *     For a shared packet without prior hash of previous packet, since
+ *     there is no previous packet. If the result is not the expected
+ *     result the test fails.
+ *
  */
 
 /** Clean up first keyword */
@@ -644,6 +656,116 @@ handle_tsig_verify_query(char* line, struct tsig_key_table* key_table,
        tsig_delete(tsig);
 }
 
+/** Handle the tsig-sign-shared */
+static void
+handle_tsig_sign_shared(char* line, struct tsig_key_table* key_table,
+       struct sldns_buffer* pkt)
+{
+       char* arg = get_arg_on_line(line, "tsig-sign-shared");
+       char* keyname, *s, *timestr, *expectedstr;
+       int expected_result, ret;
+       uint64_t timepoint;
+       struct tsig_key* key;
+       size_t pos;
+       uint8_t keynm[256];
+       size_t keynm_len;
+
+       s = arg;
+       keyname = get_next_arg_on_line(&s);
+       timestr = get_next_arg_on_line(&s);
+       expectedstr = get_next_arg_on_line(&s);
+
+       timepoint = (uint64_t)atoll(timestr);
+       if(timepoint == 0 && strcmp(timestr, "0") != 0)
+               fatal_exit("expected time argument for %s", timestr);
+       expected_result = atoi(expectedstr);
+       if(expected_result == 0 && strcmp(expectedstr, "0") != 0)
+               fatal_exit("expected int argument for %s", expectedstr);
+
+       if(vtest)
+               printf("tsig-sign-shared with %s %d %d\n", keyname,
+                       (int)timepoint, expected_result);
+
+       keynm_len = sizeof(keynm);
+       if(sldns_str2wire_dname_buf(keyname, keynm, &keynm_len) != 0)
+               fatal_exit("could not parse '%s'", keyname);
+       key = tsig_key_table_search(key_table, keynm, keynm_len);
+       if(!key)
+               fatal_exit("key not found %s", keyname);
+
+       /* Put position at the end of the packet to sign it. */
+       pos = sldns_buffer_limit(pkt);
+       sldns_buffer_clear(pkt);
+       sldns_buffer_set_position(pkt, pos);
+
+       ret = tsig_sign_shared(pkt, key->name, key->algo->wireformat_name,
+               key->data, key->data_len, timepoint);
+       sldns_buffer_flip(pkt);
+
+       if(vtest) {
+               if(ret == expected_result)
+                       printf("function ok, ret %d\n", ret);
+               else
+                       printf("function returned %d, expected result %d\n",
+                               ret, expected_result);
+       }
+       unit_assert(ret == expected_result);
+}
+
+/** Handle the tsig-verify-shared */
+static void
+handle_tsig_verify_shared(char* line, struct tsig_key_table* key_table,
+       struct sldns_buffer* pkt)
+{
+       char* arg = get_arg_on_line(line, "tsig-verify-shared");
+       char* keyname, *s, *timestr, *expectedstr;
+       int expected_result, ret;
+       uint64_t timepoint;
+       struct tsig_key* key;
+       uint8_t keynm[256];
+       size_t keynm_len, pos;
+
+       s = arg;
+       keyname = get_next_arg_on_line(&s);
+       timestr = get_next_arg_on_line(&s);
+       expectedstr = get_next_arg_on_line(&s);
+
+       timepoint = (uint64_t)atoll(timestr);
+       if(timepoint == 0 && strcmp(timestr, "0") != 0)
+               fatal_exit("expected time argument for %s", timestr);
+       expected_result = atoi(expectedstr);
+       if(expected_result == 0 && strcmp(expectedstr, "0") != 0)
+               fatal_exit("expected int argument for %s", expectedstr);
+
+       if(vtest)
+               printf("tsig-verify-shared with %s %d %d\n", keyname,
+                       (int)timepoint, expected_result);
+
+       keynm_len = sizeof(keynm);
+       if(sldns_str2wire_dname_buf(keyname, keynm, &keynm_len) != 0)
+               fatal_exit("could not parse '%s'", keyname);
+       key = tsig_key_table_search(key_table, keynm, keynm_len);
+       if(!key)
+               fatal_exit("key not found %s", keyname);
+
+       pos = sldns_buffer_limit(pkt);
+       sldns_buffer_clear(pkt);
+       sldns_buffer_set_limit(pkt, pos);
+
+       ret = tsig_verify_shared(pkt, key->name, key->algo->wireformat_name,
+               key->data, key->data_len, timepoint);
+
+       if(vtest) {
+               if(ret == expected_result)
+                       printf("function ok, ret %d\n", ret);
+               else
+                       printf("function returned %d, expected result %d\n",
+                               ret, expected_result);
+       }
+       unit_assert(ret == expected_result);
+}
+
+
 /** Handle one line from the TSIG test file */
 static void
 handle_line(char* line, struct tsig_key_table* key_table,
@@ -667,6 +789,10 @@ handle_line(char* line, struct tsig_key_table* key_table,
                handle_tsig_sign_query(s, key_table, pkt);
        } else if(strncmp(s, "tsig-verify-query", 17) == 0) {
                handle_tsig_verify_query(s, key_table, pkt);
+       } else if(strncmp(s, "tsig-sign-shared", 16) == 0) {
+               handle_tsig_sign_shared(s, key_table, pkt);
+       } else if(strncmp(s, "tsig-verify-shared", 18) == 0) {
+               handle_tsig_verify_shared(s, key_table, pkt);
        } else if(strncmp(s, "#", 1) == 0) {
                /* skip comment */
        } else if(strcmp(s, "") == 0) {
index a916c442e652b1dfdc1f23f50b6caa1f5148137a..33937123a46620a07f5c4ddcd487342953824eaf 100644 (file)
@@ -36,3 +36,15 @@ tsig-verify-query test.key 1750421767 NOERROR NOERROR 0
 #packet
 #c7588400000100010000000203777777076578616d706c65036e65740000010001c00c0001000100000e1000040a141e2800002904d00000000000000474657374036b65790000fa00ff00000000003d0b686d61632d73686132353600000068555107012c0020a377c921817d4009a6ab35e7f84aa697751b3a976701e8fb6b843965325bf9bdc75800000000
 #endpacket
+
+packet
+c7588400000100010000000103777777076578616d706c65036e65740000010001c00c0001000100000e1000040a141e2800002904d0000000000000
+endpacket
+
+tsig-sign-shared test.key 1750411954 0
+
+check-packet
+c7588400000100010000000203777777076578616d706c65036e65740000010001c00c0001000100000e1000040a141e2800002904d00000000000000474657374036b65790000fa00ff00000000003d0b686d61632d73686132353600000068552ab2012c00206a0e6bb9e1228588a8edd5da1dd611bcd0b4e2dd815e7c5501825ea374b3ebacc75800000000
+endpacket
+
+tsig-verify-shared test.key 1750411954 0
index fab8bd5bc1a818aa250d553e01488f5d09f997e1..6d7cb039d2d6d6d0c128e3049878220dc275b2ff 100644 (file)
@@ -409,217 +409,6 @@ skip_pkt_query_rrs(struct sldns_buffer* pkt, int num)
        return 1;
 }
 
-/**
- * Verify pkt with the name (domain name), algorithm and key in Base64.
- * out 0 on success, an error code otherwise.
- */
-int
-tsig_verify(sldns_buffer* pkt, const uint8_t* name, const uint8_t* alg,
-               const uint8_t* secret, size_t secret_len, uint64_t now)
-{
-       size_t   n_rrs;
-       size_t   end_of_message;
-       uint8_t  mac[2048];
-       uint16_t mac_sz;
-       uint8_t  hmac_result[2048];
-       unsigned int hmac_result_len;
-       size_t   pos;
-       uint16_t other_len;
-       size_t   algname_size;
-       uint64_t time_signed;
-       uint16_t fudge;
-       const EVP_MD* digester;
-       uint8_t* tsig_name, *tsig_algo;
-       uint16_t rdlength;
-       uint16_t current_query_id;
-
-       if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) {
-               verbose(VERB_ALGO, "No TSIG, packet too short");
-               return -1;
-       }
-       if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) < 1) {
-               verbose(VERB_ALGO, "No TSIG found, ARCOUNT == 0");
-               return -1;
-       }
-       LDNS_ARCOUNT_SET( sldns_buffer_begin(pkt)
-                       , LDNS_ARCOUNT(sldns_buffer_begin(pkt)) - 1);
-       n_rrs = LDNS_ANCOUNT(sldns_buffer_begin(pkt))
-             + LDNS_NSCOUNT(sldns_buffer_begin(pkt))
-             + LDNS_ARCOUNT(sldns_buffer_begin(pkt));
-
-       sldns_buffer_rewind(pkt);
-       sldns_buffer_skip(pkt, LDNS_HEADER_SIZE);
-
-       /* Skip qnames. */
-       if(!skip_pkt_query_rrs(pkt, LDNS_QDCOUNT(sldns_buffer_begin(pkt)))) {
-               verbose(VERB_ALGO, "No TSIG, query section RRs malformed");
-               return -1;
-       }
-       /* Skip all rrs. */
-       if(!skip_pkt_rrs(pkt, n_rrs)) {
-               verbose(VERB_ALGO, "No TSIG, packet RRs are malformed");
-               return -1;
-       }
-       end_of_message = sldns_buffer_position(pkt);
-
-       /* Skip TSIG name. */
-       if(sldns_buffer_remaining(pkt) < 1) {
-               verbose(VERB_ALGO, "No TSIG, no tsig name");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       tsig_name = sldns_buffer_current(pkt);
-       if(!pkt_dname_len(pkt)) {
-               verbose(VERB_ALGO, "No TSIG, tsig name malformed");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       if(dname_pkt_compare(pkt, tsig_name, (uint8_t*)name) != 0) {
-               sldns_buffer_set_position(pkt, end_of_message);
-               return LDNS_TSIG_ERROR_BADKEY;
-       }
-       pos = sldns_buffer_position(pkt);             /* Append pos */
-
-       /* Skip type, class, TTL and rdlength */
-       if(sldns_buffer_remaining(pkt) < 2 /* type */ + 2 /* class */
-               + 4 /* TTL */ + 2 /* rdlength */) {
-               verbose(VERB_ALGO, "No TSIG, TSIG RR malformed, packet too short");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_TSIG) {
-               verbose(VERB_ALGO, "No TSIG found, wrong RR type");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       sldns_buffer_skip(pkt, sizeof(uint16_t)       /* skip class */
-                            + sizeof(uint32_t));     /* skip TTL */
-       rdlength = sldns_buffer_read_u16(pkt);        /* read rdlength */
-       if(sldns_buffer_remaining(pkt) < rdlength) {
-               verbose(VERB_ALGO, "TSIG rdlength wrong, packet too short");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-
-       /* Read the TSIG rdata. */
-       if(rdlength < 18 /* Rdata length with '.' names, zero lengths. */) {
-               verbose(VERB_ALGO, "TSIG rdata too short");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       tsig_algo = sldns_buffer_current(pkt);
-       /* The algorithm name MUST be uncompressed wireformat (RFC8945),
-        * check that it is. Fails on malformed and skips the name. */
-       if(!(algname_size = query_dname_len(pkt))) {
-               verbose(VERB_ALGO, "TSIG algorithm name malformed");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       if(query_dname_compare(alg, tsig_algo) != 0) {
-               sldns_buffer_set_position(pkt, end_of_message);
-               return LDNS_TSIG_ERROR_BADKEY;
-       }
-       if(rdlength < algname_size + 6 /* time */ + 2 /* fudge */
-               + 2 /* mac size */) {
-               verbose(VERB_ALGO, "TSIG rdata too short for timestamp");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       time_signed = sldns_buffer_read_u48(pkt);     /* time signed */
-       fudge = sldns_buffer_read_u16(pkt);           /* fudge */
-       mac_sz = sldns_buffer_read_u16(pkt);          /* mac size */
-
-       if(rdlength < algname_size + 6 /* time */ + 2 /* fudge */
-               + 2 /* mac size */ + mac_sz + 2 /* origid */
-               + 2 /* error_code */ + 2 /* otherlen */) {
-               verbose(VERB_ALGO, "TSIG rdata too short for mac");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       if(mac_sz > 16384) {
-               /* the hash should not be too big, really 512/8=64 bytes */
-               verbose(VERB_ALGO, "TSIG mac too large");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       if(mac_sz > sizeof(mac)) {
-               verbose(VERB_ALGO, "TSIG mac too large for buffer");
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       memcpy(mac,sldns_buffer_current(pkt),mac_sz); /* copy mac */
-       sldns_buffer_skip(pkt, mac_sz);               /* skip mac */
-
-       /* Set ID to original ID */
-       current_query_id = sldns_buffer_read_u16_at(pkt, 0);
-       sldns_buffer_write_u16_at(pkt, 0, sldns_buffer_read_u16(pkt));
-       sldns_buffer_skip(pkt, sizeof(uint16_t));     /* skip error */
-       other_len = sldns_buffer_read_u16(pkt);       /* other len */
-       if(rdlength < algname_size + 6 /* time */ + 2 /* fudge */
-               + 2 /* mac size */ + mac_sz + 2 /* origid */
-               + 2 /* error_code */ + 2 /* otherlen */ + other_len) {
-               verbose(VERB_ALGO, "TSIG rdata too short for Other Data");
-               sldns_buffer_write_u16_at(pkt, 0, current_query_id);
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       if(rdlength > algname_size + 6 /* time */ + 2 /* fudge */
-               + 2 /* mac size */ + mac_sz + 2 /* origid */
-               + 2 /* error_code */ + 2 /* otherlen */ + other_len) {
-               /* Trailing data in TSIG RR */
-               verbose(VERB_ALGO, "TSIG rdata wrong, it has trailing data");
-               sldns_buffer_write_u16_at(pkt, 0, current_query_id);
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-       if(other_len > 16) {
-               verbose(VERB_ALGO, "TSIG Other Len too large");
-               sldns_buffer_write_u16_at(pkt, 0, current_query_id);
-               sldns_buffer_set_position(pkt, end_of_message);
-               return -1;
-       }
-
-       /* The buffer for the packet is used for the digest buffer
-        * for this message. It fits in the buffer. */
-       /* move CLASS (uint16_t) and TTL (uint32_t) -> TYPE position */
-       memmove( sldns_buffer_at(pkt, pos)
-              , sldns_buffer_at(pkt, pos + sizeof(uint16_t) /* type */)
-              , sizeof(uint16_t) + sizeof(uint32_t));
-       pos +=   sizeof(uint16_t) + sizeof(uint32_t);
-
-       /* Append Algorithm Name, Time signed (uint48_t), Fudge (uint16_t) */
-       memmove( sldns_buffer_at(pkt, pos)
-              , sldns_buffer_at(pkt, pos + sizeof(uint16_t) /* type */
-                                         + sizeof(uint16_t) /* rdlength */)
-              , algname_size + 6 /* sizeof(uint48_t) */ + sizeof(uint16_t));
-       pos +=   algname_size + 6 /* sizeof(uint48_t) */ + sizeof(uint16_t);
-
-       /* Append Error (uint16_t), Other Len (uint16_t), Other Data */
-       memmove( sldns_buffer_at(pkt, pos)
-              , sldns_buffer_at(pkt, sldns_buffer_position(pkt)
-                                   - 2 * sizeof(uint16_t))
-              , 2 * sizeof(uint16_t) + other_len);
-       pos +=   2 * sizeof(uint16_t) + other_len;
-
-       digester = EVP_sha256();
-       hmac_result_len = sizeof(hmac_result);
-       HMAC( digester, secret, secret_len, sldns_buffer_begin(pkt), pos
-           , hmac_result, &hmac_result_len);
-       if(CRYPTO_memcmp(mac, hmac_result, hmac_result_len) == 0) {
-               /* Restore the query id of the packet. */
-               sldns_buffer_write_u16_at(pkt, 0, current_query_id);
-               sldns_buffer_set_position(pkt, end_of_message);
-               /* The TSIG has verified. */
-               return now > time_signed ?
-                    ( time_signed - now > fudge ? LDNS_TSIG_ERROR_BADTIME : 0 )
-                    : now - time_signed > fudge ? LDNS_TSIG_ERROR_BADTIME : 0 ;
-       }
-       /* Restore the query id of the packet. */
-       sldns_buffer_write_u16_at(pkt, 0, current_query_id);
-       sldns_buffer_set_position(pkt, end_of_message);
-       return LDNS_TSIG_ERROR_BADSIG;
-}
-
 struct tsig_data*
 tsig_create(struct tsig_key_table* key_table, uint8_t* name, size_t namelen)
 {
@@ -1329,6 +1118,7 @@ tsig_lookup_key(struct tsig_key_table* key_table,
                return LDNS_RCODE_SERVFAIL;
        }
        tsig->fudge = TSIG_FUDGE_TIME; /* seconds */
+       tsig->klass = LDNS_RR_CLASS_ANY;
        if(region)
                tsig->key_name = regional_alloc(region, rr->key_name_len);
        else
@@ -1690,6 +1480,7 @@ tsig_verify_shared(sldns_buffer* pkt, const uint8_t* name, const uint8_t* alg,
 
        memset(&tsig, 0, sizeof(tsig));
        tsig.fudge = TSIG_FUDGE_TIME; /* seconds */
+       tsig.klass = LDNS_RR_CLASS_ANY;
        tsig.key_name = bufname;
        tsig.key_name_len = bufname_len;
        tsig.algo_name = NULL;
@@ -1767,6 +1558,7 @@ tsig_sign_shared(sldns_buffer* pkt, const uint8_t* name, const uint8_t* alg,
        memset(&tsig, 0, sizeof(tsig));
        tsig.time_signed = now;
        tsig.fudge = TSIG_FUDGE_TIME; /* seconds */
+       tsig.klass = LDNS_RR_CLASS_ANY;
        tsig.key_name = key.name;
        tsig.key_name_len = key.name_len;
        tsig.algo_name = NULL;
index 3b59b06ac45f738d287810376ca92b7af47535fb..b9c8a7ee750c35c5d97b696a7bbea19b3d5e1237 100644 (file)
@@ -244,16 +244,6 @@ struct tsig_algorithm* tsig_algo_find_name(const char* algo_name);
  */
 struct tsig_algorithm* tsig_algo_find_wire(uint8_t* algo);
 
-/**
- * Verify pkt with the name (domain name), algorithm and key.
- * out 0 on success, on failure:
- * -1 for malformed, no tsig RR, or too large for buffer.
- * >0 rcode with a TSIG error code otherwise.
- */
-int tsig_verify(struct sldns_buffer* pkt, const uint8_t* name,
-       const uint8_t* alg, const uint8_t* secret, size_t secret_len,
-       uint64_t now);
-
 /**
  * Sign pkt with the name (domain name), algorithm and key in Base64.
  * out 0 on success, -1 on failure.