| /* |
| * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL |
| * project. |
| */ |
| /* ==================================================================== |
| * Copyright (c) 2016 The OpenSSL Project. 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. |
| * |
| * 3. All advertising materials mentioning features or use of this |
| * software must display the following acknowledgment: |
| * "This product includes software developed by the OpenSSL Project |
| * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" |
| * |
| * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to |
| * endorse or promote products derived from this software without |
| * prior written permission. For written permission, please contact |
| * licensing@OpenSSL.org. |
| * |
| * 5. Products derived from this software may not be called "OpenSSL" |
| * nor may "OpenSSL" appear in their names without prior written |
| * permission of the OpenSSL Project. |
| * |
| * 6. Redistributions of any form whatsoever must retain the following |
| * acknowledgment: |
| * "This product includes software developed by the OpenSSL Project |
| * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY |
| * EXPRESSED 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 OpenSSL PROJECT OR |
| * ITS 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 <string.h> |
| #include <openssl/err.h> |
| #include <openssl/rand.h> |
| #include "ec_lcl.h" |
| |
| /* Length of Curve 25519 keys */ |
| #define EC_X25519_KEYLEN 32 |
| /* Group degree and order bits */ |
| #define EC_X25519_BITS 253 |
| |
| /* Copy Curve25519 public key buffer, allocating is necessary */ |
| static int x25519_init_public(EC_POINT *pub, const void *src) |
| { |
| if (pub->custom_data == NULL) { |
| pub->custom_data = OPENSSL_malloc(EC_X25519_KEYLEN); |
| if (pub->custom_data == NULL) |
| return 0; |
| } |
| if (src != NULL) |
| memcpy(pub->custom_data, src, EC_X25519_KEYLEN); |
| return 1; |
| } |
| |
| /* Copy Curve25519 private key buffer, allocating is necessary */ |
| static int x25519_init_private(EC_KEY *dst, const void *src) |
| { |
| if (dst->custom_data == NULL) { |
| dst->custom_data = OPENSSL_secure_malloc(EC_X25519_KEYLEN); |
| if (dst->custom_data == NULL) |
| return 0; |
| } |
| if (src != NULL) |
| memcpy(dst->custom_data, src, EC_X25519_KEYLEN); |
| return 1; |
| } |
| |
| static int x25519_group_init(EC_GROUP *grp) |
| { |
| return 1; |
| } |
| |
| static int x25519_group_copy(EC_GROUP *dst, const EC_GROUP *src) |
| { |
| return 1; |
| } |
| |
| static int x25519_group_get_degree(const EC_GROUP *src) |
| { |
| return EC_X25519_BITS; |
| } |
| |
| static int x25519_group_order_bits(const EC_GROUP *src) |
| { |
| return EC_X25519_BITS; |
| } |
| |
| static int x25519_set_private(EC_KEY *eckey, const BIGNUM *priv_key) |
| { |
| if (BN_num_bytes(priv_key) > EC_X25519_KEYLEN) |
| return 0; |
| if (x25519_init_private(eckey, NULL)) |
| return 0; |
| /* Convert BIGNUM form private key to internal format */ |
| if (BN_bn2lebinpad(priv_key, eckey->custom_data, EC_X25519_KEYLEN) |
| != EC_X25519_KEYLEN) |
| return 0; |
| return 1; |
| } |
| |
| static int x25519_keycheck(const EC_KEY *eckey) |
| { |
| const char *pubkey; |
| if (eckey->pub_key == NULL) |
| return 0; |
| pubkey = eckey->pub_key->custom_data; |
| if (pubkey == NULL) |
| return 0; |
| if (eckey->custom_data != NULL) { |
| uint8_t tmp[EC_X25519_KEYLEN]; |
| /* Check eckey->priv_key exists and matches eckey->custom_data */ |
| if (eckey->priv_key == NULL) |
| return 0; |
| if (BN_bn2lebinpad(eckey->priv_key, tmp, EC_X25519_KEYLEN) |
| != EC_X25519_KEYLEN |
| || CRYPTO_memcmp(tmp, eckey->custom_data, |
| EC_X25519_KEYLEN) != 0) { |
| OPENSSL_cleanse(tmp, EC_X25519_KEYLEN); |
| return 0; |
| } |
| X25519_public_from_private(tmp, eckey->custom_data); |
| if (CRYPTO_memcmp(pubkey, tmp, EC_X25519_KEYLEN) == 0) |
| return 1; |
| return 0; |
| } else { |
| return 1; |
| } |
| } |
| |
| static int x25519_keygenpub(EC_KEY *eckey) |
| { |
| X25519_public_from_private(eckey->pub_key->custom_data, |
| eckey->custom_data); |
| return 1; |
| } |
| |
| static int x25519_keygen(EC_KEY *eckey) |
| { |
| unsigned char *key; |
| if (x25519_init_private(eckey, NULL) == 0) |
| return 0; |
| key = eckey->custom_data; |
| if (RAND_bytes(key, EC_X25519_KEYLEN) <= 0) |
| return 0; |
| key[0] &= 248; |
| key[31] &= 127; |
| key[31] |= 64; |
| /* |
| * Although the private key is kept as an array in eckey->custom_data |
| * Set eckey->priv_key too so existing code which uses |
| * EC_KEY_get0_private_key() still works. |
| */ |
| if (eckey->priv_key == NULL) |
| eckey->priv_key = BN_secure_new(); |
| if (eckey->priv_key == NULL) |
| return 0; |
| if (BN_lebin2bn(eckey->custom_data, EC_X25519_KEYLEN, eckey->priv_key) == |
| NULL) |
| return 0; |
| if (eckey->pub_key == NULL) |
| eckey->pub_key = EC_POINT_new(eckey->group); |
| if (eckey->pub_key == NULL) |
| return 0; |
| return x25519_keygenpub(eckey); |
| } |
| |
| static void x25519_keyfinish(EC_KEY *eckey) |
| { |
| OPENSSL_secure_free(eckey->custom_data); |
| eckey->custom_data = NULL; |
| } |
| |
| static int x25519_keycopy(EC_KEY *dest, const EC_KEY *src) |
| { |
| if (src->custom_data == NULL) |
| return 0; |
| return x25519_init_private(dest, src->custom_data); |
| } |
| |
| static int x25519_oct2priv(EC_KEY *eckey, unsigned char *buf, size_t len) |
| { |
| if (len != EC_X25519_KEYLEN) |
| return 0; |
| if (x25519_init_private(eckey, buf) == 0) |
| return 0; |
| /* |
| * Although the private key is kept as an array in eckey->custom_data |
| * Set eckey->priv_key too so existing code which uses |
| * EC_KEY_get0_private_key() still works. |
| */ |
| if (eckey->priv_key == NULL) |
| eckey->priv_key = BN_secure_new(); |
| if (eckey->priv_key == NULL) |
| return 0; |
| if (BN_lebin2bn(buf, EC_X25519_KEYLEN, eckey->priv_key) == NULL) |
| return 0; |
| return 1; |
| } |
| |
| static size_t x25519_priv2oct(const EC_KEY *eckey, |
| unsigned char *buf, size_t len) |
| { |
| size_t keylen = EC_X25519_KEYLEN; |
| if (eckey->custom_data == NULL) |
| return 0; |
| if (buf != NULL) { |
| if (len < keylen) |
| return 0; |
| memcpy(buf, eckey->custom_data, keylen); |
| } |
| return keylen; |
| } |
| |
| static int x25519_point_init(EC_POINT *pt) |
| { |
| return x25519_init_public(pt, NULL); |
| } |
| |
| static void x25519_point_finish(EC_POINT *pt) |
| { |
| OPENSSL_free(pt->custom_data); |
| pt->custom_data = NULL; |
| } |
| |
| static void x25519_point_clear_finish(EC_POINT *pt) |
| { |
| OPENSSL_clear_free(pt->custom_data, EC_X25519_KEYLEN); |
| pt->custom_data = NULL; |
| } |
| |
| static int x25519_point_copy(EC_POINT *dst, const EC_POINT *src) |
| { |
| memcpy(dst->custom_data, src->custom_data, EC_X25519_KEYLEN); |
| return 1; |
| } |
| |
| static size_t x25519_point2oct(const EC_GROUP *grp, const EC_POINT *pt, |
| point_conversion_form_t form, |
| unsigned char *buf, size_t len, BN_CTX *ctx) |
| { |
| if (buf != NULL) { |
| if (len < EC_X25519_KEYLEN) |
| return 0; |
| memcpy(buf, pt->custom_data, EC_X25519_KEYLEN); |
| } |
| return EC_X25519_KEYLEN; |
| } |
| |
| static int x25519_oct2point(const EC_GROUP *grp, EC_POINT *pt, |
| const unsigned char *buf, size_t len, BN_CTX *ctx) |
| { |
| unsigned char *pubkey = pt->custom_data; |
| if (len != EC_X25519_KEYLEN) |
| return 0; |
| memcpy(pubkey, buf, EC_X25519_KEYLEN); |
| /* Mask off MSB */ |
| pubkey[EC_X25519_KEYLEN - 1] &= 0x7F; |
| return 1; |
| } |
| |
| static int x25519_point_cmp(const EC_GROUP *group, const EC_POINT *a, |
| const EC_POINT *b, BN_CTX *ctx) |
| { |
| /* Shouldn't happen as initialised to non-zero */ |
| if (a->custom_data == NULL || b->custom_data == NULL) |
| return -1; |
| |
| if (CRYPTO_memcmp(a->custom_data, b->custom_data, EC_X25519_KEYLEN) == 0) |
| return 0; |
| |
| return 1; |
| } |
| |
| static int x25519_compute_key(unsigned char **psec, size_t *pseclen, |
| const EC_POINT *pub_key, const EC_KEY *ecdh) |
| { |
| unsigned char *key; |
| int ret = -1; |
| if (ecdh->custom_data == NULL) |
| return -1; |
| key = OPENSSL_malloc(EC_X25519_KEYLEN); |
| if (key == NULL) |
| return 0; |
| if (X25519(key, ecdh->custom_data, pub_key->custom_data) == 0) |
| goto err; |
| *psec = key; |
| *pseclen = EC_X25519_KEYLEN; |
| return 1; |
| |
| err: |
| OPENSSL_clear_free(key, EC_X25519_KEYLEN); |
| return ret; |
| } |
| |
| const EC_METHOD *ec_x25519_meth(void) |
| { |
| static const EC_METHOD ret = { |
| EC_FLAGS_CUSTOM_CURVE | EC_FLAGS_NO_SIGN, |
| NID_undef, |
| x25519_group_init, /* group_init */ |
| 0, /* group_finish */ |
| 0, /* group_clear_finish */ |
| x25519_group_copy, /* group_copy */ |
| 0, /* group_set_curve */ |
| 0, /* group_get_curve */ |
| x25519_group_get_degree, |
| x25519_group_order_bits, |
| 0, /* group_check_discriminant */ |
| x25519_point_init, |
| x25519_point_finish, |
| x25519_point_clear_finish, |
| x25519_point_copy, |
| 0, /* point_set_to_infinity */ |
| 0, /* set_Jprojective_coordinates_GFp */ |
| 0, /* get_Jprojective_coordinates_GFp */ |
| 0, /* point_set_affine_coordinates */ |
| 0, /* point_get_affine_coordinates */ |
| 0, /* point_set_compressed_coordinates */ |
| x25519_point2oct, |
| x25519_oct2point, |
| 0, /* simple_add */ |
| 0, /* simple_dbl */ |
| 0, /* simple_invert */ |
| 0, /* simple_is_at_infinity */ |
| 0, /* simple_is_on_curve */ |
| x25519_point_cmp, |
| 0, /* simple_make_affine */ |
| 0, /* simple_points_make_affine */ |
| 0, /* points_mul */ |
| 0, /* precompute_mult */ |
| 0, /* have_precompute_mult */ |
| 0, /* field_mul */ |
| 0, /* field_sqr */ |
| 0, /* field_div */ |
| 0, /* field_encode */ |
| 0, /* field_decode */ |
| 0, /* field_set_to_one */ |
| x25519_priv2oct, |
| x25519_oct2priv, |
| x25519_set_private, |
| x25519_keygen, |
| x25519_keycheck, |
| x25519_keygenpub, |
| x25519_keycopy, |
| x25519_keyfinish, |
| x25519_compute_key |
| }; |
| |
| return &ret; |
| } |