Added DSA.
authorPekka Riikonen <priikone@silcnet.org>
Sat, 21 Jul 2007 12:59:06 +0000 (12:59 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 21 Jul 2007 12:59:06 +0000 (12:59 +0000)
lib/silccrypt/dsa.c [new file with mode: 0644]
lib/silccrypt/dsa.h [new file with mode: 0644]
lib/silccrypt/tests/test_dsa.c [new file with mode: 0644]

diff --git a/lib/silccrypt/dsa.c b/lib/silccrypt/dsa.c
new file mode 100644 (file)
index 0000000..ff3dd72
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+
+  dsa.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2007 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program 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 General Public License for more details.
+
+*/
+
+#include "silc.h"
+#include "dsa.h"
+
+/************************** DSA PKCS Algorithm API **************************/
+
+/* Notes about key formats:
+
+   For DSA private key format the PKCS#8 (defined in PKCS#11) can be used but
+   would be an overkill for this low level API.  Thus, we use our own DSA
+   private key format that is equivalent to GnuTLS and OpenSSL DSA private
+   key format:
+
+   key ::= SEQUENCE {
+     ver   INTEGER,
+     p     INTEGER,
+     q     INTEGER,
+     g     INTEGER,
+     y     INTEGER,
+     x     INTEGER }
+
+   For DSA public keys we also use our own format since the standard formats
+   require the public key ASN.1 data to be in two parts (algorithm params and
+   the key itself).  We don't require such format for this low level API and
+   expect that if that format is used it is trivial to convert it to this
+   internal format (which is just concatenation of the params and the key):
+
+   key ::= SEQUENCE {
+     p     INTEGER,
+     q     INTEGER,
+     g     INTEGER },
+     y     INTEGER
+
+   Notes about signature format:
+
+   The encoded signature format is compliant with PKIX (X.509):
+
+   sig ::= SEQUENCE {
+     r     INTEGER,
+     s     INTEGER }
+
+*/
+
+/* Generates DSA key pair.  For now this uses group size of 160 bits. */
+
+SILC_PKCS_ALG_GENERATE_KEY(silc_dsa_generate_key)
+{
+  DsaPublicKey *pubkey;
+  DsaPrivateKey *privkey;
+  SilcMPInt tmp, tmp2;
+  unsigned char rnd[4096];
+  int i, len = (keylen + 7) / 8, q_len = 160 / 8;
+
+  if (keylen < 768 || keylen > 16384)
+    return FALSE;
+
+  pubkey = silc_calloc(1, sizeof(*pubkey));
+  if (!pubkey)
+    return FALSE;
+
+  privkey = silc_calloc(1, sizeof(*privkey));
+  if (!privkey) {
+    silc_free(pubkey);
+    return FALSE;
+  }
+
+  silc_mp_init(&tmp);
+  silc_mp_init(&tmp2);
+  silc_mp_init(&privkey->p);
+  silc_mp_init(&privkey->q);
+  silc_mp_init(&privkey->g);
+  silc_mp_init(&privkey->y);
+  silc_mp_init(&privkey->x);
+  silc_mp_init(&pubkey->p);
+  silc_mp_init(&pubkey->q);
+  silc_mp_init(&pubkey->g);
+  silc_mp_init(&pubkey->y);
+
+  /* Generate primes q and p.  The p will satisfy (q * rnd) + 1 == p */
+
+  /* Generate prime q */
+  silc_math_gen_prime(&privkey->q, q_len * 8, FALSE, rng);
+  silc_mp_add(&tmp, &privkey->q, &privkey->q);
+
+  do {
+    /* Create p.  Take random data, this returns non-zero bytes.  Make the
+       number even. */
+    silc_rng_get_rn_data(rng, len - q_len, rnd, sizeof(rnd));
+    rnd[(len - q_len) - 1] &= ~1;
+    silc_mp_bin2mp(rnd, len - q_len, &tmp2);
+    silc_mp_mul(&privkey->p, &privkey->q, &tmp2);
+    silc_mp_add_ui(&privkey->p, &privkey->p, 1);
+
+    /* Make p prime.  If it doesn't seem to happen, try again from the
+       beginning. */
+    for (i = 0; i < q_len * 2; i++) {
+      if (silc_math_prime_test(&privkey->p))
+       break;
+      silc_mp_add(&privkey->p, &tmp, &privkey->p);
+      silc_mp_add_ui(&tmp2, &tmp2, 2);
+    }
+  } while (i >= q_len * 2);
+
+  /* Find generator */
+  silc_mp_set_ui(&privkey->g, 1);
+  do {
+    silc_mp_add_ui(&privkey->g, &privkey->g, 1);
+    silc_mp_pow_mod(&tmp, &privkey->g, &tmp2, &privkey->p);
+  } while (silc_mp_cmp_ui(&tmp, 1) == 0);
+  silc_mp_set(&privkey->g, &tmp);
+
+  /* Generate private key */
+  silc_rng_get_rn_data(rng, q_len, rnd, sizeof(rnd));
+  silc_mp_bin2mp(rnd, q_len, &privkey->x);
+
+  /* Generate public key */
+  silc_mp_pow_mod(&privkey->y, &privkey->g, &privkey->x, &privkey->p);
+
+  /* Now set the integers to public key too */
+  silc_mp_set(&pubkey->p, &privkey->p);
+  silc_mp_set(&pubkey->q, &privkey->q);
+  silc_mp_set(&pubkey->g, &privkey->g);
+  silc_mp_set(&pubkey->y, &privkey->y);
+
+  privkey->group_order = q_len;
+  privkey->bits = keylen;
+  pubkey->group_order = q_len;
+  pubkey->bits = keylen;
+
+  silc_mp_uninit(&tmp);
+  silc_mp_uninit(&tmp2);
+
+  if (ret_public_key)
+    *ret_public_key = pubkey;
+  if (ret_private_key)
+    *ret_private_key = privkey;
+
+  return TRUE;
+}
+
+/* Import DSA public key */
+
+SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_dsa_import_public_key)
+{
+  SilcAsn1 asn1;
+  SilcBufferStruct alg_key;
+  DsaPublicKey *pubkey;
+
+  SILC_LOG_DEBUG(("Import public key"));
+
+  if (!ret_public_key)
+    return 0;
+
+  asn1 = silc_asn1_alloc(NULL);
+  if (!asn1)
+    return 0;
+
+  /* Allocate DSA public key */
+  *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
+  if (!pubkey)
+    goto err;
+
+  /* Parse */
+  silc_buffer_set(&alg_key, key, key_len);
+  if (!silc_asn1_decode(asn1, &alg_key,
+                       SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
+                       SILC_ASN1_SEQUENCE,
+                         SILC_ASN1_INT(&pubkey->p),
+                         SILC_ASN1_INT(&pubkey->q),
+                         SILC_ASN1_INT(&pubkey->g),
+                       SILC_ASN1_END,
+                       SILC_ASN1_INT(&pubkey->y),
+                       SILC_ASN1_END))
+    goto err;
+
+  /* Set key length */
+  pubkey->bits = ((silc_mp_sizeinbase(&pubkey->p, 2) + 7) / 8) * 8;
+
+  silc_asn1_free(asn1);
+
+  return key_len;
+
+ err:
+  silc_free(pubkey);
+  silc_asn1_free(asn1);
+  return 0;
+}
+
+/* Export DSA public key */
+
+SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_dsa_export_public_key)
+{
+  DsaPublicKey *key = public_key;
+  SilcAsn1 asn1 = NULL;
+  SilcBufferStruct alg_key;
+  unsigned char *ret;
+
+  SILC_LOG_DEBUG(("Export public key"));
+
+  asn1 = silc_asn1_alloc(stack);
+  if (!asn1)
+    goto err;
+
+  /* Encode public key */
+  memset(&alg_key, 0, sizeof(alg_key));
+  if (!silc_asn1_encode(asn1, &alg_key,
+                       SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
+                       SILC_ASN1_SEQUENCE,
+                         SILC_ASN1_INT(&key->p),
+                         SILC_ASN1_INT(&key->q),
+                         SILC_ASN1_INT(&key->g),
+                       SILC_ASN1_END,
+                       SILC_ASN1_INT(&key->y),
+                       SILC_ASN1_END))
+    goto err;
+
+  ret = silc_buffer_steal(&alg_key, ret_len);
+  silc_asn1_free(asn1);
+
+  return ret;
+
+ err:
+  if (asn1)
+    silc_asn1_free(asn1);
+  return NULL;
+}
+
+/* Return key length */
+
+SILC_PKCS_ALG_PUBLIC_KEY_BITLEN(silc_dsa_public_key_bitlen)
+{
+  DsaPublicKey *key = public_key;
+  return key->bits;
+}
+
+/* Copy public key */
+
+SILC_PKCS_ALG_PUBLIC_KEY_COPY(silc_dsa_public_key_copy)
+{
+  DsaPublicKey *key = public_key, *new_key;
+
+  new_key = silc_calloc(1, sizeof(*new_key));
+  if (!new_key)
+    return NULL;
+
+  silc_mp_init(&new_key->p);
+  silc_mp_init(&new_key->q);
+  silc_mp_init(&new_key->g);
+  silc_mp_init(&new_key->y);
+  new_key->bits = key->bits;
+  new_key->group_order = key->group_order;
+
+  return new_key;
+}
+
+/* Compare public keys */
+
+SILC_PKCS_ALG_PUBLIC_KEY_COMPARE(silc_dsa_public_key_compare)
+{
+  DsaPublicKey *k1 = key1, *k2 = key2;
+
+  if (k1->bits != k2->bits)
+    return FALSE;
+  if (k1->group_order != k2->group_order)
+    return FALSE;
+  if (silc_mp_cmp(&k1->p, &k2->p) != 0)
+    return FALSE;
+  if (silc_mp_cmp(&k1->q, &k2->q) != 0)
+    return FALSE;
+  if (silc_mp_cmp(&k1->g, &k2->g) != 0)
+    return FALSE;
+  if (silc_mp_cmp(&k1->y, &k2->y) != 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Free public key */
+
+SILC_PKCS_ALG_PUBLIC_KEY_FREE(silc_dsa_public_key_free)
+{
+  DsaPublicKey *key = public_key;
+
+  silc_mp_uninit(&key->p);
+  silc_mp_uninit(&key->q);
+  silc_mp_uninit(&key->g);
+  silc_mp_uninit(&key->y);
+  silc_free(key);
+}
+
+/* Import DSA private key. */
+
+SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_dsa_import_private_key)
+{
+  SilcAsn1 asn1;
+  SilcBufferStruct alg_key;
+  DsaPrivateKey *privkey;
+  SilcUInt32 ver;
+
+  SILC_LOG_DEBUG(("Import private key"));
+
+  if (!ret_private_key)
+    return 0;
+
+  asn1 = silc_asn1_alloc(NULL);
+  if (!asn1)
+    return 0;
+
+  /* Allocate DSA private key */
+  *ret_private_key = privkey = silc_calloc(1, sizeof(*privkey));
+  if (!privkey)
+    goto err;
+
+  /* Parse */
+  silc_buffer_set(&alg_key, key, key_len);
+  if (!silc_asn1_decode(asn1, &alg_key,
+                       SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
+                       SILC_ASN1_SEQUENCE,
+                         SILC_ASN1_SHORT_INT(&ver),
+                         SILC_ASN1_INT(&privkey->p),
+                         SILC_ASN1_INT(&privkey->q),
+                         SILC_ASN1_INT(&privkey->g),
+                         SILC_ASN1_INT(&privkey->y),
+                         SILC_ASN1_INT(&privkey->x),
+                       SILC_ASN1_END, SILC_ASN1_END))
+    goto err;
+
+  /* Set key length */
+  privkey->bits = ((silc_mp_sizeinbase(&privkey->p, 2) + 7) / 8) * 8;
+
+  silc_asn1_free(asn1);
+
+  return key_len;
+
+ err:
+  silc_free(privkey);
+  silc_asn1_free(asn1);
+  return 0;
+}
+
+/* Export DSA private key. */
+
+SILC_PKCS_ALG_EXPORT_PRIVATE_KEY(silc_dsa_export_private_key)
+{
+  DsaPrivateKey *key = private_key;
+  SilcAsn1 asn1;
+  SilcBufferStruct alg_key;
+  unsigned char *ret;
+
+  SILC_LOG_DEBUG(("Export private key"));
+
+  asn1 = silc_asn1_alloc(stack);
+  if (!asn1)
+    return FALSE;
+
+  /* Encode */
+  memset(&alg_key, 0, sizeof(alg_key));
+  if (!silc_asn1_encode(asn1, &alg_key,
+                       SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
+                       SILC_ASN1_SEQUENCE,
+                         SILC_ASN1_SHORT_INT(0),
+                         SILC_ASN1_INT(&key->p),
+                         SILC_ASN1_INT(&key->q),
+                         SILC_ASN1_INT(&key->g),
+                         SILC_ASN1_INT(&key->y),
+                         SILC_ASN1_INT(&key->x),
+                       SILC_ASN1_END, SILC_ASN1_END))
+    goto err;
+
+  ret = silc_buffer_steal(&alg_key, ret_len);
+  silc_asn1_free(asn1);
+
+  return ret;
+
+ err:
+  silc_asn1_free(asn1);
+  return NULL;
+}
+
+/* Return key length */
+
+SILC_PKCS_ALG_PRIVATE_KEY_BITLEN(silc_dsa_private_key_bitlen)
+{
+  DsaPrivateKey *key = private_key;
+  return key->bits;
+}
+
+/* Free private key */
+
+SILC_PKCS_ALG_PRIVATE_KEY_FREE(silc_dsa_private_key_free)
+{
+  DsaPrivateKey *key = private_key;
+
+  silc_mp_uninit(&key->p);
+  silc_mp_uninit(&key->q);
+  silc_mp_uninit(&key->g);
+  silc_mp_uninit(&key->y);
+  silc_mp_uninit(&key->x);
+  silc_free(key);
+}
+
+/* Encryption.  Not supported */
+
+SILC_PKCS_ALG_ENCRYPT(silc_dsa_encrypt)
+{
+  SILC_LOG_WARNING(("DSA encryption is not supported"));
+  encrypt_cb(FALSE, NULL, 0, context);
+  return NULL;
+}
+
+/* Decryption.  Not supported */
+
+SILC_PKCS_ALG_DECRYPT(silc_dsa_decrypt)
+{
+  SILC_LOG_WARNING(("DSA decryption is not supported"));
+  decrypt_cb(FALSE, NULL, 0, context);
+  return NULL;
+}
+
+/* Sign */
+
+SILC_PKCS_ALG_SIGN(silc_dsa_sign)
+{
+  DsaPrivateKey *key = private_key;
+  unsigned char kbuf[512], hashr[SILC_HASH_MAXLEN];
+  SilcBufferStruct sig;
+  SilcMPInt tmp, k, kinv, r, s;
+  SilcStack stack;
+  SilcAsn1 asn1;
+
+  SILC_LOG_DEBUG(("Sign"));
+
+  if (key->group_order > sizeof(kbuf)) {
+    sign_cb(FALSE, NULL, 0, context);
+    return NULL;
+  }
+
+  if (!rng) {
+    SILC_LOG_ERROR(("DSA signing requires random number generator"));
+    sign_cb(FALSE, NULL, 0, context);
+    return NULL;
+  }
+
+  /* Compute hash if requested */
+  if (compute_hash) {
+    silc_hash_make(hash, src, src_len, hashr);
+    src = hashr;
+    src_len = silc_hash_len(hash);
+  }
+
+  stack = silc_stack_alloc(2048, silc_crypto_stack());
+
+  asn1 = silc_asn1_alloc(stack);
+  if (!asn1) {
+    silc_stack_free(stack);
+    sign_cb(FALSE, NULL, 0, context);
+    return NULL;
+  }
+
+  silc_mp_sinit(stack, &k);
+  silc_mp_sinit(stack, &kinv);
+  silc_mp_sinit(stack, &r);
+  silc_mp_sinit(stack, &s);
+  silc_mp_sinit(stack, &tmp);
+
+  do {
+    do {
+      /* Generate random k */
+      do {
+       silc_rng_get_rn_data(rng, key->group_order, kbuf, sizeof(kbuf));
+       silc_mp_bin2mp(kbuf, key->group_order, &k);
+       silc_mp_gcd(&tmp, &k, &key->q);
+      } while (silc_mp_cmp_ui(&tmp, 1) != 0);
+
+      /* Compute kinv = k^-1 mod q */
+      silc_mp_modinv(&kinv, &k, &key->q);
+
+      /* Compute signature part r = g^k mod p mod q */
+      silc_mp_pow_mod(&r, &key->g, &k, &key->p);
+      silc_mp_mod(&r, &r, &key->q);
+    } while (silc_mp_cmp_ui(&r, 0) == 0);
+
+    /* Compute signature part s = (src + x * r) / k mod q */
+    silc_mp_bin2mp(src, src_len, &tmp);
+    silc_mp_mul(&s, &key->x, &r);
+    silc_mp_add(&s, &s, &tmp);
+    silc_mp_mul(&s, &s, &kinv);
+    silc_mp_mod(&s, &s, &key->q);
+  } while (silc_mp_cmp_ui(&s, 0) == 0);
+
+  /* Encode the signature.  This format is compliant with PKIX. */
+  memset(&sig, 0, sizeof(sig));
+  if (!silc_asn1_encode(asn1, &sig,
+                       SILC_ASN1_SEQUENCE,
+                         SILC_ASN1_INT(&r),
+                         SILC_ASN1_INT(&s),
+                       SILC_ASN1_END, SILC_ASN1_END)) {
+    sign_cb(FALSE, NULL, 0, context);
+    goto out;
+  }
+
+  /* Deliver result */
+  sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig), context);
+
+ out:
+  memset(kbuf, 0, sizeof(kbuf));
+  if (compute_hash)
+    memset(hashr, 0, sizeof(hashr));
+  silc_mp_suninit(stack, &k);
+  silc_mp_suninit(stack, &kinv);
+  silc_mp_suninit(stack, &r);
+  silc_mp_suninit(stack, &s);
+  silc_mp_suninit(stack, &tmp);
+  silc_asn1_free(asn1);
+  silc_stack_free(stack);
+
+  return NULL;
+}
+
+/* Verify */
+
+SILC_PKCS_ALG_VERIFY(silc_dsa_verify)
+{
+  DsaPublicKey *key = public_key;
+  unsigned char hashr[SILC_HASH_MAXLEN];
+  SilcBool ret = FALSE;
+  SilcBufferStruct sig;
+  SilcMPInt r, s, v, w, u1, u2;
+  SilcStack stack;
+  SilcAsn1 asn1;
+
+  SILC_LOG_DEBUG(("Verify"));
+
+  stack = silc_stack_alloc(2048, silc_crypto_stack());
+
+  asn1 = silc_asn1_alloc(stack);
+  if (!asn1) {
+    silc_stack_free(stack);
+    verify_cb(FALSE, context);
+    return NULL;
+  }
+
+  /* Decode the signature */
+  silc_buffer_set(&sig, signature, signature_len);
+  if (!silc_asn1_decode(asn1, &sig,
+                       SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
+                       SILC_ASN1_SEQUENCE,
+                         SILC_ASN1_INT(&r),
+                         SILC_ASN1_INT(&s),
+                       SILC_ASN1_END, SILC_ASN1_END)) {
+    silc_asn1_free(asn1);
+    silc_stack_free(stack);
+    verify_cb(FALSE, context);
+    return NULL;
+  }
+
+  if (silc_mp_cmp_ui(&r, 0) == 0 ||
+      silc_mp_cmp_ui(&s, 0) == 0 ||
+      silc_mp_cmp(&r, &key->q) >= 0 ||
+      silc_mp_cmp(&s, &key->q) >= 0) {
+    silc_asn1_free(asn1);
+    silc_stack_free(stack);
+    verify_cb(FALSE, context);
+    return NULL;
+  }
+
+  /* Hash data if requested */
+  if (hash) {
+    silc_hash_make(hash, data, data_len, hashr);
+    data = hashr;
+    data_len = silc_hash_len(hash);
+  }
+
+  silc_mp_sinit(stack, &v);
+  silc_mp_sinit(stack, &w);
+  silc_mp_sinit(stack, &u1);
+  silc_mp_sinit(stack, &u2);
+
+  /* Compute w = s^-1 mod q */
+  silc_mp_modinv(&w, &s, &key->q);
+
+  /* Compute u1 = data * w mod q */
+  silc_mp_bin2mp(data, data_len, &u1);
+  silc_mp_mul(&u1, &u1, &w);
+  silc_mp_mod(&u1, &u1, &key->q);
+
+  /* Compute u2 = r * w mod q */
+  silc_mp_mul(&u2, &r, &w);
+  silc_mp_mod(&u2, &u2, &key->q);
+
+  /* Compute v = g ^ u1 * y ^ u2 mod p mod q */
+  silc_mp_pow_mod(&u1, &key->g, &u1, &key->p);
+  silc_mp_pow_mod(&u2, &key->y, &u2, &key->p);
+  silc_mp_mul(&v, &u1, &u2);
+  silc_mp_mod(&v, &v, &key->p);
+  silc_mp_mod(&v, &v, &key->q);
+
+  /* Compare */
+  if (silc_mp_cmp(&r, &v) == 0)
+    ret = TRUE;
+
+  /* Deliver result */
+  verify_cb(ret, context);
+
+  if (hash)
+    memset(hashr, 0, sizeof(hashr));
+  silc_mp_suninit(stack, &v);
+  silc_mp_suninit(stack, &w);
+  silc_mp_suninit(stack, &u1);
+  silc_mp_suninit(stack, &u2);
+  silc_asn1_free(asn1);
+  silc_stack_free(stack);
+
+  return NULL;
+}
diff --git a/lib/silccrypt/dsa.h b/lib/silccrypt/dsa.h
new file mode 100644 (file)
index 0000000..280d824
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+
+  dsa.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2007 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program 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 General Public License for more details.
+
+*/
+
+#ifndef DSA_H
+#define DSA_H
+
+/* DSA Public key */
+typedef struct {
+  SilcMPInt g;                 /* generator */
+  SilcMPInt p;                 /* prime */
+  SilcMPInt q;                 /* prime */
+  SilcMPInt y;                 /* public key */
+  SilcUInt16 bits;             /* bits in key */
+  SilcUInt16 group_order;      /* group order (size) */
+} DsaPublicKey;
+
+/* DSA Private key */
+typedef struct {
+  SilcMPInt g;                 /* generator */
+  SilcMPInt p;                 /* prime */
+  SilcMPInt q;                 /* prime */
+  SilcMPInt y;                 /* public key */
+  SilcMPInt x;                 /* private key */
+  SilcUInt16 bits;             /* bits in key */
+  SilcUInt16 group_order;      /* group order (size) */
+} DsaPrivateKey;
+
+SILC_PKCS_ALG_GENERATE_KEY(silc_dsa_generate_key);
+SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_dsa_import_public_key);
+SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_dsa_export_public_key);
+SILC_PKCS_ALG_PUBLIC_KEY_BITLEN(silc_dsa_public_key_bitlen);
+SILC_PKCS_ALG_PUBLIC_KEY_COPY(silc_dsa_public_key_copy);
+SILC_PKCS_ALG_PUBLIC_KEY_COMPARE(silc_dsa_public_key_compare);
+SILC_PKCS_ALG_PUBLIC_KEY_FREE(silc_dsa_public_key_free);
+SILC_PKCS_ALG_IMPORT_PRIVATE_KEY(silc_dsa_import_private_key);
+SILC_PKCS_ALG_EXPORT_PRIVATE_KEY(silc_dsa_export_private_key);
+SILC_PKCS_ALG_PRIVATE_KEY_BITLEN(silc_dsa_private_key_bitlen);
+SILC_PKCS_ALG_PRIVATE_KEY_FREE(silc_dsa_private_key_free);
+SILC_PKCS_ALG_ENCRYPT(silc_dsa_encrypt);
+SILC_PKCS_ALG_DECRYPT(silc_dsa_decrypt);
+SILC_PKCS_ALG_SIGN(silc_dsa_sign);
+SILC_PKCS_ALG_VERIFY(silc_dsa_verify);
+
+#endif /* DSA_H */
diff --git a/lib/silccrypt/tests/test_dsa.c b/lib/silccrypt/tests/test_dsa.c
new file mode 100644 (file)
index 0000000..d09a9d4
--- /dev/null
@@ -0,0 +1,67 @@
+#include "silc.h"
+#include "dsa.h"
+
+unsigned char *signature = NULL;
+SilcUInt32 signature_len;
+SilcBool success = FALSE;
+
+static void sign_cb(SilcBool success, const unsigned char *sig,
+                   SilcUInt32 sig_len, void *context)
+{
+  SILC_LOG_HEXDUMP(("Signature"), sig, sig_len);
+  signature = silc_memdup(sig, sig_len);
+  signature_len = sig_len;
+}
+
+static void verify_cb(SilcBool s, void *context)
+{
+  SILC_LOG_DEBUG(("Verify %s", s ? "success" : "failed"));
+  success = s;
+}
+
+int main(int argc, char **argv)
+{
+  const SilcPKCSAlgorithm *alg;
+  SilcRng rng;
+  void *public_key, *private_key;
+  SilcHash hash;
+  unsigned char tmp[20];
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_set_debug_string("*dsa*,*pkcs*");
+  }
+
+  silc_crypto_init(NULL);
+
+  rng = silc_rng_alloc();
+  silc_rng_init(rng);
+  silc_hash_alloc("sha1", &hash);
+
+  SILC_LOG_DEBUG(("Generate DSA keypair"));
+  alg = silc_pkcs_find_algorithm("dsa", "dss");
+  if (!silc_dsa_generate_key(alg, 2048, rng, &public_key, &private_key))
+    goto err;
+
+  SILC_LOG_DEBUG(("Key length: %d",
+                 silc_dsa_public_key_bitlen(alg, public_key)));
+
+  SILC_LOG_DEBUG(("Sign"));
+  memset(tmp, 0, sizeof(tmp));
+  silc_dsa_sign(alg, private_key, tmp, sizeof(tmp), TRUE, hash, rng,
+               sign_cb, NULL);
+
+  SILC_LOG_DEBUG(("Verify"));
+  silc_dsa_verify(alg, public_key, signature, signature_len,
+                 tmp, sizeof(tmp), hash, rng, verify_cb, NULL);
+
+  silc_rng_free(rng);
+  silc_free(signature);
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}