gateway2/Tongsuo-8.4.0/crypto/sm2/sm2_kmeth.c
2026-07-11 13:40:57 +08:00

256 lines
7.0 KiB
C

/*
* Copyright 2022-2022 The Tongsuo 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.apache.org/licenses/LICENSE-2.0
*/
#include "internal/deprecated.h"
#include "internal/cryptlib.h"
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include "crypto/sm2.h"
#include "crypto/ec.h" /* ecdh_KDF_X9_63() */
#include "crypto/sm2err.h"
int SM2_compute_key(void *out, size_t outlen, int initiator,
const uint8_t *peer_id, size_t peer_id_len,
const uint8_t *self_id, size_t self_id_len,
const EC_KEY *peer_ecdhe_key, const EC_KEY *self_ecdhe_key,
const EC_KEY *peer_pub_key, const EC_KEY *self_eckey,
const EVP_MD *md, OSSL_LIB_CTX *libctx,
const char *propq)
{
BN_CTX *ctx = NULL;
EC_POINT *UorV = NULL;
const EC_POINT *Rs, *Rp;
BIGNUM *Xuv = NULL, *Yuv = NULL, *Xs = NULL, *Xp = NULL;
BIGNUM *h = NULL, *t = NULL, *two_power_w = NULL, *order = NULL;
const BIGNUM *priv_key, *r;
const EC_GROUP *group;
int w;
int ret = 0;
size_t buflen = 0, md_len;
unsigned char *buf = NULL;
size_t field_len, idx = 0;
if (peer_id == NULL || self_id == NULL || peer_ecdhe_key == NULL
|| self_ecdhe_key == NULL || peer_pub_key == NULL
|| self_eckey == NULL || md == NULL) {
ERR_raise(ERR_LIB_SM2, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (outlen > INT_MAX) {
ERR_raise(ERR_LIB_SM2, ERR_R_MALLOC_FAILURE);
return 0;
}
priv_key = EC_KEY_get0_private_key(self_eckey);
if (priv_key == NULL) {
ERR_raise(ERR_LIB_SM2, SM2_R_INVALID_PRIVATE_KEY);
return 0;
}
Rs = EC_KEY_get0_public_key(self_ecdhe_key);
Rp = EC_KEY_get0_public_key(peer_ecdhe_key);
r = EC_KEY_get0_private_key(self_ecdhe_key);
if (Rs == NULL || Rp == NULL || r == NULL) {
ERR_raise(ERR_LIB_SM2, SM2_R_INVALID_PRIVATE_KEY);
return 0;
}
ctx = BN_CTX_new_ex(libctx);
if (ctx == NULL) {
ERR_raise(ERR_LIB_SM2, ERR_R_MALLOC_FAILURE);
goto err;
}
BN_CTX_start(ctx);
Xuv = BN_CTX_get(ctx);
Yuv = BN_CTX_get(ctx);
Xs = BN_CTX_get(ctx);
Xp = BN_CTX_get(ctx);
h = BN_CTX_get(ctx);
t = BN_CTX_get(ctx);
two_power_w = BN_CTX_get(ctx);
order = BN_CTX_get(ctx);
if (order == NULL) {
ERR_raise(ERR_LIB_SM2, ERR_R_MALLOC_FAILURE);
goto err;
}
group = EC_KEY_get0_group(self_eckey);
if (!EC_GROUP_get_order(group, order, ctx)
|| !EC_GROUP_get_cofactor(group, h, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_MALLOC_FAILURE);
goto err;
}
w = (BN_num_bits(order) + 1) / 2 - 1;
if (!BN_lshift(two_power_w, BN_value_one(), w)) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
/*Third: Caculate -- X = 2 ^ w + (x & (2 ^ w - 1)) = 2 ^ w + (x mod 2 ^ w)*/
UorV = EC_POINT_new(group);
if (UorV == NULL) {
ERR_raise(ERR_LIB_SM2, ERR_R_MALLOC_FAILURE);
goto err;
}
/* Test peer public key On curve */
if (!EC_POINT_is_on_curve(group, Rp, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_EC_LIB);
goto err;
}
/* Get x */
if (!EC_POINT_get_affine_coordinates(group, Rs, Xs, NULL, ctx)
|| !EC_POINT_get_affine_coordinates(group, Rp, Xp, NULL, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_EC_LIB);
goto err;
}
/*x mod 2 ^ w*/
/*Caculate Self x*/
if (!BN_nnmod(Xs, Xs, two_power_w, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
if (!BN_add(Xs, Xs, two_power_w)) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
/*Caculate Peer x*/
if (!BN_nnmod(Xp, Xp, two_power_w, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
if (!BN_add(Xp, Xp, two_power_w)) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
/*Forth: Caculate t*/
if (!BN_mod_mul(t, Xs, r, order, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
if (!BN_mod_add(t, t, priv_key, order, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
/*Fifth: Caculate V or U*/
if (!BN_mul(t, t, h, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
/* [x]R */
if (!EC_POINT_mul(group, UorV, NULL, Rp, Xp, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_EC_LIB);
goto err;
}
/* P + [x]R */
if (!EC_POINT_add(group, UorV, UorV,
EC_KEY_get0_public_key(peer_pub_key), ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_EC_LIB);
goto err;
}
if (!EC_POINT_mul(group, UorV, NULL, UorV, t, ctx)) {
ERR_raise(ERR_LIB_SM2, ERR_R_EC_LIB);
goto err;
}
if (EC_POINT_is_at_infinity(group, UorV)) {
ERR_raise(ERR_LIB_SM2, ERR_R_EC_LIB);
goto err;
}
/*Sixth: Caculate Key -- Need Xuorv, Yuorv, Z_A, Z_B, klen*/
if (!EC_POINT_get_affine_coordinates(group, UorV, Xuv, Yuv, ctx)) {
ERR_raise(ERR_LIB_SM2, SM2_R_POINT_ARITHMETIC_FAILURE);
goto err;
}
field_len = ((size_t)EC_GROUP_get_degree(group) + 7) / 8;
md_len = EVP_MD_size(md);
/* Xuorv || Yuorv || Z_A || Z_B */
buflen = field_len * 2 + md_len * 2 ;
buf = OPENSSL_secure_malloc(buflen);
if (buf == NULL) {
ERR_raise(ERR_LIB_SM2, ERR_R_MALLOC_FAILURE);
goto err;
}
/*1 : Get public key for UorV, Notice: the first byte is a tag, not a valid char*/
if (BN_bn2binpad(Xuv, buf, field_len) < 0
|| BN_bn2binpad(Yuv, buf + field_len, field_len) < 0) {
ERR_raise(ERR_LIB_SM2, ERR_R_BN_LIB);
goto err;
}
idx += field_len * 2;
if (initiator) {
if (!ossl_sm2_compute_z_digest((uint8_t *)(buf + idx), md,
self_id, self_id_len,
self_eckey))
goto err;
idx += md_len;
}
if (!ossl_sm2_compute_z_digest((uint8_t *)(buf + idx), md,
peer_id, peer_id_len,
peer_pub_key))
goto err;
idx += md_len;
if (!initiator) {
if (!ossl_sm2_compute_z_digest((uint8_t *)(buf + idx), md,
self_id, self_id_len,
self_eckey))
goto err;
idx += md_len;
}
if (!ossl_ecdh_kdf_X9_63(out, outlen, buf, idx, NULL, 0, md, libctx,
propq)) {
ERR_raise(ERR_LIB_SM2, ERR_R_INTERNAL_ERROR);
goto err;
}
ret = outlen;
err:
EC_POINT_free(UorV);
OPENSSL_secure_clear_free(buf, buflen);
if (ctx != NULL)
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return ret;
}