| /* |
| * Handle the openssl crypto functions |
| * |
| * Copyright (C) 2019 James.Bottomley@HansenPartnership.com |
| * |
| * SPDX-License-Identifier: LGPL-2.1-only |
| */ |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <wordexp.h> |
| |
| #include <openssl/engine.h> |
| #include <openssl/evp.h> |
| #include <openssl/pem.h> |
| #include <openssl/err.h> |
| #include <openssl/ui.h> |
| #include <openssl/x509.h> |
| |
| #include "openssl-pkcs11.h" |
| #include "crypto.h" |
| |
| /* number of bytes in the serial number */ |
| #define SERIAL_COUNT 10 |
| |
| void crypto_add_BN(int sec_num, const char *key, const BIGNUM *value, |
| EVP_MD_CTX *ctx) |
| { |
| int len = BN_num_bytes(value); |
| char *buf; |
| |
| buf = malloc(len); |
| if (!buf) |
| return; |
| |
| BN_bn2bin(value, (unsigned char *)buf); |
| EVP_DigestUpdate(ctx, buf, len); |
| cache_add_by_secnum(sec_num, key, buf, len); |
| } |
| |
| void crypto_add_serial(int sec_num, EVP_MD_CTX *ctx) |
| { |
| unsigned char hash[SHA256_DIGEST_LENGTH]; |
| char *serial; |
| int i; |
| |
| EVP_DigestFinal(ctx, hash, NULL); |
| EVP_MD_CTX_destroy(ctx); |
| if (cache_get_by_secnum(sec_num, "serial", NULL)) |
| /* the user supplied a serial, don't override */ |
| return; |
| |
| /* two chars per byte plus trailing zero */ |
| serial = malloc(SERIAL_COUNT*2 + 1); |
| for (i = 0; i < SERIAL_COUNT; i++) |
| snprintf(&serial[i*2], 3, "%02x", hash[i]); |
| cache_add_by_secnum(sec_num, "serial", serial, 0); |
| } |
| |
| static void |
| populate_global(int sec_num) |
| { |
| cache_add_by_secnum(sec_num, "CKA_TOKEN", |
| (const char *)(BOOL_FOR_PRIVATE | BOOL_FOR_PUBLIC | |
| BOOL_FOR_CERT), |
| CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_PRIVATE", |
| (const char *)BOOL_FOR_PRIVATE, CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_SIGN", |
| (const char *)BOOL_FOR_PRIVATE, CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_VERIFY", |
| (const char *)(BOOL_FOR_PUBLIC | BOOL_FOR_CERT), |
| CACHE_INT); |
| } |
| |
| static int populate_public_key(int sec_num, EVP_PKEY *pkey) |
| { |
| populate_global(sec_num); |
| switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) { |
| case EVP_PKEY_RSA: |
| crypto_rsa_populate(sec_num, pkey); |
| break; |
| case EVP_PKEY_EC: |
| crypto_ec_populate(sec_num, pkey); |
| break; |
| default: |
| fprintf(stderr, "Unknown key type\n"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int crypto_load_public_key(int sec_num, const char *pub) |
| { |
| FILE *file; |
| EVP_PKEY *pkey; |
| wordexp_t w; |
| |
| wordexp(pub, &w, 0); |
| file = fopen(w.we_wordv[0], "r"); |
| wordfree(&w); |
| if (!file) { |
| fprintf(stderr, "failed to open public key file %s: %s\n", |
| |
| pub, strerror(errno)); |
| return -1; |
| } |
| pkey = PEM_read_PUBKEY(file, NULL, NULL, NULL); |
| if (!pkey) { |
| fprintf(stderr, "failed to read public key %s:\n", pub); |
| ERR_print_errors_fp(stderr); |
| return -1; |
| } |
| |
| return populate_public_key(sec_num, pkey); |
| } |
| |
| int crypto_load_cert(int sec_num, const char *cert) |
| { |
| FILE *file; |
| X509 *x509; |
| X509_NAME *name; |
| wordexp_t w; |
| int len, ret; |
| char *d, *z; |
| ASN1_INTEGER *sn; |
| |
| wordexp(cert, &w, 0); |
| file = fopen(w.we_wordv[0], "r"); |
| wordfree(&w); |
| if (!file) { |
| fprintf(stderr, "failed to open public key file %s: %s\n", |
| |
| cert, strerror(errno)); |
| return -1; |
| } |
| x509 = PEM_read_X509(file, NULL, NULL, NULL); |
| if (!x509) { |
| fprintf(stderr, "failed to read public key %s:\n", cert); |
| ERR_print_errors_fp(stderr); |
| return -1; |
| } |
| |
| len = i2d_X509(x509, NULL); |
| z = d = OPENSSL_malloc(len); |
| i2d_X509(x509, (unsigned char **)&z); |
| cache_add_by_secnum(sec_num, "CKA_VALUE", d, len); |
| |
| name = X509_get_issuer_name(x509); |
| len = i2d_X509_NAME(name, NULL); |
| z = d = OPENSSL_malloc(len); |
| i2d_X509_NAME(name, (unsigned char **)&z); |
| cache_add_by_secnum(sec_num, "CKA_ISSUER", d, len); |
| |
| name = X509_get_subject_name(x509); |
| len = i2d_X509_NAME(name, NULL); |
| z = d = OPENSSL_malloc(len); |
| i2d_X509_NAME(name, (unsigned char **)&z); |
| cache_add_by_secnum(sec_num, "CKA_SUBJECT", d, len); |
| |
| sn = X509_get_serialNumber(x509); |
| len = i2d_ASN1_INTEGER(sn, NULL); |
| z = d = OPENSSL_malloc(len); |
| i2d_ASN1_INTEGER(sn, (unsigned char **)&z); |
| cache_add_by_secnum(sec_num, "CKA_SERIAL_NUMBER", d, len); |
| |
| cache_add_by_secnum(sec_num, "CKA_CERTIFICATE_TYPE", |
| (const char*)CKC_X_509, CACHE_INT); |
| |
| ret = populate_public_key(sec_num, X509_get_pubkey(x509)); |
| X509_free(x509); |
| return ret; |
| } |
| |
| static int |
| ui_reader(UI *ui, UI_STRING *uis) |
| { |
| char *pin = UI_get0_user_data(ui); |
| |
| if (UI_get_string_type(uis) == UIT_PROMPT) { |
| UI_set_result(ui, uis, pin); |
| |
| return 1; |
| } |
| return 0; |
| } |
| |
| int pem_cb (char *buf, int size, int rwflag, void *pin) |
| { |
| strncpy(buf, pin, size); |
| return strlen(pin); |
| } |
| |
| int crypto_load_private_key(int sec_num, const unsigned char *pin, int pin_len) |
| { |
| EVP_PKEY *pkey = NULL; |
| UI_METHOD *ui; |
| const char *priv = cache_get_by_secnum(sec_num, "private key", NULL); |
| const char *engine = cache_get_by_secnum(sec_num, "engine", NULL); |
| char auth[256]; |
| |
| /* pkcs11 pins may not be NULL terminated, but openssl expects |
| * its passwords to be */ |
| memcpy(auth, pin, pin_len); |
| auth[pin_len] = '\0'; |
| |
| if (!priv) { |
| fprintf(stderr, "No 'private key' directive in section '%s'\n", |
| cache_get_section(sec_num)); |
| return -1; |
| } |
| |
| ui = UI_create_method(PACKAGE_NAME); |
| if (!ui) { |
| fprintf(stderr, "UI creation failed\n"); |
| return -1; |
| } |
| UI_method_set_reader(ui, ui_reader); |
| |
| if (engine) { |
| ENGINE *e; |
| |
| ENGINE_load_builtin_engines(); |
| e = ENGINE_by_id(engine); |
| if (!e) |
| goto out; |
| ENGINE_init(e); |
| |
| /* cast discards const which is respected by ui_read */ |
| pkey = ENGINE_load_private_key(e, priv, ui, (void *)auth); |
| ENGINE_finish(e); |
| } else { |
| FILE *file; |
| wordexp_t w; |
| |
| wordexp(priv, &w, 0); |
| file = fopen(w.we_wordv[0], "r"); |
| wordfree(&w); |
| if (!file) { |
| fprintf(stderr, "failed to open private key file %s: %s\n", |
| priv, strerror(errno)); |
| return -1; |
| } |
| pkey = PEM_read_PrivateKey(file, NULL, pem_cb, (void *)auth); |
| } |
| out: |
| if (!pkey) { |
| fprintf(stderr, "failed to read private key %s:\n", priv); |
| ERR_print_errors_fp(stderr); |
| return -1; |
| } |
| |
| if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_RSA && |
| EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { |
| fprintf(stderr, "Unknown key type\n"); |
| return -1; |
| } |
| |
| cache_add_by_secnum(sec_num, "pkey", (const char *)pkey, CACHE_PKEY); |
| |
| return 0; |
| } |
| |
| void crypto_free_private_key(int sec_num) |
| { |
| cache_add_by_secnum(sec_num, "pkey", NULL, CACHE_PKEY); |
| } |
| |
| void crypto_cache_free_pkey(void *pkey) |
| { |
| EVP_PKEY_free(pkey); |
| } |
| |
| EVP_PKEY_CTX *crypto_get_key(int sec_num) |
| { |
| EVP_PKEY_CTX *ctx; |
| EVP_PKEY *pkey; |
| |
| pkey = (EVP_PKEY *)cache_get_by_secnum(sec_num, "pkey", NULL); |
| if (!pkey) { |
| fprintf(stderr, "crypto_encrypt internal error: no PKEY\n"); |
| return NULL; |
| } |
| ctx = EVP_PKEY_CTX_new(pkey, NULL); |
| |
| return ctx; |
| } |
| |
| static CK_KEY_TYPE get_key_type(int sec_num) |
| { |
| int type; |
| const char *val; |
| |
| val = cache_get_by_secnum(sec_num, "CKA_KEY_TYPE", &type); |
| if (type != CACHE_INT) |
| return -1; |
| return (CK_KEY_TYPE)val; |
| } |
| |
| void *crypto_sign_init(int sec_num, CK_MECHANISM_PTR mech) |
| { |
| EVP_PKEY_CTX *ctx; |
| |
| ctx = crypto_get_key(sec_num); |
| EVP_PKEY_sign_init(ctx); |
| if (get_key_type(sec_num) == CKK_RSA) |
| ctx = crypto_rsa_add_padding(ctx, mech); |
| |
| return ctx; |
| } |
| |
| int crypto_sign(int sec_num, void *opdata, void *data, unsigned long data_len, |
| void *sig, unsigned long *sig_len) |
| { |
| int ret = 0; |
| EVP_PKEY_CTX *ctx = opdata; |
| size_t len; |
| |
| /* openssl EC signatures are wrapped */ |
| if (get_key_type(sec_num) == CKK_EC) |
| return crypto_ec_sign(ctx, data, data_len, sig, sig_len); |
| |
| if (sig_len) |
| len = *sig_len; |
| |
| if (!sig) { |
| *sig_len = EVP_PKEY_size(EVP_PKEY_CTX_get0_pkey(ctx)); |
| return 0; |
| } |
| |
| if (EVP_PKEY_sign(ctx, sig, &len, data, data_len) <= 0) { |
| ERR_print_errors_fp(stderr); |
| ret = -1; |
| } |
| if (sig_len) |
| *sig_len = len; |
| EVP_PKEY_CTX_free(ctx); |
| |
| return ret; |
| } |
| |
| void crypto_fill_mechanism_list(int sec_num, unsigned long *mechs, |
| unsigned long *count) |
| { |
| switch (get_key_type(sec_num)) { |
| case CKK_RSA: |
| crypto_rsa_fill_mechanism_list(sec_num, mechs, count); |
| break; |
| case CKK_EC: |
| crypto_ec_fill_mechanism_list(sec_num, mechs, count); |
| break; |
| } |
| } |
| |
| int crypto_check_mechanism(int sec_num, CK_MECHANISM_TYPE mech, |
| CK_MECHANISM_INFO_PTR info) |
| { |
| switch (get_key_type(sec_num)) { |
| case CKK_RSA: |
| return crypto_rsa_check_mechanism(sec_num, mech, info); |
| case CKK_EC: |
| return crypto_ec_check_mechanism(sec_num, mech, info); |
| default: |
| return 0; |
| } |
| } |