| /* |
| * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. |
| * |
| * Licensed under the Apache License 2.0 (the "License"). You may not use |
| * this file except in compliance with the License. You can obtain a copy |
| * in the file LICENSE in the source distribution or at |
| * https://www.openssl.org/source/license.html |
| */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <openssl/err.h> |
| #include <openssl/rand.h> |
| #include "ec_local.h" |
| #include "s390x_arch.h" |
| |
| /* Size of parameter blocks */ |
| #define S390X_SIZE_PARAM 4096 |
| |
| /* Size of fields in parameter blocks */ |
| #define S390X_SIZE_P256 32 |
| #define S390X_SIZE_P384 48 |
| #define S390X_SIZE_P521 80 |
| |
| /* Offsets of fields in PCC parameter blocks */ |
| #define S390X_OFF_RES_X(n) (0 * n) |
| #define S390X_OFF_RES_Y(n) (1 * n) |
| #define S390X_OFF_SRC_X(n) (2 * n) |
| #define S390X_OFF_SRC_Y(n) (3 * n) |
| #define S390X_OFF_SCALAR(n) (4 * n) |
| |
| /* Offsets of fields in KDSA parameter blocks */ |
| #define S390X_OFF_R(n) (0 * n) |
| #define S390X_OFF_S(n) (1 * n) |
| #define S390X_OFF_H(n) (2 * n) |
| #define S390X_OFF_K(n) (3 * n) |
| #define S390X_OFF_X(n) (3 * n) |
| #define S390X_OFF_RN(n) (4 * n) |
| #define S390X_OFF_Y(n) (4 * n) |
| |
| static int ec_GFp_s390x_nistp_mul(const EC_GROUP *group, EC_POINT *r, |
| const BIGNUM *scalar, |
| size_t num, const EC_POINT *points[], |
| const BIGNUM *scalars[], |
| BN_CTX *ctx, unsigned int fc, int len) |
| { |
| unsigned char param[S390X_SIZE_PARAM]; |
| BIGNUM *x, *y; |
| const EC_POINT *point_ptr = NULL; |
| const BIGNUM *scalar_ptr = NULL; |
| BN_CTX *new_ctx = NULL; |
| int rc = -1; |
| |
| if (ctx == NULL) { |
| ctx = new_ctx = BN_CTX_new_ex(group->libctx); |
| if (ctx == NULL) |
| return 0; |
| } |
| |
| BN_CTX_start(ctx); |
| |
| x = BN_CTX_get(ctx); |
| y = BN_CTX_get(ctx); |
| if (x == NULL || y == NULL) { |
| rc = 0; |
| goto ret; |
| } |
| |
| /* |
| * Use PCC for EC keygen and ECDH key derivation: |
| * scalar * generator and scalar * peer public key, |
| * scalar in [0,order). |
| */ |
| if ((scalar != NULL && num == 0 && BN_is_negative(scalar) == 0) |
| || (scalar == NULL && num == 1 && BN_is_negative(scalars[0]) == 0)) { |
| |
| if (num == 0) { |
| point_ptr = EC_GROUP_get0_generator(group); |
| scalar_ptr = scalar; |
| } else { |
| point_ptr = points[0]; |
| scalar_ptr = scalars[0]; |
| } |
| |
| if (EC_POINT_is_at_infinity(group, point_ptr) == 1 |
| || BN_is_zero(scalar_ptr)) { |
| rc = EC_POINT_set_to_infinity(group, r); |
| goto ret; |
| } |
| |
| memset(¶m, 0, sizeof(param)); |
| |
| if (group->meth->point_get_affine_coordinates(group, point_ptr, |
| x, y, ctx) != 1 |
| || BN_bn2binpad(x, param + S390X_OFF_SRC_X(len), len) == -1 |
| || BN_bn2binpad(y, param + S390X_OFF_SRC_Y(len), len) == -1 |
| || BN_bn2binpad(scalar_ptr, |
| param + S390X_OFF_SCALAR(len), len) == -1 |
| || s390x_pcc(fc, param) != 0 |
| || BN_bin2bn(param + S390X_OFF_RES_X(len), len, x) == NULL |
| || BN_bin2bn(param + S390X_OFF_RES_Y(len), len, y) == NULL |
| || group->meth->point_set_affine_coordinates(group, r, |
| x, y, ctx) != 1) |
| goto ret; |
| |
| rc = 1; |
| } |
| |
| ret: |
| /* Otherwise use default. */ |
| if (rc == -1) |
| rc = ec_wNAF_mul(group, r, scalar, num, points, scalars, ctx); |
| OPENSSL_cleanse(param + S390X_OFF_SCALAR(len), len); |
| BN_CTX_end(ctx); |
| BN_CTX_free(new_ctx); |
| return rc; |
| } |
| |
| static ECDSA_SIG *ecdsa_s390x_nistp_sign_sig(const unsigned char *dgst, |
| int dgstlen, |
| const BIGNUM *kinv, |
| const BIGNUM *r, |
| EC_KEY *eckey, |
| unsigned int fc, int len) |
| { |
| unsigned char param[S390X_SIZE_PARAM]; |
| int ok = 0; |
| BIGNUM *k; |
| ECDSA_SIG *sig; |
| const EC_GROUP *group; |
| const BIGNUM *privkey; |
| int off; |
| |
| group = EC_KEY_get0_group(eckey); |
| privkey = EC_KEY_get0_private_key(eckey); |
| if (group == NULL || privkey == NULL) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, EC_R_MISSING_PARAMETERS); |
| return NULL; |
| } |
| |
| if (!EC_KEY_can_sign(eckey)) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, |
| EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING); |
| return NULL; |
| } |
| |
| k = BN_secure_new(); |
| sig = ECDSA_SIG_new(); |
| if (k == NULL || sig == NULL) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, ERR_R_MALLOC_FAILURE); |
| goto ret; |
| } |
| |
| sig->r = BN_new(); |
| sig->s = BN_new(); |
| if (sig->r == NULL || sig->s == NULL) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, ERR_R_MALLOC_FAILURE); |
| goto ret; |
| } |
| |
| memset(param, 0, sizeof(param)); |
| off = len - (dgstlen > len ? len : dgstlen); |
| memcpy(param + S390X_OFF_H(len) + off, dgst, len - off); |
| |
| if (BN_bn2binpad(privkey, param + S390X_OFF_K(len), len) == -1) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, ERR_R_BN_LIB); |
| goto ret; |
| } |
| |
| if (r == NULL || kinv == NULL) { |
| /* |
| * Generate random k and copy to param param block. RAND_priv_bytes_ex |
| * is used instead of BN_priv_rand_range or BN_generate_dsa_nonce |
| * because kdsa instruction constructs an in-range, invertible nonce |
| * internally implementing counter-measures for RNG weakness. |
| */ |
| if (RAND_priv_bytes_ex(eckey->libctx, param + S390X_OFF_RN(len), |
| len) != 1) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, |
| EC_R_RANDOM_NUMBER_GENERATION_FAILED); |
| goto ret; |
| } |
| } else { |
| /* Reconstruct k = (k^-1)^-1. */ |
| if (ec_group_do_inverse_ord(group, k, kinv, NULL) == 0 |
| || BN_bn2binpad(k, param + S390X_OFF_RN(len), len) == -1) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, ERR_R_BN_LIB); |
| goto ret; |
| } |
| /* Turns KDSA internal nonce-generation off. */ |
| fc |= S390X_KDSA_D; |
| } |
| |
| if (s390x_kdsa(fc, param, NULL, 0) != 0) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, ERR_R_ECDSA_LIB); |
| goto ret; |
| } |
| |
| if (BN_bin2bn(param + S390X_OFF_R(len), len, sig->r) == NULL |
| || BN_bin2bn(param + S390X_OFF_S(len), len, sig->s) == NULL) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_SIGN_SIG, ERR_R_BN_LIB); |
| goto ret; |
| } |
| |
| ok = 1; |
| ret: |
| OPENSSL_cleanse(param + S390X_OFF_K(len), 2 * len); |
| if (ok != 1) { |
| ECDSA_SIG_free(sig); |
| sig = NULL; |
| } |
| BN_clear_free(k); |
| return sig; |
| } |
| |
| static int ecdsa_s390x_nistp_verify_sig(const unsigned char *dgst, int dgstlen, |
| const ECDSA_SIG *sig, EC_KEY *eckey, |
| unsigned int fc, int len) |
| { |
| unsigned char param[S390X_SIZE_PARAM]; |
| int rc = -1; |
| BN_CTX *ctx; |
| BIGNUM *x, *y; |
| const EC_GROUP *group; |
| const EC_POINT *pubkey; |
| int off; |
| |
| group = EC_KEY_get0_group(eckey); |
| pubkey = EC_KEY_get0_public_key(eckey); |
| if (eckey == NULL || group == NULL || pubkey == NULL || sig == NULL) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_VERIFY_SIG, EC_R_MISSING_PARAMETERS); |
| return -1; |
| } |
| |
| if (!EC_KEY_can_sign(eckey)) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_VERIFY_SIG, |
| EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING); |
| return -1; |
| } |
| |
| ctx = BN_CTX_new_ex(group->libctx); |
| if (ctx == NULL) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_VERIFY_SIG, ERR_R_MALLOC_FAILURE); |
| return -1; |
| } |
| |
| BN_CTX_start(ctx); |
| |
| x = BN_CTX_get(ctx); |
| y = BN_CTX_get(ctx); |
| if (x == NULL || y == NULL) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_VERIFY_SIG, ERR_R_MALLOC_FAILURE); |
| goto ret; |
| } |
| |
| memset(param, 0, sizeof(param)); |
| off = len - (dgstlen > len ? len : dgstlen); |
| memcpy(param + S390X_OFF_H(len) + off, dgst, len - off); |
| |
| if (group->meth->point_get_affine_coordinates(group, pubkey, |
| x, y, ctx) != 1 |
| || BN_bn2binpad(sig->r, param + S390X_OFF_R(len), len) == -1 |
| || BN_bn2binpad(sig->s, param + S390X_OFF_S(len), len) == -1 |
| || BN_bn2binpad(x, param + S390X_OFF_X(len), len) == -1 |
| || BN_bn2binpad(y, param + S390X_OFF_Y(len), len) == -1) { |
| ECerr(EC_F_ECDSA_S390X_NISTP_VERIFY_SIG, ERR_R_BN_LIB); |
| goto ret; |
| } |
| |
| rc = s390x_kdsa(fc, param, NULL, 0) == 0 ? 1 : 0; |
| ret: |
| BN_CTX_end(ctx); |
| BN_CTX_free(ctx); |
| return rc; |
| } |
| |
| #define EC_GFP_S390X_NISTP_METHOD(bits) \ |
| \ |
| static int ec_GFp_s390x_nistp##bits##_mul(const EC_GROUP *group, \ |
| EC_POINT *r, \ |
| const BIGNUM *scalar, \ |
| size_t num, \ |
| const EC_POINT *points[], \ |
| const BIGNUM *scalars[], \ |
| BN_CTX *ctx) \ |
| { \ |
| return ec_GFp_s390x_nistp_mul(group, r, scalar, num, points, \ |
| scalars, ctx, \ |
| S390X_SCALAR_MULTIPLY_P##bits, \ |
| S390X_SIZE_P##bits); \ |
| } \ |
| \ |
| static ECDSA_SIG *ecdsa_s390x_nistp##bits##_sign_sig(const unsigned \ |
| char *dgst, \ |
| int dgstlen, \ |
| const BIGNUM *kinv,\ |
| const BIGNUM *r, \ |
| EC_KEY *eckey) \ |
| { \ |
| return ecdsa_s390x_nistp_sign_sig(dgst, dgstlen, kinv, r, eckey, \ |
| S390X_ECDSA_SIGN_P##bits, \ |
| S390X_SIZE_P##bits); \ |
| } \ |
| \ |
| static int ecdsa_s390x_nistp##bits##_verify_sig(const \ |
| unsigned char *dgst, \ |
| int dgstlen, \ |
| const ECDSA_SIG *sig, \ |
| EC_KEY *eckey) \ |
| { \ |
| return ecdsa_s390x_nistp_verify_sig(dgst, dgstlen, sig, eckey, \ |
| S390X_ECDSA_VERIFY_P##bits, \ |
| S390X_SIZE_P##bits); \ |
| } \ |
| \ |
| const EC_METHOD *EC_GFp_s390x_nistp##bits##_method(void) \ |
| { \ |
| static const EC_METHOD EC_GFp_s390x_nistp##bits##_meth = { \ |
| EC_FLAGS_DEFAULT_OCT, \ |
| NID_X9_62_prime_field, \ |
| ec_GFp_simple_group_init, \ |
| ec_GFp_simple_group_finish, \ |
| ec_GFp_simple_group_clear_finish, \ |
| ec_GFp_simple_group_copy, \ |
| ec_GFp_simple_group_set_curve, \ |
| ec_GFp_simple_group_get_curve, \ |
| ec_GFp_simple_group_get_degree, \ |
| ec_group_simple_order_bits, \ |
| ec_GFp_simple_group_check_discriminant, \ |
| ec_GFp_simple_point_init, \ |
| ec_GFp_simple_point_finish, \ |
| ec_GFp_simple_point_clear_finish, \ |
| ec_GFp_simple_point_copy, \ |
| ec_GFp_simple_point_set_to_infinity, \ |
| ec_GFp_simple_point_set_affine_coordinates, \ |
| ec_GFp_simple_point_get_affine_coordinates, \ |
| NULL, /* point_set_compressed_coordinates */ \ |
| NULL, /* point2oct */ \ |
| NULL, /* oct2point */ \ |
| ec_GFp_simple_add, \ |
| ec_GFp_simple_dbl, \ |
| ec_GFp_simple_invert, \ |
| ec_GFp_simple_is_at_infinity, \ |
| ec_GFp_simple_is_on_curve, \ |
| ec_GFp_simple_cmp, \ |
| ec_GFp_simple_make_affine, \ |
| ec_GFp_simple_points_make_affine, \ |
| ec_GFp_s390x_nistp##bits##_mul, \ |
| NULL, /* precompute_mult */ \ |
| NULL, /* have_precompute_mult */ \ |
| ec_GFp_simple_field_mul, \ |
| ec_GFp_simple_field_sqr, \ |
| NULL, /* field_div */ \ |
| ec_GFp_simple_field_inv, \ |
| NULL, /* field_encode */ \ |
| NULL, /* field_decode */ \ |
| NULL, /* field_set_to_one */ \ |
| ec_key_simple_priv2oct, \ |
| ec_key_simple_oct2priv, \ |
| NULL, /* set_private */ \ |
| ec_key_simple_generate_key, \ |
| ec_key_simple_check_key, \ |
| ec_key_simple_generate_public_key, \ |
| NULL, /* keycopy */ \ |
| NULL, /* keyfinish */ \ |
| ecdh_simple_compute_key, \ |
| ecdsa_simple_sign_setup, \ |
| ecdsa_s390x_nistp##bits##_sign_sig, \ |
| ecdsa_s390x_nistp##bits##_verify_sig, \ |
| NULL, /* field_inverse_mod_ord */ \ |
| ec_GFp_simple_blind_coordinates, \ |
| ec_GFp_simple_ladder_pre, \ |
| ec_GFp_simple_ladder_step, \ |
| ec_GFp_simple_ladder_post \ |
| }; \ |
| static const EC_METHOD *ret; \ |
| \ |
| if ((OPENSSL_s390xcap_P.pcc[1] \ |
| & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_P##bits)) \ |
| && (OPENSSL_s390xcap_P.kdsa[0] \ |
| & S390X_CAPBIT(S390X_ECDSA_VERIFY_P##bits)) \ |
| && (OPENSSL_s390xcap_P.kdsa[0] \ |
| & S390X_CAPBIT(S390X_ECDSA_SIGN_P##bits))) \ |
| ret = &EC_GFp_s390x_nistp##bits##_meth; \ |
| else \ |
| ret = EC_GFp_mont_method(); \ |
| \ |
| return ret; \ |
| } |
| |
| EC_GFP_S390X_NISTP_METHOD(256) |
| EC_GFP_S390X_NISTP_METHOD(384) |
| EC_GFP_S390X_NISTP_METHOD(521) |