aboutsummaryrefslogtreecommitdiff
path: root/src/pki_ed25519_common.c
blob: 9db14dacc18bcbd5ac0484ae38dbc5f535a51e5c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/*
 * pki_ed25519_common.c - Common ed25519 functions
 *
 * This file is part of the SSH Library
 *
 * Copyright (c) 2014 by Aris Adamantiadis
 *
 * The SSH Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * The SSH Library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with the SSH Library; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

#include "config.h"

#include "libssh/pki.h"
#include "libssh/pki_priv.h"
#include "libssh/buffer.h"

int pki_privkey_build_ed25519(ssh_key key,
                              ssh_string pubkey,
                              ssh_string privkey)
{
    if (ssh_string_len(pubkey) != ED25519_KEY_LEN ||
        ssh_string_len(privkey) != (2 * ED25519_KEY_LEN))
    {
        SSH_LOG(SSH_LOG_WARN, "Invalid ed25519 key len");
        return SSH_ERROR;
    }

#ifdef HAVE_OPENSSL_ED25519
    /* In OpenSSL implementation, the private key is the original private seed,
     * without the public key. */
    key->ed25519_privkey = malloc(ED25519_KEY_LEN);
#else
    /* In the internal implementation, the private key is the concatenation of
     * the private seed with the public key. */
    key->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
#endif
    if (key->ed25519_privkey == NULL) {
        goto error;
    }

    key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
    if (key->ed25519_pubkey == NULL) {
        goto error;
    }

#ifdef HAVE_OPENSSL_ED25519
    memcpy(key->ed25519_privkey, ssh_string_data(privkey),
           ED25519_KEY_LEN);
#else
    memcpy(key->ed25519_privkey, ssh_string_data(privkey),
           2 * ED25519_KEY_LEN);
#endif
    memcpy(key->ed25519_pubkey, ssh_string_data(pubkey),
           ED25519_KEY_LEN);

    return SSH_OK;

error:
    SAFE_FREE(key->ed25519_privkey);
    SAFE_FREE(key->ed25519_pubkey);

    return SSH_ERROR;
}

/**
 * @internal
 *
 * @brief Compare ed25519 keys if they are equal.
 *
 * @param[in] k1        The first key to compare.
 *
 * @param[in] k2        The second key to compare.
 *
 * @param[in] what      What part or type of the key do you want to compare.
 *
 * @return              0 if equal, 1 if not.
 */
int pki_ed25519_key_cmp(const ssh_key k1,
                        const ssh_key k2,
                        enum ssh_keycmp_e what)
{
    int cmp;

    switch(what) {
    case SSH_KEY_CMP_PRIVATE:
        if (k1->ed25519_privkey == NULL || k2->ed25519_privkey == NULL) {
            return 1;
        }
#ifdef HAVE_OPENSSL_ED25519
        /* In OpenSSL implementation, the private key is the original private
         * seed, without the public key. */
        cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, ED25519_KEY_LEN);
#else
        /* In the internal implementation, the private key is the concatenation
         * of the private seed with the public key. */
        cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey,
                     2 * ED25519_KEY_LEN);
#endif
        if (cmp != 0) {
            return 1;
        }
        FALL_THROUGH;
    case SSH_KEY_CMP_PUBLIC:
        if (k1->ed25519_pubkey == NULL || k2->ed25519_pubkey == NULL) {
            return 1;
        }
        cmp = memcmp(k1->ed25519_pubkey, k2->ed25519_pubkey, ED25519_KEY_LEN);
        if (cmp != 0) {
            return 1;
        }
    }

    return 0;
}

/**
 * @internal
 *
 * @brief Duplicate an Ed25519 key
 *
 * @param[out] new Pre-initialized ssh_key structure
 *
 * @param[in] key Key to copy
 *
 * @return SSH_ERROR on error, SSH_OK on success
 */
int pki_ed25519_key_dup(ssh_key new, const ssh_key key)
{
    if (key->ed25519_privkey == NULL && key->ed25519_pubkey == NULL) {
        return SSH_ERROR;
    }

    if (key->ed25519_privkey != NULL) {
#ifdef HAVE_OPENSSL_ED25519
        /* In OpenSSL implementation, the private key is the original private
         * seed, without the public key. */
        new->ed25519_privkey = malloc(ED25519_KEY_LEN);
#else
        /* In the internal implementation, the private key is the concatenation
         * of the private seed with the public key. */
        new->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
#endif
        if (new->ed25519_privkey == NULL) {
            return SSH_ERROR;
        }
#ifdef HAVE_OPENSSL_ED25519
        memcpy(new->ed25519_privkey, key->ed25519_privkey, ED25519_KEY_LEN);
#else
        memcpy(new->ed25519_privkey, key->ed25519_privkey, 2 * ED25519_KEY_LEN);
#endif
    }

    if (key->ed25519_pubkey != NULL) {
        new->ed25519_pubkey = malloc(ED25519_KEY_LEN);
        if (new->ed25519_pubkey == NULL) {
            SAFE_FREE(new->ed25519_privkey);
            return SSH_ERROR;
        }
        memcpy(new->ed25519_pubkey, key->ed25519_pubkey, ED25519_KEY_LEN);
    }

    return SSH_OK;
}

/**
 * @internal
 *
 * @brief Outputs an Ed25519 public key in a blob buffer.
 *
 * @param[out] buffer Output buffer
 *
 * @param[in] key Key to output
 *
 * @return SSH_ERROR on error, SSH_OK on success
 */
int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key)
{
    int rc;

    if (key->ed25519_pubkey == NULL){
        return SSH_ERROR;
    }

    rc = ssh_buffer_pack(buffer,
                         "dP",
                         (uint32_t)ED25519_KEY_LEN,
                         (size_t)ED25519_KEY_LEN, key->ed25519_pubkey);

    return rc;
}

/**
 * @internal
 *
 * @brief output a signature blob from an ed25519 signature
 *
 * @param[in] sig signature to convert
 *
 * @return Signature blob in SSH string, or NULL on error
 */
ssh_string pki_ed25519_signature_to_blob(ssh_signature sig)
{
    ssh_string sig_blob;

#ifdef HAVE_OPENSSL_ED25519
    /* When using the OpenSSL implementation, the signature is stored in raw_sig
     * which is shared by all algorithms.*/
    if (sig->raw_sig == NULL) {
        return NULL;
    }
#else
    /* When using the internal implementation, the signature is stored in an
     * algorithm specific field. */
    if (sig->ed25519_sig == NULL) {
        return NULL;
    }
#endif

    sig_blob = ssh_string_new(ED25519_SIG_LEN);
    if (sig_blob == NULL) {
        return NULL;
    }

#ifdef HAVE_OPENSSL_ED25519
    ssh_string_fill(sig_blob, ssh_string_data(sig->raw_sig),
                    ssh_string_len(sig->raw_sig));
#else
    ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN);
#endif

    return sig_blob;
}

/**
 * @internal
 *
 * @brief Convert a signature blob in an ed25519 signature.
 *
 * @param[out] sig a preinitialized signature
 *
 * @param[in] sig_blob a signature blob
 *
 * @return SSH_ERROR on error, SSH_OK on success
 */
int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob)
{
    size_t len;

    len = ssh_string_len(sig_blob);
    if (len != ED25519_SIG_LEN){
        SSH_LOG(SSH_LOG_WARN, "Invalid ssh-ed25519 signature len: %zu", len);
        return SSH_ERROR;
    }

#ifdef HAVE_OPENSSL_ED25519
    sig->raw_sig = ssh_string_copy(sig_blob);
#else
    sig->ed25519_sig = malloc(ED25519_SIG_LEN);
    if (sig->ed25519_sig == NULL){
        return SSH_ERROR;
    }
    memcpy(sig->ed25519_sig, ssh_string_data(sig_blob), ED25519_SIG_LEN);
#endif

    return SSH_OK;
}