Home | History | Annotate | Line # | Download | only in dist
      1 /*	$NetBSD: ssh-ed25519-sk.c,v 1.6 2026/04/08 18:58:41 christos Exp $	*/
      2 /* $OpenBSD: ssh-ed25519-sk.c,v 1.16 2026/02/14 00:18:34 jsg Exp $ */
      3 
      4 /*
      5  * Copyright (c) 2019 Markus Friedl.  All rights reserved.
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 #include "includes.h"
     20 __RCSID("$NetBSD: ssh-ed25519-sk.c,v 1.6 2026/04/08 18:58:41 christos Exp $");
     21 
     22 /* #define DEBUG_SK 1 */
     23 
     24 #define SSHKEY_INTERNAL
     25 #include <sys/types.h>
     26 
     27 #include "crypto_api.h"
     28 
     29 #include <string.h>
     30 #include <stdarg.h>
     31 
     32 #include "log.h"
     33 #include "sshbuf.h"
     34 #include "sshkey.h"
     35 #include "ssherr.h"
     36 #include "digest.h"
     37 
     38 /* Reuse some ED25519 internals */
     39 extern struct sshkey_impl_funcs sshkey_ed25519_funcs;
     40 
     41 static void
     42 ssh_ed25519_sk_cleanup(struct sshkey *k)
     43 {
     44 	sshkey_sk_cleanup(k);
     45 	sshkey_ed25519_funcs.cleanup(k);
     46 }
     47 
     48 static int
     49 ssh_ed25519_sk_equal(const struct sshkey *a, const struct sshkey *b)
     50 {
     51 	if (!sshkey_sk_fields_equal(a, b))
     52 		return 0;
     53 	if (!sshkey_ed25519_funcs.equal(a, b))
     54 		return 0;
     55 	return 1;
     56 }
     57 
     58 static int
     59 ssh_ed25519_sk_serialize_public(const struct sshkey *key, struct sshbuf *b,
     60     enum sshkey_serialize_rep opts)
     61 {
     62 	int r;
     63 
     64 	if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0)
     65 		return r;
     66 	if ((r = sshkey_serialize_sk(key, b)) != 0)
     67 		return r;
     68 
     69 	return 0;
     70 }
     71 
     72 static int
     73 ssh_ed25519_sk_serialize_private(const struct sshkey *key, struct sshbuf *b,
     74     enum sshkey_serialize_rep opts)
     75 {
     76 	int r;
     77 
     78 	if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0)
     79 		return r;
     80 	if ((r = sshkey_serialize_private_sk(key, b)) != 0)
     81 		return r;
     82 
     83 	return 0;
     84 }
     85 
     86 static int
     87 ssh_ed25519_sk_copy_public(const struct sshkey *from, struct sshkey *to)
     88 {
     89 	int r;
     90 
     91 	if ((r = sshkey_ed25519_funcs.copy_public(from, to)) != 0)
     92 		return r;
     93 	if ((r = sshkey_copy_public_sk(from, to)) != 0)
     94 		return r;
     95 	return 0;
     96 }
     97 
     98 static int
     99 ssh_ed25519_sk_deserialize_public(const char *ktype, struct sshbuf *b,
    100     struct sshkey *key)
    101 {
    102 	int r;
    103 
    104 	if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0)
    105 		return r;
    106 	if ((r = sshkey_deserialize_sk(b, key)) != 0)
    107 		return r;
    108 	return 0;
    109 }
    110 
    111 static int
    112 ssh_ed25519_sk_deserialize_private(const char *ktype, struct sshbuf *b,
    113     struct sshkey *key)
    114 {
    115 	int r;
    116 
    117 	if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0)
    118 		return r;
    119 	if ((r = sshkey_private_deserialize_sk(b, key)) != 0)
    120 		return r;
    121 	return 0;
    122 }
    123 
    124 static int
    125 ssh_ed25519_sk_verify(const struct sshkey *key,
    126     const u_char *sig, size_t siglen,
    127     const u_char *data, size_t dlen, const char *alg, u_int compat,
    128     struct sshkey_sig_details **detailsp)
    129 {
    130 	struct sshbuf *b = NULL;
    131 	struct sshbuf *encoded = NULL;
    132 	char *ktype = NULL;
    133 	const u_char *sigblob;
    134 	const u_char *sm;
    135 	u_char *m = NULL;
    136 	u_char apphash[32];
    137 	u_char msghash[32];
    138 	u_char sig_flags;
    139 	u_int sig_counter;
    140 	size_t len;
    141 	unsigned long long smlen = 0, mlen = 0;
    142 	int r = SSH_ERR_INTERNAL_ERROR;
    143 	int ret;
    144 	struct sshkey_sig_details *details = NULL;
    145 
    146 	if (detailsp != NULL)
    147 		*detailsp = NULL;
    148 
    149 	if (key == NULL ||
    150 	    sshkey_type_plain(key->type) != KEY_ED25519_SK ||
    151 	    key->ed25519_pk == NULL ||
    152 	    sig == NULL || siglen == 0)
    153 		return SSH_ERR_INVALID_ARGUMENT;
    154 
    155 	if ((b = sshbuf_from(sig, siglen)) == NULL)
    156 		return SSH_ERR_ALLOC_FAIL;
    157 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
    158 	    sshbuf_get_string_direct(b, &sigblob, &len) != 0 ||
    159 	    sshbuf_get_u8(b, &sig_flags) != 0 ||
    160 	    sshbuf_get_u32(b, &sig_counter) != 0) {
    161 		r = SSH_ERR_INVALID_FORMAT;
    162 		goto out;
    163 	}
    164 #ifdef DEBUG_SK
    165 	fprintf(stderr, "%s: data:\n", __func__);
    166 	/* sshbuf_dump_data(data, datalen, stderr); */
    167 	fprintf(stderr, "%s: sigblob:\n", __func__);
    168 	sshbuf_dump_data(sigblob, len, stderr);
    169 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
    170 	    __func__, sig_flags, sig_counter);
    171 #endif
    172 	if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
    173 		r = SSH_ERR_KEY_TYPE_MISMATCH;
    174 		goto out;
    175 	}
    176 	if (sshbuf_len(b) != 0) {
    177 		r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
    178 		goto out;
    179 	}
    180 	if (len > crypto_sign_ed25519_BYTES) {
    181 		r = SSH_ERR_INVALID_FORMAT;
    182 		goto out;
    183 	}
    184 	if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
    185 	    strlen(key->sk_application), apphash, sizeof(apphash)) != 0 ||
    186 	    ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen,
    187 	    msghash, sizeof(msghash)) != 0) {
    188 		r = SSH_ERR_INVALID_ARGUMENT;
    189 		goto out;
    190 	}
    191 #ifdef DEBUG_SK
    192 	fprintf(stderr, "%s: hashed application:\n", __func__);
    193 	sshbuf_dump_data(apphash, sizeof(apphash), stderr);
    194 	fprintf(stderr, "%s: hashed message:\n", __func__);
    195 	sshbuf_dump_data(msghash, sizeof(msghash), stderr);
    196 #endif
    197 	if ((details = calloc(1, sizeof(*details))) == NULL) {
    198 		r = SSH_ERR_ALLOC_FAIL;
    199 		goto out;
    200 	}
    201 	details->sk_counter = sig_counter;
    202 	details->sk_flags = sig_flags;
    203 	if ((encoded = sshbuf_new()) == NULL) {
    204 		r = SSH_ERR_ALLOC_FAIL;
    205 		goto out;
    206 	}
    207 	if (sshbuf_put(encoded, sigblob, len) != 0 ||
    208 	    sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 ||
    209 	    sshbuf_put_u8(encoded, sig_flags) != 0 ||
    210 	    sshbuf_put_u32(encoded, sig_counter) != 0 ||
    211 	    sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) {
    212 		r = SSH_ERR_ALLOC_FAIL;
    213 		goto out;
    214 	}
    215 #ifdef DEBUG_SK
    216 	fprintf(stderr, "%s: signed buf:\n", __func__);
    217 	sshbuf_dump(encoded, stderr);
    218 #endif
    219 	sm = sshbuf_ptr(encoded);
    220 	smlen = sshbuf_len(encoded);
    221 	mlen = smlen;
    222 	if ((m = malloc(smlen)) == NULL) {
    223 		r = SSH_ERR_ALLOC_FAIL;
    224 		goto out;
    225 	}
    226 	if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
    227 	    key->ed25519_pk)) != 0) {
    228 		debug2_f("crypto_sign_ed25519_open failed: %d", ret);
    229 	}
    230 	if (ret != 0 || mlen != smlen - len) {
    231 		r = SSH_ERR_SIGNATURE_INVALID;
    232 		goto out;
    233 	}
    234 	/* XXX compare 'm' and 'sm + len' ? */
    235 	/* success */
    236 	r = 0;
    237 	if (detailsp != NULL) {
    238 		*detailsp = details;
    239 		details = NULL;
    240 	}
    241  out:
    242 	if (m != NULL)
    243 		freezero(m, smlen); /* NB mlen may be invalid if r != 0 */
    244 	sshkey_sig_details_free(details);
    245 	sshbuf_free(b);
    246 	sshbuf_free(encoded);
    247 	free(ktype);
    248 	return r;
    249 }
    250 
    251 static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = {
    252 	/* .size = */		NULL,
    253 	/* .alloc = */		NULL,
    254 	/* .cleanup = */	ssh_ed25519_sk_cleanup,
    255 	/* .equal = */		ssh_ed25519_sk_equal,
    256 	/* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public,
    257 	/* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public,
    258 	/* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private,
    259 	/* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private,
    260 	/* .generate = */	NULL,
    261 	/* .copy_public = */	ssh_ed25519_sk_copy_public,
    262 	/* .sign = */		NULL,
    263 	/* .verify = */		ssh_ed25519_sk_verify,
    264 };
    265 
    266 const struct sshkey_impl sshkey_ed25519_sk_impl = {
    267 	/* .name = */		"sk-ssh-ed25519 (at) openssh.com",
    268 	/* .shortname = */	"ED25519-SK",
    269 	/* .sigalg = */		NULL,
    270 	/* .type = */		KEY_ED25519_SK,
    271 	/* .nid = */		0,
    272 	/* .cert = */		0,
    273 	/* .sigonly = */	0,
    274 	/* .keybits = */	256,
    275 	/* .funcs = */		&sshkey_ed25519_sk_funcs,
    276 };
    277 
    278 const struct sshkey_impl sshkey_ed25519_sk_cert_impl = {
    279 	/* .name = */		"sk-ssh-ed25519-cert-v01 (at) openssh.com",
    280 	/* .shortname = */	"ED25519-SK-CERT",
    281 	/* .sigalg = */		NULL,
    282 	/* .type = */		KEY_ED25519_SK_CERT,
    283 	/* .nid = */		0,
    284 	/* .cert = */		1,
    285 	/* .sigonly = */	0,
    286 	/* .keybits = */	256,
    287 	/* .funcs = */		&sshkey_ed25519_sk_funcs,
    288 };
    289