libekmfweb: Retrieve a key from EKMF Web using an EC-DH protocol

To retrieve a secure key from EKMF Web, a Elliptic-curve Diffie-Hellman
(EC-DH) protocol is used to securely transport the key, without revealing
the key to be transported in clear. The key to be transported remains
encrypted all the time, either encrypted with the master key of the
cryptographic adapter on the source or target side, or with a transport
key, that is derived using the EC-DH key agreement protocol. The transport
key is also a secure key, itself encrypted with the master key of the
cryptographic adapters on both sides.

To generate the transport key, each side generates a new secure ECC
session key with its cryptographic adapter. This session keys are then
used with EC-DH to derive the secure transport key. The secure key to
be transported is then exported by EKMF Web under the transport key, is
sent to the client, and is then imported using the transport key.

The key retrieval request is signed using the identity key of the client.
EKMF Web knows the public key of the client's identity key through a one
time registration process, and can therefore verify the signature with
the client's public key. The response is also signed by the EKMF Web
server's identity key, and the client can verify the signature with the
server's public key that it retrieved once during registration.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Ingo Franzki
2020-05-04 11:20:40 +02:00
committed by Jan Höppner
parent 8137128a96
commit 9f99706c26
7 changed files with 2649 additions and 6 deletions
+84
View File
@@ -13,6 +13,8 @@
#include <stddef.h>
#include <stdbool.h>
typedef void CURL;
struct ekmf_config {
/** The base URL of the server. Should use https:// ! */
const char *base_url;
@@ -52,6 +54,9 @@ struct ekmf_config {
* This key represents the client identity against EKMFWeb. Some
* requests sent to EKMFWeb are signed with this (secure) key */
const char *identity_secure_key;
/** File name of a PEM file containing the EKMFWeb servers public key
* used to sign key export responses. */
const char *ekmf_server_pubkey;
};
struct ekmf_cca_lib {
@@ -344,4 +349,83 @@ int ekmf_generate_ss_cert(const struct ekmf_config *config,
const char *cert_pem_filename,
const struct ekmf_ext_lib *ext_lib, bool verbose);
/**
* Request the EKMFWeb server's public signing key and store it into PEM file
* specified in field server_pubkey of the config structure.
*
* To perform a single request, set curl_handle to NULL. This will cause the
* function to initialize a new CURL handle, use it, and destroy it.
* If you plan to perform multiple requests to the same host, supply the address
* of a CURL pointer that is initially NULL. This function will then initialize
* a new CURL handle on the first call. On subsequent calls, pass in the address
* of the same CURL pointer so that the CURL handle is reused. After the last
* request, the CURL handle must be destroyed by calling ekmf_curl_destroy).
*
* @param config the configuration structure
* @param curl_handle address of a CURL handle used for reusing the same
* CURL handle with multiple requests.
* @param error_msg on return: If not NULL, then a textual error message
* is returned in case of a failing request. The caller
* must free the error string when it is not NULL.
* @param verbose if true, verbose messages are printed
*
* @returns zero for success, a negative errno in case of an error.
* -EACCES is returned, if no or no valid login token is available.
*/
int ekmf_get_public_key(const struct ekmf_config *config, CURL **curl_handle,
char **error_msg, bool verbose);
/**
* Requests a key to be retrieved from EKMFweb and imported under the current
* HSM's master key.
*
* To perform a single request, set curl_handle to NULL. This will cause the
* function to initialize a new CURL handle, use it, and destroy it.
* If you plan to perform multiple requests to the same host, supply the address
* of a CURL pointer that is initially NULL. This function will then initialize
* a new CURL handle on the first call. On subsequent calls, pass in the address
* of the same CURL pointer so that the CURL handle is reused. After the last
* request, the CURL handle must be destroyed by calling ekmf_curl_destroy).
*
* @param config the configuration structure
* @param curl_handle address of a CURL handle used for reusing the same
* CURL handle with multiple requests.
* @param key_uuid the UUID of the key to retrieve
* @param sess_ec_curve_nid The OpenSSL nid of the EC curve used for the session
* ECC key. If 0, then the default curve is used.
* @param sign_rsa_digest_nid The OpenSSL nid of a digest used to sign the
* request with if the identity key is an RSA-type key.
* If 0, then the default digest is used.
* Ignored for ECC-type identity keys.
* @param use_rsa_pss If true, and the identity key is an RSA-type key,
* use RSA-PSS to sign the request.
* @param signature_kid the Key ID for the signature of the request
* @param key_blob a buffer to store the retrieved key blob to
* @param key_blob_length On entry: the size ofthe buffer
* On return: the size of the key blob retrieved
* @param error_msg on return: If not NULL, then a textual error message
* is returned in case of a failing request. The caller
* must free the error string when it is not NULL.
* @param ext_lib External secure key crypto library to use
* @param verbose if true, verbose messages are printed
*
* @returns zero for success, a negative errno in case of an error.
* -EACCES is returned, if no or no valid login token is available.
* -EPERM is returned if the login token does not have permission to
* retrieve the key
*/
int ekmf_retrieve_key(const struct ekmf_config *config, CURL **curl_handle,
const char *key_uuid, int sess_ec_curve_nid,
int sign_rsa_digest_nid, bool use_rsa_pss,
const char *signature_kid, unsigned char *key_blob,
size_t *key_blob_length, char **error_msg,
const struct ekmf_ext_lib *ext_lib, bool verbose);
/**
* Close the connection to the EKMFWeb server by destroying the CURL handle.
*
* @param curl_handle the CURL handle to destroy
*/
void ekmf_curl_destroy(CURL *curl_handle);
#endif
+592 -1
View File
@@ -52,6 +52,13 @@ struct cca_rsa_key_pair_value_struct {
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
@@ -160,9 +167,14 @@ static int _cca_get_library_functions(const struct ekmf_cca_lib *cca_lib,
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_CSNDKTC == NULL || cca->dll_CSNDDSG == NULL ||
cca->dll_CSNBKTB2 == NULL || cca->dll_CSNDEDH == NULL ||
cca->dll_CSNDSYI2 == NULL)
return -EIO;
return 0;
@@ -600,6 +612,171 @@ out:
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.
@@ -773,6 +950,419 @@ int cca_reencipher_key(const struct ekmf_cca_lib *cca_lib,
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;
}
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;
}
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,
&param1, param2, &param1, param2,
&param1, param2, &param1, param2,
&param1, 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,
&param1, param2,
&key_name_length, key_name,
&param1, param2,
&param1, param2,
&param1, 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,
&param1, param2,
&pub_length, (unsigned char *)pub_ecc_key_token,
&param1, param2,
&info_length, (unsigned char *)party_info,
&key_bit_length,
&param1, param2, &param1, param2,
&param1, param2, &param1, param2,
&param1, param2, &param1, 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, };
@@ -1200,3 +1790,4 @@ out:
return rc;
}
+112
View File
@@ -15,6 +15,8 @@
#include <openssl/evp.h>
#include <json-c/json.h>
#include "ekmfweb/ekmfweb.h"
/* CCA PKA Key Generate function */
@@ -80,14 +82,87 @@ typedef void (*CSNDDSG_t)(long *return_code,
long *signature_bit_length,
unsigned char *signature_field);
/* CCA Key Token Build2 function */
typedef void (*CSNBKTB2_t)(long *return_code,
long *reason_code,
long *exit_data_length,
unsigned char *exit_data,
long *rule_array_count,
unsigned char *rule_array,
long *clear_key_bit_length,
unsigned char *clear_key_value,
long *key_name_length,
unsigned char *key_name,
long *user_associated_data_length,
unsigned char *user_associated_data,
long *token_data_length,
unsigned char *token_data,
long *verb_data_length,
unsigned char *verb_data,
long *target_key_token_length,
unsigned char *target_key_token);
/* CCA EC Diffie-Hellman function */
typedef void (*CSNDEDH_t)(long *return_code,
long *reason_code,
long *exit_data_length,
unsigned char *exit_data,
long *rule_array_count,
unsigned char *rule_array,
long *private_key_identifier_length,
unsigned char *private_key_identifier,
long *private_KEK_key_identifier_length,
unsigned char *private_KEK_key_identifier,
long *public_key_identifier_length,
unsigned char *public_key_identifier,
long *chaining_vector_length,
unsigned char *chaining_vector,
long *party_info_length,
unsigned char *party_info,
long *key_bit_length,
long *reserved_1_length,
unsigned char *reserved_1,
long *reserved_2_length,
unsigned char *reserved_2,
long *reserved_3_length,
unsigned char *reserved_3,
long *reserved_4_length,
unsigned char *reserved_4,
long *reserved_5_length,
unsigned char *reserved_5,
long *output_KEK_key_identifier_length,
unsigned char *output_KEK_key_identifier,
long *output_key_identifier_length,
unsigned char *output_key_identifier);
/* CCA Symmetric Key Import2 function */
typedef void (*CSNDSYI2_t)(long *return_code,
long *reason_code,
long *exit_data_length,
unsigned char *exit_data,
long *rule_array_count,
unsigned char *rule_array,
long *enciphered_key_length,
unsigned char *enciphered_key,
long *transport_key_identifier_length,
unsigned char *transport_key_identifier,
long *key_name_length,
unsigned char *key_name,
long *target_key_identifier_length,
unsigned char *target_key_identifier);
struct cca_lib {
CSNDPKB_t dll_CSNDPKB;
CSNDPKG_t dll_CSNDPKG;
CSNDKTC_t dll_CSNDKTC;
CSNDDSG_t dll_CSNDDSG;
CSNBKTB2_t dll_CSNBKTB2;
CSNDEDH_t dll_CSNDEDH;
CSNDSYI2_t dll_CSNDSYI2;
};
#define CCA_MAX_PKA_KEY_TOKEN_SIZE 3500
#define CCA_MAX_SYM_KEY_TOKEN_SIZE 725
int cca_generate_ecc_key_pair(const struct ekmf_cca_lib *cca_lib,
int curve_nid, unsigned char *key_token,
@@ -109,10 +184,47 @@ int cca_get_ecc_pub_key_as_pkey(const unsigned char *key_token,
size_t key_token_length,
EVP_PKEY **pkey, bool verbose);
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);
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);
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);
enum cca_kdf {
CCA_KDF_ANS_X9_63_CCA = 1, /* CCA DERIVE01 method */
CCA_KDF_ANS_X9_63_SHA224 = 2, /* CCA DERIVE02 method with SHA-224 */
CCA_KDF_ANS_X9_63_SHA256 = 3, /* CCA DERIVE02 method with SHA-256 */
CCA_KDF_ANS_X9_63_SHA384 = 4, /* CCA DERIVE02 method with SHA-284 */
CCA_KDF_ANS_X9_63_SHA512 = 5, /* CCA DERIVE02 method with SHA-512 */
};
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);
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);
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,
+994 -5
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -7,5 +7,8 @@ LIBEKMFWEB_1.0 {
ekmf_reencipher_identity_key;
ekmf_generate_csr;
ekmf_generate_ss_cert;
ekmf_get_public_key;
ekmf_retrieve_key;
ekmf_curl_destroy;
local: *;
};
+832
View File
@@ -11,6 +11,7 @@
#include <errno.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
@@ -20,8 +21,14 @@
#include <openssl/rsa.h>
#include <openssl/x509v3.h>
#include "lib/zt_common.h"
#include "utilities.h"
#ifndef JSON_C_TO_STRING_NOSLASHESCAPE
#define JSON_C_TO_STRING_NOSLASHESCAPE (1 << 4)
#endif
/**
* Decodes a Base64URL encoded string. Base64URL is like Base64, but using a
* URL and Filename Safe Alphabet, not using characters like '+', '/', or '='.
@@ -364,6 +371,547 @@ out:
return rc;
}
/**
* Creates a JSON Web Signature object with the specified parts and returns a
* a character string containing the serialized JWS (see RFC 7515 for details)
*
* @param algorithm the JWS algorithm (e.g. ES512) (in the JWS header)
* @param b64 the b64 property of the JWS header. If b64 is true,
* then the payload (if any) is base64url encoded,
* if false, the payload (if any) is used as-is.
* @param kid the Key ID JWS header field (can be NULL)
* @param payload the JWS payload.
* @param payload_len the length of the payload in bytes
* @param detached_payload if true a JWS with detached payload is created (see
* RFC 7515 Appendix F)
* @param md_ctx An OpenSSL MD that has been set up with the desired
* digest and signing algorithm, options, and key
* @param jws On return: a C-string allocated by this function
* containing the serialized JWS. The caller must
* free the memory used by the returned string.
*
* @returns zero for success, a negative errno in case of an error
*/
int create_json_web_signature(const char *algorithm, bool b64, const char *kid,
const unsigned char *payload, size_t payload_len,
bool detached_payload, EVP_MD_CTX *md_ctx,
char **jws)
{
unsigned char *signature = NULL;
json_object *header_obj = NULL;
json_object *crit_obj = NULL;
size_t signature_b64_len = 0;
size_t payload_b64_len = 0;
char *signature_b64 = NULL;
size_t header_b64_len = 0;
size_t signature_len = 0;
char *payload_b64 = NULL;
ECDSA_SIG *ec_sig = NULL;
char *header_b64 = NULL;
const unsigned char *p;
const char *header;
size_t prime_len;
EVP_PKEY *pkey;
int rc;
if (algorithm == NULL || payload == NULL || md_ctx == NULL ||
jws == NULL)
return -EINVAL;
pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(md_ctx));
if (pkey == NULL) {
rc = -EIO;
goto out;
}
header_obj = json_object_new_object();
if (header_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(header_obj, "alg",
json_object_new_string(algorithm), 0);
if (kid != NULL)
rc |= json_object_object_add_ex(header_obj, "kid",
json_object_new_string(kid), 0);
rc |= json_object_object_add_ex(header_obj, "b64",
json_object_new_boolean(b64), 0);
crit_obj = json_object_new_array();
rc |= (crit_obj == NULL ? -1 : 0);
rc |= json_object_array_add(crit_obj, json_object_new_string("b64"));
rc |= json_object_object_add_ex(header_obj, "crit", crit_obj, 0);
crit_obj = NULL;
if (rc != 0) {
rc = -EIO;
goto out;
}
header = json_object_to_json_string_ext(header_obj,
JSON_C_TO_STRING_PLAIN |
JSON_C_TO_STRING_NOSLASHESCAPE);
if (header == NULL) {
rc = -ENOMEM;
goto out;
}
rc = encode_base64url(NULL, &header_b64_len, (unsigned char *)header,
strlen(header));
if (rc != 0)
goto out;
header_b64 = malloc(header_b64_len);
if (header_b64 == NULL) {
rc = -ENOMEM;
goto out;
}
rc = encode_base64url(header_b64, &header_b64_len,
(unsigned char *)header, strlen(header));
if (rc != 0)
goto out;
if (b64) {
rc = encode_base64url(NULL, &payload_b64_len, payload,
payload_len);
if (rc != 0)
goto out;
payload_b64 = malloc(payload_b64_len);
if (payload_b64 == NULL) {
rc = -ENOMEM;
goto out;
}
rc = encode_base64url(payload_b64, &payload_b64_len, payload,
payload_len);
if (rc != 0)
goto out;
}
/* Sign: BASE64URL(UTF8(JWSHeader)) | '.' | [BASE64URL](JWS Payload) */
rc = EVP_DigestSignUpdate(md_ctx, header_b64, strlen(header_b64));
if (rc != 1) {
rc = -EIO;
goto out;
}
rc = EVP_DigestSignUpdate(md_ctx, ".", 1);
if (rc != 1) {
rc = -EIO;
goto out;
}
if (b64)
rc = EVP_DigestSignUpdate(md_ctx, payload_b64,
strlen(payload_b64));
else
rc = EVP_DigestSignUpdate(md_ctx, payload, payload_len);
if (rc != 1) {
rc = -EIO;
goto out;
}
signature_len = EVP_PKEY_size(pkey);
signature = malloc(signature_len);
if (signature == NULL) {
rc = -ENOMEM;
goto out;
}
rc = EVP_DigestSignFinal(md_ctx, signature, &signature_len);
if (rc != 1) {
rc = -EIO;
goto out;
}
switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_EC:
prime_len = ecc_get_curve_prime_length(EC_GROUP_get_curve_name(
EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey))));
p = signature;
if (d2i_ECDSA_SIG(&ec_sig, &p, signature_len) == NULL) {
rc = -EIO;
goto out;
}
if (signature_len < 2 * prime_len) {
rc = -EINVAL;
goto out;
}
memset(signature, 0, signature_len);
BN_bn2binpad(ECDSA_SIG_get0_r(ec_sig), signature, prime_len);
BN_bn2binpad(ECDSA_SIG_get0_s(ec_sig), signature + prime_len,
prime_len);
signature_len = 2 * prime_len;
break;
case EVP_PKEY_RSA:
case EVP_PKEY_RSA_PSS:
/* No signature encoding for RSA */
break;
default:
rc = -EINVAL;
goto out;
}
rc = encode_base64url(NULL, &signature_b64_len, signature,
signature_len);
if (rc != 0)
goto out;
signature_b64 = malloc(signature_b64_len);
if (signature_b64 == NULL) {
rc = -ENOMEM;
goto out;
}
rc = encode_base64url(signature_b64, &signature_b64_len, signature,
signature_len);
if (rc != 0)
goto out;
if (detached_payload) {
if (asprintf(jws, "%s..%s", header_b64, signature_b64) < 0) {
rc = -ENOMEM;
goto out;
}
} else if (b64) {
if (asprintf(jws, "%s.%s.%s", header_b64, payload_b64,
signature_b64) < 0) {
rc = -ENOMEM;
goto out;
}
} else {
if (asprintf(jws, "%s.%.*s.%s", header_b64, (int)payload_len,
payload, signature_b64) < 0) {
rc = -ENOMEM;
goto out;
}
}
rc = 0;
out:
if (header_obj != NULL)
json_object_put(header_obj);
if (header_b64 != NULL)
free(header_b64);
if (payload_b64 != NULL)
free(payload_b64);
if (signature != NULL)
free(signature);
if (signature_b64 != NULL)
free(signature_b64);
if (ec_sig != NULL)
ECDSA_SIG_free(ec_sig);
return rc;
}
/**
* Verifies a JSON Web Signature object (see RFC 7515 for details).
*
* @param jws the JWS string
* @param payload if not NULL: the detached JWS payload.
* @param payload_len the length of the detached payload in bytes
* @param md_ctx An OpenSSL MD that has been set up with the desired
* digest and signing algorithm, options, and key
*
* @returns zero for success, a negative errno in case of an error
*/
int verify_json_web_signature(const char *jws, const unsigned char *payload,
size_t payload_len, EVP_PKEY *pkey)
{
size_t header_len, hdr_pld_len, payload_b64_len, signature_len = 0;
unsigned char *signature = NULL, *der = NULL, *sig = NULL;
json_object *header_obj = NULL, *b64_obj = NULL;
int der_len, rc, curve_nid = 0, digest_nid = 0;
struct ekmf_rsa_pss_params rsa_pss_params;
bool b64 = true, rsa_pss = false;
EVP_MD_CTX *md_ctx = NULL;
EVP_PKEY_CTX *pctx = NULL;
ECDSA_SIG *ec_sig = NULL;
char *payload_b64 = NULL;
const EVP_MD *md = NULL;
BIGNUM *bn_r = NULL;
BIGNUM *bn_s = NULL;
const char *alg;
size_t sig_len;
char *ch;
if (jws == NULL || pkey == NULL)
return -EINVAL;
rc = parse_json_web_token(jws, &header_obj, NULL, &signature,
&signature_len);
if (rc != 0)
goto out;
ch = strchr(jws, '.');
if (ch == NULL) {
rc = -EBADMSG;
goto out;
}
header_len = ch - jws;
ch = strchr(++ch, '.');
if (ch == NULL) {
rc = -EBADMSG;
goto out;
}
hdr_pld_len = ch - jws;
if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) {
curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(
EVP_PKEY_get0_EC_KEY(pkey)));
}
alg = json_get_string(header_obj, "alg");
if (alg == NULL) {
rc = -EIO;
goto out;
}
/*
* Only the following combinations are allowed per RFC7518 for JSON
* Web Signatures (JWS) using ECC or RSA signing keys:
* alg=ES256: ECDSA using P-256 and SHA-256
* alg=ES384: ECDSA using P-384 and SHA-384
* alg=ES512: ECDSA using P-521 and SHA-512
* alg=RS256: RSA-PKCS1 using SHA-256
* alg=RS384: RSA-PKCS1 using SHA-384
* alg=RS512: RSA-PKCS1 using SHA-512
* alg=PS256: RSA-PSS using SHA-256, MGF1 with SHA-256, salt=digest
* alg=PS384: RSA-PSS using SHA-384, MGF1 with SHA-384, salt=digest
* alg=PS512: RSA-PSS using SHA-512, MGF1 with SHA-512, salt=digest
*/
if (strncmp(alg, "ES", 2) == 0) {
if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) {
rc = EINVAL;
goto out;
}
if ((strncmp(alg + 2, "512", 3) == 0 &&
curve_nid != NID_secp521r1) ||
(strncmp(alg + 2, "384", 3) == 0 &&
curve_nid != NID_secp384r1) ||
(strncmp(alg + 2, "256", 3) == 0 &&
curve_nid != NID_X9_62_prime256v1)) {
rc = EINVAL;
goto out;
}
} else if (strncmp(alg, "RS", 2) == 0) {
if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
rc = EINVAL;
goto out;
}
} else if (strncmp(alg, "PS", 2) == 0) {
if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA &&
EVP_PKEY_id(pkey) != EVP_PKEY_RSA_PSS) {
rc = EINVAL;
goto out;
}
rsa_pss = true;
} else {
rc = -ENOTSUP;
goto out;
}
if (strncmp(alg + 2, "512", 3) == 0)
digest_nid = NID_sha512;
else if (strncmp(alg + 2, "384", 3) == 0)
digest_nid = NID_sha384;
else if (strncmp(alg + 2, "256", 3) == 0)
digest_nid = NID_sha256;
if (digest_nid == 0) {
rc = -ENOTSUP;
goto out;
}
md = EVP_get_digestbynid(digest_nid);
if (md == NULL) {
rc = -ENOTSUP;
goto out;
}
md_ctx = EVP_MD_CTX_new();
if (md_ctx == NULL) {
rc = -ENOMEM;
goto out;
}
rc = EVP_DigestVerifyInit(md_ctx, &pctx, md, NULL, pkey);
if (rc != 1) {
rc = -EIO;
goto out;
}
if (rsa_pss) {
rsa_pss_params.mgf_digest_nid = digest_nid;
rsa_pss_params.salt_len = RSA_PSS_SALTLEN_DIGEST;
rc = setup_rsa_pss_pkey_context(pctx, &rsa_pss_params);
if (rc != 0)
goto out;
}
switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_EC:
ec_sig = ECDSA_SIG_new();
if (ec_sig == NULL) {
rc = -ENOMEM;
goto out;
}
bn_r = BN_bin2bn(signature, signature_len / 2, NULL);
bn_s = BN_bin2bn(signature + signature_len / 2,
signature_len / 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, &der);
if (der_len <= 0) {
rc = -EIO;
goto out;
}
sig = der;
sig_len = der_len;
break;
case EVP_PKEY_RSA:
case EVP_PKEY_RSA_PSS:
/* No signature encoding for RSA */
sig = signature;
sig_len = signature_len;
break;
default:
rc = -EINVAL;
goto out;
}
if (payload != NULL && payload_len > 0) {
/* Detached payload */
if (json_object_object_get_ex(header_obj, "b64", &b64_obj) &&
json_object_is_type(b64_obj, json_type_boolean))
b64 = json_object_get_boolean(b64_obj);
if (b64) {
rc = encode_base64url(NULL, &payload_b64_len, payload,
payload_len);
if (rc != 0)
goto out;
payload_b64 = malloc(payload_b64_len);
if (payload_b64 == NULL) {
rc = -ENOMEM;
goto out;
}
rc = encode_base64url(payload_b64, &payload_b64_len,
payload, payload_len);
if (rc != 0)
goto out;
}
/* Take header plus '.' as is */
rc = EVP_DigestVerifyUpdate(md_ctx, jws, header_len + 1);
if (rc != 1) {
rc = -EIO;
goto out;
}
if (b64)
rc = EVP_DigestVerifyUpdate(md_ctx, payload_b64,
payload_b64_len);
else
rc = EVP_DigestVerifyUpdate(md_ctx, payload,
payload_len);
if (rc != 1) {
rc = -EIO;
goto out;
}
} else {
/* Take header plus '.' plus payload as is */
rc = EVP_DigestVerifyUpdate(md_ctx, jws, hdr_pld_len);
if (rc != 1) {
rc = -EIO;
goto out;
}
}
rc = EVP_DigestVerifyFinal(md_ctx, sig, sig_len);
if (rc != 1) {
rc = -EIO;
goto out;
}
rc = 0;
out:
if (header_obj != NULL)
json_object_put(header_obj);
if (signature != NULL)
free(signature);
if (payload_b64 != NULL)
free(payload_b64);
if (ec_sig != NULL)
ECDSA_SIG_free(ec_sig);
if (der != NULL)
OPENSSL_free(der);
if (bn_r != NULL)
BN_free(bn_r);
if (bn_s != NULL)
BN_free(bn_s);
if (md_ctx != NULL)
EVP_MD_CTX_free(md_ctx);
return rc;
}
/**
* Builds a JSON Object containing a timestamp value in ISO 8601 format, e.g.
* { "timestamp": "2020-04-27T10:02:18.123Z" }. The time is expressed in UTC,
* regardless of the local time zone.
*
* @returns a JSON object containing the timestamp, or NULL in case of an error.
*/
json_object *get_json_timestamp(void)
{
char timestamp[100];
struct timeval tv;
struct tm *tm;
char temp[20];
if (gettimeofday(&tv, NULL) != 0)
return NULL;
tm = gmtime(&tv.tv_sec);
if (strftime(timestamp, sizeof(timestamp), "%FT%T", tm) == 0)
return NULL;
snprintf(temp, sizeof(temp), ".%06ldZ", tv.tv_usec);
strcat(timestamp, temp);
return json_object_new_string(timestamp);
}
struct ecc_curve_info {
int curve_nid;
enum {
@@ -789,6 +1337,125 @@ out:
return rc;
}
/**
* Converts a JSON Web Key (ECC or RSA) into a OpenSSL PKEY
*
* @param jwk The JSON Web Key to convert
* @param pkey_type If the JWK contains an RSA key, then the pkey_type
* can be EVP_PKEY_RSA or EVP_PKEY_RSA_PSS
* @param pkey On return: the OpenSSL PKEY
*
* @returns zero for success, a negative errno in case of an error
*/
int json_web_key_as_pkey(json_object *jwk, int pkey_type, EVP_PKEY **pkey)
{
unsigned char *x = NULL, *y = NULL, *n = NULL, *e = NULL;
size_t prime_len, len, n_len, e_len;
const char *kty, *crv;
int nid, rc = 0;
if (jwk == NULL || pkey == NULL)
return -EINVAL;
*pkey = NULL;
kty = json_get_string(jwk, "kty");
if (kty == NULL) {
rc = -EIO;
goto out;
}
if (strcmp(kty, "EC") == 0) {
crv = json_get_string(jwk, "crv");
if (crv == NULL) {
rc = -EIO;
goto out;
}
nid = ecc_get_curve_by_id(crv);
if (nid == 0) {
rc = -EIO;
goto out;
}
prime_len = ecc_get_curve_prime_length(nid);
if (prime_len == 0) {
rc = -EIO;
goto out;
}
x = malloc(prime_len);
y = malloc(prime_len);
if (x == NULL || y == NULL) {
rc = -ENOMEM;
goto out;
}
len = prime_len;
rc = json_object_get_base64url(jwk, "x", x, &len);
if (rc != 0)
goto out;
len = prime_len;
rc = json_object_get_base64url(jwk, "y", y, &len);
if (rc != 0)
goto out;
rc = ecc_pub_key_as_pkey(nid, prime_len, x, y, pkey);
if (rc != 0)
goto out;
} else if (strcmp(kty, "RSA") == 0) {
n_len = 0;
rc = json_object_get_base64url(jwk, "n", NULL, &n_len);
if (rc != 0)
goto out;
n = malloc(n_len);
if (n == NULL) {
rc = -ENOMEM;
goto out;
}
rc = json_object_get_base64url(jwk, "n", n, &n_len);
if (rc != 0)
goto out;
e_len = 0;
rc = json_object_get_base64url(jwk, "e", NULL, &e_len);
if (rc != 0)
goto out;
e = malloc(e_len);
if (e == NULL) {
rc = -ENOMEM;
goto out;
}
rc = json_object_get_base64url(jwk, "e", e, &e_len);
if (rc != 0)
goto out;
rc = rsa_pub_key_as_pkey(n, n_len, e, e_len, pkey_type, pkey);
if (rc != 0)
goto out;
} else {
return -EIO;
}
out:
if (x != NULL)
free(x);
if (y != NULL)
free(y);
if (n != NULL)
free(n);
if (e != NULL)
free(e);
return rc;
}
/**
* Write a secure key blob to the specified file.
*
@@ -982,6 +1649,71 @@ int write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr)
return 0;
}
/**
* Reads a public key from the specified PEM file.
*
* @param pem_filename the name of the PEM file to read
* @param pkey on Return: the PKEY object
*
* @returns zero for success, a negative errno in case of an error:
* -EINVAL: invalid parameter
* -EIO: error during reading in the certificate
* any other errno as returned by fopen
*/
int read_public_key(const char *pem_filename, EVP_PKEY **pkey)
{
FILE *fp;
if (pem_filename == NULL || pkey == NULL)
return -EINVAL;
fp = fopen(pem_filename, "r");
if (fp == NULL)
return -errno;
*pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
fclose(fp);
if (*pkey == NULL)
return -EIO;
return 0;
}
/**
* Writes apublic key to the specified PEM file.
*
* @param pem_filename the name of the PEM file to write to
* @param pkey the PKEY object to write
*
* @returns zero for success, a negative errno in case of an error:
* -EINVAL: invalid parameter
* -EIO: error during writing out the certificate
* any other errno as returned by fopen
*/
int write_public_key(const char *pem_filename, EVP_PKEY *pkey)
{
FILE *fp;
int rc;
if (pem_filename == NULL || pkey == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
if (fp == NULL)
return -errno;
rc = PEM_write_PUBKEY(fp, pkey);
fclose(fp);
if (rc != 1)
return -EIO;
return 0;
}
/* Secure key PKEY context control */
#define EVP_PKEY_CTRL_SK_KEY_BLOB 0x10000001
#define EVP_PKEY_CTRL_SK_SIGN_FUNCTIONS 0x10000002
@@ -1835,3 +2567,103 @@ out:
return rc;
}
/**
* Gets a String field from a JSON object.
*
* @param obj the JSON object
* @param name the name of the String field to get
*
* @returns the contents of the String field or NULL.
* Note: The memory returned is owned by the JSON object, and must not be freed
* by the caller. It is valid until the JSON object is freed, which also
* frees the memory used for the string value.
*/
const char *json_get_string(json_object *obj, const char *name)
{
json_object *field;
if (!json_object_object_get_ex(obj, name, &field) ||
!json_object_is_type(field, json_type_string))
return NULL;
return json_object_get_string(field);
}
/**
* Gets a base64url field form a JSON object, decodes it and returns the
* decoded data.
*
* @param obj the JSON object
* @param name the name of the String field to get
* @param data buffer to return the decoded data, or NULL to
* return only the required buffer size
* @param data_len on entry: the size of tne buffer
* on exit: the size of the decoded data
* @returns zero for success, a negative errno in case of an error:
* -EINVAL: a function parameter is invalid
* -ENOMEM: failed to allocate memory
* -EIO: OpenSSL failed to calculate the y coordinate
*/
int json_object_get_base64url(json_object *obj, const char *name,
unsigned char *data, size_t *data_len)
{
const char *b64;
b64 = json_get_string(obj, name);
if (b64 == NULL)
return -ENOENT;
return decode_base64url(data, data_len, b64, strlen(b64));
}
/**
* Base64URL encodes the data and creates a JSON string object of it
*
* @param data the data to base64url encode
* @param len the length of the data
* @returns a new JSON object, or NULL in case of an error
*/
json_object *json_object_new_base64url(const unsigned char *data, size_t len)
{
json_object *ret = NULL;
char *b64 = NULL;
size_t b64len;
int rc;
rc = encode_base64url(NULL, &b64len, data, len);
if (rc != 0)
goto out;
b64 = malloc(b64len);
if (b64 == NULL)
goto out;
rc = encode_base64url(b64, &b64len, data, len);
if (rc != 0)
goto out;
ret = json_object_new_string(b64);
out:
if (b64 != NULL)
free(b64);
return ret;
}
#ifdef IMPLEMENT_LOCAL_JSON_OBJECT_OBJECT_ADD
/**
* JSON-C of version 0.12 does not have json_object_object_add_ex(), and
* json_object_object_add does not return a return code, so implement
* json_object_object_add_ex here instead.
*/
int json_object_object_add_ex(struct json_object *obj, const char *const key,
struct json_object *const val,
const unsigned int UNUSED(opts))
{
json_object_object_add(obj, key, val);
return 0;
}
#endif
+32
View File
@@ -31,6 +31,16 @@ int parse_json_web_token(const char *token, json_object **header_obj,
json_object **payload_obj, unsigned char **signature,
size_t *signature_len);
int create_json_web_signature(const char *algorithm, bool b64, const char *kid,
const unsigned char *payload, size_t payload_len,
bool detached_payload, EVP_MD_CTX *md_ctx,
char **jws);
int verify_json_web_signature(const char *jws, const unsigned char *payload,
size_t payload_len, EVP_PKEY *pkey);
json_object *get_json_timestamp(void);
size_t ecc_get_curve_prime_bits(int curve_nid);
size_t ecc_get_curve_prime_length(int curve_nid);
const char *ecc_get_curve_id(int curve_nid);
@@ -51,6 +61,8 @@ int rsa_pub_key_as_pkey(const unsigned char *modulus, size_t modulus_length,
const unsigned char *pub_exp, size_t pub_exp_length,
int pkey_type, EVP_PKEY **pkey);
int json_web_key_as_pkey(json_object *jwk, int pkey_type, EVP_PKEY **pkey);
int write_key_blob(const char *filename, unsigned char *key_blob,
size_t key_blob_len);
@@ -63,6 +75,10 @@ int write_x509_certificate(const char *pem_filename, X509 *cert);
int write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr);
int read_public_key(const char *pem_filename, EVP_PKEY **pkey);
int write_public_key(const char *pem_filename, EVP_PKEY *pkey);
typedef int (*rsa_sign_t)(const unsigned char *key_blob, size_t key_blob_length,
unsigned char *sig, size_t *siglen,
const unsigned char *tbs, size_t tbslen,
@@ -104,4 +120,20 @@ int build_certificate_extensions(X509 *cert, X509_REQ *req,
int generate_x509_serial_number(X509 *cert, size_t sn_bit_size);
const char *json_get_string(json_object *obj, const char *name);
int json_object_get_base64url(json_object *obj, const char *name,
unsigned char *data, size_t *data_len);
json_object *json_object_new_base64url(const unsigned char *data, size_t len);
#ifndef JSON_C_OBJECT_ADD_KEY_IS_NEW
#define JSON_C_OBJECT_ADD_KEY_IS_NEW (1 << 1)
#define IMPLEMENT_LOCAL_JSON_OBJECT_OBJECT_ADD
int json_object_object_add_ex(struct json_object *obj, const char *const key,
struct json_object *const val,
const unsigned int opts);
#endif
#endif