summaryrefslogtreecommitdiffstats
path: root/src/lib/crypto/dsa.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/crypto/dsa.cpp')
-rw-r--r--src/lib/crypto/dsa.cpp382
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;
+}