Home | History | Annotate | Line # | Download | only in dist
      1 /*	$NetBSD: ssh-ed25519-sk.c,v 1.7 2026/10/07 17:32:08 christos Exp $	*/
      2 /* $OpenBSD: ssh-ed25519-sk.c,v 1.17 2026/09/16 00:43:00 djm 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.7 2026/10/07 17:32:08 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 	u_char apphash[32];
    135 	u_char msghash[32];
    136 	u_char sig_flags;
    137 	u_int sig_counter;
    138 	size_t len;
    139 	int r = SSH_ERR_INTERNAL_ERROR;
    140 	int ret;
    141 	struct sshkey_sig_details *details = NULL;
    142 
    143 	if (detailsp != NULL)
    144 		*detailsp = NULL;
    145 
    146 	if (key == NULL ||
    147 	    sshkey_type_plain(key->type) != KEY_ED25519_SK ||
    148 	    key->ed25519_pk == NULL ||
    149 	    sig == NULL || siglen == 0)
    150 		return SSH_ERR_INVALID_ARGUMENT;
    151 
    152 	if ((b = sshbuf_from(sig, siglen)) == NULL)
    153 		return SSH_ERR_ALLOC_FAIL;
    154 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
    155 	    sshbuf_get_string_direct(b, &sigblob, &len) != 0 ||
    156 	    sshbuf_get_u8(b, &sig_flags) != 0 ||
    157 	    sshbuf_get_u32(b, &sig_counter) != 0) {
    158 		r = SSH_ERR_INVALID_FORMAT;
    159 		goto out;
    160 	}
    161 #ifdef DEBUG_SK
    162 	fprintf(stderr, "%s: data:\n", __func__);
    163 	/* sshbuf_dump_data(data, datalen, stderr); */
    164 	fprintf(stderr, "%s: sigblob:\n", __func__);
    165 	sshbuf_dump_data(sigblob, len, stderr);
    166 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
    167 	    __func__, sig_flags, sig_counter);
    168 #endif
    169 	if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
    170 		r = SSH_ERR_KEY_TYPE_MISMATCH;
    171 		goto out;
    172 	}
    173 	if (sshbuf_len(b) != 0) {
    174 		r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
    175 		goto out;
    176 	}
    177 	if (len != crypto_sign_ed25519_BYTES) {
    178 		r = SSH_ERR_INVALID_FORMAT;
    179 		goto out;
    180 	}
    181 	if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
    182 	    strlen(key->sk_application), apphash, sizeof(apphash)) != 0 ||
    183 	    ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen,
    184 	    msghash, sizeof(msghash)) != 0) {
    185 		r = SSH_ERR_INVALID_ARGUMENT;
    186 		goto out;
    187 	}
    188 #ifdef DEBUG_SK
    189 	fprintf(stderr, "%s: hashed application:\n", __func__);
    190 	sshbuf_dump_data(apphash, sizeof(apphash), stderr);
    191 	fprintf(stderr, "%s: hashed message:\n", __func__);
    192 	sshbuf_dump_data(msghash, sizeof(msghash), stderr);
    193 #endif
    194 	if ((details = calloc(1, sizeof(*details))) == NULL) {
    195 		r = SSH_ERR_ALLOC_FAIL;
    196 		goto out;
    197 	}
    198 	details->sk_counter = sig_counter;
    199 	details->sk_flags = sig_flags;
    200 	if ((encoded = sshbuf_new()) == NULL) {
    201 		r = SSH_ERR_ALLOC_FAIL;
    202 		goto out;
    203 	}
    204 	if (sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 ||
    205 	    sshbuf_put_u8(encoded, sig_flags) != 0 ||
    206 	    sshbuf_put_u32(encoded, sig_counter) != 0 ||
    207 	    sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) {
    208 		r = SSH_ERR_ALLOC_FAIL;
    209 		goto out;
    210 	}
    211 #ifdef DEBUG_SK
    212 	fprintf(stderr, "%s: signed buf:\n", __func__);
    213 	sshbuf_dump(encoded, stderr);
    214 #endif
    215 	if ((ret = crypto_sign_ed25519_verify_detached(sigblob,
    216 	    sshbuf_ptr(encoded), sshbuf_len(encoded),
    217 	    key->ed25519_pk)) != 0) {
    218 		debug2_f("crypto_sign_ed25519_verify_detached failed: %d", ret);
    219 	}
    220 	if (ret != 0) {
    221 		r = SSH_ERR_SIGNATURE_INVALID;
    222 		goto out;
    223 	}
    224 	/* success */
    225 	r = 0;
    226 	if (detailsp != NULL) {
    227 		*detailsp = details;
    228 		details = NULL;
    229 	}
    230  out:
    231 	sshkey_sig_details_free(details);
    232 	sshbuf_free(b);
    233 	sshbuf_free(encoded);
    234 	free(ktype);
    235 	return r;
    236 }
    237 
    238 static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = {
    239 	/* .size = */		NULL,
    240 	/* .alloc = */		NULL,
    241 	/* .cleanup = */	ssh_ed25519_sk_cleanup,
    242 	/* .equal = */		ssh_ed25519_sk_equal,
    243 	/* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public,
    244 	/* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public,
    245 	/* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private,
    246 	/* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private,
    247 	/* .generate = */	NULL,
    248 	/* .copy_public = */	ssh_ed25519_sk_copy_public,
    249 	/* .sign = */		NULL,
    250 	/* .verify = */		ssh_ed25519_sk_verify,
    251 };
    252 
    253 const struct sshkey_impl sshkey_ed25519_sk_impl = {
    254 	/* .name = */		"sk-ssh-ed25519 (at) openssh.com",
    255 	/* .shortname = */	"ED25519-SK",
    256 	/* .sigalg = */		NULL,
    257 	/* .type = */		KEY_ED25519_SK,
    258 	/* .nid = */		0,
    259 	/* .cert = */		0,
    260 	/* .sigonly = */	0,
    261 	/* .keybits = */	256,
    262 	/* .funcs = */		&sshkey_ed25519_sk_funcs,
    263 };
    264 
    265 const struct sshkey_impl sshkey_ed25519_sk_cert_impl = {
    266 	/* .name = */		"sk-ssh-ed25519-cert-v01 (at) openssh.com",
    267 	/* .shortname = */	"ED25519-SK-CERT",
    268 	/* .sigalg = */		NULL,
    269 	/* .type = */		KEY_ED25519_SK_CERT,
    270 	/* .nid = */		0,
    271 	/* .cert = */		1,
    272 	/* .sigonly = */	0,
    273 	/* .keybits = */	256,
    274 	/* .funcs = */		&sshkey_ed25519_sk_funcs,
    275 };
    276