mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
RFC 7517 requires that the x and y coordinates of a ECC JSON Web Key (JWK) are specified in its full size of a coordinate for the curve used. Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
1806 lines
55 KiB
C
1806 lines
55 KiB
C
/*
|
|
* libekmfweb - EKMFWeb client library
|
|
*
|
|
* Copyright IBM Corp. 2020
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
#include <dlfcn.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <err.h>
|
|
|
|
#include "lib/zt_common.h"
|
|
|
|
#include <openssl/sha.h>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/ecdsa.h>
|
|
|
|
#include "cca.h"
|
|
#include "utilities.h"
|
|
|
|
#define pr_verbose(verbose, fmt...) do { \
|
|
if (verbose) \
|
|
warnx(fmt); \
|
|
} while (0)
|
|
|
|
/* Internal CCA definitions */
|
|
|
|
#define CCA_KEYWORD_SIZE 8
|
|
#define CCA_KEY_ID_SIZE 64
|
|
|
|
struct cca_ecc_key_pair_value_struct {
|
|
uint8_t curve_type;
|
|
uint8_t reserved;
|
|
uint16_t curve_length;
|
|
uint16_t priv_key_length;
|
|
uint16_t public_key_len;
|
|
} __packed;
|
|
|
|
struct cca_rsa_key_pair_value_struct {
|
|
uint16_t modulus_bit_length;
|
|
uint16_t modulus_length;
|
|
uint16_t public_exp_length;
|
|
uint16_t reserved;
|
|
uint16_t p_length;
|
|
uint16_t q_length;
|
|
uint16_t dp_length;
|
|
uint16_t dq_length;
|
|
uint16_t u_length;
|
|
unsigned char public_exponent[3];
|
|
} __packed;
|
|
|
|
struct cca_ecc_pub_key_value_struct {
|
|
uint8_t curve_type;
|
|
uint8_t reserved;
|
|
uint16_t curve_length;
|
|
uint16_t public_key_len;
|
|
} __packed;
|
|
|
|
#define CCA_PRIME_CURVE 0x00
|
|
#define CCA_BRAINPOOL_CURVE 0x01
|
|
|
|
struct cca_token_header {
|
|
uint8_t token_identifier;
|
|
uint8_t token_version1; /* Used for PKA key tokens */
|
|
uint16_t token_length;
|
|
uint8_t token_version2; /* Used for symmetric key tokens */
|
|
uint8_t reserved[3];
|
|
} __packed;
|
|
|
|
/* Key token identifiers */
|
|
#define CCA_TOKEN_ID_NULL 0x00
|
|
#define CCA_TOKEN_ID_INTERNAL_SYMMETRIC 0x01
|
|
#define CCA_TOKEN_ID_EXTERNAL_SYMMETRIC 0x02
|
|
#define CCA_TOKEN_ID_EXTERNAL_PKA 0x1e
|
|
#define CCA_TOKEN_ID_INTERNAL_PKA 0x1f
|
|
|
|
/* Key token versions */
|
|
#define CCA_TOKEN_VERS1_V0 0x00
|
|
#define CCA_TOKEN_VERS2_DES_V0 0x00
|
|
#define CCA_TOKEN_VERS2_DES_V1 0x01
|
|
#define CCA_TOKEN_VERS2_AES_DATA 0x04
|
|
#define CCA_TOKEN_VERS2_AES_CIPHER 0x05
|
|
|
|
struct cca_section_header {
|
|
uint8_t section_identifier;
|
|
uint8_t section_version;
|
|
uint16_t section_length;
|
|
} __packed;
|
|
|
|
#define CCA_SECTION_ID_RSA_ME_1024_PRIV 0x02
|
|
#define CCA_SECTION_ID_RSA_PUBL 0x04
|
|
#define CCA_SECTION_ID_RSA_CRT_2048_PRIV 0x05
|
|
#define CCA_SECTION_ID_RSA_ME_1024_OPK_PRIV 0x06
|
|
#define CCA_SECTION_ID_RSA_CRT_4096_OPK_PRIV 0x08
|
|
#define CCA_SECTION_ID_RSA_ME_4096_PRIV 0x09
|
|
#define CCA_SECTION_ID_ECC_PRIV 0x20
|
|
#define CCA_SECTION_ID_ECC_PUBL 0x21
|
|
#define CCA_SECTION_ID_RSA_ME_1024_EOPK_PRIV 0x30
|
|
#define CCA_SECTION_ID_RSA_CRT_4096_EOPK_PRIV 0x31
|
|
|
|
struct cca_ecc_pub_key_section {
|
|
struct cca_section_header section_header;
|
|
uint8_t reserved1[4];
|
|
uint8_t curve_type;
|
|
uint8_t reserved2;
|
|
uint16_t prime_bits_length;
|
|
uint16_t pub_key_length; /* Incl. compression indication byte */
|
|
/* Public key of length pub_key_length */
|
|
} __packed;
|
|
|
|
struct cca_rsa_pub_key_section {
|
|
struct cca_section_header section_header;
|
|
uint16_t reserved1;
|
|
uint16_t pub_exp_length;
|
|
uint16_t modulus_bits_length;
|
|
uint16_t modulus_length; /* if 0 -> see priv key section */
|
|
/* Public exponent of length pub_exp_length */
|
|
/* Modulus of length modulus_length */
|
|
} __packed;
|
|
|
|
struct cca_rsa_crt_priv_key_section {
|
|
struct cca_section_header section_header;
|
|
uint16_t assoc_data_length;
|
|
uint16_t payload_length;
|
|
uint16_t reserved1;
|
|
uint8_t assoc_data_version;
|
|
uint8_t key_format;
|
|
uint8_t key_source;
|
|
uint8_t reserved2;
|
|
uint8_t hash_type;
|
|
uint8_t hash[32];
|
|
uint8_t reserved3[3];
|
|
uint8_t key_usage;
|
|
uint8_t format_restriction;
|
|
uint16_t p_length;
|
|
uint16_t q_length;
|
|
uint16_t dp_length;
|
|
uint16_t dq_length;
|
|
uint16_t u_length;
|
|
uint16_t modulus_length;
|
|
uint32_t reserved4;
|
|
uint8_t opk[48];
|
|
uint8_t kvp[16];
|
|
uint16_t reserved6;
|
|
/* Public modulus in length modulus_length */
|
|
/* Encrypted payload (AESKW-wrapped key material) */
|
|
} __packed;
|
|
|
|
#define POINT_CONVERSION_COMPRESSED 0x02
|
|
#define POINT_CONVERSION_UNCOMPRESSED 0x04
|
|
#define POINT_CONVERSION_HYBRID 0x06
|
|
#define POINT_CONVERSION_ODD_EVEN 0x01
|
|
|
|
/**
|
|
* Gets the CCA library function entry points from the library handle
|
|
*/
|
|
static int _cca_get_library_functions(const struct ekmf_cca_lib *cca_lib,
|
|
struct cca_lib *cca)
|
|
{
|
|
if (cca_lib == NULL || cca == NULL)
|
|
return -EINVAL;
|
|
|
|
cca->dll_CSNDPKB = (CSNDPKB_t)dlsym(cca_lib->cca_lib, "CSNDPKB");
|
|
cca->dll_CSNDPKG = (CSNDPKG_t)dlsym(cca_lib->cca_lib, "CSNDPKG");
|
|
cca->dll_CSNDKTC = (CSNDKTC_t)dlsym(cca_lib->cca_lib, "CSNDKTC");
|
|
cca->dll_CSNDDSG = (CSNDDSG_t)dlsym(cca_lib->cca_lib, "CSNDDSG");
|
|
cca->dll_CSNBKTB2 = (CSNBKTB2_t)dlsym(cca_lib->cca_lib, "CSNBKTB2");
|
|
cca->dll_CSNDEDH = (CSNDEDH_t)dlsym(cca_lib->cca_lib, "CSNDEDH");
|
|
cca->dll_CSNDSYI2 = (CSNDSYI2_t)dlsym(cca_lib->cca_lib, "CSNDSYI2");
|
|
|
|
if (cca->dll_CSNDPKB == NULL || cca->dll_CSNDPKG == NULL ||
|
|
cca->dll_CSNDKTC == NULL || cca->dll_CSNDDSG == NULL ||
|
|
cca->dll_CSNBKTB2 == NULL || cca->dll_CSNDEDH == NULL ||
|
|
cca->dll_CSNDSYI2 == NULL)
|
|
return -EIO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Generates an CCA ECC key of the specified curve type and length using the
|
|
* CCA host library.
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param curve_nid the nid specifying the curve.
|
|
* @param key_token a buffer to store the generated key token
|
|
* @param key_token_length On entry: the size of the buffer
|
|
* On return: the size of the key token
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_generate_ecc_key_pair(const struct ekmf_cca_lib *cca_lib,
|
|
int curve_nid, unsigned char *key_token,
|
|
size_t *key_token_length, bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
unsigned char transport_key_identifier[CCA_KEY_ID_SIZE] = { 0, };
|
|
unsigned char key_skeleton[CCA_MAX_PKA_KEY_TOKEN_SIZE] = { 0, };
|
|
long key_value_structure_length, private_key_name_length = 0;
|
|
unsigned char regeneration_data[CCA_KEY_ID_SIZE] = { 0, };
|
|
struct cca_ecc_key_pair_value_struct key_value_structure;
|
|
unsigned char private_key_name[CCA_KEY_ID_SIZE] = { 0, };
|
|
unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0, };
|
|
long regeneration_data_length = 0, key_skeleton_length;
|
|
unsigned char *exit_data = NULL;
|
|
unsigned char *param2 = NULL;
|
|
struct cca_lib cca;
|
|
long token_length;
|
|
long param1 = 0;
|
|
int rc;
|
|
|
|
if (cca_lib == NULL || key_token == NULL || key_token_length == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
memset(key_token, 0, *key_token_length);
|
|
token_length = *key_token_length;
|
|
|
|
memset(&key_value_structure, 0, sizeof(key_value_structure));
|
|
if (ecc_is_prime_curve(curve_nid)) {
|
|
key_value_structure.curve_type = CCA_PRIME_CURVE;
|
|
} else if (ecc_is_brainpool_curve(curve_nid)) {
|
|
key_value_structure.curve_type = CCA_BRAINPOOL_CURVE;
|
|
} else {
|
|
pr_verbose(verbose, "Unsupported curve: %d", curve_nid);
|
|
return -EINVAL;
|
|
}
|
|
key_value_structure.curve_length = ecc_get_curve_prime_bits(curve_nid);
|
|
key_value_structure_length = sizeof(key_value_structure);
|
|
|
|
rule_array_count = 3;
|
|
memcpy(rule_array, "ECC-PAIR", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "KEY-MGMT", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "ECC-VER1", CCA_KEYWORD_SIZE);
|
|
|
|
key_skeleton_length = sizeof(key_skeleton);
|
|
|
|
cca.dll_CSNDPKB(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&key_value_structure_length,
|
|
(unsigned char *)&key_value_structure,
|
|
&private_key_name_length, private_key_name,
|
|
¶m1, param2, ¶m1, param2,
|
|
¶m1, param2, ¶m1, param2,
|
|
¶m1, param2,
|
|
&key_skeleton_length, key_skeleton);
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDPKB (EC KEY TOKEN BUILD) failed: "
|
|
"return_code: %ld reason_code: %ld", return_code,
|
|
reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
rule_array_count = 1;
|
|
memset(rule_array, 0, sizeof(rule_array));
|
|
memcpy(rule_array, "MASTER ", (size_t)CCA_KEYWORD_SIZE);
|
|
|
|
cca.dll_CSNDPKG(&return_code, &reason_code,
|
|
NULL, NULL,
|
|
&rule_array_count, rule_array,
|
|
®eneration_data_length, regeneration_data,
|
|
&key_skeleton_length, key_skeleton,
|
|
transport_key_identifier,
|
|
&token_length, key_token);
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDPKG (EC KEY GENERATE) failed: "
|
|
"return_code: %ld reason_code: %ld", return_code,
|
|
reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
*key_token_length = token_length;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Generates an CCA RSA key of the specified key size and optionally the
|
|
* specified public exponent using the CCA host library.
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param modulus_bits the size of the key in bits (512, 1024, 2048, 4096)
|
|
* @param pub_exp the public exponent or zero. Possible values are:
|
|
* 3, 5, 17, 257, or 65537. Specify zero to choose the
|
|
* exponent by random (only possible for modulus_bits
|
|
* up to 2048).
|
|
* @param key_token a buffer to store the generated key token
|
|
* @param key_token_length On entry: the size of the buffer
|
|
* On return: the size of the key token
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_generate_rsa_key_pair(const struct ekmf_cca_lib *cca_lib,
|
|
size_t modulus_bits, unsigned int pub_exp,
|
|
unsigned char *key_token,
|
|
size_t *key_token_length, bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
unsigned char transport_key_identifier[CCA_KEY_ID_SIZE] = { 0, };
|
|
unsigned char key_skeleton[CCA_MAX_PKA_KEY_TOKEN_SIZE] = { 0, };
|
|
long key_value_structure_length, private_key_name_length = 0;
|
|
unsigned char regeneration_data[CCA_KEY_ID_SIZE] = { 0, };
|
|
struct cca_rsa_key_pair_value_struct key_value_structure;
|
|
unsigned char private_key_name[CCA_KEY_ID_SIZE] = { 0, };
|
|
unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0, };
|
|
long regeneration_data_length = 0, key_skeleton_length;
|
|
unsigned char *exit_data = NULL;
|
|
unsigned char *param2 = NULL;
|
|
struct cca_lib cca;
|
|
long token_length;
|
|
long param1 = 0;
|
|
int rc;
|
|
|
|
if (cca_lib == NULL || key_token == NULL || key_token_length == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
memset(key_token, 0, *key_token_length);
|
|
token_length = *key_token_length;
|
|
|
|
memset(&key_value_structure, 0, sizeof(key_value_structure));
|
|
key_value_structure.modulus_bit_length = modulus_bits;
|
|
switch (pub_exp) {
|
|
case 0:
|
|
if (modulus_bits > 2048) {
|
|
pr_verbose(verbose, "cannot auto-generate public "
|
|
"exponent for keys > 2048");
|
|
return -EINVAL;
|
|
}
|
|
key_value_structure.public_exp_length = 0;
|
|
break;
|
|
case 3:
|
|
key_value_structure.public_exp_length = 1;
|
|
key_value_structure.public_exponent[0] = 3;
|
|
break;
|
|
case 5:
|
|
key_value_structure.public_exp_length = 1;
|
|
key_value_structure.public_exponent[0] = 5;
|
|
break;
|
|
case 17:
|
|
key_value_structure.public_exp_length = 1;
|
|
key_value_structure.public_exponent[0] = 17;
|
|
break;
|
|
case 257:
|
|
key_value_structure.public_exp_length = 2;
|
|
key_value_structure.public_exponent[0] = 0x01;
|
|
key_value_structure.public_exponent[0] = 0x01;
|
|
break;
|
|
case 65537:
|
|
key_value_structure.public_exp_length = 3;
|
|
key_value_structure.public_exponent[0] = 0x01;
|
|
key_value_structure.public_exponent[1] = 0x00;
|
|
key_value_structure.public_exponent[2] = 0x01;
|
|
break;
|
|
default:
|
|
pr_verbose(verbose, "Invalid public exponent: %d", pub_exp);
|
|
return -EINVAL;
|
|
}
|
|
|
|
key_value_structure_length = sizeof(key_value_structure) +
|
|
key_value_structure.public_exp_length;
|
|
|
|
rule_array_count = 2;
|
|
memcpy(rule_array, "RSA-AESC", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "KEY-MGMT", CCA_KEYWORD_SIZE);
|
|
|
|
key_skeleton_length = sizeof(key_skeleton);
|
|
|
|
cca.dll_CSNDPKB(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&key_value_structure_length,
|
|
(unsigned char *)&key_value_structure,
|
|
&private_key_name_length, private_key_name,
|
|
¶m1, param2, ¶m1, param2,
|
|
¶m1, param2, ¶m1, param2,
|
|
¶m1, param2,
|
|
&key_skeleton_length, key_skeleton);
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDPKB (RSA KEY TOKEN BUILD) failed: "
|
|
"return_code: %ld reason_code: %ld", return_code,
|
|
reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
rule_array_count = 1;
|
|
memset(rule_array, 0, sizeof(rule_array));
|
|
memcpy(rule_array, "MASTER ", (size_t)CCA_KEYWORD_SIZE);
|
|
|
|
cca.dll_CSNDPKG(&return_code, &reason_code,
|
|
NULL, NULL,
|
|
&rule_array_count, rule_array,
|
|
®eneration_data_length, regeneration_data,
|
|
&key_skeleton_length, key_skeleton,
|
|
transport_key_identifier,
|
|
&token_length, key_token);
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDPKG (RSA KEY GENERATE) failed: "
|
|
"return_code: %ld reason_code: %ld", return_code,
|
|
reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
*key_token_length = token_length;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Finds a specific section of a CCA internal PKA key token.
|
|
*/
|
|
static const void *_cca_get_pka_section(const unsigned char *key_token,
|
|
size_t key_token_length,
|
|
unsigned int section_id, bool verbose)
|
|
{
|
|
const struct cca_section_header *section_hdr;
|
|
const struct cca_token_header *token_hdr;
|
|
size_t ofs;
|
|
|
|
if (key_token == NULL)
|
|
return NULL;
|
|
|
|
if (key_token_length < sizeof(struct cca_token_header)) {
|
|
pr_verbose(verbose, "key token length too small");
|
|
return NULL;
|
|
}
|
|
|
|
token_hdr = (struct cca_token_header *)key_token;
|
|
if (token_hdr->token_length > key_token_length) {
|
|
pr_verbose(verbose, "key token length too small");
|
|
return NULL;
|
|
}
|
|
if (token_hdr->token_identifier != CCA_TOKEN_ID_INTERNAL_PKA) {
|
|
pr_verbose(verbose, "not an internal PKA token");
|
|
return NULL;
|
|
}
|
|
if (token_hdr->token_version1 != CCA_TOKEN_VERS1_V0) {
|
|
pr_verbose(verbose, "invalid token version");
|
|
return NULL;
|
|
}
|
|
|
|
ofs = sizeof(struct cca_token_header);
|
|
section_hdr = (struct cca_section_header *)&key_token[ofs];
|
|
|
|
while (section_hdr->section_identifier != section_id) {
|
|
ofs += section_hdr->section_length;
|
|
if (ofs >= token_hdr->token_length) {
|
|
pr_verbose(verbose, "section %u not found", section_id);
|
|
return NULL;
|
|
}
|
|
section_hdr = (struct cca_section_header *)&key_token[ofs];
|
|
}
|
|
|
|
if (ofs + section_hdr->section_length > token_hdr->token_length) {
|
|
pr_verbose(verbose, "section exceed the token length");
|
|
return NULL;
|
|
}
|
|
|
|
return section_hdr;
|
|
}
|
|
|
|
/**
|
|
* Queries the PKEY type of the key token.
|
|
*
|
|
* @param key_token the key token containing an CCA ECC key
|
|
* @param key_token_length the size of the key token
|
|
* @param pkey_type On return: the PKEY type of the key token
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_get_key_type(const unsigned char *key_token, size_t key_token_length,
|
|
int *pkey_type)
|
|
{
|
|
if (key_token == NULL || pkey_type == NULL)
|
|
return -EINVAL;
|
|
|
|
if (_cca_get_pka_section(key_token, key_token_length,
|
|
CCA_SECTION_ID_ECC_PUBL, false) != NULL)
|
|
*pkey_type = EVP_PKEY_EC;
|
|
else if (_cca_get_pka_section(key_token, key_token_length,
|
|
CCA_SECTION_ID_RSA_PUBL, false) != NULL)
|
|
*pkey_type = EVP_PKEY_RSA;
|
|
else
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Extracts the ECC public key from an CCA internal ECC key token, and returns a
|
|
* it a OpenSSL PKEY.
|
|
*
|
|
* @param key_token the key token containing an CCA ECC key
|
|
* @param key_token_length the size of the key token
|
|
* @param pkey On return: a PKEY containing the public key
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_get_ecc_pub_key_as_pkey(const unsigned char *key_token,
|
|
size_t key_token_length,
|
|
EVP_PKEY **pkey, bool verbose)
|
|
{
|
|
struct cca_ecc_pub_key_section *ecc_pub_section;
|
|
const unsigned char *ecc_pub_key, *x, *y;
|
|
unsigned char *buf = NULL;
|
|
size_t prime_len;
|
|
int nid, y_bit = 0;
|
|
int rc = 0;
|
|
|
|
if (key_token == NULL || pkey == NULL)
|
|
return -EINVAL;
|
|
|
|
ecc_pub_section = (struct cca_ecc_pub_key_section *)
|
|
_cca_get_pka_section(key_token, key_token_length,
|
|
CCA_SECTION_ID_ECC_PUBL, verbose);
|
|
if (ecc_pub_section == NULL)
|
|
return -EINVAL;
|
|
if (ecc_pub_section->section_header.section_version != 0x00) {
|
|
pr_verbose(verbose, "invalid ECC public key section version");
|
|
return -EINVAL;
|
|
}
|
|
if (ecc_pub_section->section_header.section_length <
|
|
sizeof(struct cca_ecc_pub_key_section)) {
|
|
pr_verbose(verbose, "invalid ECC public key section length");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ecc_pub_key = ((unsigned char *)ecc_pub_section) +
|
|
sizeof(struct cca_ecc_pub_key_section);
|
|
|
|
if (ecc_pub_section->curve_type == CCA_PRIME_CURVE)
|
|
nid = ecc_get_prime_curve_by_prime_bits(
|
|
ecc_pub_section->prime_bits_length);
|
|
else if (ecc_pub_section->curve_type == CCA_BRAINPOOL_CURVE)
|
|
nid = ecc_get_brainpool_curve_by_prime_bits(
|
|
ecc_pub_section->prime_bits_length);
|
|
else
|
|
nid = 0;
|
|
if (nid == 0) {
|
|
pr_verbose(verbose, "unsupported curve");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
prime_len = ecc_get_curve_prime_length(nid);
|
|
|
|
x = ecc_pub_key + 1;
|
|
|
|
/* First byte of public key contains indication of key compression */
|
|
switch (ecc_pub_key[0]) {
|
|
case POINT_CONVERSION_COMPRESSED:
|
|
case POINT_CONVERSION_COMPRESSED + POINT_CONVERSION_ODD_EVEN:
|
|
/* Compressed form, only x is available */
|
|
y_bit = (ecc_pub_key[0] & POINT_CONVERSION_ODD_EVEN) ? 1 : 0;
|
|
|
|
buf = malloc(prime_len);
|
|
if (buf == NULL) {
|
|
pr_verbose(verbose, "malloc failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
rc = ecc_calculate_y_coordinate(nid, prime_len, x, y_bit, buf);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "ecc_calculate_y_coordinate "
|
|
"failed");
|
|
goto out;
|
|
}
|
|
|
|
y = buf;
|
|
break;
|
|
|
|
case POINT_CONVERSION_UNCOMPRESSED:
|
|
case POINT_CONVERSION_HYBRID:
|
|
case POINT_CONVERSION_HYBRID + POINT_CONVERSION_ODD_EVEN:
|
|
/* Uncompressed or hybrid, x and y are available */
|
|
y = x + prime_len;
|
|
break;
|
|
|
|
default:
|
|
pr_verbose(verbose, "invalid compression indication");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
rc = ecc_pub_key_as_pkey(nid, prime_len, x, y, pkey);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "ecc_pub_key_as_pkey failed");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (buf != NULL)
|
|
free(buf);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Extracts the ECC public key from an CCA internal ECC key token, and returns a
|
|
* JSON object representing the public key as JSON Web Key (JWK, see RFC7517).
|
|
* The returned JSON objects must be freed by the caller using json_object_put()
|
|
* when no longer needed.
|
|
*
|
|
* @param key_token the key token containing an CCA ECC key
|
|
* @param key_token_length the size of the key token
|
|
* qparam jwk the ECC public key as JWT JSON object
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_get_ecc_pub_key_as_json_web_key(const unsigned char *key_token,
|
|
size_t key_token_length,
|
|
json_object **jwk, bool verbose)
|
|
{
|
|
const struct cca_ecc_pub_key_section *ecc_pub_section;
|
|
const struct cca_section_header *section_hdr;
|
|
const struct cca_token_header *token_hdr;
|
|
const unsigned char *ecc_pub_key, *x, *y;
|
|
json_object *jwk_obj = NULL;
|
|
unsigned char *buf = NULL;
|
|
size_t ofs, prime_len;
|
|
int nid, y_bit = 0;
|
|
int rc = 0;
|
|
|
|
if (key_token == NULL || jwk == NULL)
|
|
return -EINVAL;
|
|
|
|
if (key_token_length < sizeof(struct cca_token_header)) {
|
|
pr_verbose(verbose, "key token length too small");
|
|
return -EINVAL;
|
|
}
|
|
|
|
token_hdr = (struct cca_token_header *)key_token;
|
|
if (token_hdr->token_length > key_token_length) {
|
|
pr_verbose(verbose, "key token length too small");
|
|
return -EINVAL;
|
|
}
|
|
if (token_hdr->token_identifier != CCA_TOKEN_ID_INTERNAL_PKA) {
|
|
pr_verbose(verbose, "not an internal PKA token");
|
|
return -EINVAL;
|
|
}
|
|
if (token_hdr->token_version1 != CCA_TOKEN_VERS1_V0) {
|
|
pr_verbose(verbose, "invalid token version");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ofs = sizeof(struct cca_token_header);
|
|
section_hdr = (struct cca_section_header *)&key_token[ofs];
|
|
|
|
while (section_hdr->section_identifier != CCA_SECTION_ID_ECC_PUBL) {
|
|
ofs += section_hdr->section_length;
|
|
if (ofs >= token_hdr->token_length) {
|
|
pr_verbose(verbose, "no ECC public key section found");
|
|
return -EINVAL;
|
|
}
|
|
section_hdr = (struct cca_section_header *)&key_token[ofs];
|
|
}
|
|
|
|
if (section_hdr->section_version != 0x00) {
|
|
pr_verbose(verbose, "invalid ECC public key section version");
|
|
return -EINVAL;
|
|
}
|
|
if (section_hdr->section_length <
|
|
sizeof(struct cca_ecc_pub_key_section)) {
|
|
pr_verbose(verbose, "invalid ECC public key section length");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ecc_pub_section = (struct cca_ecc_pub_key_section *)section_hdr;
|
|
ofs += sizeof(struct cca_ecc_pub_key_section);
|
|
ecc_pub_key = &key_token[ofs];
|
|
|
|
if (ecc_pub_section->curve_type == CCA_PRIME_CURVE)
|
|
nid = ecc_get_prime_curve_by_prime_bits(
|
|
ecc_pub_section->prime_bits_length);
|
|
else if (ecc_pub_section->curve_type == CCA_BRAINPOOL_CURVE)
|
|
nid = ecc_get_brainpool_curve_by_prime_bits(
|
|
ecc_pub_section->prime_bits_length);
|
|
else
|
|
nid = 0;
|
|
if (nid == 0) {
|
|
pr_verbose(verbose, "unsupported curve");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
prime_len = ecc_get_curve_prime_length(nid);
|
|
|
|
x = ecc_pub_key + 1;
|
|
|
|
/* First byte of public key contains indication of key compression */
|
|
switch (ecc_pub_key[0]) {
|
|
case POINT_CONVERSION_COMPRESSED:
|
|
case POINT_CONVERSION_COMPRESSED + POINT_CONVERSION_ODD_EVEN:
|
|
/* Compressed form, only x is available */
|
|
y_bit = (ecc_pub_key[0] & POINT_CONVERSION_ODD_EVEN) ? 1 : 0;
|
|
|
|
buf = malloc(prime_len);
|
|
if (buf == NULL) {
|
|
pr_verbose(verbose, "malloc failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
rc = ecc_calculate_y_coordinate(nid, prime_len, x, y_bit, buf);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "ecc_calculate_y_coordinate "
|
|
"failed");
|
|
goto out;
|
|
}
|
|
|
|
y = buf;
|
|
break;
|
|
|
|
case POINT_CONVERSION_UNCOMPRESSED:
|
|
case POINT_CONVERSION_HYBRID:
|
|
case POINT_CONVERSION_HYBRID + POINT_CONVERSION_ODD_EVEN:
|
|
/* Uncompressed or hybrid, x and y are available */
|
|
y = x + prime_len;
|
|
break;
|
|
|
|
default:
|
|
pr_verbose(verbose, "invalid compression indication");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
/* construct the JWK */
|
|
jwk_obj = json_object_new_object();
|
|
if (jwk_obj == NULL) {
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Note: The order of the fields is important, EKMFWeb expects it in
|
|
* exactly this order!
|
|
*/
|
|
rc = json_object_object_add_ex(jwk_obj, "kty",
|
|
json_object_new_string("EC"), 0);
|
|
rc |= json_object_object_add_ex(jwk_obj, "crv", json_object_new_string(
|
|
ecc_get_curve_id(nid)), 0);
|
|
rc |= json_object_object_add_ex(jwk_obj, "x",
|
|
json_object_new_base64url(x, prime_len),
|
|
0);
|
|
rc |= json_object_object_add_ex(jwk_obj, "y",
|
|
json_object_new_base64url(y, prime_len),
|
|
0);
|
|
if (rc != 0) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
*jwk = jwk_obj;
|
|
|
|
out:
|
|
if (buf != NULL)
|
|
free(buf);
|
|
if (rc != 0 && jwk_obj != NULL)
|
|
json_object_put(jwk_obj);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Extracts the RSA public key from an CCA internal RSA key token, and returns a
|
|
* it a OpenSSL PKEY.
|
|
*
|
|
* @param key_token the key token containing an CCA RSA key
|
|
* @param key_token_length the size of the key token
|
|
* @param pkey_type the PKEY type (EVP_PKEY_RSA or EVP_PKEY_RSA_PSS)*
|
|
* @param pkey On return: a PKEY containing the public key
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_get_rsa_pub_key_as_pkey(const unsigned char *key_token,
|
|
size_t key_token_length,
|
|
int pkey_type, EVP_PKEY **pkey, bool verbose)
|
|
{
|
|
const struct cca_rsa_crt_priv_key_section *rsa_priv_section;
|
|
const struct cca_rsa_pub_key_section *rsa_pub_section;
|
|
const unsigned char *pub_exp, *modulus;
|
|
size_t modulus_length;
|
|
int rc = 0;
|
|
|
|
if (key_token == NULL || pkey == NULL)
|
|
return -EINVAL;
|
|
|
|
rsa_pub_section = (struct cca_rsa_pub_key_section *)
|
|
_cca_get_pka_section(key_token, key_token_length,
|
|
CCA_SECTION_ID_RSA_PUBL, verbose);
|
|
if (rsa_pub_section == NULL)
|
|
return -EINVAL;
|
|
if (rsa_pub_section->section_header.section_version != 0x00) {
|
|
pr_verbose(verbose, "invalid RSA public key section version");
|
|
return -EINVAL;
|
|
}
|
|
if (rsa_pub_section->section_header.section_length <
|
|
sizeof(struct cca_ecc_pub_key_section)) {
|
|
pr_verbose(verbose, "invalid RSA public key section length");
|
|
return -EINVAL;
|
|
}
|
|
|
|
pub_exp = ((unsigned char *)rsa_pub_section) +
|
|
sizeof(struct cca_rsa_pub_key_section);
|
|
modulus = pub_exp + rsa_pub_section->pub_exp_length;
|
|
modulus_length = rsa_pub_section->modulus_length;
|
|
|
|
/*
|
|
* The public key section may have a modulus_length of zero, need to
|
|
* get the modulus from the private key section instead.
|
|
*/
|
|
if (rsa_pub_section->modulus_length == 0) {
|
|
rsa_priv_section = (struct cca_rsa_crt_priv_key_section *)
|
|
_cca_get_pka_section(key_token, key_token_length,
|
|
CCA_SECTION_ID_RSA_CRT_4096_EOPK_PRIV, verbose);
|
|
|
|
if (rsa_priv_section == NULL)
|
|
return -EINVAL;
|
|
if (rsa_priv_section->section_header.section_version != 0x00) {
|
|
pr_verbose(verbose, "invalid RSA private key section "
|
|
"version");
|
|
return -EINVAL;
|
|
}
|
|
if (rsa_priv_section->section_header.section_length <
|
|
sizeof(struct cca_rsa_crt_priv_key_section)) {
|
|
pr_verbose(verbose, "invalid RSA private key section "
|
|
"length");
|
|
return -EINVAL;
|
|
}
|
|
|
|
modulus = ((unsigned char *)rsa_priv_section) +
|
|
sizeof(struct cca_rsa_crt_priv_key_section);
|
|
modulus_length = rsa_priv_section->modulus_length;
|
|
}
|
|
|
|
rc = rsa_pub_key_as_pkey(modulus, modulus_length, pub_exp,
|
|
rsa_pub_section->pub_exp_length,
|
|
pkey_type, pkey);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "rsa_pub_key_as_pkey failed");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Re-enciphers a key token with a new CCA master key.
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param key_token the key token containing an CCA ECC or RSA key.
|
|
* The re-enciphered key token is returned in the same
|
|
* buffer. The size of the re-enciphered key token
|
|
* remains the same.
|
|
* @param key_token_length the size of the key token
|
|
* @param to_new If true: the key token is re-enciphered from the
|
|
* current to the new master key.
|
|
* If false: the key token is re-enciphered from the
|
|
* old to the current master key.
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
* -ENODEV is returned if the master keys are not loaded.
|
|
*/
|
|
int cca_reencipher_key(const struct ekmf_cca_lib *cca_lib,
|
|
const unsigned char *key_token, size_t key_token_length,
|
|
bool to_new, bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0, };
|
|
unsigned char *exit_data = NULL;
|
|
struct cca_lib cca;
|
|
long token_length;
|
|
int rc, type;
|
|
|
|
if (cca_lib == NULL || key_token == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
rc = cca_get_key_type(key_token, key_token_length, &type);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to determine the key token type");
|
|
return rc;
|
|
}
|
|
|
|
rule_array_count = 2;
|
|
switch (type) {
|
|
case EVP_PKEY_EC:
|
|
memcpy(rule_array, "ECC ", CCA_KEYWORD_SIZE);
|
|
break;
|
|
case EVP_PKEY_RSA:
|
|
case EVP_PKEY_RSA_PSS:
|
|
memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE);
|
|
break;
|
|
default:
|
|
pr_verbose(verbose, "Invalid key token type: %d", type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (to_new)
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "RTNMK ",
|
|
CCA_KEYWORD_SIZE);
|
|
else
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "RTCMK ",
|
|
CCA_KEYWORD_SIZE);
|
|
|
|
token_length = key_token_length;
|
|
|
|
cca.dll_CSNDKTC(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&token_length, (unsigned char *)key_token);
|
|
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDKTC (PKA KEY TOKEN CHANGE) failed:"
|
|
" return_code: %ld reason_code: %ld", return_code,
|
|
reason_code);
|
|
|
|
if (return_code == 12 && reason_code == 764) {
|
|
pr_verbose(verbose, "The master keys are not loaded");
|
|
return -ENODEV;
|
|
}
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Import a CCA key from a JSON object representing a key as JSON Web Key (JWK,
|
|
* see RFC7517). The JWK can either be an ECC public key (kty=EC), or an
|
|
* symmetric key (kty=oct) containing an CCA external variable length key token
|
|
* (alg=A256KW-CCA).
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param jwk the JWT JSON object containing the key to import
|
|
* @param key_token a buffer to store the imported key token
|
|
* @param key_token_length On entry: the size of the buffer
|
|
* On return: the size of the key token
|
|
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_import_key_from_json_web_key(const struct ekmf_cca_lib *cca_lib,
|
|
json_object *jwk, unsigned char *key_token,
|
|
size_t *key_token_length, bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
long key_value_struct_length, private_key_name_length = 0;
|
|
struct cca_ecc_pub_key_value_struct *key_value_struct = NULL;
|
|
unsigned char private_key_name[CCA_KEY_ID_SIZE] = { 0, };
|
|
unsigned char rule_array[1 * CCA_KEYWORD_SIZE] = { 0, };
|
|
unsigned char *exit_data = NULL;
|
|
size_t prime_len, q_len, len;
|
|
unsigned char *param2 = NULL;
|
|
struct cca_token_header *hdr;
|
|
const char *kty, *crv, *alg;
|
|
struct cca_lib cca;
|
|
long token_length;
|
|
unsigned char *q;
|
|
long param1 = 0;
|
|
int nid, rc = 0;
|
|
|
|
if (cca_lib == NULL || jwk == NULL || key_token == NULL ||
|
|
key_token_length == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
memset(key_token, 0, *key_token_length);
|
|
|
|
kty = json_get_string(jwk, "kty");
|
|
if (kty == NULL) {
|
|
pr_verbose(verbose, "JWK does not contain field 'kty'");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(kty, "EC") == 0) {
|
|
crv = json_get_string(jwk, "crv");
|
|
if (crv == NULL) {
|
|
pr_verbose(verbose, "JWK does not contain field 'crv'");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
nid = ecc_get_curve_by_id(crv);
|
|
if (nid == 0) {
|
|
pr_verbose(verbose, "curve '%s' not supported", crv);
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
prime_len = ecc_get_curve_prime_length(nid);
|
|
if (prime_len == 0) {
|
|
pr_verbose(verbose, "curve %d not supported", nid);
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
q_len = 1 + 2 * prime_len;
|
|
key_value_struct_length =
|
|
sizeof(struct cca_ecc_pub_key_value_struct) + q_len;
|
|
key_value_struct = (struct cca_ecc_pub_key_value_struct *)
|
|
malloc(key_value_struct_length);
|
|
if (key_value_struct == NULL) {
|
|
pr_verbose(verbose, "malloc failed");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
memset(key_value_struct, 0, sizeof(*key_value_struct));
|
|
if (ecc_is_prime_curve(nid)) {
|
|
key_value_struct->curve_type = CCA_PRIME_CURVE;
|
|
} else if (ecc_is_brainpool_curve(nid)) {
|
|
key_value_struct->curve_type = CCA_BRAINPOOL_CURVE;
|
|
} else {
|
|
pr_verbose(verbose, "Unsupported curve: %d", nid);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
key_value_struct->curve_length = ecc_get_curve_prime_bits(nid);
|
|
key_value_struct->public_key_len = q_len;
|
|
|
|
q = ((unsigned char *)key_value_struct) +
|
|
sizeof(struct cca_ecc_pub_key_value_struct);
|
|
q[0] = POINT_CONVERSION_UNCOMPRESSED;
|
|
|
|
len = prime_len;
|
|
rc = json_object_get_base64url(jwk, "x", &q[1], &len);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get and decode x");
|
|
goto out;
|
|
}
|
|
if (len != prime_len) {
|
|
/* RFC 7517: Must be full size of a coordinate */
|
|
pr_verbose(verbose, "x coordinate length is wrong");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
len = prime_len;
|
|
rc = json_object_get_base64url(jwk, "y", &q[1 + prime_len],
|
|
&len);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get and decode y");
|
|
goto out;
|
|
}
|
|
if (len != prime_len) {
|
|
/* RFC 7517: Must be full size of a coordinate */
|
|
pr_verbose(verbose, "y coordinate length is wrong");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
rule_array_count = 1;
|
|
memcpy(rule_array, "ECC-PUBL", CCA_KEYWORD_SIZE);
|
|
|
|
token_length = *key_token_length;
|
|
|
|
cca.dll_CSNDPKB(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&key_value_struct_length,
|
|
(unsigned char *)key_value_struct,
|
|
&private_key_name_length, private_key_name,
|
|
¶m1, param2, ¶m1, param2,
|
|
¶m1, param2, ¶m1, param2,
|
|
¶m1, param2,
|
|
&token_length, key_token);
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDPKB (EC KEY TOKEN BUILD) "
|
|
"failed: return_code: %ld reason_code: %ld",
|
|
return_code, reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
*key_token_length = token_length;
|
|
} else if (strcmp(kty, "oct") == 0) {
|
|
alg = json_get_string(jwk, "alg");
|
|
if (alg == NULL) {
|
|
pr_verbose(verbose, "JWK does not contain field 'alg'");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(alg, "A256KW-CCA") != 0) {
|
|
pr_verbose(verbose, "JWK alg is not A256KW-CCA");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
rc = json_object_get_base64url(jwk, "k", key_token,
|
|
key_token_length);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "failed to get and decode k");
|
|
goto out;
|
|
}
|
|
|
|
/* Ensure that this is an CCA external AES CIPHER key token */
|
|
if (*key_token_length < sizeof(struct cca_token_header)) {
|
|
pr_verbose(verbose, "key token is too small");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
hdr = (struct cca_token_header *)key_token;
|
|
if (hdr->token_identifier != CCA_TOKEN_ID_EXTERNAL_SYMMETRIC ||
|
|
hdr->token_version2 != CCA_TOKEN_VERS2_AES_CIPHER ||
|
|
*key_token_length < hdr->token_length) {
|
|
pr_verbose(verbose, "key token is not a valid CCA "
|
|
"external AES CIPHER key");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
} else {
|
|
pr_verbose(verbose, "Key type '%s' not supported", kty);
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (key_value_struct != NULL)
|
|
free(key_value_struct);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Drives an AES-256 key using the ED-DH key derivation method using a local ECC
|
|
* private/public key pair, a foreign public ECC key, and a shared party
|
|
* information data. The derived key is an internal CCA AES key token containing
|
|
* the derived key in its IMPORTER key form.
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param priv_ecc_keyf_token the ECC private key token
|
|
* @param priv_ecc_key_token_length the length of the ECC private key token
|
|
* @param pub_ecc_key_token the ECC public key token of the other side
|
|
* @param pub_ecc_key_token_length the length of the ECC public key token
|
|
* @param party_info the shared data used on both sides
|
|
* @param party_info_length the length of the shared data
|
|
* @param kdf the key derivation function to use
|
|
* @param derived_key_token a buffer to store the derived key token
|
|
* @param derived_key_token_length On entry: the size of the buffer
|
|
* On return: the size of the derived key token
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_ec_dh_derive_importer(const struct ekmf_cca_lib *cca_lib,
|
|
const unsigned char *priv_ecc_key_token,
|
|
size_t priv_ecc_key_token_length,
|
|
const unsigned char *pub_ecc_key_token,
|
|
size_t pub_ecc_key_token_length,
|
|
const unsigned char *party_info,
|
|
size_t party_info_length,
|
|
enum cca_kdf kdf,
|
|
unsigned char *derived_key_token,
|
|
size_t *derived_key_token_length,
|
|
bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
long priv_length, pub_length, info_length, derived_length;
|
|
unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0, };
|
|
unsigned char key_name[CCA_KEY_ID_SIZE] = { 0, };
|
|
long key_name_length = 0, key_skeleton_length;
|
|
unsigned char *exit_data = NULL;
|
|
unsigned char *param2 = NULL;
|
|
long key_bit_length = 256;
|
|
struct cca_lib cca;
|
|
long param1 = 0;
|
|
int rc;
|
|
|
|
if (cca_lib == NULL || priv_ecc_key_token == NULL ||
|
|
pub_ecc_key_token == NULL || party_info == NULL ||
|
|
derived_key_token == NULL || derived_key_token_length == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
memset(derived_key_token, 0, *derived_key_token_length);
|
|
|
|
rule_array_count = 3;
|
|
memcpy(rule_array, "INTERNAL", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "AES ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "IMPORTER", CCA_KEYWORD_SIZE);
|
|
|
|
key_skeleton_length = *derived_key_token_length;
|
|
|
|
cca.dll_CSNBKTB2(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
¶m1, param2,
|
|
&key_name_length, key_name,
|
|
¶m1, param2,
|
|
¶m1, param2,
|
|
¶m1, param2,
|
|
&key_skeleton_length, derived_key_token);
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNBKTB2 (KEY TOKEN BUILD2) failed: "
|
|
"return_code: %ld reason_code: %ld", return_code,
|
|
reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
switch (kdf) {
|
|
case CCA_KDF_ANS_X9_63_CCA:
|
|
rule_array_count = 1;
|
|
memcpy(rule_array, "DERIV01 ", CCA_KEYWORD_SIZE);
|
|
break;
|
|
case CCA_KDF_ANS_X9_63_SHA224:
|
|
rule_array_count = 2;
|
|
memcpy(rule_array, "DERIV02 ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "SHA-224 ",
|
|
CCA_KEYWORD_SIZE);
|
|
break;
|
|
case CCA_KDF_ANS_X9_63_SHA256:
|
|
rule_array_count = 2;
|
|
memcpy(rule_array, "DERIV02 ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "SHA-256 ",
|
|
CCA_KEYWORD_SIZE);
|
|
break;
|
|
case CCA_KDF_ANS_X9_63_SHA384:
|
|
rule_array_count = 2;
|
|
memcpy(rule_array, "DERIV02 ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "SHA-384 ",
|
|
CCA_KEYWORD_SIZE);
|
|
break;
|
|
case CCA_KDF_ANS_X9_63_SHA512:
|
|
rule_array_count = 2;
|
|
memcpy(rule_array, "DERIV02 ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "SHA-512 ",
|
|
CCA_KEYWORD_SIZE);
|
|
break;
|
|
default:
|
|
pr_verbose(verbose, "Invalid CCA KDF: %d", kdf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
priv_length = priv_ecc_key_token_length;
|
|
pub_length = pub_ecc_key_token_length;
|
|
derived_length = *derived_key_token_length;
|
|
info_length = party_info_length;
|
|
|
|
cca.dll_CSNDEDH(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&priv_length, (unsigned char *)priv_ecc_key_token,
|
|
¶m1, param2,
|
|
&pub_length, (unsigned char *)pub_ecc_key_token,
|
|
¶m1, param2,
|
|
&info_length, (unsigned char *)party_info,
|
|
&key_bit_length,
|
|
¶m1, param2, ¶m1, param2,
|
|
¶m1, param2, ¶m1, param2,
|
|
¶m1, param2, ¶m1, param2,
|
|
&derived_length, derived_key_token);
|
|
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDEDH (EC DIFFIE-HELLMAN) failed: "
|
|
"return_code: %ld reason_code: %ld", return_code,
|
|
reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
*derived_key_token_length = derived_length;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Imports an CCA external variable length AES key token using a wrapping key
|
|
* in IMPORTER key form.
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param external_key_token the external key to import
|
|
* @param external_key_token_length the length of the external key
|
|
* @param importer_key_token the wrapping key in IMPORTER key form
|
|
* @param importer_key_token_length the length of the wrapping key
|
|
* @param imported_key_token a buffer to store the derived key token
|
|
* @param imported_key_token_length On entry: the size of the buffer
|
|
* On return: the size of the imported key token
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_import_external_key(const struct ekmf_cca_lib *cca_lib,
|
|
const unsigned char *external_key_token,
|
|
size_t external_key_token_length,
|
|
const unsigned char *importer_key_token,
|
|
size_t importer_key_token_length,
|
|
unsigned char *imported_key_token,
|
|
size_t *imported_key_token_length,
|
|
bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0, };
|
|
unsigned char key_name[CCA_KEY_ID_SIZE] = { 0, };
|
|
long ext_length, importer_length, imp_length;
|
|
unsigned char *exit_data = NULL;
|
|
long key_name_length = 0;
|
|
struct cca_lib cca;
|
|
int rc;
|
|
|
|
if (cca_lib == NULL || external_key_token == NULL ||
|
|
importer_key_token == NULL || imported_key_token == NULL ||
|
|
imported_key_token_length == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
memset(imported_key_token, 0, *imported_key_token_length);
|
|
|
|
rule_array_count = 2;
|
|
memcpy(rule_array, "AES ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "AESKW ", CCA_KEYWORD_SIZE);
|
|
|
|
ext_length = external_key_token_length;
|
|
importer_length = importer_key_token_length;
|
|
imp_length = *imported_key_token_length;
|
|
|
|
cca.dll_CSNDSYI2(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&ext_length, (unsigned char *)external_key_token,
|
|
&importer_length, (unsigned char *)importer_key_token,
|
|
&key_name_length, key_name,
|
|
&imp_length, imported_key_token);
|
|
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDSYI2 (SYMM. KEY IMPORT) failed: "
|
|
"return_code: %ld reason_code: %ld", return_code,
|
|
reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
*imported_key_token_length = imp_length;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const unsigned char der_DigestInfo_SHA1[] = {
|
|
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
|
|
0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, };
|
|
static const unsigned char der_DigestInfo_SHA224[] = {
|
|
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
|
|
0x00, 0x04, 0x1C, };
|
|
static const unsigned char der_DigestInfo_SHA256[] = {
|
|
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
|
|
0x00, 0x04, 0x20, };
|
|
static const unsigned char der_DigestInfo_SHA384[] = {
|
|
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
|
|
0x00, 0x04, 0x30, };
|
|
static const unsigned char der_DigestInfo_SHA512[] = {
|
|
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
|
|
0x00, 0x04, 0x40, };
|
|
static const unsigned char der_DigestInfo_SHA3_224[] = {
|
|
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x07, 0x05,
|
|
0x00, 0x04, 0x1C, };
|
|
static const unsigned char der_DigestInfo_SHA3_256[] = {
|
|
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08, 0x05,
|
|
0x00, 0x04, 0x20, };
|
|
static const unsigned char der_DigestInfo_SHA3_384[] = {
|
|
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09, 0x05,
|
|
0x00, 0x04, 0x30, };
|
|
static const unsigned char der_DigestInfo_SHA3_512[] = {
|
|
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a, 0x05,
|
|
0x00, 0x04, 0x40, };
|
|
|
|
struct digest_info {
|
|
int digest_nid;
|
|
size_t digest_size;
|
|
const char *cca_keyword;
|
|
const unsigned char *der;
|
|
size_t der_size;
|
|
};
|
|
|
|
static const struct digest_info digest_list[] = {
|
|
{ .digest_nid = NID_sha1, .digest_size = SHA_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-1 ", .der = der_DigestInfo_SHA1,
|
|
.der_size = sizeof(der_DigestInfo_SHA1), },
|
|
{ .digest_nid = NID_sha224, .digest_size = SHA224_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-224 ", .der = der_DigestInfo_SHA224,
|
|
.der_size = sizeof(der_DigestInfo_SHA224), },
|
|
{ .digest_nid = NID_sha256, .digest_size = SHA256_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-256 ", .der = der_DigestInfo_SHA256,
|
|
.der_size = sizeof(der_DigestInfo_SHA256), },
|
|
{ .digest_nid = NID_sha384, .digest_size = SHA384_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-384 ", .der = der_DigestInfo_SHA384,
|
|
.der_size = sizeof(der_DigestInfo_SHA384), },
|
|
{ .digest_nid = NID_sha512, .digest_size = SHA512_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-512 ", .der = der_DigestInfo_SHA512,
|
|
.der_size = sizeof(der_DigestInfo_SHA512), },
|
|
{ .digest_nid = NID_sha3_224, .digest_size = SHA224_DIGEST_LENGTH,
|
|
.cca_keyword = NULL, .der = der_DigestInfo_SHA3_224,
|
|
.der_size = sizeof(der_DigestInfo_SHA3_224), },
|
|
{ .digest_nid = NID_sha3_256, .digest_size = SHA256_DIGEST_LENGTH,
|
|
.cca_keyword = NULL, .der = der_DigestInfo_SHA3_256,
|
|
.der_size = sizeof(der_DigestInfo_SHA3_256), },
|
|
{ .digest_nid = NID_sha3_384, .digest_size = SHA384_DIGEST_LENGTH,
|
|
.cca_keyword = NULL, .der = der_DigestInfo_SHA3_384,
|
|
.der_size = sizeof(der_DigestInfo_SHA3_384), },
|
|
{ .digest_nid = NID_sha3_512, .digest_size = SHA512_DIGEST_LENGTH,
|
|
.cca_keyword = NULL, .der = der_DigestInfo_SHA3_512,
|
|
.der_size = sizeof(der_DigestInfo_SHA3_512), },
|
|
};
|
|
|
|
static const int digest_list_num = sizeof(digest_list) /
|
|
sizeof(struct digest_info);
|
|
|
|
static const struct digest_info *get_digest_info(int digest_nid)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < digest_list_num; i++) {
|
|
if (digest_list[i].digest_nid == digest_nid)
|
|
return &digest_list[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct cca_private_data {
|
|
CSNDDSG_t dll_CSNDDSG;
|
|
bool verbose;
|
|
};
|
|
|
|
/**
|
|
* Sign data using RSA.
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param key_token the RSA key token
|
|
* @param key_token_length the length of the key token
|
|
* @param sig a buffer to store the signature on return.
|
|
* @param siglen on input: the size if the signature buffer
|
|
* on return: the size of the signature
|
|
* @param tbs the data to be signed.
|
|
* @param tbslen the size of the data to be signed
|
|
* @param padding_type the OpenSSL padding type (RSA_X931_PADDING or
|
|
* RSA_PKCS1_PADDING)
|
|
* @param digest_nid the OpenSSL nid of the message digest used to
|
|
* produce the data to be signed
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_rsa_sign(const struct ekmf_cca_lib *cca_lib,
|
|
const unsigned char *key_token, size_t key_token_length,
|
|
unsigned char *sig, size_t *siglen,
|
|
const unsigned char *tbs, size_t tbslen,
|
|
int padding_type, int md_nid, bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
long token_length, hash_length, sign_bit_length, sign_length;
|
|
unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0, };
|
|
unsigned char *hash = NULL, *buf = NULL;
|
|
const struct digest_info *digest;
|
|
unsigned char *exit_data = NULL;
|
|
struct cca_lib cca;
|
|
int rc;
|
|
|
|
if (cca_lib == NULL || key_token == NULL || sig == NULL ||
|
|
siglen == NULL || tbs == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
rule_array_count = 3;
|
|
memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE);
|
|
|
|
switch (padding_type) {
|
|
case RSA_X931_PADDING:
|
|
hash = (unsigned char *)tbs;
|
|
hash_length = tbslen;
|
|
|
|
memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "X9.31 ",
|
|
CCA_KEYWORD_SIZE);
|
|
break;
|
|
|
|
case RSA_PKCS1_PADDING:
|
|
digest = get_digest_info(md_nid);
|
|
if (digest == NULL) {
|
|
pr_verbose(verbose, "Invalid digest nid: %d", md_nid);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (tbslen != digest->digest_size) {
|
|
pr_verbose(verbose, "Invalid data length: %lu", tbslen);
|
|
return -EINVAL;
|
|
}
|
|
|
|
hash_length = digest->der_size + tbslen;
|
|
buf = (unsigned char *)malloc(hash_length);
|
|
if (buf == NULL) {
|
|
pr_verbose(verbose, "malloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(buf, digest->der, digest->der_size);
|
|
memcpy(buf + digest->der_size, tbs, tbslen);
|
|
hash = buf;
|
|
|
|
memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "PKCS-1.1",
|
|
CCA_KEYWORD_SIZE);
|
|
break;
|
|
|
|
default:
|
|
pr_verbose(verbose, "Invalid padding type: %d", padding_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
token_length = key_token_length;
|
|
sign_length = *siglen;
|
|
|
|
cca.dll_CSNDDSG(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&token_length, (unsigned char *)key_token,
|
|
&hash_length, hash,
|
|
&sign_length, &sign_bit_length, sig);
|
|
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDDSG (DIG. SIGNATURE CREATE, RSA) "
|
|
"failed: return_code: %ld reason_code: %ld",
|
|
return_code, reason_code);
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
*siglen = sign_length;
|
|
rc = 0;
|
|
|
|
out:
|
|
if (buf != NULL)
|
|
free(buf);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Sign data using RSA-PSS.
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param key_token the RSA key token
|
|
* @param key_token_length the length of the key token
|
|
* @param sig a buffer to store the signature on return.
|
|
* @param siglen on input: the size if the signature buffer
|
|
* on return: the size of the signature
|
|
* @param tbs the data to be signed.
|
|
* @param tbslen the size of the data to be signed
|
|
* @param digest_nid the OpenSSL nid of the message digest used to
|
|
* produce the data to be signed
|
|
* @param mgf_digest_nid the OpenSSL nid of the mask generation function for
|
|
* PSS padding
|
|
* @param saltlen the length of the salt for PSS
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_rsa_pss_sign(const struct ekmf_cca_lib *cca_lib,
|
|
const unsigned char *key_token, size_t key_token_length,
|
|
unsigned char *sig, size_t *siglen,
|
|
const unsigned char *tbs, size_t tbslen,
|
|
int digest_nid, int mgf_digest_nid, int saltlen,
|
|
bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
unsigned char rule_array[4 * CCA_KEYWORD_SIZE] = { 0, };
|
|
long token_length, hash_length, sign_bit_length, sign_length;
|
|
const struct digest_info *digest;
|
|
unsigned char *exit_data = NULL;
|
|
unsigned char *buf = NULL;
|
|
struct cca_lib cca;
|
|
uint32_t salt_len;
|
|
int rc;
|
|
|
|
if (cca_lib == NULL || key_token == NULL || sig == NULL ||
|
|
siglen == NULL || tbs == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
if (mgf_digest_nid != digest_nid) {
|
|
pr_verbose(verbose, "Mgf nid must be the same as the message "
|
|
"digest nid");
|
|
return -EINVAL;
|
|
}
|
|
|
|
digest = get_digest_info(digest_nid);
|
|
if (digest == NULL || digest->cca_keyword == NULL) {
|
|
pr_verbose(verbose, "Invalid mgf nid: %d", digest_nid);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rule_array_count = 4;
|
|
memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "PKCS-PSS", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + 3 * CCA_KEYWORD_SIZE, digest->cca_keyword,
|
|
CCA_KEYWORD_SIZE);
|
|
|
|
hash_length = sizeof(uint32_t) + tbslen;
|
|
buf = (unsigned char *)malloc(hash_length);
|
|
if (buf == NULL) {
|
|
pr_verbose(verbose, "malloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
salt_len = saltlen;
|
|
memcpy(buf, &salt_len, sizeof(uint32_t));
|
|
memcpy(buf + sizeof(uint32_t), tbs, tbslen);
|
|
|
|
token_length = key_token_length;
|
|
sign_length = *siglen;
|
|
|
|
cca.dll_CSNDDSG(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&token_length, (unsigned char *)key_token,
|
|
&hash_length, buf,
|
|
&sign_length, &sign_bit_length, sig);
|
|
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDDSG (DIG. SIGNATURE CREATE, "
|
|
"RSA-PSS) failed: return_code: %ld reason_code: %ld",
|
|
return_code, reason_code);
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
*siglen = sign_length;
|
|
rc = 0;
|
|
|
|
out:
|
|
free(buf);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Sign data using ECDSA.
|
|
*
|
|
* @param cca_lib the CCA library structure
|
|
* @param key_token the RSA key token
|
|
* @param key_token_length the length of the key token
|
|
* @param sig a buffer to store the signature on return.
|
|
* @param siglen on input: the size if the signature buffer
|
|
* on return: the size of the signature
|
|
* @param tbs the data to be signed.
|
|
* @param tbslen the size of the data to be signed
|
|
* @param digest_nid the OpenSSL nid of the message digest used to
|
|
* produce the data to be signed
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns a negative errno in case of an error, 0 if success.
|
|
*/
|
|
int cca_ecdsa_sign(const struct ekmf_cca_lib *cca_lib,
|
|
const unsigned char *key_token, size_t key_token_length,
|
|
unsigned char *sig, size_t *siglen,
|
|
const unsigned char *tbs, size_t tbslen,
|
|
int UNUSED(digest_nid), bool verbose)
|
|
{
|
|
long return_code, reason_code, rule_array_count, exit_data_len = 0;
|
|
long token_length, hash_length, sign_bit_length, sign_length;
|
|
unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0, };
|
|
unsigned char *exit_data = NULL;
|
|
unsigned char *der = NULL;
|
|
ECDSA_SIG *ec_sig = NULL;
|
|
BIGNUM *bn_r = NULL;
|
|
BIGNUM *bn_s = NULL;
|
|
struct cca_lib cca;
|
|
int rc, der_len;
|
|
|
|
if (cca_lib == NULL || key_token == NULL || sig == NULL ||
|
|
siglen == NULL || tbs == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = _cca_get_library_functions(cca_lib, &cca);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get CCA functions from library");
|
|
return rc;
|
|
}
|
|
|
|
rule_array_count = 2;
|
|
memcpy(rule_array, "ECDSA ", CCA_KEYWORD_SIZE);
|
|
memcpy(rule_array + CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE);
|
|
|
|
hash_length = tbslen;
|
|
token_length = key_token_length;
|
|
sign_length = *siglen;
|
|
|
|
cca.dll_CSNDDSG(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&token_length, (unsigned char *)key_token,
|
|
&hash_length, (unsigned char *)tbs,
|
|
&sign_length, &sign_bit_length, sig);
|
|
|
|
if (return_code != 0) {
|
|
pr_verbose(verbose, "CCA CSNDDSG (DIG. SIGNATURE CREATE, ECDSA)"
|
|
" failed: return_code: %ld reason_code: %ld",
|
|
return_code, reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
ec_sig = ECDSA_SIG_new();
|
|
if (ec_sig == NULL) {
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
bn_r = BN_bin2bn(sig, sign_length / 2, NULL);
|
|
bn_s = BN_bin2bn(sig + sign_length / 2, sign_length / 2, NULL);
|
|
if (bn_r == NULL || bn_s == NULL) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
if (ECDSA_SIG_set0(ec_sig, bn_r, bn_s) != 1) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
bn_r = NULL;
|
|
bn_s = NULL;
|
|
|
|
der_len = i2d_ECDSA_SIG(ec_sig, NULL);
|
|
if (der_len > (int)*siglen) {
|
|
rc = -ERANGE;
|
|
goto out;
|
|
}
|
|
|
|
memset(sig, 0, *siglen);
|
|
der = sig;
|
|
*siglen = i2d_ECDSA_SIG(ec_sig, &der);
|
|
|
|
if (*siglen == 0) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
if (ec_sig != NULL)
|
|
ECDSA_SIG_free(ec_sig);
|
|
if (bn_r != NULL)
|
|
BN_free(bn_r);
|
|
if (bn_s != NULL)
|
|
BN_free(bn_s);
|
|
|
|
return rc;
|
|
}
|
|
|