Sign/verify initial implementation. Examples to follow. 44/65844/7
authorLukasz Pawelczyk <l.pawelczyk@samsung.com>
Tue, 12 Apr 2016 11:42:52 +0000 (13:42 +0200)
committerLukasz Pawelczyk <l.pawelczyk@samsung.com>
Wed, 20 Apr 2016 14:56:56 +0000 (16:56 +0200)
Change-Id: I31205fdbb897011b2cf5cbff02acbae396e95205

api/yaca/error.h
src/sign.c

index e8d11ed..1e5ee16 100644 (file)
@@ -33,7 +33,7 @@ extern "C" {
 
 enum __yaca_error_code {
        YACA_ERROR_INVALID_ARGUMENT = -1,
-       YACA_ERROR_NOT_IMPLEMENTED= -2,
+       YACA_ERROR_NOT_IMPLEMENTED = -2,
        YACA_ERROR_OPENSSL_FAILURE = -3,
        YACA_ERROR_NOT_SUPPORTED = -4,
        YACA_ERROR_TOO_BIG_ARGUMENT = -5,
index 2f37c0f..aee4c8c 100644 (file)
 
 #include <assert.h>
 
-#include <openssl/crypto.h>
-#include <openssl/rand.h>
+#include <openssl/evp.h>
 
 #include <yaca/crypto.h>
 #include <yaca/error.h>
 
 #include "internal.h"
 
+/* Operation type saved in context to recognize what
+ * type of operation is performed and how to perform it.
+*/
+enum sign_op_type {
+       OP_SIGN = 0,
+       OP_VERIFY_SYMMETRIC = 1,
+       OP_VERIFY_ASYMMETRIC = 2
+};
+
+struct yaca_sign_ctx_s
+{
+       struct yaca_ctx_s ctx;
+
+       EVP_MD_CTX *mdctx;
+       enum sign_op_type op_type;
+};
+
+static struct yaca_sign_ctx_s *get_sign_ctx(const yaca_ctx_h ctx)
+{
+       if (ctx == YACA_CTX_NULL)
+               return NULL;
+
+       switch (ctx->type)
+       {
+       case YACA_CTX_SIGN:
+               return (struct yaca_sign_ctx_s *)ctx;
+       default:
+               return NULL;
+       }
+}
+
+static int get_sign_output_length(const yaca_ctx_h ctx, size_t input_len)
+{
+       struct yaca_sign_ctx_s *c = get_sign_ctx(ctx);
+
+       if (c == NULL || c->mdctx == NULL || c->mdctx->pctx == NULL)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(c->mdctx->pctx);
+       if (pkey == NULL)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       size_t len = EVP_PKEY_size(pkey);
+       if (len <= 0)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       return len;
+}
+
+static void destroy_sign_context(yaca_ctx_h ctx)
+{
+       struct yaca_sign_ctx_s *c = get_sign_ctx(ctx);
+
+       if (c == NULL)
+               return;
+
+       EVP_MD_CTX_destroy(c->mdctx);
+       c->mdctx = NULL;
+}
+
+static int create_sign_pkey(const yaca_key_h key, EVP_PKEY **pkey)
+{
+       const struct yaca_key_simple_s *simple_key = key_get_simple(key);
+       const struct yaca_key_evp_s *evp_key = key_get_evp(key);
+
+       if (pkey == NULL)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       if (simple_key != NULL)
+       {
+               *pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC,
+                                            NULL,
+                                            (unsigned char *)simple_key->d,
+                                            simple_key->bits / 8);
+               if (*pkey == NULL)
+                       return YACA_ERROR_OPENSSL_FAILURE;
+
+               return 0;
+       }
+
+       if (evp_key != NULL)
+       {
+               *pkey = evp_key->evp;
+               /* Add a reference so we can free it afterwards anyway */
+               CRYPTO_add(&(*pkey)->references, 1, CRYPTO_LOCK_EVP_PKEY);
+
+               return 0;
+       }
+
+       return YACA_ERROR_INVALID_ARGUMENT;
+}
+
+/* TODO: error checking?
+ * should DES and IV keys be rejected by us or silently let them work?
+ */
 API int yaca_sign_init(yaca_ctx_h *ctx,
                       yaca_digest_algo_e algo,
                       const yaca_key_h key)
 {
-       return YACA_ERROR_NOT_IMPLEMENTED;
+       struct yaca_sign_ctx_s *nc = NULL;
+       EVP_PKEY *pkey;
+       const EVP_MD *md;
+       int ret;
+
+       if (ctx == NULL)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       ret = create_sign_pkey(key, &pkey);
+       if (ret != 0)
+               return ret;
+
+       // TODO: use zalloc when available
+       nc = yaca_malloc(sizeof(struct yaca_sign_ctx_s));
+       if (nc == NULL) {
+               ret = YACA_ERROR_OUT_OF_MEMORY;
+               goto free_key;
+       }
+
+       nc->ctx.type = YACA_CTX_SIGN;
+       nc->ctx.ctx_destroy = destroy_sign_context;
+       nc->ctx.get_output_length = get_sign_output_length;
+
+       switch (key->type)
+       {
+       case YACA_KEY_TYPE_SYMMETRIC:
+       case YACA_KEY_TYPE_RSA_PRIV:
+               nc->op_type = OP_SIGN;
+               break;
+       case YACA_KEY_TYPE_DSA_PRIV:
+       case YACA_KEY_TYPE_ECDSA_PRIV:
+               ret = YACA_ERROR_NOT_IMPLEMENTED;
+               goto free_ctx;
+       default:
+               ret = YACA_ERROR_INVALID_ARGUMENT;
+               goto free_ctx;
+       }
+
+       ret = digest_get_algorithm(algo, &md);
+       if (ret != 0)
+               goto free_ctx;
+
+       nc->mdctx = EVP_MD_CTX_create();
+       if (nc->mdctx == NULL) {
+               ret = YACA_ERROR_OPENSSL_FAILURE;
+               goto free_ctx;
+       }
+
+       ret = EVP_DigestSignInit(nc->mdctx, NULL, md, NULL, pkey);
+       if (ret == -2) {
+               ret = YACA_ERROR_NOT_SUPPORTED;
+               goto ctx;
+       }
+       if (ret != 1) {
+               ret = YACA_ERROR_OPENSSL_FAILURE;
+               goto ctx;
+       }
+
+       *ctx = (yaca_ctx_h)nc;
+
+       ret = 0;
+
+ctx:
+       if (ret != 0)
+               EVP_MD_CTX_destroy(nc->mdctx);
+free_ctx:
+       if (ret != 0)
+               yaca_free(nc);
+free_key:
+       EVP_PKEY_free(pkey);
+
+       return ret;
 }
 
 API int yaca_sign_update(yaca_ctx_h ctx,
                         const char *data,
                         size_t data_len)
 {
-       return YACA_ERROR_NOT_IMPLEMENTED;
+       struct yaca_sign_ctx_s *c = get_sign_ctx(ctx);
+       int ret;
+
+       if (c == NULL || c->op_type != OP_SIGN ||
+           data == NULL || data_len == 0)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       ret = EVP_DigestSignUpdate(c->mdctx, data, data_len);
+       if (ret == -2)
+               return YACA_ERROR_NOT_SUPPORTED;
+       if (ret != 1)
+               return YACA_ERROR_OPENSSL_FAILURE;
+
+       return 0;
 }
 
 API int yaca_sign_final(yaca_ctx_h ctx,
                        char *mac,
                        size_t *mac_len)
 {
-       return YACA_ERROR_NOT_IMPLEMENTED;
+       struct yaca_sign_ctx_s *c = get_sign_ctx(ctx);
+       int ret;
+
+       if (c == NULL ||  c->op_type != OP_SIGN ||
+           mac == NULL || mac_len == NULL || *mac_len == 0)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       ret = EVP_DigestSignFinal(c->mdctx, (unsigned char *)mac, mac_len);
+       if (ret == -2)
+               return YACA_ERROR_NOT_SUPPORTED;
+       if (ret != 1)
+               return YACA_ERROR_OPENSSL_FAILURE;
+
+       return 0;
 }
 
 API int yaca_verify_init(yaca_ctx_h *ctx,
                         yaca_digest_algo_e algo,
                         const yaca_key_h key)
 {
-       return YACA_ERROR_NOT_IMPLEMENTED;
+       struct yaca_sign_ctx_s *nc = NULL;
+       EVP_PKEY *pkey;
+       const EVP_MD *md;
+       int ret;
+
+       if (ctx == NULL)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       ret = create_sign_pkey(key, &pkey);
+       if (ret != 0)
+               return ret;
+
+       // TODO: use zalloc when available
+       nc = yaca_malloc(sizeof(struct yaca_sign_ctx_s));
+       if (nc == NULL) {
+               ret = YACA_ERROR_OUT_OF_MEMORY;
+               goto free_key;
+       }
+
+       nc->ctx.type = YACA_CTX_SIGN;
+       nc->ctx.ctx_destroy = destroy_sign_context;
+       nc->ctx.get_output_length = NULL;
+
+       switch (key->type)
+       {
+       case YACA_KEY_TYPE_SYMMETRIC:
+               nc->op_type = OP_VERIFY_SYMMETRIC;
+               break;
+       case YACA_KEY_TYPE_RSA_PUB:
+               nc->op_type = OP_VERIFY_ASYMMETRIC;
+               break;
+       case YACA_KEY_TYPE_DSA_PUB:
+       case YACA_KEY_TYPE_ECDSA_PUB:
+               ret = YACA_ERROR_NOT_IMPLEMENTED;
+               goto free_ctx;
+       default:
+               ret = YACA_ERROR_INVALID_ARGUMENT;
+               goto free_ctx;
+       }
+
+       ret = digest_get_algorithm(algo, &md);
+       if (ret < 0)
+               goto free_ctx;
+
+       nc->mdctx = EVP_MD_CTX_create();
+       if (nc->mdctx == NULL) {
+               ret = YACA_ERROR_OPENSSL_FAILURE;
+               goto free_ctx;
+       }
+
+       switch (nc->op_type)
+       {
+       case OP_VERIFY_SYMMETRIC:
+               ret = EVP_DigestSignInit(nc->mdctx, NULL, md, NULL, pkey);
+               break;
+       case OP_VERIFY_ASYMMETRIC:
+               ret = EVP_DigestVerifyInit(nc->mdctx, NULL, md, NULL, pkey);
+               break;
+       default:
+               ret = YACA_ERROR_INVALID_ARGUMENT;
+               goto ctx;
+       }
+
+       if (ret == -2) {
+               ret = YACA_ERROR_NOT_SUPPORTED;
+               goto ctx;
+       }
+       if (ret != 1) {
+               ret = YACA_ERROR_OPENSSL_FAILURE;
+               goto ctx;
+       }
+
+       *ctx = (yaca_ctx_h)nc;
+
+       ret = 0;
+
+ctx:
+       if (ret != 0)
+               EVP_MD_CTX_destroy(nc->mdctx);
+free_ctx:
+       if (ret != 0)
+               yaca_free(nc);
+free_key:
+       EVP_PKEY_free(pkey);
+
+       return ret;
 }
 
 API int yaca_verify_update(yaca_ctx_h ctx,
                           const char *data,
                           size_t data_len)
 {
-       return YACA_ERROR_NOT_IMPLEMENTED;
+       struct yaca_sign_ctx_s *c = get_sign_ctx(ctx);
+       int ret;
+
+       if (c == NULL || data == NULL || data_len == 0)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       switch (c->op_type)
+       {
+       case OP_VERIFY_SYMMETRIC:
+               ret = EVP_DigestSignUpdate(c->mdctx, data, data_len);
+               break;
+       case OP_VERIFY_ASYMMETRIC:
+               ret = EVP_DigestVerifyUpdate(c->mdctx, data, data_len);
+               break;
+       default:
+               return YACA_ERROR_INVALID_ARGUMENT;
+       }
+
+       if (ret == -2)
+               return YACA_ERROR_NOT_SUPPORTED;
+       if (ret != 1)
+               return YACA_ERROR_OPENSSL_FAILURE;
+
+       return 0;
 }
 
 API int yaca_verify_final(yaca_ctx_h ctx,
                          const char *mac,
                          size_t mac_len)
 {
-       return YACA_ERROR_NOT_IMPLEMENTED;
+       struct yaca_sign_ctx_s *c = get_sign_ctx(ctx);
+       char mac_cmp[mac_len];
+       size_t mac_cmp_len = mac_len;
+       int ret;
+
+       if (c == NULL || mac == NULL || mac_len == 0)
+               return YACA_ERROR_INVALID_ARGUMENT;
+
+       switch (c->op_type)
+       {
+       case OP_VERIFY_SYMMETRIC:
+               ret = EVP_DigestSignFinal(c->mdctx,
+                                         (unsigned char *)mac_cmp,
+                                         &mac_cmp_len);
+               if (ret == -2)
+                       return YACA_ERROR_NOT_SUPPORTED;
+               if (ret != 1)
+                       return YACA_ERROR_OPENSSL_FAILURE;
+
+               if (mac_len != mac_cmp_len ||
+                   CRYPTO_memcmp(mac, mac_cmp, mac_len) != 0)
+                       return YACA_ERROR_SIGNATURE_INVALID;
+
+               return 0;
+       case OP_VERIFY_ASYMMETRIC:
+               ret = EVP_DigestVerifyFinal(c->mdctx,
+                                           (unsigned char *)mac,
+                                           mac_len);
+               if (ret == 0)
+                       return YACA_ERROR_SIGNATURE_INVALID;
+               if (ret == -2)
+                       return YACA_ERROR_NOT_SUPPORTED;
+               if (ret != 1)
+                       return YACA_ERROR_OPENSSL_FAILURE;
+
+               return 0;
+       default:
+               return YACA_ERROR_INVALID_ARGUMENT;
+       }
 }