]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
tests/mini-dtls-fragments: extend with fragmenting ClientHello
authorAlexander Sosedkin <asosedkin@redhat.com>
Mon, 20 Apr 2026 14:08:11 +0000 (16:08 +0200)
committerAlexander Sosedkin <asosedkin@redhat.com>
Wed, 29 Apr 2026 13:35:02 +0000 (15:35 +0200)
Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
tests/mini-dtls-fragments.c

index 8d5a18acd3ca080b06ab21dfa06244feb372a27c..93490bac204e902bbba5ba4bab7f29e978ab8ba7 100644 (file)
@@ -132,6 +132,39 @@ static ssize_t client_push_normal(gnutls_transport_ptr_t tr, const void *b,
        return queue_put(&c2s, b, l);
 }
 
+static void write_u16(uint8_t *p, uint16_t val)
+{
+       p[0] = val >> 8;
+       p[1] = val & 0xff;
+}
+
+static void write_u24(uint8_t *p, uint32_t val)
+{
+       p[0] = (val >> 16) & 0xff;
+       p[1] = (val >> 8) & 0xff;
+       p[2] = val & 0xff;
+}
+
+static void write_u48(uint8_t *p, uint64_t seq)
+{
+       int i;
+       for (i = 5; i >= 0; i--) {
+               p[i] = seq & 0xff;
+               seq >>= 8;
+       }
+}
+
+static uint64_t read_u48(const uint8_t *p)
+{
+       uint64_t seq = 0;
+       int i;
+       for (i = 5; i >= 0; i--) {
+               seq <<= 8;
+               seq |= p[i];
+       }
+       return seq;
+}
+
 static void test(gnutls_push_func client_push)
 {
        gnutls_session_t client, server;
@@ -316,12 +349,86 @@ static void test_malicious1816(void)
        gnutls_certificate_free_credentials(scred);
 }
 
+static ssize_t queue_put_renumbered(queue_t *q, const uint8_t *data, size_t l,
+                                   int delta_n)
+{
+       if (delta_n == 0 || l < 13 || data[3] != 0 || data[4] != 0)
+               return queue_put(&c2s, data, l);
+
+       uint8_t *p = malloc(l);
+       assert(p);
+       memcpy(p, data, l);
+       write_u48(p + 5, read_u48(p + 5) + delta_n);
+       ssize_t ret = queue_put(q, p, l);
+       free(p);
+       return ret;
+}
+
+static void split_client_hello(const uint8_t *data, size_t len, uint8_t **frag1,
+                              size_t *frag1_len, uint8_t **frag2,
+                              size_t *frag2_len)
+{
+       size_t body_size = len - 25;
+       *frag1_len = 13 + 12 + 1;
+       *frag2_len = 13 + 12 + (body_size - 1);
+
+       *frag1 = malloc(13 + 12 + 1);
+       assert(*frag1);
+       *frag2 = malloc(13 + 12 + body_size - 1);
+       assert(*frag2);
+
+       /* first fragment: record header + handshake header + first body byte */
+       memcpy(*frag1, data, 13); /* record header */
+       write_u16(*frag1 + 11, 12 + 1); /* record length */
+       memcpy(*frag1 + 13, data + 13, 12); /* handshake header */
+       write_u24(*frag1 + 19, 0); /* fragment_offset = 0 */
+       write_u24(*frag1 + 22, 1); /* fragment_length = 1 */
+       (*frag1)[25] = data[25]; /* first body byte */
+
+       /* second fragment: record header + handshake header + remaining body */
+       memcpy(*frag2, data, 13); /* record header */
+       write_u16(*frag2 + 11, *frag2_len - 13); /* record length */
+       write_u48(*frag2 + 5, read_u48(*frag2 + 5) + 1); /* sequence number */
+       memcpy(*frag2 + 13, data + 13, 12); /* handshake header */
+       write_u24(*frag2 + 19, 1); /* fragment_offset = 1 */
+       write_u24(*frag2 + 22, body_size - 1); /* shortened fragment_length */
+       memcpy(*frag2 + 25, data + 26, body_size - 1); /* remaining body */
+}
+
+static ssize_t client_push_split_hello(gnutls_transport_ptr_t tr, const void *b,
+                                      size_t l)
+{
+       static int seq_offset = 0; /* for renumbering follow-up epoch0 ones */
+
+       const uint8_t *data = (const uint8_t *)b;
+       uint8_t *frag1, *frag2;
+       size_t frag1_len, frag2_len;
+
+       /* Pass through anything that isn't an epoch0 ClientHello with body */
+       if (l < 13 + 12 + 1 || /* too short for DTLS record header */
+           data[0] != 22 || /* not a handshake record */
+           data[3] != 0 || data[4] != 0 || /* not epoch 0 */
+           data[13] != 1) /* not ClientHello */
+               return queue_put_renumbered(&c2s, b, l, seq_offset);
+
+       /* epoch0 Client Hello: special treatment of splitting into fragments */
+       split_client_hello(data, l, &frag1, &frag1_len, &frag2, &frag2_len);
+       queue_put(&c2s, frag1, frag1_len);
+       queue_put(&c2s, frag2, frag2_len);
+       free(frag1);
+       free(frag2);
+       seq_offset++;
+       return l;
+}
+
 void doit(void)
 {
        global_init();
        test(client_push_normal);
        success("malicious reassembly bug exploitation (#1816):\n");
        test_malicious1816();
+       success("split client hello smoke-test\n");
+       test(client_push_split_hello);
        gnutls_global_deinit();
 }