Coverage Report

Created: 2025-08-29 06:35

/src/dropbear/src/kex-pqhybrid.c
Line
Count
Source (jump to first uncovered line)
1
#include "includes.h"
2
#include "algo.h"
3
#include "buffer.h"
4
#include "session.h"
5
#include "bignum.h"
6
#include "dbrandom.h"
7
#include "crypto_desc.h"
8
#include "curve25519.h"
9
#include "kex.h"
10
11
#if DROPBEAR_PQHYBRID
12
13
0
struct kex_pqhybrid_param *gen_kexpqhybrid_param() {
14
0
    struct kex_pqhybrid_param *param = m_malloc(sizeof(*param));
15
0
    const struct dropbear_kem_desc *kem = ses.newkeys->algo_kex->details;
16
17
0
    param->curve25519 = gen_kexcurve25519_param();
18
19
0
    if (IS_DROPBEAR_CLIENT) {
20
0
        param->kem_cli_secret = buf_new(kem->secret_len);
21
0
        param->concat_public = buf_new(kem->public_len + CURVE25519_LEN);
22
0
        kem->kem_gen(
23
0
            buf_getwriteptr(param->concat_public, kem->public_len),
24
0
            buf_getwriteptr(param->kem_cli_secret, kem->secret_len));
25
0
        buf_incrwritepos(param->concat_public, kem->public_len);
26
0
        buf_incrwritepos(param->kem_cli_secret, kem->secret_len);
27
0
        buf_setpos(param->kem_cli_secret, 0);
28
        /* Append the curve25519 parameter */
29
0
        buf_putbytes(param->concat_public, param->curve25519->pub, CURVE25519_LEN);
30
0
    }
31
32
0
    return param;
33
0
}
34
35
0
void free_kexpqhybrid_param(struct kex_pqhybrid_param *param) {
36
0
    free_kexcurve25519_param(param->curve25519);
37
0
    if (param->kem_cli_secret) {
38
0
        buf_burn_free(param->kem_cli_secret);
39
0
        param->kem_cli_secret = NULL;
40
0
    }
41
0
    buf_free(param->concat_public);
42
0
    m_free(param);
43
0
}
44
45
void kexpqhybrid_comb_key(struct kex_pqhybrid_param *param,
46
0
    buffer *buf_pub, sign_key *hostkey) {
47
48
0
    const struct dropbear_kem_desc *kem = ses.newkeys->algo_kex->details;
49
0
    const struct ltc_hash_descriptor *hash_desc
50
0
        = ses.newkeys->algo_kex->hash_desc;
51
52
    /* Either public key (from client) or ciphertext (from server) */
53
0
    unsigned char *remote_pub_kem = NULL;
54
0
    buffer *pub_25519 = NULL;
55
0
    buffer *k_out = NULL;
56
0
    unsigned int remote_len;
57
0
    hash_state hs;
58
0
    const buffer * Q_C = NULL;
59
0
    const buffer * Q_S = NULL;
60
61
    /* Extract input parts from the remote peer */
62
0
    if (IS_DROPBEAR_CLIENT) {
63
        /* S_REPLY = S_CT2 || S_PK1 */
64
0
        remote_len = kem->ciphertext_len;
65
0
    } else {
66
        /* C_INIT = C_PK2 || C_PK1 */
67
0
        remote_len = kem->public_len;
68
0
    }
69
0
    remote_pub_kem = buf_getptr(buf_pub, remote_len);
70
0
    buf_incrpos(buf_pub, remote_len);
71
0
    pub_25519 = buf_getptrcopy(buf_pub, CURVE25519_LEN);
72
0
    buf_incrpos(buf_pub, CURVE25519_LEN);
73
    /* Check all is consumed */
74
0
    if (buf_pub->pos != buf_pub->len) {
75
0
        dropbear_exit("Bad sntrup");
76
0
    }
77
78
    /* k_out = K_PQ || K_CL */
79
0
    k_out = buf_new(kem->output_len + CURVE25519_LEN);
80
81
    /* Derive pq kem part (K_PQ) */
82
0
    if (IS_DROPBEAR_CLIENT) {
83
0
        kem->kem_dec(
84
0
            buf_getwriteptr(k_out, kem->output_len),
85
0
            remote_pub_kem,
86
0
            buf_getptr(param->kem_cli_secret, kem->secret_len));
87
0
        buf_burn_free(param->kem_cli_secret);
88
0
        param->kem_cli_secret = NULL;
89
0
    } else {
90
        /* Server returns ciphertext */
91
0
        assert(param->concat_public == NULL);
92
0
        param->concat_public = buf_new(kem->ciphertext_len + CURVE25519_LEN);
93
0
        kem->kem_enc(
94
0
            buf_getwriteptr(param->concat_public, kem->ciphertext_len),
95
0
            buf_getwriteptr(k_out, kem->output_len),
96
0
            remote_pub_kem);
97
0
        buf_incrwritepos(param->concat_public, kem->ciphertext_len);
98
        /* Append the curve25519 parameter */
99
0
        buf_putbytes(param->concat_public, param->curve25519->pub, CURVE25519_LEN);
100
0
    }
101
0
    buf_incrwritepos(k_out, kem->output_len);
102
103
    /* Derive ec part (K_CL) */
104
0
    kexcurve25519_derive(param->curve25519, pub_25519,
105
0
        buf_getwriteptr(k_out, CURVE25519_LEN));
106
0
    buf_incrwritepos(k_out, CURVE25519_LEN);
107
108
    /* dh_K_bytes = HASH(k_out)
109
       dh_K_bytes is a SSH string with length prefix, since
110
       that is what needs to be hashed in gen_new_keys() */
111
0
    ses.dh_K_bytes = buf_new(4 + hash_desc->hashsize);
112
0
    buf_putint(ses.dh_K_bytes, hash_desc->hashsize);
113
0
    hash_desc->init(&hs);
114
0
    hash_desc->process(&hs, k_out->data, k_out->len);
115
0
    hash_desc->done(&hs, buf_getwriteptr(ses.dh_K_bytes, hash_desc->hashsize));
116
0
    m_burn(&hs, sizeof(hash_state));
117
0
    buf_incrwritepos(ses.dh_K_bytes, hash_desc->hashsize);
118
119
    /* Create the remainder of the hash buffer */
120
0
    if (IS_DROPBEAR_CLIENT) {
121
0
        Q_C = param->concat_public;
122
0
        Q_S = buf_pub;
123
0
    } else {
124
0
        Q_S = param->concat_public;
125
0
        Q_C = buf_pub;
126
0
    }
127
128
    /* K_S, the host key */
129
0
    buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey);
130
0
    buf_putbufstring(ses.kexhashbuf, Q_C);
131
0
    buf_putbufstring(ses.kexhashbuf, Q_S);
132
    /* K, the shared secret */
133
0
    buf_putbytes(ses.kexhashbuf, ses.dh_K_bytes->data, ses.dh_K_bytes->len);
134
135
    /* calculate the hash H to sign */
136
0
    finish_kexhashbuf();
137
138
0
    buf_burn_free(k_out);
139
0
    buf_free(pub_25519);
140
0
}
141
142
#endif /* DROPBEAR_PQHYBRID */