xref: /freebsd/crypto/openssh/ed25519-openssl.c (revision 2574974648c68c738aec3ff96644d888d7913a37)
1*25749746SEd Maste /* $OpenBSD: ed25519-openssl.c,v 1.1 2025/10/30 20:49:10 djm Exp $ */
2*25749746SEd Maste /*
3*25749746SEd Maste  * Copyright (c) 2025 OpenSSH
4*25749746SEd Maste  *
5*25749746SEd Maste  * Permission to use, copy, modify, and distribute this software for any
6*25749746SEd Maste  * purpose with or without fee is hereby granted, provided that the above
7*25749746SEd Maste  * copyright notice and this permission notice appear in all copies.
8*25749746SEd Maste  *
9*25749746SEd Maste  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*25749746SEd Maste  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*25749746SEd Maste  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*25749746SEd Maste  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*25749746SEd Maste  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*25749746SEd Maste  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*25749746SEd Maste  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*25749746SEd Maste  */
17*25749746SEd Maste 
18*25749746SEd Maste /*
19*25749746SEd Maste  * OpenSSL-based implementation of Ed25519 crypto_sign API
20*25749746SEd Maste  * Alternative to the internal SUPERCOP-based implementation in ed25519.c
21*25749746SEd Maste  */
22*25749746SEd Maste 
23*25749746SEd Maste #include "includes.h"
24*25749746SEd Maste 
25*25749746SEd Maste #ifdef OPENSSL_HAS_ED25519
26*25749746SEd Maste 
27*25749746SEd Maste #include <sys/types.h>
28*25749746SEd Maste 
29*25749746SEd Maste #include <string.h>
30*25749746SEd Maste #include <stdint.h>
31*25749746SEd Maste #include <limits.h>
32*25749746SEd Maste 
33*25749746SEd Maste #include <openssl/evp.h>
34*25749746SEd Maste 
35*25749746SEd Maste #include "crypto_api.h"
36*25749746SEd Maste #include "log.h"
37*25749746SEd Maste 
38*25749746SEd Maste #if crypto_sign_ed25519_SECRETKEYBYTES <= crypto_sign_ed25519_PUBLICKEYBYTES
39*25749746SEd Maste #error "crypto_sign_ed25519_SECRETKEYBYTES < crypto_sign_ed25519_PUBLICKEYBYTES"
40*25749746SEd Maste #endif
41*25749746SEd Maste 
42*25749746SEd Maste #define SSH_ED25519_RAW_SECRET_KEY_LEN \
43*25749746SEd Maste     (crypto_sign_ed25519_SECRETKEYBYTES - crypto_sign_ed25519_PUBLICKEYBYTES)
44*25749746SEd Maste 
45*25749746SEd Maste int
crypto_sign_ed25519_keypair(unsigned char * pk,unsigned char * sk)46*25749746SEd Maste crypto_sign_ed25519_keypair(unsigned char *pk, unsigned char *sk)
47*25749746SEd Maste {
48*25749746SEd Maste 	EVP_PKEY_CTX *ctx = NULL;
49*25749746SEd Maste 	EVP_PKEY *pkey = NULL;
50*25749746SEd Maste 	size_t pklen, sklen;
51*25749746SEd Maste 	int ret = -1;
52*25749746SEd Maste 
53*25749746SEd Maste 	if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL)) == NULL) {
54*25749746SEd Maste 		debug3_f("EVP_PKEY_CTX_new_id failed");
55*25749746SEd Maste 		goto out;
56*25749746SEd Maste 	}
57*25749746SEd Maste 	if (EVP_PKEY_keygen_init(ctx) <= 0) {
58*25749746SEd Maste 		debug3_f("EVP_PKEY_keygen_init failed");
59*25749746SEd Maste 		goto out;
60*25749746SEd Maste 	}
61*25749746SEd Maste 	if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
62*25749746SEd Maste 		debug3_f("EVP_PKEY_keygen failed");
63*25749746SEd Maste 		goto out;
64*25749746SEd Maste 	}
65*25749746SEd Maste 
66*25749746SEd Maste 	/* Extract public key */
67*25749746SEd Maste 	pklen = crypto_sign_ed25519_PUBLICKEYBYTES;
68*25749746SEd Maste 	if (!EVP_PKEY_get_raw_public_key(pkey, pk, &pklen)) {
69*25749746SEd Maste 		debug3_f("EVP_PKEY_get_raw_public_key failed");
70*25749746SEd Maste 		goto out;
71*25749746SEd Maste 	}
72*25749746SEd Maste 	if (pklen != crypto_sign_ed25519_PUBLICKEYBYTES) {
73*25749746SEd Maste 		debug3_f("public key length mismatch: %zu", pklen);
74*25749746SEd Maste 		goto out;
75*25749746SEd Maste 	}
76*25749746SEd Maste 
77*25749746SEd Maste 	sklen = SSH_ED25519_RAW_SECRET_KEY_LEN;
78*25749746SEd Maste 	/* Extract private key (32 bytes seed) */
79*25749746SEd Maste 	if (!EVP_PKEY_get_raw_private_key(pkey, sk, &sklen)) {
80*25749746SEd Maste 		debug3_f("EVP_PKEY_get_raw_private_key failed");
81*25749746SEd Maste 		goto out;
82*25749746SEd Maste 	}
83*25749746SEd Maste 	if (sklen != SSH_ED25519_RAW_SECRET_KEY_LEN) {
84*25749746SEd Maste 		debug3_f("private key length mismatch: %zu", sklen);
85*25749746SEd Maste 		goto out;
86*25749746SEd Maste 	}
87*25749746SEd Maste 
88*25749746SEd Maste 	/* Append public key to secret key (SUPERCOP format compatibility) */
89*25749746SEd Maste 	memcpy(sk + sklen, pk, crypto_sign_ed25519_PUBLICKEYBYTES);
90*25749746SEd Maste 
91*25749746SEd Maste 	ret = 0;
92*25749746SEd Maste out:
93*25749746SEd Maste 	EVP_PKEY_free(pkey);
94*25749746SEd Maste 	EVP_PKEY_CTX_free(ctx);
95*25749746SEd Maste 	return ret;
96*25749746SEd Maste }
97*25749746SEd Maste 
98*25749746SEd Maste int
crypto_sign_ed25519(unsigned char * sm,unsigned long long * smlen,const unsigned char * m,unsigned long long mlen,const unsigned char * sk)99*25749746SEd Maste crypto_sign_ed25519(unsigned char *sm, unsigned long long *smlen,
100*25749746SEd Maste     const unsigned char *m, unsigned long long mlen,
101*25749746SEd Maste     const unsigned char *sk)
102*25749746SEd Maste {
103*25749746SEd Maste 	EVP_PKEY *pkey = NULL;
104*25749746SEd Maste 	EVP_MD_CTX *mdctx = NULL;
105*25749746SEd Maste 	size_t siglen;
106*25749746SEd Maste 	int ret = -1;
107*25749746SEd Maste 
108*25749746SEd Maste 	/* Create EVP_PKEY from secret key (first 32 bytes are the seed) */
109*25749746SEd Maste 	if ((pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
110*25749746SEd Maste 	    sk, SSH_ED25519_RAW_SECRET_KEY_LEN)) == NULL) {
111*25749746SEd Maste 		debug3_f("EVP_PKEY_new_raw_private_key failed");
112*25749746SEd Maste 		goto out;
113*25749746SEd Maste 	}
114*25749746SEd Maste 
115*25749746SEd Maste 	/* Sign the message */
116*25749746SEd Maste 	if ((mdctx = EVP_MD_CTX_new()) == NULL) {
117*25749746SEd Maste 		debug3_f("EVP_MD_CTX_new failed");
118*25749746SEd Maste 		goto out;
119*25749746SEd Maste 	}
120*25749746SEd Maste 	if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
121*25749746SEd Maste 		debug3_f("EVP_DigestSignInit failed");
122*25749746SEd Maste 		goto out;
123*25749746SEd Maste 	}
124*25749746SEd Maste 	siglen = crypto_sign_ed25519_BYTES;
125*25749746SEd Maste 	if (EVP_DigestSign(mdctx, sm, &siglen, m, mlen) != 1) {
126*25749746SEd Maste 		debug3_f("EVP_DigestSign failed");
127*25749746SEd Maste 		goto out;
128*25749746SEd Maste 	}
129*25749746SEd Maste 	if (siglen != crypto_sign_ed25519_BYTES) {
130*25749746SEd Maste 		debug3_f("signature length mismatch: %zu", siglen);
131*25749746SEd Maste 		goto out;
132*25749746SEd Maste 	}
133*25749746SEd Maste 
134*25749746SEd Maste 	/* Append message after signature (SUPERCOP format) */
135*25749746SEd Maste 	if (mlen > ULLONG_MAX - siglen) {
136*25749746SEd Maste 		debug3_f("message length overflow: siglen=%zu mlen=%llu",
137*25749746SEd Maste 		    siglen, mlen);
138*25749746SEd Maste 		goto out;
139*25749746SEd Maste 	}
140*25749746SEd Maste 	memmove(sm + siglen, m, mlen);
141*25749746SEd Maste 	*smlen = siglen + mlen;
142*25749746SEd Maste 
143*25749746SEd Maste 	ret = 0;
144*25749746SEd Maste out:
145*25749746SEd Maste 	EVP_MD_CTX_free(mdctx);
146*25749746SEd Maste 	EVP_PKEY_free(pkey);
147*25749746SEd Maste 	return ret;
148*25749746SEd Maste }
149*25749746SEd Maste 
150*25749746SEd Maste int
crypto_sign_ed25519_open(unsigned char * m,unsigned long long * mlen,const unsigned char * sm,unsigned long long smlen,const unsigned char * pk)151*25749746SEd Maste crypto_sign_ed25519_open(unsigned char *m, unsigned long long *mlen,
152*25749746SEd Maste     const unsigned char *sm, unsigned long long smlen,
153*25749746SEd Maste     const unsigned char *pk)
154*25749746SEd Maste {
155*25749746SEd Maste 	EVP_PKEY *pkey = NULL;
156*25749746SEd Maste 	EVP_MD_CTX *mdctx = NULL;
157*25749746SEd Maste 	int ret = -1;
158*25749746SEd Maste 	const unsigned char *msg;
159*25749746SEd Maste 	size_t msglen;
160*25749746SEd Maste 
161*25749746SEd Maste 	if (smlen < crypto_sign_ed25519_BYTES) {
162*25749746SEd Maste 		debug3_f("signed message bad length: %llu", smlen);
163*25749746SEd Maste 		return -1;
164*25749746SEd Maste 	}
165*25749746SEd Maste 	/* Signature is first crypto_sign_ed25519_BYTES, message follows */
166*25749746SEd Maste 	msg = sm + crypto_sign_ed25519_BYTES;
167*25749746SEd Maste 	msglen = smlen - crypto_sign_ed25519_BYTES;
168*25749746SEd Maste 
169*25749746SEd Maste 	/* Make sure the message buffer is big enough. */
170*25749746SEd Maste 	if (*mlen < msglen) {
171*25749746SEd Maste 		debug_f("message bad length: %llu", *mlen);
172*25749746SEd Maste 		return -1;
173*25749746SEd Maste 	}
174*25749746SEd Maste 
175*25749746SEd Maste 	/* Create EVP_PKEY from public key */
176*25749746SEd Maste 	if ((pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,
177*25749746SEd Maste 	    pk, crypto_sign_ed25519_PUBLICKEYBYTES)) == NULL) {
178*25749746SEd Maste 		debug3_f("EVP_PKEY_new_raw_public_key failed");
179*25749746SEd Maste 		goto out;
180*25749746SEd Maste 	}
181*25749746SEd Maste 
182*25749746SEd Maste 	if ((mdctx = EVP_MD_CTX_new()) == NULL) {
183*25749746SEd Maste 		debug3_f("EVP_MD_CTX_new failed");
184*25749746SEd Maste 		goto out;
185*25749746SEd Maste 	}
186*25749746SEd Maste 	if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) <= 0) {
187*25749746SEd Maste 		debug3_f("EVP_DigestVerifyInit failed");
188*25749746SEd Maste 		goto out;
189*25749746SEd Maste 	}
190*25749746SEd Maste 	if (EVP_DigestVerify(mdctx, sm, crypto_sign_ed25519_BYTES,
191*25749746SEd Maste 	    msg, msglen) != 1) {
192*25749746SEd Maste 		debug3_f("EVP_DigestVerify failed");
193*25749746SEd Maste 		goto out;
194*25749746SEd Maste 	}
195*25749746SEd Maste 
196*25749746SEd Maste 	/* Copy message out */
197*25749746SEd Maste 	*mlen = msglen;
198*25749746SEd Maste 	memmove(m, msg, msglen);
199*25749746SEd Maste 
200*25749746SEd Maste 	ret = 0;
201*25749746SEd Maste out:
202*25749746SEd Maste 	EVP_MD_CTX_free(mdctx);
203*25749746SEd Maste 	EVP_PKEY_free(pkey);
204*25749746SEd Maste 	return ret;
205*25749746SEd Maste }
206*25749746SEd Maste 
207*25749746SEd Maste #endif /* OPENSSL_HAS_ED25519 */
208