Source code
Revision control
Copy as Markdown
Other Tools
/*-
* 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
*
* 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;
}