GOST public key algorithm ENGINE donated to the OpenSSL by Cryptocom.
[openssl.git] / engines / ccgost / gost_sign.c
diff --git a/engines/ccgost/gost_sign.c b/engines/ccgost/gost_sign.c
new file mode 100644 (file)
index 0000000..d4c9e5d
--- /dev/null
@@ -0,0 +1,302 @@
+/**********************************************************************
+ *                          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 algoritgthm      *
+ *       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 "sign.h"
+#include "paramset.h"
+#include "tools.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 = DSA_SIG_new();
+       BIGNUM *md = hashsum2bn(dgst);
+       /* check if H(M) mod q is zero */
+       BN_CTX *ctx=BN_CTX_new();
+       BN_CTX_start(ctx);
+       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);
+       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();
+                       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();
+               BN_mod_add(newsig->s,tmp,tmp2,dsa->q,ctx);
+    } while (BN_is_zero(newsig->s));           
+err:
+       BN_free(md);
+       BN_CTX_end(ctx);
+       BN_CTX_free(ctx);
+       return newsig;
+}      
+
+
+/*
+ * Packs signature according to Cryptocom rules
+ * and frees up DSA_SIG structure
+ */
+
+int pack_sign_cc(DSA_SIG *s,int order,unsigned char *sig, unsigned int *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, unsigned int *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, *tmp=NULL;
+       BIGNUM *q2=NULL;
+       BIGNUM *u=NULL,*v=NULL,*z1=NULL,*z2=NULL;
+       BIGNUM *tmp2=NULL,*tmp3=NULL;
+       int ok;
+       BN_CTX *ctx = BN_CTX_new();
+
+       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);
+         return 0;
+       }
+       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);
+       
+       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);
+       
+       BN_free(md);
+       BN_CTX_end(ctx);
+       BN_CTX_free(ctx);
+       if (ok!=0) {
+               GOSTerr(GOST_F_GOST_DO_VERIFY,GOST_R_SIGNATURE_MISMATCH);
+       }       
+       return (ok==0);
+}      
+
+/*
+ * 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 = BN_CTX_new();
+       if (!dsa->g) {
+               GOSTerr(GOST_F_GOST_COMPUTE_PUBLIC,GOST_R_KEY_IS_NOT_INITALIZED);
+               return 0;
+       }       
+       /* Compute public key  y = a^x mod p */
+       dsa->pub_key=BN_new();
+       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();
+       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;
+}      
+