1*a2c931d9Sdjm /* $OpenBSD: ssh-ed25519-sk.c,v 1.15 2022/10/28 00:44:44 djm Exp $ */
259bab1bcSmarkus /*
359bab1bcSmarkus * Copyright (c) 2019 Markus Friedl. All rights reserved.
459bab1bcSmarkus *
559bab1bcSmarkus * Permission to use, copy, modify, and distribute this software for any
659bab1bcSmarkus * purpose with or without fee is hereby granted, provided that the above
759bab1bcSmarkus * copyright notice and this permission notice appear in all copies.
859bab1bcSmarkus *
959bab1bcSmarkus * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1059bab1bcSmarkus * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1159bab1bcSmarkus * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1259bab1bcSmarkus * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1359bab1bcSmarkus * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1459bab1bcSmarkus * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1559bab1bcSmarkus * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1659bab1bcSmarkus */
17584bea6cSdjm
18584bea6cSdjm /* #define DEBUG_SK 1 */
19584bea6cSdjm
2059bab1bcSmarkus #define SSHKEY_INTERNAL
2159bab1bcSmarkus #include <sys/types.h>
2259bab1bcSmarkus #include <limits.h>
2359bab1bcSmarkus
2459bab1bcSmarkus #include "crypto_api.h"
2559bab1bcSmarkus
2659bab1bcSmarkus #include <string.h>
2759bab1bcSmarkus #include <stdarg.h>
2859bab1bcSmarkus
2959bab1bcSmarkus #include "log.h"
3059bab1bcSmarkus #include "sshbuf.h"
3159bab1bcSmarkus #include "sshkey.h"
3259bab1bcSmarkus #include "ssherr.h"
3359bab1bcSmarkus #include "ssh.h"
3459bab1bcSmarkus #include "digest.h"
3559bab1bcSmarkus
36712f5ecfSdjm /* Reuse some ED25519 internals */
37712f5ecfSdjm extern struct sshkey_impl_funcs sshkey_ed25519_funcs;
38712f5ecfSdjm
399c1667dbSdjm static void
ssh_ed25519_sk_cleanup(struct sshkey * k)409c1667dbSdjm ssh_ed25519_sk_cleanup(struct sshkey *k)
419c1667dbSdjm {
42712f5ecfSdjm sshkey_sk_cleanup(k);
43712f5ecfSdjm sshkey_ed25519_funcs.cleanup(k);
44712f5ecfSdjm }
45712f5ecfSdjm
46712f5ecfSdjm static int
ssh_ed25519_sk_equal(const struct sshkey * a,const struct sshkey * b)47712f5ecfSdjm ssh_ed25519_sk_equal(const struct sshkey *a, const struct sshkey *b)
48712f5ecfSdjm {
49712f5ecfSdjm if (!sshkey_sk_fields_equal(a, b))
50712f5ecfSdjm return 0;
51712f5ecfSdjm if (!sshkey_ed25519_funcs.equal(a, b))
52712f5ecfSdjm return 0;
53712f5ecfSdjm return 1;
549c1667dbSdjm }
559c1667dbSdjm
56eefcf659Sdjm static int
ssh_ed25519_sk_serialize_public(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)57eefcf659Sdjm ssh_ed25519_sk_serialize_public(const struct sshkey *key, struct sshbuf *b,
58c8d92406Sdjm enum sshkey_serialize_rep opts)
59eefcf659Sdjm {
60eefcf659Sdjm int r;
61eefcf659Sdjm
62c8d92406Sdjm if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0)
63eefcf659Sdjm return r;
64eefcf659Sdjm if ((r = sshkey_serialize_sk(key, b)) != 0)
65eefcf659Sdjm return r;
66eefcf659Sdjm
67eefcf659Sdjm return 0;
68eefcf659Sdjm }
69eefcf659Sdjm
700d39f001Sdjm static int
ssh_ed25519_sk_serialize_private(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)71d03db38bSdjm ssh_ed25519_sk_serialize_private(const struct sshkey *key, struct sshbuf *b,
72d03db38bSdjm enum sshkey_serialize_rep opts)
73d03db38bSdjm {
74d03db38bSdjm int r;
75d03db38bSdjm
76d03db38bSdjm if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0)
77d03db38bSdjm return r;
78d03db38bSdjm if ((r = sshkey_serialize_private_sk(key, b)) != 0)
79d03db38bSdjm return r;
80d03db38bSdjm
81d03db38bSdjm return 0;
82d03db38bSdjm }
83d03db38bSdjm
84d03db38bSdjm static int
ssh_ed25519_sk_copy_public(const struct sshkey * from,struct sshkey * to)850d39f001Sdjm ssh_ed25519_sk_copy_public(const struct sshkey *from, struct sshkey *to)
860d39f001Sdjm {
870d39f001Sdjm int r;
880d39f001Sdjm
890d39f001Sdjm if ((r = sshkey_ed25519_funcs.copy_public(from, to)) != 0)
900d39f001Sdjm return r;
910d39f001Sdjm if ((r = sshkey_copy_public_sk(from, to)) != 0)
920d39f001Sdjm return r;
930d39f001Sdjm return 0;
940d39f001Sdjm }
950d39f001Sdjm
96c8d92406Sdjm static int
ssh_ed25519_sk_deserialize_public(const char * ktype,struct sshbuf * b,struct sshkey * key)97c8d92406Sdjm ssh_ed25519_sk_deserialize_public(const char *ktype, struct sshbuf *b,
98c8d92406Sdjm struct sshkey *key)
99c8d92406Sdjm {
100c8d92406Sdjm int r;
101c8d92406Sdjm
102c8d92406Sdjm if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0)
103c8d92406Sdjm return r;
104c8d92406Sdjm if ((r = sshkey_deserialize_sk(b, key)) != 0)
105c8d92406Sdjm return r;
106c8d92406Sdjm return 0;
107c8d92406Sdjm }
108c8d92406Sdjm
109c5c174faSdjm static int
ssh_ed25519_sk_deserialize_private(const char * ktype,struct sshbuf * b,struct sshkey * key)110*a2c931d9Sdjm ssh_ed25519_sk_deserialize_private(const char *ktype, struct sshbuf *b,
111*a2c931d9Sdjm struct sshkey *key)
112*a2c931d9Sdjm {
113*a2c931d9Sdjm int r;
114*a2c931d9Sdjm
115*a2c931d9Sdjm if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0)
116*a2c931d9Sdjm return r;
117*a2c931d9Sdjm if ((r = sshkey_private_deserialize_sk(b, key)) != 0)
118*a2c931d9Sdjm return r;
119*a2c931d9Sdjm return 0;
120*a2c931d9Sdjm }
121*a2c931d9Sdjm
122*a2c931d9Sdjm static int
ssh_ed25519_sk_verify(const struct sshkey * key,const u_char * sig,size_t siglen,const u_char * data,size_t dlen,const char * alg,u_int compat,struct sshkey_sig_details ** detailsp)12359bab1bcSmarkus ssh_ed25519_sk_verify(const struct sshkey *key,
124c5c174faSdjm const u_char *sig, size_t siglen,
125c5c174faSdjm const u_char *data, size_t dlen, const char *alg, u_int compat,
126493ad5b0Sdjm struct sshkey_sig_details **detailsp)
12759bab1bcSmarkus {
12859bab1bcSmarkus struct sshbuf *b = NULL;
12959bab1bcSmarkus struct sshbuf *encoded = NULL;
13059bab1bcSmarkus char *ktype = NULL;
13159bab1bcSmarkus const u_char *sigblob;
13259bab1bcSmarkus const u_char *sm;
13359bab1bcSmarkus u_char *m = NULL;
13459bab1bcSmarkus u_char apphash[32];
13559bab1bcSmarkus u_char msghash[32];
13659bab1bcSmarkus u_char sig_flags;
13759bab1bcSmarkus u_int sig_counter;
13859bab1bcSmarkus size_t len;
13959bab1bcSmarkus unsigned long long smlen = 0, mlen = 0;
14059bab1bcSmarkus int r = SSH_ERR_INTERNAL_ERROR;
14159bab1bcSmarkus int ret;
142493ad5b0Sdjm struct sshkey_sig_details *details = NULL;
143493ad5b0Sdjm
144493ad5b0Sdjm if (detailsp != NULL)
145493ad5b0Sdjm *detailsp = NULL;
14659bab1bcSmarkus
14759bab1bcSmarkus if (key == NULL ||
14859bab1bcSmarkus sshkey_type_plain(key->type) != KEY_ED25519_SK ||
14959bab1bcSmarkus key->ed25519_pk == NULL ||
150c5c174faSdjm sig == NULL || siglen == 0)
15159bab1bcSmarkus return SSH_ERR_INVALID_ARGUMENT;
15259bab1bcSmarkus
153c5c174faSdjm if ((b = sshbuf_from(sig, siglen)) == NULL)
15459bab1bcSmarkus return SSH_ERR_ALLOC_FAIL;
15559bab1bcSmarkus if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
15637ada6ffSmarkus sshbuf_get_string_direct(b, &sigblob, &len) != 0 ||
15737ada6ffSmarkus sshbuf_get_u8(b, &sig_flags) != 0 ||
15837ada6ffSmarkus sshbuf_get_u32(b, &sig_counter) != 0) {
15959bab1bcSmarkus r = SSH_ERR_INVALID_FORMAT;
16059bab1bcSmarkus goto out;
16159bab1bcSmarkus }
162584bea6cSdjm #ifdef DEBUG_SK
163584bea6cSdjm fprintf(stderr, "%s: data:\n", __func__);
164584bea6cSdjm /* sshbuf_dump_data(data, datalen, stderr); */
165584bea6cSdjm fprintf(stderr, "%s: sigblob:\n", __func__);
166584bea6cSdjm sshbuf_dump_data(sigblob, len, stderr);
167584bea6cSdjm fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
168584bea6cSdjm __func__, sig_flags, sig_counter);
169584bea6cSdjm #endif
17059bab1bcSmarkus if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
17159bab1bcSmarkus r = SSH_ERR_KEY_TYPE_MISMATCH;
17259bab1bcSmarkus goto out;
17359bab1bcSmarkus }
17459bab1bcSmarkus if (sshbuf_len(b) != 0) {
17559bab1bcSmarkus r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
17659bab1bcSmarkus goto out;
17759bab1bcSmarkus }
17859bab1bcSmarkus if (len > crypto_sign_ed25519_BYTES) {
17959bab1bcSmarkus r = SSH_ERR_INVALID_FORMAT;
18059bab1bcSmarkus goto out;
18159bab1bcSmarkus }
18259bab1bcSmarkus if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application,
18359bab1bcSmarkus strlen(key->sk_application), apphash, sizeof(apphash)) != 0 ||
184c5c174faSdjm ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen,
18559bab1bcSmarkus msghash, sizeof(msghash)) != 0) {
18659bab1bcSmarkus r = SSH_ERR_INVALID_ARGUMENT;
18759bab1bcSmarkus goto out;
18859bab1bcSmarkus }
189584bea6cSdjm #ifdef DEBUG_SK
190584bea6cSdjm fprintf(stderr, "%s: hashed application:\n", __func__);
191584bea6cSdjm sshbuf_dump_data(apphash, sizeof(apphash), stderr);
192584bea6cSdjm fprintf(stderr, "%s: hashed message:\n", __func__);
193584bea6cSdjm sshbuf_dump_data(msghash, sizeof(msghash), stderr);
194584bea6cSdjm #endif
195493ad5b0Sdjm if ((details = calloc(1, sizeof(*details))) == NULL) {
196493ad5b0Sdjm r = SSH_ERR_ALLOC_FAIL;
197493ad5b0Sdjm goto out;
198493ad5b0Sdjm }
199493ad5b0Sdjm details->sk_counter = sig_counter;
200493ad5b0Sdjm details->sk_flags = sig_flags;
20159bab1bcSmarkus if ((encoded = sshbuf_new()) == NULL) {
20259bab1bcSmarkus r = SSH_ERR_ALLOC_FAIL;
20359bab1bcSmarkus goto out;
20459bab1bcSmarkus }
20559bab1bcSmarkus if (sshbuf_put(encoded, sigblob, len) != 0 ||
20659bab1bcSmarkus sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 ||
20759bab1bcSmarkus sshbuf_put_u8(encoded, sig_flags) != 0 ||
20859bab1bcSmarkus sshbuf_put_u32(encoded, sig_counter) != 0 ||
20959bab1bcSmarkus sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) {
21059bab1bcSmarkus r = SSH_ERR_ALLOC_FAIL;
21159bab1bcSmarkus goto out;
21259bab1bcSmarkus }
213584bea6cSdjm #ifdef DEBUG_SK
214584bea6cSdjm fprintf(stderr, "%s: signed buf:\n", __func__);
215584bea6cSdjm sshbuf_dump(encoded, stderr);
216584bea6cSdjm #endif
21759bab1bcSmarkus sm = sshbuf_ptr(encoded);
21859bab1bcSmarkus smlen = sshbuf_len(encoded);
21959bab1bcSmarkus mlen = smlen;
22059bab1bcSmarkus if ((m = malloc(smlen)) == NULL) {
22159bab1bcSmarkus r = SSH_ERR_ALLOC_FAIL;
22259bab1bcSmarkus goto out;
22359bab1bcSmarkus }
22459bab1bcSmarkus if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
22559bab1bcSmarkus key->ed25519_pk)) != 0) {
22648e6b99dSdjm debug2_f("crypto_sign_ed25519_open failed: %d", ret);
22759bab1bcSmarkus }
22859bab1bcSmarkus if (ret != 0 || mlen != smlen - len) {
22959bab1bcSmarkus r = SSH_ERR_SIGNATURE_INVALID;
23059bab1bcSmarkus goto out;
23159bab1bcSmarkus }
23259bab1bcSmarkus /* XXX compare 'm' and 'sm + len' ? */
23359bab1bcSmarkus /* success */
23459bab1bcSmarkus r = 0;
235493ad5b0Sdjm if (detailsp != NULL) {
236493ad5b0Sdjm *detailsp = details;
237493ad5b0Sdjm details = NULL;
238493ad5b0Sdjm }
23959bab1bcSmarkus out:
240c9831b39Sjsg if (m != NULL)
241c9831b39Sjsg freezero(m, smlen); /* NB mlen may be invalid if r != 0 */
242493ad5b0Sdjm sshkey_sig_details_free(details);
24359bab1bcSmarkus sshbuf_free(b);
24459bab1bcSmarkus sshbuf_free(encoded);
24559bab1bcSmarkus free(ktype);
24659bab1bcSmarkus return r;
24759bab1bcSmarkus }
2489c1667dbSdjm
2499c1667dbSdjm static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = {
2509c1667dbSdjm /* .size = */ NULL,
2519c1667dbSdjm /* .alloc = */ NULL,
2529c1667dbSdjm /* .cleanup = */ ssh_ed25519_sk_cleanup,
253712f5ecfSdjm /* .equal = */ ssh_ed25519_sk_equal,
254eefcf659Sdjm /* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public,
255c8d92406Sdjm /* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public,
256d03db38bSdjm /* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private,
257*a2c931d9Sdjm /* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private,
258b6025febSdjm /* .generate = */ NULL,
2590d39f001Sdjm /* .copy_public = */ ssh_ed25519_sk_copy_public,
260c5c174faSdjm /* .sign = */ NULL,
261c5c174faSdjm /* .verify = */ ssh_ed25519_sk_verify,
2629c1667dbSdjm };
2639c1667dbSdjm
2649c1667dbSdjm const struct sshkey_impl sshkey_ed25519_sk_impl = {
2659c1667dbSdjm /* .name = */ "sk-ssh-ed25519@openssh.com",
2669c1667dbSdjm /* .shortname = */ "ED25519-SK",
2679c1667dbSdjm /* .sigalg = */ NULL,
2689c1667dbSdjm /* .type = */ KEY_ED25519_SK,
2699c1667dbSdjm /* .nid = */ 0,
2709c1667dbSdjm /* .cert = */ 0,
2719c1667dbSdjm /* .sigonly = */ 0,
2729c1667dbSdjm /* .keybits = */ 256,
2739c1667dbSdjm /* .funcs = */ &sshkey_ed25519_sk_funcs,
2749c1667dbSdjm };
2759c1667dbSdjm
2769c1667dbSdjm const struct sshkey_impl sshkey_ed25519_sk_cert_impl = {
2779c1667dbSdjm /* .name = */ "sk-ssh-ed25519-cert-v01@openssh.com",
2789c1667dbSdjm /* .shortname = */ "ED25519-SK-CERT",
2799c1667dbSdjm /* .sigalg = */ NULL,
2809c1667dbSdjm /* .type = */ KEY_ED25519_SK_CERT,
2819c1667dbSdjm /* .nid = */ 0,
2829c1667dbSdjm /* .cert = */ 1,
2839c1667dbSdjm /* .sigonly = */ 0,
2849c1667dbSdjm /* .keybits = */ 256,
2859c1667dbSdjm /* .funcs = */ &sshkey_ed25519_sk_funcs,
2869c1667dbSdjm };
287