diff options
Diffstat (limited to 'src/lib/crypto/dsa.cpp')
-rw-r--r-- | src/lib/crypto/dsa.cpp | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/src/lib/crypto/dsa.cpp b/src/lib/crypto/dsa.cpp new file mode 100644 index 0000000..8763f00 --- /dev/null +++ b/src/lib/crypto/dsa.cpp @@ -0,0 +1,382 @@ +/*- + * 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 <stdlib.h> +#include <string.h> +#include <botan/ffi.h> +#include <rnp/rnp_def.h> +#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; +} |