You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
466 lines
13 KiB
466 lines
13 KiB
/********************************************************************** |
|
* gost2001.c * |
|
* Copyright (c) 2005-2006 Cryptocom LTD * |
|
* This file is distributed under the same license as OpenSSL * |
|
* * |
|
* Implementation of GOST R 34.10-2001 * |
|
* Requires OpenSSL 0.9.9 for compilation * |
|
**********************************************************************/ |
|
#include "gost_lcl.h" |
|
#include "gost_params.h" |
|
#include <string.h> |
|
#include <openssl/rand.h> |
|
#include <openssl/ecdsa.h> |
|
#include <openssl/err.h> |
|
#include "e_gost_err.h" |
|
#ifdef DEBUG_SIGN |
|
extern |
|
void dump_signature(const char *message, const unsigned char *buffer, |
|
size_t len); |
|
void dump_dsa_sig(const char *message, DSA_SIG *sig); |
|
#else |
|
|
|
# define dump_signature(a,b,c) |
|
# define dump_dsa_sig(a,b) |
|
#endif |
|
|
|
/* |
|
* Fills EC_KEY structure hidden in the app_data field of DSA structure |
|
* with parameter information, extracted from parameter array in |
|
* params.c file. |
|
* |
|
* Also fils DSA->q field with copy of EC_GROUP order field to make |
|
* DSA_size function work |
|
*/ |
|
int fill_GOST2001_params(EC_KEY *eckey, int nid) |
|
{ |
|
R3410_2001_params *params = R3410_2001_paramset; |
|
EC_GROUP *grp = NULL; |
|
BIGNUM *p = NULL, *q = NULL, *a = NULL, *b = NULL, *x = NULL, *y = NULL; |
|
EC_POINT *P = NULL; |
|
BN_CTX *ctx = BN_CTX_new(); |
|
int ok = 0; |
|
|
|
if(!ctx) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
|
|
BN_CTX_start(ctx); |
|
p = BN_CTX_get(ctx); |
|
a = BN_CTX_get(ctx); |
|
b = BN_CTX_get(ctx); |
|
x = BN_CTX_get(ctx); |
|
y = BN_CTX_get(ctx); |
|
q = BN_CTX_get(ctx); |
|
if(!p || !a || !b || !x || !y || !q) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
while (params->nid != NID_undef && params->nid != nid) |
|
params++; |
|
if (params->nid == NID_undef) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, |
|
GOST_R_UNSUPPORTED_PARAMETER_SET); |
|
goto err; |
|
} |
|
if(!BN_hex2bn(&p, params->p) |
|
|| !BN_hex2bn(&a, params->a) |
|
|| !BN_hex2bn(&b, params->b)) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, |
|
ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
|
|
grp = EC_GROUP_new_curve_GFp(p, a, b, ctx); |
|
if(!grp) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
|
|
P = EC_POINT_new(grp); |
|
if(!P) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
|
|
if(!BN_hex2bn(&x, params->x) |
|
|| !BN_hex2bn(&y, params->y) |
|
|| !EC_POINT_set_affine_coordinates_GFp(grp, P, x, y, ctx) |
|
|| !BN_hex2bn(&q, params->q)) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
#ifdef DEBUG_KEYS |
|
fprintf(stderr, "Set params index %d oid %s\nq=", |
|
(params - R3410_2001_paramset), OBJ_nid2sn(params->nid)); |
|
BN_print_fp(stderr, q); |
|
fprintf(stderr, "\n"); |
|
#endif |
|
|
|
if(!EC_GROUP_set_generator(grp, P, q, NULL)) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
EC_GROUP_set_curve_name(grp, params->nid); |
|
if(!EC_KEY_set_group(eckey, grp)) { |
|
GOSTerr(GOST_F_FILL_GOST2001_PARAMS, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
ok = 1; |
|
err: |
|
if (P) EC_POINT_free(P); |
|
if (grp) EC_GROUP_free(grp); |
|
if (ctx) { |
|
BN_CTX_end(ctx); |
|
BN_CTX_free(ctx); |
|
} |
|
return ok; |
|
} |
|
|
|
/* |
|
* Computes gost2001 signature as DSA_SIG structure |
|
* |
|
* |
|
*/ |
|
DSA_SIG *gost2001_do_sign(const unsigned char *dgst, int dlen, EC_KEY *eckey) |
|
{ |
|
DSA_SIG *newsig = NULL, *ret = NULL; |
|
BIGNUM *md = hashsum2bn(dgst); |
|
BIGNUM *order = NULL; |
|
const EC_GROUP *group; |
|
const BIGNUM *priv_key; |
|
BIGNUM *r = NULL, *s = NULL, *X = NULL, *tmp = NULL, *tmp2 = NULL, *k = |
|
NULL, *e = NULL; |
|
EC_POINT *C = NULL; |
|
BN_CTX *ctx = BN_CTX_new(); |
|
if(!ctx || !md) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
BN_CTX_start(ctx); |
|
OPENSSL_assert(dlen == 32); |
|
newsig = DSA_SIG_new(); |
|
if (!newsig) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, GOST_R_NO_MEMORY); |
|
goto err; |
|
} |
|
group = EC_KEY_get0_group(eckey); |
|
if(!group) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
order = BN_CTX_get(ctx); |
|
if(!order || !EC_GROUP_get_order(group, order, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
priv_key = EC_KEY_get0_private_key(eckey); |
|
if(!priv_key) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
e = BN_CTX_get(ctx); |
|
if(!e || !BN_mod(e, md, order, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
#ifdef DEBUG_SIGN |
|
fprintf(stderr, "digest as bignum="); |
|
BN_print_fp(stderr, md); |
|
fprintf(stderr, "\ndigest mod q="); |
|
BN_print_fp(stderr, e); |
|
fprintf(stderr, "\n"); |
|
#endif |
|
if (BN_is_zero(e)) { |
|
BN_one(e); |
|
} |
|
k = BN_CTX_get(ctx); |
|
C = EC_POINT_new(group); |
|
if(!k || !C) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
do { |
|
do { |
|
if (!BN_rand_range(k, order)) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, |
|
GOST_R_RANDOM_NUMBER_GENERATOR_FAILED); |
|
goto err; |
|
} |
|
if (!EC_POINT_mul(group, C, k, NULL, NULL, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_EC_LIB); |
|
goto err; |
|
} |
|
if (!X) |
|
X = BN_CTX_get(ctx); |
|
if (!r) |
|
r = BN_CTX_get(ctx); |
|
if (!X || !r) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
if (!EC_POINT_get_affine_coordinates_GFp(group, C, X, NULL, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_EC_LIB); |
|
goto err; |
|
} |
|
|
|
if(!BN_nnmod(r, X, order, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
} |
|
while (BN_is_zero(r)); |
|
/* s = (r*priv_key+k*e) mod order */ |
|
if (!tmp) |
|
tmp = BN_CTX_get(ctx); |
|
if (!tmp2) |
|
tmp2 = BN_CTX_get(ctx); |
|
if (!s) |
|
s = BN_CTX_get(ctx); |
|
if (!tmp || !tmp2 || !s) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
|
|
if(!BN_mod_mul(tmp, priv_key, r, order, ctx) |
|
|| !BN_mod_mul(tmp2, k, e, order, ctx) |
|
|| !BN_mod_add(s, tmp, tmp2, order, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
} |
|
while (BN_is_zero(s)); |
|
|
|
newsig->s = BN_dup(s); |
|
newsig->r = BN_dup(r); |
|
if(!newsig->s || !newsig->r) { |
|
GOSTerr(GOST_F_GOST2001_DO_SIGN, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
|
|
ret = newsig; |
|
err: |
|
if(ctx) { |
|
BN_CTX_end(ctx); |
|
BN_CTX_free(ctx); |
|
} |
|
if (C) EC_POINT_free(C); |
|
if (md) BN_free(md); |
|
if (!ret && newsig) { |
|
DSA_SIG_free(newsig); |
|
} |
|
return ret; |
|
} |
|
|
|
/* |
|
* Verifies gost 2001 signature |
|
* |
|
*/ |
|
int gost2001_do_verify(const unsigned char *dgst, int dgst_len, |
|
DSA_SIG *sig, EC_KEY *ec) |
|
{ |
|
BN_CTX *ctx = BN_CTX_new(); |
|
const EC_GROUP *group = EC_KEY_get0_group(ec); |
|
BIGNUM *order; |
|
BIGNUM *md = NULL, *e = NULL, *R = NULL, *v = NULL, *z1 = NULL, *z2 = |
|
NULL; |
|
BIGNUM *X = NULL, *tmp = NULL; |
|
EC_POINT *C = NULL; |
|
const EC_POINT *pub_key = NULL; |
|
int ok = 0; |
|
|
|
if(!ctx || !group) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
|
|
BN_CTX_start(ctx); |
|
order = BN_CTX_get(ctx); |
|
e = BN_CTX_get(ctx); |
|
z1 = BN_CTX_get(ctx); |
|
z2 = BN_CTX_get(ctx); |
|
tmp = BN_CTX_get(ctx); |
|
X = BN_CTX_get(ctx); |
|
R = BN_CTX_get(ctx); |
|
v = BN_CTX_get(ctx); |
|
if(!order || !e || !z1 || !z2 || !tmp || !X || !R || !v) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
|
|
pub_key = EC_KEY_get0_public_key(ec); |
|
if(!pub_key || !EC_GROUP_get_order(group, order, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
|
|
if (BN_is_zero(sig->s) || BN_is_zero(sig->r) || |
|
(BN_cmp(sig->s, order) >= 1) || (BN_cmp(sig->r, order) >= 1)) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, |
|
GOST_R_SIGNATURE_PARTS_GREATER_THAN_Q); |
|
goto err; |
|
|
|
} |
|
md = hashsum2bn(dgst); |
|
|
|
if(!md || !BN_mod(e, md, order, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
#ifdef DEBUG_SIGN |
|
fprintf(stderr, "digest as bignum: "); |
|
BN_print_fp(stderr, md); |
|
fprintf(stderr, "\ndigest mod q: "); |
|
BN_print_fp(stderr, e); |
|
#endif |
|
if (BN_is_zero(e) && !BN_one(e)) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
v = BN_mod_inverse(v, e, order, ctx); |
|
if(!v |
|
|| !BN_mod_mul(z1, sig->s, v, order, ctx) |
|
|| !BN_sub(tmp, order, sig->r) |
|
|| !BN_mod_mul(z2, tmp, v, order, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
#ifdef DEBUG_SIGN |
|
fprintf(stderr, "\nInverted digest value: "); |
|
BN_print_fp(stderr, v); |
|
fprintf(stderr, "\nz1: "); |
|
BN_print_fp(stderr, z1); |
|
fprintf(stderr, "\nz2: "); |
|
BN_print_fp(stderr, z2); |
|
#endif |
|
C = EC_POINT_new(group); |
|
if (!C) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
if (!EC_POINT_mul(group, C, z1, pub_key, z2, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_EC_LIB); |
|
goto err; |
|
} |
|
if (!EC_POINT_get_affine_coordinates_GFp(group, C, X, NULL, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_EC_LIB); |
|
goto err; |
|
} |
|
if(!BN_mod(R, X, order, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, ERR_R_INTERNAL_ERROR); |
|
goto err; |
|
} |
|
#ifdef DEBUG_SIGN |
|
fprintf(stderr, "\nX="); |
|
BN_print_fp(stderr, X); |
|
fprintf(stderr, "\nX mod q="); |
|
BN_print_fp(stderr, R); |
|
fprintf(stderr, "\n"); |
|
#endif |
|
if (BN_cmp(R, sig->r) != 0) { |
|
GOSTerr(GOST_F_GOST2001_DO_VERIFY, GOST_R_SIGNATURE_MISMATCH); |
|
} else { |
|
ok = 1; |
|
} |
|
err: |
|
if (C) EC_POINT_free(C); |
|
if (ctx) { |
|
BN_CTX_end(ctx); |
|
BN_CTX_free(ctx); |
|
} |
|
if (md) BN_free(md); |
|
return ok; |
|
} |
|
|
|
/* |
|
* Computes GOST R 34.10-2001 public key |
|
* |
|
* |
|
*/ |
|
int gost2001_compute_public(EC_KEY *ec) |
|
{ |
|
const EC_GROUP *group = EC_KEY_get0_group(ec); |
|
EC_POINT *pub_key = NULL; |
|
const BIGNUM *priv_key = NULL; |
|
BN_CTX *ctx = NULL; |
|
int ok = 0; |
|
|
|
if (!group) { |
|
GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, |
|
GOST_R_KEY_IS_NOT_INITIALIZED); |
|
return 0; |
|
} |
|
ctx = BN_CTX_new(); |
|
if(!ctx) { |
|
GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
BN_CTX_start(ctx); |
|
if (!(priv_key = EC_KEY_get0_private_key(ec))) { |
|
GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_EC_LIB); |
|
goto err; |
|
} |
|
|
|
pub_key = EC_POINT_new(group); |
|
if(!pub_key) { |
|
GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_MALLOC_FAILURE); |
|
goto err; |
|
} |
|
if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) { |
|
GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_EC_LIB); |
|
goto err; |
|
} |
|
if (!EC_KEY_set_public_key(ec, pub_key)) { |
|
GOSTerr(GOST_F_GOST2001_COMPUTE_PUBLIC, ERR_R_EC_LIB); |
|
goto err; |
|
} |
|
ok = 256; |
|
err: |
|
if (pub_key) EC_POINT_free(pub_key); |
|
if (ctx) { |
|
BN_CTX_end(ctx); |
|
BN_CTX_free(ctx); |
|
} |
|
return ok; |
|
} |
|
|
|
/* |
|
* |
|
* Generates GOST R 34.10-2001 keypair |
|
* |
|
* |
|
*/ |
|
int gost2001_keygen(EC_KEY *ec) |
|
{ |
|
BIGNUM *order = BN_new(), *d = BN_new(); |
|
const EC_GROUP *group = EC_KEY_get0_group(ec); |
|
|
|
if(!group || !EC_GROUP_get_order(group, order, NULL)) { |
|
GOSTerr(GOST_F_GOST2001_KEYGEN, ERR_R_INTERNAL_ERROR); |
|
BN_free(d); |
|
BN_free(order); |
|
return 0; |
|
} |
|
|
|
do { |
|
if (!BN_rand_range(d, order)) { |
|
GOSTerr(GOST_F_GOST2001_KEYGEN, |
|
GOST_R_RANDOM_NUMBER_GENERATOR_FAILED); |
|
BN_free(d); |
|
BN_free(order); |
|
return 0; |
|
} |
|
} |
|
while (BN_is_zero(d)); |
|
|
|
if(!EC_KEY_set_private_key(ec, d)) { |
|
GOSTerr(GOST_F_GOST2001_KEYGEN, ERR_R_INTERNAL_ERROR); |
|
BN_free(d); |
|
BN_free(order); |
|
return 0; |
|
} |
|
BN_free(d); |
|
BN_free(order); |
|
return gost2001_compute_public(ec); |
|
}
|
|
|