| /********************************************************************** |
| * gost_sign.c * |
| * Copyright (c) 2005-2006 Cryptocom LTD * |
| * This file is distributed under the same license as OpenSSL * |
| * * |
| * Implementation of GOST R 34.10-94 signature algorithm * |
| * for OpenSSL * |
| * Requires OpenSSL 0.9.9 for compilation * |
| **********************************************************************/ |
| #include <string.h> |
| #include <openssl/rand.h> |
| #include <openssl/bn.h> |
| #include <openssl/dsa.h> |
| #include <openssl/evp.h> |
| #include <openssl/err.h> |
| |
| #include "gost_params.h" |
| #include "gost_lcl.h" |
| #include "e_gost_err.h" |
| |
| #ifdef DEBUG_SIGN |
| void dump_signature(const char *message, const unsigned char *buffer, |
| size_t len) |
| { |
| size_t i; |
| fprintf(stderr, "signature %s Length=%d", message, len); |
| for (i = 0; i < len; i++) { |
| if (i % 16 == 0) |
| fputc('\n', stderr); |
| fprintf(stderr, " %02x", buffer[i]); |
| } |
| fprintf(stderr, "\nEnd of signature\n"); |
| } |
| |
| void dump_dsa_sig(const char *message, DSA_SIG *sig) |
| { |
| fprintf(stderr, "%s\nR=", message); |
| BN_print_fp(stderr, sig->r); |
| fprintf(stderr, "\nS="); |
| BN_print_fp(stderr, sig->s); |
| fprintf(stderr, "\n"); |
| } |
| |
| #else |
| |
| # define dump_signature(a,b,c) |
| # define dump_dsa_sig(a,b) |
| #endif |
| |
| /* |
| * Computes signature and returns it as DSA_SIG structure |
| */ |
| DSA_SIG *gost_do_sign(const unsigned char *dgst, int dlen, DSA *dsa) |
| { |
| BIGNUM *k = NULL, *tmp = NULL, *tmp2 = NULL; |
| DSA_SIG *newsig = NULL, *ret = NULL; |
| BIGNUM *md = hashsum2bn(dgst); |
| /* check if H(M) mod q is zero */ |
| BN_CTX *ctx = BN_CTX_new(); |
| if(!ctx) { |
| GOSTerr(GOST_F_GOST_DO_SIGN, ERR_R_MALLOC_FAILURE); |
| goto err; |
| } |
| BN_CTX_start(ctx); |
| newsig = DSA_SIG_new(); |
| if (!newsig) { |
| GOSTerr(GOST_F_GOST_DO_SIGN, GOST_R_NO_MEMORY); |
| goto err; |
| } |
| tmp = BN_CTX_get(ctx); |
| k = BN_CTX_get(ctx); |
| tmp2 = BN_CTX_get(ctx); |
| if(!tmp || !k || !tmp2) { |
| GOSTerr(GOST_F_GOST_DO_SIGN, ERR_R_MALLOC_FAILURE); |
| goto err; |
| } |
| BN_mod(tmp, md, dsa->q, ctx); |
| if (BN_is_zero(tmp)) { |
| BN_one(md); |
| } |
| do { |
| do { |
| /* |
| * Generate random number k less than q |
| */ |
| BN_rand_range(k, dsa->q); |
| /* generate r = (a^x mod p) mod q */ |
| BN_mod_exp(tmp, dsa->g, k, dsa->p, ctx); |
| if (!(newsig->r)) { |
| newsig->r = BN_new(); |
| if(!newsig->r) { |
| GOSTerr(GOST_F_GOST_DO_SIGN, ERR_R_MALLOC_FAILURE); |
| goto err; |
| } |
| } |
| BN_mod(newsig->r, tmp, dsa->q, ctx); |
| } |
| while (BN_is_zero(newsig->r)); |
| /* generate s = (xr + k(Hm)) mod q */ |
| BN_mod_mul(tmp, dsa->priv_key, newsig->r, dsa->q, ctx); |
| BN_mod_mul(tmp2, k, md, dsa->q, ctx); |
| if (!newsig->s) { |
| newsig->s = BN_new(); |
| if(!newsig->s) { |
| GOSTerr(GOST_F_GOST_DO_SIGN, ERR_R_MALLOC_FAILURE); |
| goto err; |
| } |
| } |
| BN_mod_add(newsig->s, tmp, tmp2, dsa->q, ctx); |
| } |
| while (BN_is_zero(newsig->s)); |
| |
| ret = newsig; |
| err: |
| BN_free(md); |
| if(ctx) { |
| BN_CTX_end(ctx); |
| BN_CTX_free(ctx); |
| } |
| if(!ret && newsig) { |
| DSA_SIG_free(newsig); |
| } |
| return ret; |
| } |
| |
| /* |
| * Packs signature according to Cryptocom rules |
| * and frees up DSA_SIG structure |
| */ |
| /*- |
| int pack_sign_cc(DSA_SIG *s,int order,unsigned char *sig, size_t *siglen) |
| { |
| *siglen = 2*order; |
| memset(sig,0,*siglen); |
| store_bignum(s->r, sig,order); |
| store_bignum(s->s, sig + order,order); |
| dump_signature("serialized",sig,*siglen); |
| DSA_SIG_free(s); |
| return 1; |
| } |
| */ |
| /* |
| * Packs signature according to Cryptopro rules |
| * and frees up DSA_SIG structure |
| */ |
| int pack_sign_cp(DSA_SIG *s, int order, unsigned char *sig, size_t *siglen) |
| { |
| *siglen = 2 * order; |
| memset(sig, 0, *siglen); |
| store_bignum(s->s, sig, order); |
| store_bignum(s->r, sig + order, order); |
| dump_signature("serialized", sig, *siglen); |
| DSA_SIG_free(s); |
| return 1; |
| } |
| |
| /* |
| * Verifies signature passed as DSA_SIG structure |
| * |
| */ |
| |
| int gost_do_verify(const unsigned char *dgst, int dgst_len, |
| DSA_SIG *sig, DSA *dsa) |
| { |
| BIGNUM *md = NULL, *tmp = NULL; |
| BIGNUM *q2 = NULL; |
| BIGNUM *u = NULL, *v = NULL, *z1 = NULL, *z2 = NULL; |
| BIGNUM *tmp2 = NULL, *tmp3 = NULL; |
| int ok = 0; |
| BN_CTX *ctx = BN_CTX_new(); |
| if(!ctx) { |
| GOSTerr(GOST_F_GOST_DO_VERIFY, ERR_R_MALLOC_FAILURE); |
| goto err; |
| } |
| |
| BN_CTX_start(ctx); |
| if (BN_cmp(sig->s, dsa->q) >= 1 || BN_cmp(sig->r, dsa->q) >= 1) { |
| GOSTerr(GOST_F_GOST_DO_VERIFY, GOST_R_SIGNATURE_PARTS_GREATER_THAN_Q); |
| goto err; |
| } |
| md = hashsum2bn(dgst); |
| |
| tmp = BN_CTX_get(ctx); |
| v = BN_CTX_get(ctx); |
| q2 = BN_CTX_get(ctx); |
| z1 = BN_CTX_get(ctx); |
| z2 = BN_CTX_get(ctx); |
| tmp2 = BN_CTX_get(ctx); |
| tmp3 = BN_CTX_get(ctx); |
| u = BN_CTX_get(ctx); |
| if(!tmp || !v || !q2 || !z1 || !z2 || !tmp2 || !tmp3 || !u) { |
| GOSTerr(GOST_F_GOST_DO_VERIFY, ERR_R_MALLOC_FAILURE); |
| goto err; |
| } |
| |
| BN_mod(tmp, md, dsa->q, ctx); |
| if (BN_is_zero(tmp)) { |
| BN_one(md); |
| } |
| BN_copy(q2, dsa->q); |
| BN_sub_word(q2, 2); |
| BN_mod_exp(v, md, q2, dsa->q, ctx); |
| BN_mod_mul(z1, sig->s, v, dsa->q, ctx); |
| BN_sub(tmp, dsa->q, sig->r); |
| BN_mod_mul(z2, tmp, v, dsa->p, ctx); |
| BN_mod_exp(tmp, dsa->g, z1, dsa->p, ctx); |
| BN_mod_exp(tmp2, dsa->pub_key, z2, dsa->p, ctx); |
| BN_mod_mul(tmp3, tmp, tmp2, dsa->p, ctx); |
| BN_mod(u, tmp3, dsa->q, ctx); |
| ok = (BN_cmp(u, sig->r) == 0); |
| |
| if (!ok) { |
| GOSTerr(GOST_F_GOST_DO_VERIFY, GOST_R_SIGNATURE_MISMATCH); |
| } |
| err: |
| if(md) BN_free(md); |
| if(ctx) { |
| BN_CTX_end(ctx); |
| BN_CTX_free(ctx); |
| } |
| return ok; |
| } |
| |
| /* |
| * Computes public keys for GOST R 34.10-94 algorithm |
| * |
| */ |
| int gost94_compute_public(DSA *dsa) |
| { |
| /* Now fill algorithm parameters with correct values */ |
| BN_CTX *ctx; |
| if (!dsa->g) { |
| GOSTerr(GOST_F_GOST94_COMPUTE_PUBLIC, GOST_R_KEY_IS_NOT_INITALIZED); |
| return 0; |
| } |
| ctx = BN_CTX_new(); |
| if(!ctx) { |
| GOSTerr(GOST_F_GOST94_COMPUTE_PUBLIC, ERR_R_MALLOC_FAILURE); |
| return 0; |
| } |
| |
| dsa->pub_key = BN_new(); |
| if(!dsa->pub_key) { |
| GOSTerr(GOST_F_GOST94_COMPUTE_PUBLIC, ERR_R_MALLOC_FAILURE); |
| BN_CTX_free(ctx); |
| return 0; |
| } |
| /* Compute public key y = a^x mod p */ |
| BN_mod_exp(dsa->pub_key, dsa->g, dsa->priv_key, dsa->p, ctx); |
| BN_CTX_free(ctx); |
| return 1; |
| } |
| |
| /* |
| * Fill GOST 94 params, searching them in R3410_paramset array |
| * by nid of paramset |
| * |
| */ |
| int fill_GOST94_params(DSA *dsa, int nid) |
| { |
| R3410_params *params = R3410_paramset; |
| while (params->nid != NID_undef && params->nid != nid) |
| params++; |
| if (params->nid == NID_undef) { |
| GOSTerr(GOST_F_FILL_GOST94_PARAMS, GOST_R_UNSUPPORTED_PARAMETER_SET); |
| return 0; |
| } |
| #define dump_signature(a,b,c) |
| if (dsa->p) { |
| BN_free(dsa->p); |
| } |
| dsa->p = NULL; |
| BN_dec2bn(&(dsa->p), params->p); |
| if (dsa->q) { |
| BN_free(dsa->q); |
| } |
| dsa->q = NULL; |
| BN_dec2bn(&(dsa->q), params->q); |
| if (dsa->g) { |
| BN_free(dsa->g); |
| } |
| dsa->g = NULL; |
| BN_dec2bn(&(dsa->g), params->a); |
| return 1; |
| } |
| |
| /* |
| * Generate GOST R 34.10-94 keypair |
| * |
| * |
| */ |
| int gost_sign_keygen(DSA *dsa) |
| { |
| dsa->priv_key = BN_new(); |
| if(!dsa->priv_key) { |
| GOSTerr(GOST_F_GOST_SIGN_KEYGEN, ERR_R_MALLOC_FAILURE); |
| return 0; |
| } |
| BN_rand_range(dsa->priv_key, dsa->q); |
| return gost94_compute_public(dsa); |
| } |
| |
| /* Unpack signature according to cryptocom rules */ |
| /*- |
| DSA_SIG *unpack_cc_signature(const unsigned char *sig,size_t siglen) |
| { |
| DSA_SIG *s; |
| s = DSA_SIG_new(); |
| if (s == NULL) |
| { |
| GOSTerr(GOST_F_UNPACK_CC_SIGNATURE,GOST_R_NO_MEMORY); |
| return(NULL); |
| } |
| s->r = getbnfrombuf(sig, siglen/2); |
| s->s = getbnfrombuf(sig + siglen/2, siglen/2); |
| return s; |
| } |
| */ |
| /* Unpack signature according to cryptopro rules */ |
| DSA_SIG *unpack_cp_signature(const unsigned char *sig, size_t siglen) |
| { |
| DSA_SIG *s; |
| |
| s = DSA_SIG_new(); |
| if (s == NULL) { |
| GOSTerr(GOST_F_UNPACK_CP_SIGNATURE, GOST_R_NO_MEMORY); |
| return NULL; |
| } |
| s->s = getbnfrombuf(sig, siglen / 2); |
| s->r = getbnfrombuf(sig + siglen / 2, siglen / 2); |
| return s; |
| } |
| |
| /* Convert little-endian byte array into bignum */ |
| BIGNUM *hashsum2bn(const unsigned char *dgst) |
| { |
| unsigned char buf[32]; |
| int i; |
| for (i = 0; i < 32; i++) { |
| buf[31 - i] = dgst[i]; |
| } |
| return getbnfrombuf(buf, 32); |
| } |
| |
| /* Convert byte buffer to bignum, skipping leading zeros*/ |
| BIGNUM *getbnfrombuf(const unsigned char *buf, size_t len) |
| { |
| while (*buf == 0 && len > 0) { |
| buf++; |
| len--; |
| } |
| if (len) { |
| return BN_bin2bn(buf, len, NULL); |
| } else { |
| BIGNUM *b = BN_new(); |
| BN_zero(b); |
| return b; |
| } |
| } |
| |
| /* |
| * Pack bignum into byte buffer of given size, filling all leading bytes by |
| * zeros |
| */ |
| int store_bignum(BIGNUM *bn, unsigned char *buf, int len) |
| { |
| int bytes = BN_num_bytes(bn); |
| if (bytes > len) |
| return 0; |
| memset(buf, 0, len); |
| BN_bn2bin(bn, buf + len - bytes); |
| return 1; |
| } |