jcs's openbsd hax
openbsd

support ed25519 signatures via libcrypto. Mostly by Jeremy Allison Feedback tb@, ok tb@ markus@

djm ce006319 a802f38a

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