diff options
Diffstat (limited to 'src/lib/crypto/ec.cpp')
-rw-r--r-- | src/lib/crypto/ec.cpp | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/src/lib/crypto/ec.cpp b/src/lib/crypto/ec.cpp new file mode 100644 index 0000000..144c362 --- /dev/null +++ b/src/lib/crypto/ec.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com). + * 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. + */ + +#include <botan/ffi.h> +#include <string.h> +#include <cassert> +#include "ec.h" +#include "types.h" +#include "utils.h" +#include "mem.h" +#include "bn.h" + +static id_str_pair ec_algo_to_botan[] = { + {PGP_PKA_ECDH, "ECDH"}, + {PGP_PKA_ECDSA, "ECDSA"}, + {PGP_PKA_SM2, "SM2_Sig"}, + {0, NULL}, +}; + +rnp_result_t +x25519_generate(rnp::RNG *rng, pgp_ec_key_t *key) +{ + botan_privkey_t pr_key = NULL; + botan_pubkey_t pu_key = NULL; + rnp_result_t ret = RNP_ERROR_KEY_GENERATION; + + rnp::secure_array<uint8_t, 32> keyle; + + if (botan_privkey_create(&pr_key, "Curve25519", "", rng->handle())) { + goto end; + } + + if (botan_privkey_export_pubkey(&pu_key, pr_key)) { + goto end; + } + + /* botan returns key in little-endian, while mpi is big-endian */ + if (botan_privkey_x25519_get_privkey(pr_key, keyle.data())) { + goto end; + } + for (int i = 0; i < 32; i++) { + key->x.mpi[31 - i] = keyle[i]; + } + key->x.len = 32; + /* botan doesn't tweak secret key bits, so we should do that here */ + if (!x25519_tweak_bits(*key)) { + goto end; + } + + if (botan_pubkey_x25519_get_pubkey(pu_key, &key->p.mpi[1])) { + goto end; + } + key->p.len = 33; + key->p.mpi[0] = 0x40; + + ret = RNP_SUCCESS; +end: + botan_privkey_destroy(pr_key); + botan_pubkey_destroy(pu_key); + return ret; +} + +rnp_result_t +ec_generate(rnp::RNG * rng, + pgp_ec_key_t * key, + const pgp_pubkey_alg_t alg_id, + const pgp_curve_t curve) +{ + /** + * Keeps "0x04 || x || y" + * \see 13.2. ECDSA, ECDH, SM2 Conversion Primitives + * + * P-521 is biggest supported curve + */ + botan_privkey_t pr_key = NULL; + botan_pubkey_t pu_key = NULL; + bignum_t * px = NULL; + bignum_t * py = NULL; + bignum_t * x = NULL; + rnp_result_t ret = RNP_ERROR_KEY_GENERATION; + size_t filed_byte_size = 0; + + if (!alg_allows_curve(alg_id, curve)) { + return RNP_ERROR_BAD_PARAMETERS; + } + + const char *ec_algo = id_str_pair::lookup(ec_algo_to_botan, alg_id, NULL); + assert(ec_algo); + const ec_curve_desc_t *ec_desc = get_curve_desc(curve); + if (!ec_desc) { + ret = RNP_ERROR_BAD_PARAMETERS; + goto end; + } + filed_byte_size = BITS_TO_BYTES(ec_desc->bitlen); + + // at this point it must succeed + if (botan_privkey_create(&pr_key, ec_algo, ec_desc->botan_name, rng->handle())) { + goto end; + } + + if (botan_privkey_export_pubkey(&pu_key, pr_key)) { + goto end; + } + + // Crash if seckey is null. It's clean and easy to debug design + px = bn_new(); + py = bn_new(); + x = bn_new(); + + if (!px || !py || !x) { + RNP_LOG("Allocation failed"); + ret = RNP_ERROR_OUT_OF_MEMORY; + goto end; + } + + if (botan_pubkey_get_field(BN_HANDLE_PTR(px), pu_key, "public_x")) { + goto end; + } + + if (botan_pubkey_get_field(BN_HANDLE_PTR(py), pu_key, "public_y")) { + goto end; + } + + if (botan_privkey_get_field(BN_HANDLE_PTR(x), pr_key, "x")) { + goto end; + } + + size_t x_bytes; + size_t y_bytes; + x_bytes = bn_num_bytes(*px); + y_bytes = bn_num_bytes(*py); + + // Safety check + if ((x_bytes > filed_byte_size) || (y_bytes > filed_byte_size)) { + RNP_LOG("Key generation failed"); + ret = RNP_ERROR_BAD_PARAMETERS; + goto end; + } + + /* + * Convert coordinates to MPI stored as + * "0x04 || x || y" + * + * \see 13.2. ECDSA and ECDH Conversion Primitives + * + * Note: Generated pk/sk may not always have exact number of bytes + * which is important when converting to octet-string + */ + memset(key->p.mpi, 0, sizeof(key->p.mpi)); + key->p.mpi[0] = 0x04; + bn_bn2bin(px, &key->p.mpi[1 + filed_byte_size - x_bytes]); + bn_bn2bin(py, &key->p.mpi[1 + filed_byte_size + (filed_byte_size - y_bytes)]); + key->p.len = 2 * filed_byte_size + 1; + /* secret key value */ + bn2mpi(x, &key->x); + ret = RNP_SUCCESS; +end: + botan_privkey_destroy(pr_key); + botan_pubkey_destroy(pu_key); + bn_free(px); + bn_free(py); + bn_free(x); + return ret; +} |