/*- * Copyright (c) 2017-2022 Ribose Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Alistair Crooks (agc@NetBSD.org) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) * All rights reserved. * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted * their moral rights under the UK Copyright Design and Patents Act 1988 to * be recorded as the authors of this copyright work. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. * * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. */ /** \file */ #include #include #include #include "hash_botan.hpp" #include "crypto/rsa.h" #include "config.h" #include "utils.h" #include "bn.h" rnp_result_t rsa_validate_key(rnp::RNG *rng, const pgp_rsa_key_t *key, bool secret) { bignum_t * n = NULL; bignum_t * e = NULL; bignum_t * p = NULL; bignum_t * q = NULL; botan_pubkey_t bpkey = NULL; botan_privkey_t bskey = NULL; rnp_result_t ret = RNP_ERROR_GENERIC; /* load and check public key part */ if (!(n = mpi2bn(&key->n)) || !(e = mpi2bn(&key->e))) { RNP_LOG("out of memory"); ret = RNP_ERROR_OUT_OF_MEMORY; goto done; } if (botan_pubkey_load_rsa(&bpkey, BN_HANDLE_PTR(n), BN_HANDLE_PTR(e)) != 0) { goto done; } if (botan_pubkey_check_key(bpkey, rng->handle(), 0)) { goto done; } if (!secret) { ret = RNP_SUCCESS; goto done; } /* load and check secret key part */ if (!(p = mpi2bn(&key->p)) || !(q = mpi2bn(&key->q))) { RNP_LOG("out of memory"); ret = RNP_ERROR_OUT_OF_MEMORY; goto done; } /* p and q are reversed from normal usage in PGP */ if (botan_privkey_load_rsa(&bskey, BN_HANDLE_PTR(q), BN_HANDLE_PTR(p), BN_HANDLE_PTR(e))) { goto done; } if (botan_privkey_check_key(bskey, rng->handle(), 0)) { goto done; } ret = RNP_SUCCESS; done: botan_pubkey_destroy(bpkey); botan_privkey_destroy(bskey); bn_free(n); bn_free(e); bn_free(p); bn_free(q); return ret; } static bool rsa_load_public_key(botan_pubkey_t *bkey, const pgp_rsa_key_t *key) { bignum_t *n = NULL; bignum_t *e = NULL; bool res = false; *bkey = NULL; n = mpi2bn(&key->n); e = mpi2bn(&key->e); if (!n || !e) { RNP_LOG("out of memory"); goto done; } res = !botan_pubkey_load_rsa(bkey, BN_HANDLE_PTR(n), BN_HANDLE_PTR(e)); done: bn_free(n); bn_free(e); return res; } static bool rsa_load_secret_key(botan_privkey_t *bkey, const pgp_rsa_key_t *key) { bignum_t *p = NULL; bignum_t *q = NULL; bignum_t *e = NULL; bool res = false; *bkey = NULL; p = mpi2bn(&key->p); q = mpi2bn(&key->q); e = mpi2bn(&key->e); if (!p || !q || !e) { RNP_LOG("out of memory"); goto done; } /* p and q are reversed from normal usage in PGP */ res = !botan_privkey_load_rsa(bkey, BN_HANDLE_PTR(q), BN_HANDLE_PTR(p), BN_HANDLE_PTR(e)); done: bn_free(p); bn_free(q); bn_free(e); return res; } rnp_result_t rsa_encrypt_pkcs1(rnp::RNG * rng, pgp_rsa_encrypted_t *out, const uint8_t * in, size_t in_len, const pgp_rsa_key_t *key) { rnp_result_t ret = RNP_ERROR_GENERIC; botan_pubkey_t rsa_key = NULL; botan_pk_op_encrypt_t enc_op = NULL; if (!rsa_load_public_key(&rsa_key, key)) { RNP_LOG("failed to load key"); return RNP_ERROR_OUT_OF_MEMORY; } if (botan_pk_op_encrypt_create(&enc_op, rsa_key, "PKCS1v15", 0) != 0) { goto done; } out->m.len = sizeof(out->m.mpi); if (botan_pk_op_encrypt(enc_op, rng->handle(), out->m.mpi, &out->m.len, in, in_len)) { out->m.len = 0; goto done; } ret = RNP_SUCCESS; done: botan_pk_op_encrypt_destroy(enc_op); botan_pubkey_destroy(rsa_key); return ret; } rnp_result_t rsa_verify_pkcs1(const pgp_rsa_signature_t *sig, pgp_hash_alg_t hash_alg, const uint8_t * hash, size_t hash_len, const pgp_rsa_key_t * key) { char padding_name[64] = {0}; botan_pubkey_t rsa_key = NULL; botan_pk_op_verify_t verify_op = NULL; rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID; if (!rsa_load_public_key(&rsa_key, key)) { RNP_LOG("failed to load key"); return RNP_ERROR_OUT_OF_MEMORY; } snprintf(padding_name, sizeof(padding_name), "EMSA-PKCS1-v1_5(Raw,%s)", rnp::Hash_Botan::name_backend(hash_alg)); if (botan_pk_op_verify_create(&verify_op, rsa_key, padding_name, 0) != 0) { goto done; } if (botan_pk_op_verify_update(verify_op, hash, hash_len) != 0) { goto done; } if (botan_pk_op_verify_finish(verify_op, sig->s.mpi, sig->s.len) != 0) { goto done; } ret = RNP_SUCCESS; done: botan_pk_op_verify_destroy(verify_op); botan_pubkey_destroy(rsa_key); return ret; } rnp_result_t rsa_sign_pkcs1(rnp::RNG * rng, pgp_rsa_signature_t *sig, pgp_hash_alg_t hash_alg, const uint8_t * hash, size_t hash_len, const pgp_rsa_key_t *key) { char padding_name[64] = {0}; botan_privkey_t rsa_key; botan_pk_op_sign_t sign_op; rnp_result_t ret = RNP_ERROR_GENERIC; if (mpi_bytes(&key->q) == 0) { RNP_LOG("private key not set"); return ret; } if (!rsa_load_secret_key(&rsa_key, key)) { RNP_LOG("failed to load key"); return RNP_ERROR_OUT_OF_MEMORY; } snprintf(padding_name, sizeof(padding_name), "EMSA-PKCS1-v1_5(Raw,%s)", rnp::Hash_Botan::name_backend(hash_alg)); if (botan_pk_op_sign_create(&sign_op, rsa_key, padding_name, 0) != 0) { goto done; } if (botan_pk_op_sign_update(sign_op, hash, hash_len)) { goto done; } sig->s.len = sizeof(sig->s.mpi); if (botan_pk_op_sign_finish(sign_op, rng->handle(), sig->s.mpi, &sig->s.len)) { goto done; } ret = RNP_SUCCESS; done: botan_pk_op_sign_destroy(sign_op); botan_privkey_destroy(rsa_key); return ret; } rnp_result_t rsa_decrypt_pkcs1(rnp::RNG * rng, uint8_t * out, size_t * out_len, const pgp_rsa_encrypted_t *in, const pgp_rsa_key_t * key) { botan_privkey_t rsa_key = NULL; botan_pk_op_decrypt_t decrypt_op = NULL; rnp_result_t ret = RNP_ERROR_GENERIC; if (mpi_bytes(&key->q) == 0) { RNP_LOG("private key not set"); return ret; } if (!rsa_load_secret_key(&rsa_key, key)) { RNP_LOG("failed to load key"); return RNP_ERROR_OUT_OF_MEMORY; } size_t skip = 0; if (botan_pk_op_decrypt_create(&decrypt_op, rsa_key, "PKCS1v15", 0)) { goto done; } /* Skip trailing zeroes if any as Botan3 doesn't like m.len > e.len */ while ((in->m.len - skip > key->e.len) && !in->m.mpi[skip]) { skip++; } *out_len = PGP_MPINT_SIZE; if (botan_pk_op_decrypt(decrypt_op, out, out_len, in->m.mpi + skip, in->m.len - skip)) { goto done; } ret = RNP_SUCCESS; done: botan_privkey_destroy(rsa_key); botan_pk_op_decrypt_destroy(decrypt_op); return ret; } rnp_result_t rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits) { if ((numbits < 1024) || (numbits > PGP_MPINT_BITS)) { return RNP_ERROR_BAD_PARAMETERS; } botan_privkey_t rsa_key = NULL; rnp_result_t ret = RNP_ERROR_GENERIC; int cmp; bignum_t * n = bn_new(); bignum_t * e = bn_new(); bignum_t * p = bn_new(); bignum_t * q = bn_new(); bignum_t * d = bn_new(); bignum_t * u = bn_new(); if (!n || !e || !p || !q || !d || !u) { ret = RNP_ERROR_OUT_OF_MEMORY; goto end; } if (botan_privkey_create( &rsa_key, "RSA", std::to_string(numbits).c_str(), rng->handle())) { goto end; } if (botan_privkey_check_key(rsa_key, rng->handle(), 1) != 0) { goto end; } if (botan_privkey_get_field(BN_HANDLE_PTR(n), rsa_key, "n") || botan_privkey_get_field(BN_HANDLE_PTR(e), rsa_key, "e") || botan_privkey_get_field(BN_HANDLE_PTR(d), rsa_key, "d") || botan_privkey_get_field(BN_HANDLE_PTR(p), rsa_key, "p") || botan_privkey_get_field(BN_HANDLE_PTR(q), rsa_key, "q")) { goto end; } /* RFC 4880, 5.5.3 tells that p < q. GnuPG relies on this. */ (void) botan_mp_cmp(&cmp, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q)); if (cmp > 0) { (void) botan_mp_swap(BN_HANDLE_PTR(p), BN_HANDLE_PTR(q)); } if (botan_mp_mod_inverse(BN_HANDLE_PTR(u), BN_HANDLE_PTR(p), BN_HANDLE_PTR(q)) != 0) { RNP_LOG("Error computing RSA u param"); ret = RNP_ERROR_BAD_STATE; goto end; } bn2mpi(n, &key->n); bn2mpi(e, &key->e); bn2mpi(p, &key->p); bn2mpi(q, &key->q); bn2mpi(d, &key->d); bn2mpi(u, &key->u); ret = RNP_SUCCESS; end: botan_privkey_destroy(rsa_key); bn_free(n); bn_free(e); bn_free(p); bn_free(q); bn_free(d); bn_free(u); return ret; }