/*- * Copyright (c) 2017-2018 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 #include "dsa.h" #include "bn.h" #include "utils.h" #define DSA_MAX_Q_BITLEN 256 rnp_result_t dsa_validate_key(rnp::RNG *rng, const pgp_dsa_key_t *key, bool secret) { bignum_t * p = NULL; bignum_t * q = NULL; bignum_t * g = NULL; bignum_t * y = NULL; bignum_t * x = NULL; botan_pubkey_t bpkey = NULL; botan_privkey_t bskey = NULL; rnp_result_t ret = RNP_ERROR_GENERIC; /* load and check public key part */ p = mpi2bn(&key->p); q = mpi2bn(&key->q); g = mpi2bn(&key->g); y = mpi2bn(&key->y); if (!p || !q || !g || !y) { RNP_LOG("out of memory"); ret = RNP_ERROR_OUT_OF_MEMORY; goto done; } if (botan_pubkey_load_dsa( &bpkey, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q), BN_HANDLE_PTR(g), BN_HANDLE_PTR(y))) { 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 (!(x = mpi2bn(&key->x))) { RNP_LOG("out of memory"); ret = RNP_ERROR_OUT_OF_MEMORY; goto done; } if (botan_privkey_load_dsa( &bskey, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q), BN_HANDLE_PTR(g), BN_HANDLE_PTR(x))) { goto done; } if (botan_privkey_check_key(bskey, rng->handle(), 0)) { goto done; } ret = RNP_SUCCESS; done: bn_free(p); bn_free(q); bn_free(g); bn_free(y); bn_free(x); botan_privkey_destroy(bskey); botan_pubkey_destroy(bpkey); return ret; } rnp_result_t dsa_sign(rnp::RNG * rng, pgp_dsa_signature_t *sig, const uint8_t * hash, size_t hash_len, const pgp_dsa_key_t *key) { botan_privkey_t dsa_key = NULL; botan_pk_op_sign_t sign_op = NULL; size_t q_order = 0; uint8_t sign_buf[2 * BITS_TO_BYTES(DSA_MAX_Q_BITLEN)] = {0}; bignum_t * p = NULL, *q = NULL, *g = NULL, *x = NULL; rnp_result_t ret = RNP_ERROR_SIGNING_FAILED; size_t sigbuf_size = sizeof(sign_buf); size_t z_len = 0; memset(sig, 0, sizeof(*sig)); q_order = mpi_bytes(&key->q); if ((2 * q_order) > sizeof(sign_buf)) { RNP_LOG("wrong q order"); return RNP_ERROR_BAD_PARAMETERS; } // As 'Raw' is used we need to reduce hash size (as per FIPS-186-4, 4.6) z_len = hash_len < q_order ? hash_len : q_order; p = mpi2bn(&key->p); q = mpi2bn(&key->q); g = mpi2bn(&key->g); x = mpi2bn(&key->x); if (!p || !q || !g || !x) { RNP_LOG("out of memory"); ret = RNP_ERROR_OUT_OF_MEMORY; goto end; } if (botan_privkey_load_dsa( &dsa_key, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q), BN_HANDLE_PTR(g), BN_HANDLE_PTR(x))) { RNP_LOG("Can't load key"); goto end; } if (botan_pk_op_sign_create(&sign_op, dsa_key, "Raw", 0)) { goto end; } if (botan_pk_op_sign_update(sign_op, hash, z_len)) { goto end; } if (botan_pk_op_sign_finish(sign_op, rng->handle(), sign_buf, &sigbuf_size)) { RNP_LOG("Signing has failed"); goto end; } // Now load the DSA (r,s) values from the signature. if (!mem2mpi(&sig->r, sign_buf, q_order) || !mem2mpi(&sig->s, sign_buf + q_order, q_order)) { goto end; } ret = RNP_SUCCESS; end: bn_free(p); bn_free(q); bn_free(g); bn_free(x); botan_pk_op_sign_destroy(sign_op); botan_privkey_destroy(dsa_key); return ret; } rnp_result_t dsa_verify(const pgp_dsa_signature_t *sig, const uint8_t * hash, size_t hash_len, const pgp_dsa_key_t * key) { botan_pubkey_t dsa_key = NULL; botan_pk_op_verify_t verify_op = NULL; uint8_t sign_buf[2 * BITS_TO_BYTES(DSA_MAX_Q_BITLEN)] = {0}; size_t q_order = 0; size_t r_blen, s_blen; bignum_t * p = NULL, *q = NULL, *g = NULL, *y = NULL; rnp_result_t ret = RNP_ERROR_GENERIC; size_t z_len = 0; q_order = mpi_bytes(&key->q); if ((2 * q_order) > sizeof(sign_buf)) { return RNP_ERROR_BAD_PARAMETERS; } z_len = hash_len < q_order ? hash_len : q_order; r_blen = mpi_bytes(&sig->r); s_blen = mpi_bytes(&sig->s); if ((r_blen > q_order) || (s_blen > q_order)) { RNP_LOG("Wrong signature"); return RNP_ERROR_BAD_PARAMETERS; } p = mpi2bn(&key->p); q = mpi2bn(&key->q); g = mpi2bn(&key->g); y = mpi2bn(&key->y); if (!p || !q || !g || !y) { RNP_LOG("out of memory"); ret = RNP_ERROR_OUT_OF_MEMORY; goto end; } if (botan_pubkey_load_dsa( &dsa_key, BN_HANDLE_PTR(p), BN_HANDLE_PTR(q), BN_HANDLE_PTR(g), BN_HANDLE_PTR(y))) { RNP_LOG("Wrong key"); goto end; } mpi2mem(&sig->r, sign_buf + q_order - r_blen); mpi2mem(&sig->s, sign_buf + 2 * q_order - s_blen); if (botan_pk_op_verify_create(&verify_op, dsa_key, "Raw", 0)) { RNP_LOG("Can't create verifier"); goto end; } if (botan_pk_op_verify_update(verify_op, hash, z_len)) { goto end; } ret = (botan_pk_op_verify_finish(verify_op, sign_buf, 2 * q_order) == BOTAN_FFI_SUCCESS) ? RNP_SUCCESS : RNP_ERROR_SIGNATURE_INVALID; end: bn_free(p); bn_free(q); bn_free(g); bn_free(y); botan_pk_op_verify_destroy(verify_op); botan_pubkey_destroy(dsa_key); return ret; } rnp_result_t dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits) { if ((keylen < 1024) || (keylen > 3072) || (qbits < 160) || (qbits > 256)) { return RNP_ERROR_BAD_PARAMETERS; } botan_privkey_t key_priv = NULL; botan_pubkey_t key_pub = NULL; rnp_result_t ret = RNP_ERROR_GENERIC; bignum_t * p = bn_new(); bignum_t * q = bn_new(); bignum_t * g = bn_new(); bignum_t * y = bn_new(); bignum_t * x = bn_new(); if (!p || !q || !g || !y || !x) { ret = RNP_ERROR_OUT_OF_MEMORY; goto end; } if (botan_privkey_create_dsa(&key_priv, rng->handle(), keylen, qbits) || botan_privkey_check_key(key_priv, rng->handle(), 1) || botan_privkey_export_pubkey(&key_pub, key_priv)) { RNP_LOG("Wrong parameters"); ret = RNP_ERROR_BAD_PARAMETERS; goto end; } if (botan_pubkey_get_field(BN_HANDLE_PTR(p), key_pub, "p") || botan_pubkey_get_field(BN_HANDLE_PTR(q), key_pub, "q") || botan_pubkey_get_field(BN_HANDLE_PTR(g), key_pub, "g") || botan_pubkey_get_field(BN_HANDLE_PTR(y), key_pub, "y") || botan_privkey_get_field(BN_HANDLE_PTR(x), key_priv, "x")) { RNP_LOG("Botan FFI call failed"); ret = RNP_ERROR_GENERIC; goto end; } if (!bn2mpi(p, &key->p) || !bn2mpi(q, &key->q) || !bn2mpi(g, &key->g) || !bn2mpi(y, &key->y) || !bn2mpi(x, &key->x)) { RNP_LOG("failed to copy mpi"); goto end; } ret = RNP_SUCCESS; end: bn_free(p); bn_free(q); bn_free(g); bn_free(y); bn_free(x); botan_privkey_destroy(key_priv); botan_pubkey_destroy(key_pub); return ret; } pgp_hash_alg_t dsa_get_min_hash(size_t qsize) { /* * I'm using _broken_ SHA1 here only because * some old implementations may not understand keys created * with other hashes. If you're sure we don't have to support * such implementations, please be my guest and remove it. */ return (qsize < 160) ? PGP_HASH_UNKNOWN : (qsize == 160) ? PGP_HASH_SHA1 : (qsize <= 224) ? PGP_HASH_SHA224 : (qsize <= 256) ? PGP_HASH_SHA256 : (qsize <= 384) ? PGP_HASH_SHA384 : (qsize <= 512) ? PGP_HASH_SHA512 /*(qsize>512)*/ : PGP_HASH_UNKNOWN; } size_t dsa_choose_qsize_by_psize(size_t psize) { return (psize == 1024) ? 160 : (psize <= 2047) ? 224 : (psize <= 3072) ? DSA_MAX_Q_BITLEN : 0; }