1 /* $OpenBSD: ssh-rsa.c,v 1.80 2024/08/15 00:51:51 djm Exp $ */
3 * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
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.
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.
22 #include <sys/types.h>
24 #include <openssl/evp.h>
25 #include <openssl/err.h>
32 #define SSHKEY_INTERNAL
37 #include "openbsd-compat/openssl-compat.h"
40 ssh_rsa_size(const struct sshkey
*k
)
44 return EVP_PKEY_bits(k
->pkey
);
48 ssh_rsa_alloc(struct sshkey
*k
)
50 if ((k
->pkey
= EVP_PKEY_new()) == NULL
)
51 return SSH_ERR_ALLOC_FAIL
;
56 ssh_rsa_cleanup(struct sshkey
*k
)
58 EVP_PKEY_free(k
->pkey
);
63 ssh_rsa_equal(const struct sshkey
*a
, const struct sshkey
*b
)
65 if (a
->pkey
== NULL
|| b
->pkey
== NULL
)
67 return EVP_PKEY_cmp(a
->pkey
, b
->pkey
) == 1;
71 ssh_rsa_serialize_public(const struct sshkey
*key
, struct sshbuf
*b
,
72 enum sshkey_serialize_rep opts
)
75 const BIGNUM
*rsa_n
, *rsa_e
;
78 if (key
->pkey
== NULL
)
79 return SSH_ERR_INVALID_ARGUMENT
;
80 if ((rsa
= EVP_PKEY_get0_RSA(key
->pkey
)) == NULL
)
81 return SSH_ERR_LIBCRYPTO_ERROR
;
83 RSA_get0_key(rsa
, &rsa_n
, &rsa_e
, NULL
);
84 if ((r
= sshbuf_put_bignum2(b
, rsa_e
)) != 0 ||
85 (r
= sshbuf_put_bignum2(b
, rsa_n
)) != 0)
92 ssh_rsa_serialize_private(const struct sshkey
*key
, struct sshbuf
*b
,
93 enum sshkey_serialize_rep opts
)
96 const BIGNUM
*rsa_n
, *rsa_e
, *rsa_d
, *rsa_iqmp
, *rsa_p
, *rsa_q
;
99 if ((rsa
= EVP_PKEY_get0_RSA(key
->pkey
)) == NULL
)
100 return SSH_ERR_LIBCRYPTO_ERROR
;
101 RSA_get0_key(rsa
, &rsa_n
, &rsa_e
, &rsa_d
);
102 RSA_get0_factors(rsa
, &rsa_p
, &rsa_q
);
103 RSA_get0_crt_params(rsa
, NULL
, NULL
, &rsa_iqmp
);
105 if (!sshkey_is_cert(key
)) {
106 /* Note: can't reuse ssh_rsa_serialize_public: e, n vs. n, e */
107 if ((r
= sshbuf_put_bignum2(b
, rsa_n
)) != 0 ||
108 (r
= sshbuf_put_bignum2(b
, rsa_e
)) != 0)
111 if ((r
= sshbuf_put_bignum2(b
, rsa_d
)) != 0 ||
112 (r
= sshbuf_put_bignum2(b
, rsa_iqmp
)) != 0 ||
113 (r
= sshbuf_put_bignum2(b
, rsa_p
)) != 0 ||
114 (r
= sshbuf_put_bignum2(b
, rsa_q
)) != 0)
121 ssh_rsa_generate(struct sshkey
*k
, int bits
)
123 EVP_PKEY_CTX
*ctx
= NULL
;
124 EVP_PKEY
*res
= NULL
;
126 int ret
= SSH_ERR_INTERNAL_ERROR
;
128 if (bits
< SSH_RSA_MINIMUM_MODULUS_SIZE
||
129 bits
> SSHBUF_MAX_BIGNUM
* 8)
130 return SSH_ERR_KEY_LENGTH
;
132 if ((ctx
= EVP_PKEY_CTX_new_id(EVP_PKEY_RSA
, NULL
)) == NULL
) {
133 ret
= SSH_ERR_ALLOC_FAIL
;
136 if (EVP_PKEY_keygen_init(ctx
) <= 0) {
137 ret
= SSH_ERR_LIBCRYPTO_ERROR
;
140 if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx
, bits
) <= 0) {
141 ret
= SSH_ERR_KEY_LENGTH
;
144 if (EVP_PKEY_keygen(ctx
, &res
) <= 0 || res
== NULL
) {
145 ret
= SSH_ERR_LIBCRYPTO_ERROR
;
152 EVP_PKEY_CTX_free(ctx
);
157 ssh_rsa_copy_public(const struct sshkey
*from
, struct sshkey
*to
)
159 const BIGNUM
*rsa_n
, *rsa_e
;
160 BIGNUM
*rsa_n_dup
= NULL
, *rsa_e_dup
= NULL
;
161 int r
= SSH_ERR_INTERNAL_ERROR
;
165 if ((rsa_from
= EVP_PKEY_get0_RSA(from
->pkey
)) == NULL
||
166 (rsa_to
= RSA_new()) == NULL
)
167 return SSH_ERR_LIBCRYPTO_ERROR
;
169 RSA_get0_key(rsa_from
, &rsa_n
, &rsa_e
, NULL
);
170 if ((rsa_n_dup
= BN_dup(rsa_n
)) == NULL
||
171 (rsa_e_dup
= BN_dup(rsa_e
)) == NULL
) {
172 r
= SSH_ERR_ALLOC_FAIL
;
175 if (!RSA_set0_key(rsa_to
, rsa_n_dup
, rsa_e_dup
, NULL
)) {
176 r
= SSH_ERR_LIBCRYPTO_ERROR
;
179 rsa_n_dup
= rsa_e_dup
= NULL
; /* transferred */
181 if (EVP_PKEY_set1_RSA(to
->pkey
, rsa_to
) != 1) {
182 r
= SSH_ERR_LIBCRYPTO_ERROR
;
189 BN_clear_free(rsa_n_dup
);
190 BN_clear_free(rsa_e_dup
);
195 ssh_rsa_deserialize_public(const char *ktype
, struct sshbuf
*b
,
198 int ret
= SSH_ERR_INTERNAL_ERROR
;
199 BIGNUM
*rsa_n
= NULL
, *rsa_e
= NULL
;
202 if ((rsa
= RSA_new()) == NULL
)
203 return SSH_ERR_LIBCRYPTO_ERROR
;
205 if (sshbuf_get_bignum2(b
, &rsa_e
) != 0 ||
206 sshbuf_get_bignum2(b
, &rsa_n
) != 0) {
207 ret
= SSH_ERR_INVALID_FORMAT
;
210 if (!RSA_set0_key(rsa
, rsa_n
, rsa_e
, NULL
)) {
211 ret
= SSH_ERR_LIBCRYPTO_ERROR
;
214 rsa_n
= rsa_e
= NULL
; /* transferred */
215 if (EVP_PKEY_set1_RSA(key
->pkey
, rsa
) != 1) {
216 ret
= SSH_ERR_LIBCRYPTO_ERROR
;
219 if ((ret
= sshkey_check_rsa_length(key
, 0)) != 0)
222 RSA_print_fp(stderr
, rsa
, 8);
228 BN_clear_free(rsa_n
);
229 BN_clear_free(rsa_e
);
234 ssh_rsa_deserialize_private(const char *ktype
, struct sshbuf
*b
,
238 BIGNUM
*rsa_n
= NULL
, *rsa_e
= NULL
, *rsa_d
= NULL
;
239 BIGNUM
*rsa_iqmp
= NULL
, *rsa_p
= NULL
, *rsa_q
= NULL
;
240 BIGNUM
*rsa_dmp1
= NULL
, *rsa_dmq1
= NULL
;
243 if (sshkey_is_cert(key
)) {
244 /* sshkey_private_deserialize already has pubkey from cert */
245 if ((rsa
= EVP_PKEY_get1_RSA(key
->pkey
)) == NULL
) {
246 r
= SSH_ERR_LIBCRYPTO_ERROR
;
250 if ((rsa
= RSA_new()) == NULL
) {
251 r
= SSH_ERR_LIBCRYPTO_ERROR
;
254 /* Note: can't reuse ssh_rsa_deserialize_public: e,n vs. n,e */
255 if ((r
= sshbuf_get_bignum2(b
, &rsa_n
)) != 0 ||
256 (r
= sshbuf_get_bignum2(b
, &rsa_e
)) != 0)
258 if (!RSA_set0_key(rsa
, rsa_n
, rsa_e
, NULL
)) {
259 r
= SSH_ERR_LIBCRYPTO_ERROR
;
262 rsa_n
= rsa_e
= NULL
; /* transferred */
264 if ((r
= sshbuf_get_bignum2(b
, &rsa_d
)) != 0 ||
265 (r
= sshbuf_get_bignum2(b
, &rsa_iqmp
)) != 0 ||
266 (r
= sshbuf_get_bignum2(b
, &rsa_p
)) != 0 ||
267 (r
= sshbuf_get_bignum2(b
, &rsa_q
)) != 0)
269 if ((r
= ssh_rsa_complete_crt_parameters(rsa_d
, rsa_p
, rsa_q
,
270 rsa_iqmp
, &rsa_dmp1
, &rsa_dmq1
)) != 0)
272 if (!RSA_set0_key(rsa
, NULL
, NULL
, rsa_d
)) {
273 r
= SSH_ERR_LIBCRYPTO_ERROR
;
276 rsa_d
= NULL
; /* transferred */
277 if (!RSA_set0_factors(rsa
, rsa_p
, rsa_q
)) {
278 r
= SSH_ERR_LIBCRYPTO_ERROR
;
281 rsa_p
= rsa_q
= NULL
; /* transferred */
282 if (!RSA_set0_crt_params(rsa
, rsa_dmp1
, rsa_dmq1
, rsa_iqmp
)) {
283 r
= SSH_ERR_LIBCRYPTO_ERROR
;
286 rsa_dmp1
= rsa_dmq1
= rsa_iqmp
= NULL
;
287 if (RSA_blinding_on(rsa
, NULL
) != 1) {
288 r
= SSH_ERR_LIBCRYPTO_ERROR
;
291 if (EVP_PKEY_set1_RSA(key
->pkey
, rsa
) != 1) {
292 r
= SSH_ERR_LIBCRYPTO_ERROR
;
295 if ((r
= sshkey_check_rsa_length(key
, 0)) != 0)
301 BN_clear_free(rsa_n
);
302 BN_clear_free(rsa_e
);
303 BN_clear_free(rsa_d
);
304 BN_clear_free(rsa_p
);
305 BN_clear_free(rsa_q
);
306 BN_clear_free(rsa_iqmp
);
307 BN_clear_free(rsa_dmp1
);
308 BN_clear_free(rsa_dmq1
);
313 rsa_hash_alg_ident(int hash_alg
)
316 case SSH_DIGEST_SHA1
:
318 case SSH_DIGEST_SHA256
:
319 return "rsa-sha2-256";
320 case SSH_DIGEST_SHA512
:
321 return "rsa-sha2-512";
327 * Returns the hash algorithm ID for a given algorithm identifier as used
328 * inside the signature blob,
331 rsa_hash_id_from_ident(const char *ident
)
333 if (strcmp(ident
, "ssh-rsa") == 0)
334 return SSH_DIGEST_SHA1
;
335 if (strcmp(ident
, "rsa-sha2-256") == 0)
336 return SSH_DIGEST_SHA256
;
337 if (strcmp(ident
, "rsa-sha2-512") == 0)
338 return SSH_DIGEST_SHA512
;
343 * Return the hash algorithm ID for the specified key name. This includes
344 * all the cases of rsa_hash_id_from_ident() but also the certificate key
348 rsa_hash_id_from_keyname(const char *alg
)
352 if ((r
= rsa_hash_id_from_ident(alg
)) != -1)
354 if (strcmp(alg
, "ssh-rsa-cert-v01@openssh.com") == 0)
355 return SSH_DIGEST_SHA1
;
356 if (strcmp(alg
, "rsa-sha2-256-cert-v01@openssh.com") == 0)
357 return SSH_DIGEST_SHA256
;
358 if (strcmp(alg
, "rsa-sha2-512-cert-v01@openssh.com") == 0)
359 return SSH_DIGEST_SHA512
;
364 ssh_rsa_complete_crt_parameters(const BIGNUM
*rsa_d
, const BIGNUM
*rsa_p
,
365 const BIGNUM
*rsa_q
, const BIGNUM
*rsa_iqmp
, BIGNUM
**rsa_dmp1
,
368 BIGNUM
*aux
= NULL
, *d_consttime
= NULL
;
372 *rsa_dmq1
= *rsa_dmp1
= NULL
;
373 if ((ctx
= BN_CTX_new()) == NULL
)
374 return SSH_ERR_ALLOC_FAIL
;
375 if ((aux
= BN_new()) == NULL
||
376 (*rsa_dmq1
= BN_new()) == NULL
||
377 (*rsa_dmp1
= BN_new()) == NULL
)
378 return SSH_ERR_ALLOC_FAIL
;
379 if ((d_consttime
= BN_dup(rsa_d
)) == NULL
) {
380 r
= SSH_ERR_ALLOC_FAIL
;
383 BN_set_flags(aux
, BN_FLG_CONSTTIME
);
384 BN_set_flags(d_consttime
, BN_FLG_CONSTTIME
);
386 if ((BN_sub(aux
, rsa_q
, BN_value_one()) == 0) ||
387 (BN_mod(*rsa_dmq1
, d_consttime
, aux
, ctx
) == 0) ||
388 (BN_sub(aux
, rsa_p
, BN_value_one()) == 0) ||
389 (BN_mod(*rsa_dmp1
, d_consttime
, aux
, ctx
) == 0)) {
390 r
= SSH_ERR_LIBCRYPTO_ERROR
;
397 BN_clear_free(d_consttime
);
402 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
404 ssh_rsa_sign(struct sshkey
*key
,
405 u_char
**sigp
, size_t *lenp
,
406 const u_char
*data
, size_t datalen
,
407 const char *alg
, const char *sk_provider
, const char *sk_pin
, u_int compat
)
410 size_t diff
, len
= 0;
412 int hash_alg
, ret
= SSH_ERR_INTERNAL_ERROR
;
413 struct sshbuf
*b
= NULL
;
420 if (alg
== NULL
|| strlen(alg
) == 0)
421 hash_alg
= SSH_DIGEST_SHA1
;
423 hash_alg
= rsa_hash_id_from_keyname(alg
);
425 if (key
== NULL
|| key
->pkey
== NULL
|| hash_alg
== -1 ||
426 sshkey_type_plain(key
->type
) != KEY_RSA
)
427 return SSH_ERR_INVALID_ARGUMENT
;
428 slen
= EVP_PKEY_size(key
->pkey
);
429 if (slen
<= 0 || slen
> SSHBUF_MAX_BIGNUM
)
430 return SSH_ERR_INVALID_ARGUMENT
;
431 if (EVP_PKEY_bits(key
->pkey
) < SSH_RSA_MINIMUM_MODULUS_SIZE
)
432 return SSH_ERR_KEY_LENGTH
;
434 if ((ret
= sshkey_pkey_digest_sign(key
->pkey
, hash_alg
, &sig
, &len
,
437 if (len
< (size_t)slen
) {
439 memmove(sig
+ diff
, sig
, len
);
440 explicit_bzero(sig
, diff
);
441 } else if (len
> (size_t)slen
) {
442 ret
= SSH_ERR_INTERNAL_ERROR
;
446 /* encode signature */
447 if ((b
= sshbuf_new()) == NULL
) {
448 ret
= SSH_ERR_ALLOC_FAIL
;
451 if ((ret
= sshbuf_put_cstring(b
, rsa_hash_alg_ident(hash_alg
))) != 0 ||
452 (ret
= sshbuf_put_string(b
, sig
, slen
)) != 0)
456 if ((*sigp
= malloc(len
)) == NULL
) {
457 ret
= SSH_ERR_ALLOC_FAIL
;
460 memcpy(*sigp
, sshbuf_ptr(b
), len
);
472 ssh_rsa_verify(const struct sshkey
*key
,
473 const u_char
*sig
, size_t siglen
,
474 const u_char
*data
, size_t dlen
, const char *alg
, u_int compat
,
475 struct sshkey_sig_details
**detailsp
)
477 char *sigtype
= NULL
;
478 int hash_alg
, want_alg
, ret
= SSH_ERR_INTERNAL_ERROR
;
479 size_t len
= 0, diff
, modlen
, rsasize
;
480 struct sshbuf
*b
= NULL
;
481 u_char digest
[SSH_DIGEST_MAX_LENGTH
], *osigblob
, *sigblob
= NULL
;
483 if (key
== NULL
|| key
->pkey
== NULL
||
484 sshkey_type_plain(key
->type
) != KEY_RSA
||
485 sig
== NULL
|| siglen
== 0)
486 return SSH_ERR_INVALID_ARGUMENT
;
487 if (EVP_PKEY_bits(key
->pkey
) < SSH_RSA_MINIMUM_MODULUS_SIZE
)
488 return SSH_ERR_KEY_LENGTH
;
490 if ((b
= sshbuf_from(sig
, siglen
)) == NULL
)
491 return SSH_ERR_ALLOC_FAIL
;
492 if (sshbuf_get_cstring(b
, &sigtype
, NULL
) != 0) {
493 ret
= SSH_ERR_INVALID_FORMAT
;
496 if ((hash_alg
= rsa_hash_id_from_ident(sigtype
)) == -1) {
497 ret
= SSH_ERR_KEY_TYPE_MISMATCH
;
501 * Allow ssh-rsa-cert-v01 certs to generate SHA2 signatures for
502 * legacy reasons, but otherwise the signature type should match.
504 if (alg
!= NULL
&& strcmp(alg
, "ssh-rsa-cert-v01@openssh.com") != 0) {
505 if ((want_alg
= rsa_hash_id_from_keyname(alg
)) == -1) {
506 ret
= SSH_ERR_INVALID_ARGUMENT
;
509 if (hash_alg
!= want_alg
) {
510 ret
= SSH_ERR_SIGNATURE_INVALID
;
514 if (sshbuf_get_string(b
, &sigblob
, &len
) != 0) {
515 ret
= SSH_ERR_INVALID_FORMAT
;
518 if (sshbuf_len(b
) != 0) {
519 ret
= SSH_ERR_UNEXPECTED_TRAILING_DATA
;
522 /* RSA_verify expects a signature of RSA_size */
523 modlen
= EVP_PKEY_size(key
->pkey
);
525 ret
= SSH_ERR_KEY_BITS_MISMATCH
;
527 } else if (len
< modlen
) {
530 if ((sigblob
= realloc(sigblob
, modlen
)) == NULL
) {
531 sigblob
= osigblob
; /* put it back for clear/free */
532 ret
= SSH_ERR_ALLOC_FAIL
;
535 memmove(sigblob
+ diff
, sigblob
, len
);
536 explicit_bzero(sigblob
, diff
);
540 rsasize
= EVP_PKEY_size(key
->pkey
);
541 if (rsasize
<= 0 || rsasize
> SSHBUF_MAX_BIGNUM
||
542 len
== 0 || len
> rsasize
) {
543 ret
= SSH_ERR_INVALID_ARGUMENT
;
546 ret
= sshkey_pkey_digest_verify(key
->pkey
, hash_alg
, data
, dlen
,
550 freezero(sigblob
, len
);
553 explicit_bzero(digest
, sizeof(digest
));
557 static const struct sshkey_impl_funcs sshkey_rsa_funcs
= {
558 /* .size = */ ssh_rsa_size
,
559 /* .alloc = */ ssh_rsa_alloc
,
560 /* .cleanup = */ ssh_rsa_cleanup
,
561 /* .equal = */ ssh_rsa_equal
,
562 /* .ssh_serialize_public = */ ssh_rsa_serialize_public
,
563 /* .ssh_deserialize_public = */ ssh_rsa_deserialize_public
,
564 /* .ssh_serialize_private = */ ssh_rsa_serialize_private
,
565 /* .ssh_deserialize_private = */ ssh_rsa_deserialize_private
,
566 /* .generate = */ ssh_rsa_generate
,
567 /* .copy_public = */ ssh_rsa_copy_public
,
568 /* .sign = */ ssh_rsa_sign
,
569 /* .verify = */ ssh_rsa_verify
,
572 const struct sshkey_impl sshkey_rsa_impl
= {
573 /* .name = */ "ssh-rsa",
574 /* .shortname = */ "RSA",
575 /* .sigalg = */ NULL
,
576 /* .type = */ KEY_RSA
,
581 /* .funcs = */ &sshkey_rsa_funcs
,
584 const struct sshkey_impl sshkey_rsa_cert_impl
= {
585 /* .name = */ "ssh-rsa-cert-v01@openssh.com",
586 /* .shortname = */ "RSA-CERT",
587 /* .sigalg = */ NULL
,
588 /* .type = */ KEY_RSA_CERT
,
593 /* .funcs = */ &sshkey_rsa_funcs
,
596 /* SHA2 signature algorithms */
598 const struct sshkey_impl sshkey_rsa_sha256_impl
= {
599 /* .name = */ "rsa-sha2-256",
600 /* .shortname = */ "RSA",
601 /* .sigalg = */ NULL
,
602 /* .type = */ KEY_RSA
,
607 /* .funcs = */ &sshkey_rsa_funcs
,
610 const struct sshkey_impl sshkey_rsa_sha512_impl
= {
611 /* .name = */ "rsa-sha2-512",
612 /* .shortname = */ "RSA",
613 /* .sigalg = */ NULL
,
614 /* .type = */ KEY_RSA
,
619 /* .funcs = */ &sshkey_rsa_funcs
,
622 const struct sshkey_impl sshkey_rsa_sha256_cert_impl
= {
623 /* .name = */ "rsa-sha2-256-cert-v01@openssh.com",
624 /* .shortname = */ "RSA-CERT",
625 /* .sigalg = */ "rsa-sha2-256",
626 /* .type = */ KEY_RSA_CERT
,
631 /* .funcs = */ &sshkey_rsa_funcs
,
634 const struct sshkey_impl sshkey_rsa_sha512_cert_impl
= {
635 /* .name = */ "rsa-sha2-512-cert-v01@openssh.com",
636 /* .shortname = */ "RSA-CERT",
637 /* .sigalg = */ "rsa-sha2-512",
638 /* .type = */ KEY_RSA_CERT
,
643 /* .funcs = */ &sshkey_rsa_funcs
,
645 #endif /* WITH_OPENSSL */