vaseboot/VasEBoot-core/lib/libgcrypt/mpi/ec-hw-s390x.c

413 lines
11 KiB
C

/* ec-hw-s390x.c - zSeries ECC acceleration
* Copyright (C) 2021 Jussi Kivilinna <jussi.kivilinna@iki.fi>
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#ifdef HAVE_GCC_INLINE_ASM_S390X
#include "mpi-internal.h"
#include "g10lib.h"
#include "context.h"
#include "ec-context.h"
#include "ec-internal.h"
#include "../cipher/bufhelp.h"
#include "../cipher/asm-inline-s390x.h"
#define S390X_PCC_PARAM_BLOCK_SIZE 4096
extern void reverse_buffer (unsigned char *buffer, unsigned int length);
static int s390_mul_point_montgomery (mpi_point_t result, gcry_mpi_t scalar,
mpi_point_t point, mpi_ec_t ctx,
byte *param_block_buf);
static int
mpi_copy_to_raw(byte *raw, unsigned int raw_nbytes, gcry_mpi_t a)
{
unsigned int num_to_zero;
unsigned int nbytes;
int i, j;
if (mpi_has_sign (a))
return -1;
if (mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
{
unsigned int nbits;
byte *buf;
buf = mpi_get_opaque (a, &nbits);
nbytes = (nbits + 7) / 8;
if (raw_nbytes < nbytes)
return -1;
num_to_zero = raw_nbytes - nbytes;
if (num_to_zero > 0)
memset (raw, 0, num_to_zero);
if (nbytes > 0)
memcpy (raw + num_to_zero, buf, nbytes);
return 0;
}
nbytes = a->nlimbs * BYTES_PER_MPI_LIMB;
if (raw_nbytes < nbytes)
return -1;
num_to_zero = raw_nbytes - nbytes;
if (num_to_zero > 0)
memset (raw, 0, num_to_zero);
for (j = a->nlimbs - 1, i = 0; i < a->nlimbs; i++, j--)
{
buf_put_be64(raw + num_to_zero + i * BYTES_PER_MPI_LIMB, a->d[j]);
}
return 0;
}
int
_gcry_s390x_ec_hw_mul_point (mpi_point_t result, gcry_mpi_t scalar,
mpi_point_t point, mpi_ec_t ctx)
{
byte param_block_buf[S390X_PCC_PARAM_BLOCK_SIZE];
byte *param_out_x = NULL;
byte *param_out_y = NULL;
byte *param_in_x = NULL;
byte *param_in_y = NULL;
byte *param_scalar = NULL;
unsigned int field_nbits;
unsigned int pcc_func;
gcry_mpi_t x, y;
gcry_mpi_t d = NULL;
int rc = -1;
if (ctx->name == NULL)
return -1;
if (!(_gcry_get_hw_features () & HWF_S390X_MSA_9))
return -1; /* ECC acceleration not supported by HW. */
if (ctx->model == MPI_EC_MONTGOMERY)
return s390_mul_point_montgomery (result, scalar, point, ctx,
param_block_buf);
if (ctx->model == MPI_EC_WEIERSTRASS && ctx->nbits == 256 &&
strcmp (ctx->name, "NIST P-256") == 0)
{
struct pcc_param_block_nistp256_s
{
byte out_x[256 / 8];
byte out_y[256 / 8];
byte in_x[256 / 8];
byte in_y[256 / 8];
byte scalar[256 / 8];
byte c_and_ribm[64];
} *params = (void *)param_block_buf;
memset (params->c_and_ribm, 0, sizeof(params->c_and_ribm));
pcc_func = PCC_FUNCTION_NIST_P256;
field_nbits = 256;
param_out_x = params->out_x;
param_out_y = params->out_y;
param_in_x = params->in_x;
param_in_y = params->in_y;
param_scalar = params->scalar;
}
else if (ctx->model == MPI_EC_WEIERSTRASS && ctx->nbits == 384 &&
strcmp (ctx->name, "NIST P-384") == 0)
{
struct pcc_param_block_nistp384_s
{
byte out_x[384 / 8];
byte out_y[384 / 8];
byte in_x[384 / 8];
byte in_y[384 / 8];
byte scalar[384 / 8];
byte c_and_ribm[64];
} *params = (void *)param_block_buf;
memset (params->c_and_ribm, 0, sizeof(params->c_and_ribm));
pcc_func = PCC_FUNCTION_NIST_P384;
field_nbits = 384;
param_out_x = params->out_x;
param_out_y = params->out_y;
param_in_x = params->in_x;
param_in_y = params->in_y;
param_scalar = params->scalar;
}
else if (ctx->model == MPI_EC_WEIERSTRASS && ctx->nbits == 521 &&
strcmp (ctx->name, "NIST P-521") == 0)
{
struct pcc_param_block_nistp521_s
{
byte out_x[640 / 8]; /* note: first 14 bytes not modified by pcc */
byte out_y[640 / 8]; /* note: first 14 bytes not modified by pcc */
byte in_x[640 / 8];
byte in_y[640 / 8];
byte scalar[640 / 8];
byte c_and_ribm[64];
} *params = (void *)param_block_buf;
memset (params->out_x, 0, 14);
memset (params->out_y, 0, 14);
memset (params->c_and_ribm, 0, sizeof(params->c_and_ribm));
pcc_func = PCC_FUNCTION_NIST_P521;
field_nbits = 640;
param_out_x = params->out_x;
param_out_y = params->out_y;
param_in_x = params->in_x;
param_in_y = params->in_y;
param_scalar = params->scalar;
}
else if (ctx->model == MPI_EC_EDWARDS && ctx->nbits == 255 &&
strcmp (ctx->name, "Ed25519") == 0)
{
struct pcc_param_block_ed25519_s
{
byte out_x[256 / 8];
byte out_y[256 / 8];
byte in_x[256 / 8];
byte in_y[256 / 8];
byte scalar[256 / 8];
byte c_and_ribm[64];
} *params = (void *)param_block_buf;
memset (params->c_and_ribm, 0, sizeof(params->c_and_ribm));
pcc_func = PCC_FUNCTION_ED25519;
field_nbits = 256;
param_out_x = params->out_x;
param_out_y = params->out_y;
param_in_x = params->in_x;
param_in_y = params->in_y;
param_scalar = params->scalar;
}
else if (ctx->model == MPI_EC_EDWARDS && ctx->nbits == 448 &&
strcmp (ctx->name, "Ed448") == 0)
{
struct pcc_param_block_ed448_s
{
byte out_x[512 / 8]; /* note: first 8 bytes not modified by pcc */
byte out_y[512 / 8]; /* note: first 8 bytes not modified by pcc */
byte in_x[512 / 8];
byte in_y[512 / 8];
byte scalar[512 / 8];
byte c_and_ribm[64];
} *params = (void *)param_block_buf;
memset (params->out_x, 0, 8);
memset (params->out_y, 0, 8);
memset (params->c_and_ribm, 0, sizeof(params->c_and_ribm));
pcc_func = PCC_FUNCTION_ED448;
field_nbits = 512;
param_out_x = params->out_x;
param_out_y = params->out_y;
param_in_x = params->in_x;
param_in_y = params->in_y;
param_scalar = params->scalar;
}
if (param_scalar == NULL)
return -1; /* No curve match. */
if (!(pcc_query () & km_function_to_mask (pcc_func)))
return -1; /* HW does not support acceleration for this curve. */
x = mpi_new (0);
y = mpi_new (0);
if (_gcry_mpi_ec_get_affine (x, y, point, ctx) < 0)
{
/* Point at infinity. */
goto out;
}
if (mpi_has_sign (scalar) || mpi_cmp (scalar, ctx->n) >= 0)
{
d = mpi_is_secure (scalar) ? mpi_snew (ctx->nbits) : mpi_new (ctx->nbits);
_gcry_mpi_mod (d, scalar, ctx->n);
}
else
{
d = scalar;
}
if (mpi_copy_to_raw (param_in_x, field_nbits / 8, x) < 0)
goto out;
if (mpi_copy_to_raw (param_in_y, field_nbits / 8, y) < 0)
goto out;
if (mpi_copy_to_raw (param_scalar, field_nbits / 8, d) < 0)
goto out;
if (pcc_scalar_multiply (pcc_func, param_block_buf) != 0)
goto out;
_gcry_mpi_set_buffer (result->x, param_out_x, field_nbits / 8, 0);
_gcry_mpi_set_buffer (result->y, param_out_y, field_nbits / 8, 0);
mpi_set_ui (result->z, 1);
mpi_normalize (result->x);
mpi_normalize (result->y);
if (ctx->model == MPI_EC_EDWARDS)
mpi_point_resize (result, ctx);
rc = 0;
out:
if (d != scalar)
mpi_release (d);
mpi_release (y);
mpi_release (x);
wipememory (param_block_buf, S390X_PCC_PARAM_BLOCK_SIZE);
return rc;
}
static int
s390_mul_point_montgomery (mpi_point_t result, gcry_mpi_t scalar,
mpi_point_t point, mpi_ec_t ctx,
byte *param_block_buf)
{
byte *param_out_x = NULL;
byte *param_in_x = NULL;
byte *param_scalar = NULL;
unsigned int field_nbits;
unsigned int pcc_func;
gcry_mpi_t x;
gcry_mpi_t d = NULL;
int rc = -1;
if (ctx->nbits == 255 && strcmp (ctx->name, "Curve25519") == 0)
{
struct pcc_param_block_x25519_s
{
byte out_x[256 / 8];
byte in_x[256 / 8];
byte scalar[256 / 8];
byte c_and_ribm[64];
} *params = (void *)param_block_buf;
memset (params->c_and_ribm, 0, sizeof(params->c_and_ribm));
pcc_func = PCC_FUNCTION_X25519;
field_nbits = 256;
param_out_x = params->out_x;
param_in_x = params->in_x;
param_scalar = params->scalar;
}
else if (ctx->nbits == 448 && strcmp (ctx->name, "X448") == 0)
{
struct pcc_param_block_x448_s
{
byte out_x[512 / 8]; /* note: first 8 bytes not modified by pcc */
byte in_x[512 / 8];
byte scalar[512 / 8];
byte c_and_ribm[64];
} *params = (void *)param_block_buf;
memset (params->out_x, 0, 8);
memset (params->c_and_ribm, 0, sizeof(params->c_and_ribm));
pcc_func = PCC_FUNCTION_X448;
field_nbits = 512;
param_out_x = params->out_x;
param_in_x = params->in_x;
param_scalar = params->scalar;
}
if (param_scalar == NULL)
return -1; /* No curve match. */
if (!(pcc_query () & km_function_to_mask (pcc_func)))
return -1; /* HW does not support acceleration for this curve. */
x = mpi_new (0);
if (mpi_is_opaque (scalar))
{
const unsigned int pbits = ctx->nbits;
unsigned int n;
unsigned char *raw;
raw = _gcry_mpi_get_opaque_copy (scalar, &n);
if ((n + 7) / 8 != (pbits + 7) / 8)
log_fatal ("scalar size (%d) != prime size (%d)\n",
(n + 7) / 8, (pbits + 7) / 8);
reverse_buffer (raw, (n + 7 ) / 8);
if ((pbits % 8))
raw[0] &= (1 << (pbits % 8)) - 1;
raw[0] |= (1 << ((pbits + 7) % 8));
raw[(pbits + 7) / 8 - 1] &= (256 - ctx->h);
d = mpi_is_secure (scalar) ? mpi_snew (pbits) : mpi_new (pbits);
_gcry_mpi_set_buffer (d, raw, (n + 7) / 8, 0);
xfree (raw);
}
else
{
d = scalar;
}
if (_gcry_mpi_ec_get_affine (x, NULL, point, ctx) < 0)
{
/* Point at infinity. */
goto out;
}
if (mpi_copy_to_raw (param_in_x, field_nbits / 8, x) < 0)
goto out;
if (mpi_copy_to_raw (param_scalar, field_nbits / 8, d) < 0)
goto out;
if (pcc_scalar_multiply (pcc_func, param_block_buf) != 0)
goto out;
_gcry_mpi_set_buffer (result->x, param_out_x, field_nbits / 8, 0);
mpi_set_ui (result->z, 1);
mpi_point_resize (result, ctx);
rc = 0;
out:
if (d != scalar)
mpi_release (d);
mpi_release (x);
wipememory (param_block_buf, S390X_PCC_PARAM_BLOCK_SIZE);
return rc;
}
#endif /* HAVE_GCC_INLINE_ASM_S390X */