| /* |
| * 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 <libiberty.h> |
| |
| #include <openssl/engine.h> |
| #include <openssl/evp.h> |
| #include <openssl/pem.h> |
| #include <openssl/err.h> |
| #include <openssl/rsa.h> |
| #include <openssl/ui.h> |
| |
| #include "openssl-pkcs11.h" |
| |
| /* number of bytes in the serial number */ |
| #define SERIAL_COUNT 10 |
| |
| static 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); |
| } |
| |
| static 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_rsa(int sec_num, EVP_PKEY *pkey) |
| { |
| RSA *rsa = EVP_PKEY_get1_RSA(pkey); |
| unsigned long size = RSA_size(rsa); |
| const BIGNUM *n, *e; |
| EVP_MD_CTX *ctx; |
| |
| #if OPENSSL_VERSION_NUMBER < 0x10100000 |
| n = rsa->n; |
| e = rsa->e; |
| #else |
| RSA_get0_key(rsa, &n, &e, NULL); |
| #endif |
| ctx = EVP_MD_CTX_create(); |
| EVP_DigestInit(ctx, EVP_sha256()); |
| |
| crypto_add_BN(sec_num, "CKA_PUBLIC_EXPONENT", e, ctx); |
| crypto_add_BN(sec_num, "CKA_MODULUS", n, ctx); |
| crypto_add_serial(sec_num, ctx); |
| cache_add_by_secnum(sec_num, "CKA_MODULUS_BITS", |
| (const char *)size, CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_KEY_TYPE", |
| (const char *)CKK_RSA, CACHE_INT); |
| /* booleans: only need to add true ones */ |
| cache_add_by_secnum(sec_num, "CKA_TOKEN", |
| (const char *)(BOOL_FOR_PRIVATE | BOOL_FOR_PUBLIC), |
| CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_PRIVATE", |
| (const char *)BOOL_FOR_PRIVATE, CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_ENCRYPT", |
| (const char *)BOOL_FOR_PUBLIC, CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_DECRYPT", |
| (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_RECOVER", |
| (const char *)BOOL_FOR_PUBLIC, CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_SENSITIVE", |
| (const char *)BOOL_FOR_PRIVATE, CACHE_INT); |
| cache_add_by_secnum(sec_num, "CKA_NEVER_EXTRACTABLE", |
| (const char *)BOOL_FOR_PRIVATE, CACHE_INT); |
| |
| RSA_free(rsa); |
| } |
| |
| 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; |
| } |
| |
| if (EVP_PKEY_type(EVP_PKEY_id(pkey)) == EVP_PKEY_RSA) { |
| populate_rsa(sec_num, pkey); |
| } else { |
| fprintf(stderr, "Unknown key type\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| 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); |
| |
| 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_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 *)pin); |
| 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 *)pin); |
| } |
| 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) { |
| 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); |
| } |
| |
| static EVP_PKEY_CTX *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; |
| } |
| |
| const EVP_MD *get_mgf1(unsigned long mgf1) |
| { |
| switch (mgf1) { |
| case CKG_MGF1_SHA1: |
| return EVP_sha1(); |
| case CKG_MGF1_SHA224: |
| return EVP_sha224(); |
| case CKG_MGF1_SHA256: |
| return EVP_sha256(); |
| case CKG_MGF1_SHA384: |
| return EVP_sha384(); |
| case CKG_MGF1_SHA512: |
| return EVP_sha512(); |
| default: |
| fprintf(stderr, "Unknown mgf1 type %ld\n", mgf1); |
| return NULL; |
| } |
| } |
| |
| const EVP_MD *get_hash(unsigned long hash) |
| { |
| switch (hash) { |
| case CKM_SHA_1: |
| return EVP_sha1(); |
| case CKM_SHA224: |
| return EVP_sha224(); |
| case CKM_SHA256: |
| return EVP_sha256(); |
| case CKM_SHA384: |
| return EVP_sha384(); |
| case CKM_SHA512: |
| return EVP_sha512(); |
| default: |
| fprintf(stderr, "Unknown hash type %ld\n", hash); |
| return NULL; |
| } |
| } |
| |
| static EVP_PKEY_CTX *add_padding(EVP_PKEY_CTX *ctx, CK_MECHANISM_PTR mech) |
| { |
| if (mech->mechanism == CKM_RSA_PKCS) { |
| EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING); |
| } else if (mech->mechanism == CKM_RSA_X_509) { |
| EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING); |
| } else if (mech->mechanism == CKM_RSA_PKCS_PSS) { |
| CK_RSA_PKCS_PSS_PARAMS *p = mech->pParameter; |
| |
| if (mech->ulParameterLen != sizeof(*p)) { |
| fprintf(stderr, "PSS mechanism parameter length %ld != %ld\n", |
| mech->ulParameterLen, (long)sizeof(*p)); |
| goto err; |
| } |
| EVP_PKEY_CTX_set_signature_md(ctx, get_hash(p->hashAlg)); |
| EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING); |
| EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, get_mgf1(p->mgf)); |
| EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, p->sLen); |
| } else if (mech->mechanism == CKM_RSA_PKCS_OAEP) { |
| CK_RSA_PKCS_OAEP_PARAMS *p = mech->pParameter; |
| |
| if (mech->ulParameterLen != sizeof(*p)) { |
| fprintf(stderr, "OAEP mechanism parameter length %ld != %ld\n", |
| mech->ulParameterLen, (long)sizeof(*p)); |
| goto err; |
| } |
| EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); |
| EVP_PKEY_CTX_set_rsa_oaep_md(ctx, get_hash(p->hashAlg)); |
| EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, get_mgf1(p->mgf)); |
| if (p->source & CKZ_DATA_SPECIFIED) { |
| void *l = BUF_memdup(p->pSourceData, |
| p->ulSourceDataLen); |
| |
| EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, l, |
| p->ulSourceDataLen); |
| } |
| } else { |
| fprintf(stderr, "unknown mechanism %ld\n", mech->mechanism); |
| goto err; |
| } |
| |
| return ctx; |
| err: |
| EVP_PKEY_CTX_free(ctx); |
| return NULL; |
| } |
| |
| void *crypto_sign_init(int sec_num, CK_MECHANISM_PTR mech) |
| { |
| EVP_PKEY_CTX *ctx; |
| |
| ctx = get_key(sec_num); |
| EVP_PKEY_sign_init(ctx); |
| return add_padding(ctx, mech); |
| } |
| |
| int crypto_sign(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; |
| |
| 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_decrypt_init(int sec_num, CK_MECHANISM_PTR mech) |
| { |
| EVP_PKEY_CTX *ctx; |
| |
| ctx = get_key(sec_num); |
| EVP_PKEY_decrypt_init(ctx); |
| return add_padding(ctx, mech); |
| } |
| |
| int crypto_decrypt(void *opdata, void *enc_data, unsigned long enc_len, |
| void *data, unsigned long *data_len) |
| { |
| EVP_PKEY_CTX *ctx = opdata; |
| int ret = 0; |
| size_t len; |
| |
| if (!data) { |
| *data_len = EVP_PKEY_size(EVP_PKEY_CTX_get0_pkey(ctx)); |
| return 0; |
| } |
| |
| if (EVP_PKEY_decrypt(ctx, data, &len, enc_data, enc_len) <= 0) { |
| ERR_print_errors_fp(stderr); |
| ret = -1; |
| } |
| if (data_len) |
| *data_len = len; |
| EVP_PKEY_CTX_free(ctx); |
| |
| return ret; |
| } |
| |
| void crypto_fill_mechanism_list(int sec_num, unsigned long *mechs, |
| unsigned long *count) |
| { |
| CK_MECHANISM_TYPE types[] = { |
| CKM_RSA_PKCS, |
| CKM_RSA_X_509, |
| CKM_RSA_PKCS_PSS, |
| CKM_RSA_PKCS_OAEP, |
| }; |
| |
| *count = ARRAY_SIZE(types); |
| if (mechs) |
| memcpy(mechs, types, sizeof(types)); |
| } |