mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
With OpenSSL 4.0.0 function X509_NAME_get_entry() returns a const pointer. Make the local variable also const to avoid compile warnings like: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
1064 lines
28 KiB
C
1064 lines
28 KiB
C
/*
|
|
* libseckey - Secure key library
|
|
*
|
|
* Copyright IBM Corp. 2021
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <openssl/evp.h>
|
|
#include <openssl/sha.h>
|
|
#include <openssl/bn.h>
|
|
#include <openssl/ec.h>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/pem.h>
|
|
|
|
#include "lib/zt_common.h"
|
|
|
|
#include "libseckey/sk_utilities.h"
|
|
#include "libseckey/sk_ep11.h"
|
|
|
|
void SK_UTIL_warnx(const char *func, const char *fmt, ...)
|
|
{
|
|
char tmp_fmt[200];
|
|
va_list ap;
|
|
|
|
if (snprintf(tmp_fmt, sizeof(tmp_fmt), "DBG: %s: %s", func, fmt) >
|
|
(int)sizeof(tmp_fmt))
|
|
return;
|
|
|
|
va_start(ap, fmt);
|
|
vwarnx(tmp_fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static const unsigned char der_prime192v1[] = {
|
|
0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01
|
|
};
|
|
static const unsigned char der_secp224r1[] = {
|
|
0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x21
|
|
};
|
|
static const unsigned char der_prime256v1[] = {
|
|
0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07
|
|
};
|
|
static const unsigned char der_secp384r1[] = {
|
|
0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22
|
|
};
|
|
static const unsigned char der_secp521r1[] = {
|
|
0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23
|
|
};
|
|
static const unsigned char der_brainpoolP160r1[] = {
|
|
0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x01
|
|
};
|
|
static const unsigned char der_brainpoolP192r1[] = {
|
|
0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x03
|
|
};
|
|
static const unsigned char der_brainpoolP224r1[] = {
|
|
0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x05
|
|
};
|
|
static const unsigned char der_brainpoolP256r1[] = {
|
|
0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07
|
|
};
|
|
static const unsigned char der_brainpoolP320r1[] = {
|
|
0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x09
|
|
};
|
|
static const unsigned char der_brainpoolP384r1[] = {
|
|
0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B
|
|
};
|
|
static const unsigned char der_brainpoolP512r1[] = {
|
|
0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D
|
|
};
|
|
|
|
static const struct sk_ec_curve_info ec_curve_list[] = {
|
|
{ .curve_nid = NID_X9_62_prime192v1, .type = SK_EC_TYPE_PRIME,
|
|
.prime_bits = 192, .prime_len = 24, .der = der_prime192v1,
|
|
.der_size = sizeof(der_prime192v1) },
|
|
{ .curve_nid = NID_secp224r1, .type = SK_EC_TYPE_PRIME,
|
|
.prime_bits = 224, .prime_len = 28, .der = der_secp224r1,
|
|
.der_size = sizeof(der_secp224r1)},
|
|
{ .curve_nid = NID_X9_62_prime256v1, .type = SK_EC_TYPE_PRIME,
|
|
.prime_bits = 256, .prime_len = 32, .der = der_prime256v1,
|
|
.der_size = sizeof(der_prime256v1)},
|
|
{ .curve_nid = NID_secp384r1, .type = SK_EC_TYPE_PRIME,
|
|
.prime_bits = 384, .prime_len = 48, .der = der_secp384r1,
|
|
.der_size = sizeof(der_secp384r1)},
|
|
{ .curve_nid = NID_secp521r1, .type = SK_EC_TYPE_PRIME,
|
|
.prime_bits = 521, .prime_len = 66, .der = der_secp521r1,
|
|
.der_size = sizeof(der_secp521r1)},
|
|
{ .curve_nid = NID_brainpoolP160r1, .type = SK_EC_TYPE_BRAINPOOL,
|
|
.prime_bits = 160, .prime_len = 20, .der = der_brainpoolP160r1,
|
|
.der_size = sizeof(der_brainpoolP160r1)},
|
|
{ .curve_nid = NID_brainpoolP192r1, .type = SK_EC_TYPE_BRAINPOOL,
|
|
.prime_bits = 192, .prime_len = 24, .der = der_brainpoolP192r1,
|
|
.der_size = sizeof(der_brainpoolP192r1)},
|
|
{ .curve_nid = NID_brainpoolP224r1, .type = SK_EC_TYPE_BRAINPOOL,
|
|
.prime_bits = 224, .prime_len = 28, .der = der_brainpoolP224r1,
|
|
.der_size = sizeof(der_brainpoolP224r1)},
|
|
{ .curve_nid = NID_brainpoolP256r1, .type = SK_EC_TYPE_BRAINPOOL,
|
|
.prime_bits = 256, .prime_len = 32, .der = der_brainpoolP256r1,
|
|
.der_size = sizeof(der_brainpoolP256r1)},
|
|
{ .curve_nid = NID_brainpoolP320r1, .type = SK_EC_TYPE_BRAINPOOL,
|
|
.prime_bits = 320, .prime_len = 40, .der = der_brainpoolP320r1,
|
|
.der_size = sizeof(der_brainpoolP320r1)},
|
|
{ .curve_nid = NID_brainpoolP384r1, .type = SK_EC_TYPE_BRAINPOOL,
|
|
.prime_bits = 384, .prime_len = 48, .der = der_brainpoolP384r1,
|
|
.der_size = sizeof(der_brainpoolP384r1)},
|
|
{ .curve_nid = NID_brainpoolP512r1, .type = SK_EC_TYPE_BRAINPOOL,
|
|
.prime_bits = 512, .prime_len = 64, .der = der_brainpoolP512r1,
|
|
.der_size = sizeof(der_brainpoolP512r1)},
|
|
};
|
|
|
|
static const int ec_curve_num =
|
|
sizeof(ec_curve_list) / sizeof(struct sk_ec_curve_info);
|
|
|
|
/**
|
|
* Returns the curve info of the specified curve, or NULL if the curve
|
|
* is not known.
|
|
*
|
|
* @param nid the OpenSSL nid of the EC curve
|
|
*
|
|
* @returns the address of the curve info or NULL if the curve was not found
|
|
*/
|
|
const struct sk_ec_curve_info *SK_UTIL_ec_get_curve_info(int curve_nid)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ec_curve_num; i++) {
|
|
if (ec_curve_list[i].curve_nid == curve_nid)
|
|
return &ec_curve_list[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Returns the nid of the Prime curve by its specified prime bit size, or 0
|
|
* if the curve is not known.
|
|
*
|
|
* @param prime_bits the prime bit size of the curve to search for
|
|
*
|
|
* @returns the OpenSSL nid of the EC curve or 0 if the curve was not found
|
|
*/
|
|
int SK_UTIL_ec_get_prime_curve_by_prime_bits(size_t prime_bits)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ec_curve_num; i++) {
|
|
if (ec_curve_list[i].type == SK_EC_TYPE_PRIME &&
|
|
ec_curve_list[i].prime_bits == prime_bits)
|
|
return ec_curve_list[i].curve_nid;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the nid of the Brainpool curve by its specified prime bit size, or 0
|
|
* if the curve is not known.
|
|
*
|
|
* @param prime_bits the prime bit size of the curve to search for
|
|
*
|
|
* @returns the OpenSSL nid of the EC curve or 0 if the curve was not found
|
|
*/
|
|
int SK_UTIL_ec_get_brainpool_curve_by_prime_bits(size_t prime_bits)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ec_curve_num; i++) {
|
|
if (ec_curve_list[i].type == SK_EC_TYPE_BRAINPOOL &&
|
|
ec_curve_list[i].prime_bits == prime_bits)
|
|
return ec_curve_list[i].curve_nid;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Calculates the y coordinate of a point on an EC curve using the x coordinate
|
|
* and the y bit. x and y must be supplied by the caller with prime_len bytes.
|
|
* On return y contains the calculated y coordinate.
|
|
*
|
|
* @param nid the OpenSSL nid of the EC curve used
|
|
* @param prime_len the length of the prime in bytes. This is also the
|
|
* length of the x and y coordinates.
|
|
* @param x the x coordinate as big endian binary number in
|
|
* prime_len size
|
|
* @param y_bit the y-bit to identify which of the two possible
|
|
* values for y should be used
|
|
* @param y buffer to store the y coordinate as big endian
|
|
* binary number in prime_len size.
|
|
* @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
|
|
* -ENOENT: OpenSSL does not know/support the curve (nid)
|
|
*/
|
|
int SK_UTIL_ec_calculate_y_coordinate(int nid, size_t prime_len,
|
|
const unsigned char *x, int y_bit,
|
|
unsigned char *y)
|
|
{
|
|
EC_GROUP *group = NULL;
|
|
EC_POINT *point = NULL;
|
|
BIGNUM *bn_x = NULL;
|
|
BIGNUM *bn_y = NULL;
|
|
BN_CTX *ctx = NULL;
|
|
int rc = 0;
|
|
|
|
if (x == NULL || y == NULL)
|
|
return -EINVAL;
|
|
|
|
bn_x = BN_bin2bn(x, prime_len, NULL);
|
|
if (bn_x == NULL) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
group = EC_GROUP_new_by_curve_name(nid);
|
|
if (group == NULL) {
|
|
rc = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
point = EC_POINT_new(group);
|
|
if (point == NULL) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
bn_y = BN_new();
|
|
if (bn_y == NULL) {
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
ctx = BN_CTX_new();
|
|
if (ctx == NULL) {
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
if (!EC_POINT_set_compressed_coordinates(group, point, bn_x,
|
|
y_bit, ctx)) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
if (!EC_POINT_is_on_curve(group, point, ctx)) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y,
|
|
ctx)) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
if (BN_bn2binpad(bn_y, y, prime_len) <= 0) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (ctx != NULL)
|
|
BN_CTX_free(ctx);
|
|
if (point != NULL)
|
|
EC_POINT_free(point);
|
|
if (group != NULL)
|
|
EC_GROUP_free(group);
|
|
if (bn_x != NULL)
|
|
BN_free(bn_x);
|
|
if (bn_y != NULL)
|
|
BN_free(bn_y);
|
|
|
|
return rc;
|
|
}
|
|
|
|
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, };
|
|
|
|
static const struct sk_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),
|
|
.pkcs11_mech = CKM_SHA_1, .pkcs11_mgf = CKG_MGF1_SHA1,
|
|
.x9_31_md = 0x33, },
|
|
{ .digest_nid = NID_sha224, .digest_size = SHA224_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-224 ", .der = der_DigestInfo_SHA224,
|
|
.der_size = sizeof(der_DigestInfo_SHA224),
|
|
.pkcs11_mech = CKM_SHA224, .pkcs11_mgf = CKG_MGF1_SHA224,
|
|
.x9_31_md = 0, },
|
|
{ .digest_nid = NID_sha256, .digest_size = SHA256_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-256 ", .der = der_DigestInfo_SHA256,
|
|
.der_size = sizeof(der_DigestInfo_SHA256),
|
|
.pkcs11_mech = CKM_SHA256, .pkcs11_mgf = CKG_MGF1_SHA256,
|
|
.x9_31_md = 0x34, },
|
|
{ .digest_nid = NID_sha384, .digest_size = SHA384_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-384 ", .der = der_DigestInfo_SHA384,
|
|
.der_size = sizeof(der_DigestInfo_SHA384),
|
|
.pkcs11_mech = CKM_SHA384, .pkcs11_mgf = CKG_MGF1_SHA384,
|
|
.x9_31_md = 0x36, },
|
|
{ .digest_nid = NID_sha512, .digest_size = SHA512_DIGEST_LENGTH,
|
|
.cca_keyword = "SHA-512 ", .der = der_DigestInfo_SHA512,
|
|
.der_size = sizeof(der_DigestInfo_SHA512),
|
|
.pkcs11_mech = CKM_SHA512, .pkcs11_mgf = CKG_MGF1_SHA512,
|
|
.x9_31_md = 0x35, },
|
|
{ .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),
|
|
.pkcs11_mech = CKM_IBM_SHA3_224, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_224,
|
|
.x9_31_md = 0, },
|
|
{ .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),
|
|
.pkcs11_mech = CKM_IBM_SHA3_256, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_256,
|
|
.x9_31_md = 0, },
|
|
{ .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),
|
|
.pkcs11_mech = CKM_IBM_SHA3_384, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_384,
|
|
.x9_31_md = 0, },
|
|
{ .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),
|
|
.pkcs11_mech = CKM_IBM_SHA3_512, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_512,
|
|
.x9_31_md = 0, },
|
|
};
|
|
|
|
static const int digest_list_num = sizeof(digest_list) /
|
|
sizeof(struct sk_digest_info);
|
|
|
|
/**
|
|
* Returns the digest info of the specified digest nid, or NULL if the digest
|
|
* is not known.
|
|
*
|
|
* @param nid the OpenSSL nid of the digest
|
|
*
|
|
* @returns the address of the digest info or NULL if the digest was not found
|
|
*/
|
|
const struct sk_digest_info *SK_UTIL_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;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Checks if an exact duplicate of the name entry is part of the name already.
|
|
*/
|
|
static bool SK_UTILS_is_duplicate_name_entry(const X509_NAME *name,
|
|
const X509_NAME_ENTRY *entry)
|
|
{
|
|
const X509_NAME_ENTRY *ne;
|
|
int count, i;
|
|
|
|
count = X509_NAME_entry_count(name);
|
|
for (i = 0; i < count; i++) {
|
|
ne = X509_NAME_get_entry(name, i);
|
|
if (ne == NULL)
|
|
break;
|
|
|
|
if (OBJ_cmp(X509_NAME_ENTRY_get_object(entry),
|
|
X509_NAME_ENTRY_get_object(ne)) == 0 &&
|
|
ASN1_STRING_cmp(X509_NAME_ENTRY_get_data(entry),
|
|
X509_NAME_ENTRY_get_data(ne)) == 0)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Parse an array of relative distinguished names and builds an X.509 subject
|
|
* name. The RDNs are created with type MBSTRING_ASC, unless utf8 is requested,
|
|
* then they are created with MBSTRING_UTF8.
|
|
* To create a multiple-RDS name, prepend the RDS to add to the previous RDS
|
|
* with a '+' character.
|
|
*
|
|
* @param name the X.509 name created. If *name is not NULL, then
|
|
* the RDNs are added to the existing X.509 name.
|
|
* @param rdns an array of strings, each string representing an
|
|
* RDN in the form '[+]type=value'. If the type is
|
|
* prepended with a '+', then this RDN is added to the
|
|
* previous one.
|
|
* @param num_rdns number of elements in the array.
|
|
* @param utf8 if true, RDNs of type MBSTRING_UTF8 are created,
|
|
* otherwise type is MBSTRING_ASC is used.
|
|
*
|
|
* @returns zero for success, a negative errno in case of an error:
|
|
* -EINVAL: invalid parameter
|
|
* -EBADMSG: an RDN is not formatted correctly
|
|
* -EIO: OpenSSL failed to create an X.509 name entry
|
|
* -EEXIST: if one of the name entries to add is a duplicate
|
|
*/
|
|
int SK_UTIL_build_subject_name(X509_NAME **name, const char *rdns[],
|
|
size_t num_rdns, bool utf8)
|
|
{
|
|
char *rdn, *type, *value;
|
|
X509_NAME_ENTRY *ne;
|
|
X509_NAME *n;
|
|
int rc = 0;
|
|
bool multi;
|
|
size_t i;
|
|
|
|
if (name == NULL || rdns == NULL)
|
|
return -EINVAL;
|
|
|
|
if (*name != NULL)
|
|
n = *name;
|
|
else
|
|
n = X509_NAME_new();
|
|
if (n == NULL)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_rdns; i++) {
|
|
if (rdns[i] == NULL) {
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
rdn = strdup(rdns[i]);
|
|
if (rdn == NULL) {
|
|
rc = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
multi = (rdn[0] == '+');
|
|
type = &rdn[multi ? 1 : 0];
|
|
|
|
for (value = type; *value != '=' && *value != '\0'; value++)
|
|
;
|
|
if (*value != '=') {
|
|
rc = -EBADMSG;
|
|
free(rdn);
|
|
break;
|
|
}
|
|
*value = '\0';
|
|
value++;
|
|
|
|
ne = X509_NAME_ENTRY_create_by_txt(NULL, type,
|
|
utf8 ? MBSTRING_UTF8 :
|
|
MBSTRING_ASC,
|
|
(unsigned char *)value, -1);
|
|
if (ne == NULL) {
|
|
rc = -EBADMSG;
|
|
free(rdn);
|
|
break;
|
|
}
|
|
|
|
if (SK_UTILS_is_duplicate_name_entry(n, ne)) {
|
|
rc = -EEXIST;
|
|
X509_NAME_ENTRY_free(ne);
|
|
free(rdn);
|
|
break;
|
|
}
|
|
|
|
rc = X509_NAME_add_entry(n, ne, -1, multi ? -1 : 0);
|
|
|
|
free(rdn);
|
|
X509_NAME_ENTRY_free(ne);
|
|
|
|
if (rc != 1) {
|
|
rc = -EIO;
|
|
break;
|
|
}
|
|
rc = 0;
|
|
}
|
|
|
|
if (rc == 0)
|
|
*name = n;
|
|
else if (*name == NULL)
|
|
X509_NAME_free(n);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Compares X509 Extensions by their nid
|
|
*/
|
|
static int X509_EXTENSION_compfunc(const X509_EXTENSION * const *a,
|
|
const X509_EXTENSION * const *b)
|
|
{
|
|
|
|
return (OBJ_obj2nid(X509_EXTENSION_get_object((X509_EXTENSION *)*a)) -
|
|
OBJ_obj2nid(X509_EXTENSION_get_object((X509_EXTENSION *)*b)));
|
|
}
|
|
|
|
/**
|
|
* Parse an array of textual X.509 certificate extensions and adds them to
|
|
* either an X.509 certificate signing request, or an X.509 certificate.
|
|
*
|
|
* When adding extensions, a check is performed if an extension with the same
|
|
* nid is already added. If so, a duplicate extension is not added, even if
|
|
* its value is different from the existing one.
|
|
*
|
|
* @param cert the X.509 certificate to add the extensions to.
|
|
* Either req or cert can be specified.
|
|
* @param req the X.509 certificate signing request to add the
|
|
* extensions to. Either req or cert can be specified.
|
|
* @param exts an array of strings, each string representing an
|
|
* certificate extension in the form 'type=value'.
|
|
* can be NULL if num_exts is zero.
|
|
* @param num_exts number of elements in the array.
|
|
* @param addl_exts a stack of extensions to add (can be NULL)
|
|
*
|
|
* @returns zero for success, a negative errno in case of an error:
|
|
* -EINVAL: invalid parameter
|
|
* -EBADMSG: an extension is not formatted correctly
|
|
* -EIO: OpenSSL failed to create an X.509 extension
|
|
* -EEXIST: if one of the extensions to add is a duplicate
|
|
*/
|
|
int SK_UTIL_build_certificate_extensions(X509 *cert, X509_REQ *req,
|
|
const char *exts[], size_t num_exts,
|
|
const STACK_OF(X509_EXTENSION)
|
|
*addl_exts)
|
|
{
|
|
STACK_OF(X509_EXTENSION) *sk_ext;
|
|
char *ext, *type, *value;
|
|
X509V3_CTX x509v3_ctx;
|
|
int count, k, rc = 0;
|
|
X509_EXTENSION *ex;
|
|
size_t i;
|
|
|
|
if (num_exts > 0 && exts == NULL)
|
|
return -EINVAL;
|
|
if (cert == NULL && req == NULL)
|
|
return -EINVAL;
|
|
if (cert != NULL && req != NULL)
|
|
return -EINVAL;
|
|
|
|
sk_ext = sk_X509_EXTENSION_new_null();
|
|
if (sk_ext == NULL)
|
|
return -ENOMEM;
|
|
|
|
sk_X509_EXTENSION_set_cmp_func(sk_ext, X509_EXTENSION_compfunc);
|
|
|
|
for (i = 0; exts != NULL && i < num_exts; i++) {
|
|
if (exts[i] == NULL) {
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
ext = strdup(exts[i]);
|
|
if (ext == NULL) {
|
|
rc = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
type = &ext[0];
|
|
|
|
for (value = type; *value != '=' && *value != '\0'; value++)
|
|
;
|
|
if (*value != '=') {
|
|
rc = -EBADMSG;
|
|
free(ext);
|
|
break;
|
|
}
|
|
*value = '\0';
|
|
value++;
|
|
|
|
rc = -EBADMSG;
|
|
ex = X509V3_EXT_conf(NULL, NULL, type, value);
|
|
if (ex != NULL) {
|
|
if (sk_X509_EXTENSION_find(sk_ext, ex) >= 0) {
|
|
rc = -EEXIST;
|
|
X509_EXTENSION_free(ex);
|
|
free(ext);
|
|
break;
|
|
}
|
|
|
|
rc = sk_X509_EXTENSION_push(sk_ext, ex);
|
|
if (rc < 1) {
|
|
rc = -EIO;
|
|
X509_EXTENSION_free(ex);
|
|
free(ext);
|
|
break;
|
|
}
|
|
rc = 0;
|
|
}
|
|
|
|
free(ext);
|
|
}
|
|
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (addl_exts != NULL) {
|
|
count = sk_X509_EXTENSION_num(addl_exts);
|
|
for (k = 0; k < count; k++) {
|
|
ex = sk_X509_EXTENSION_value(addl_exts, k);
|
|
if (ex != NULL) {
|
|
if (sk_X509_EXTENSION_find(sk_ext, ex) >= 0) {
|
|
rc = -EEXIST;
|
|
break;
|
|
}
|
|
|
|
rc = sk_X509_EXTENSION_push(sk_ext,
|
|
X509_EXTENSION_dup(ex));
|
|
if (rc < 1) {
|
|
rc = -EIO;
|
|
break;
|
|
}
|
|
rc = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (req != NULL && sk_X509_EXTENSION_num(sk_ext) > 0) {
|
|
if (X509_REQ_add_extensions(req, sk_ext) != 1)
|
|
rc = -EIO;
|
|
sk_X509_EXTENSION_pop_free(sk_ext, X509_EXTENSION_free);
|
|
sk_ext = NULL;
|
|
goto out;
|
|
}
|
|
|
|
if (cert != NULL && sk_X509_EXTENSION_num(sk_ext) > 0) {
|
|
X509V3_set_ctx_nodb(&x509v3_ctx);
|
|
X509V3_set_ctx(&x509v3_ctx, cert, cert, NULL, NULL, 0);
|
|
|
|
rc = 0;
|
|
while ((ex = sk_X509_EXTENSION_pop(sk_ext)) != NULL) {
|
|
if (rc == 0) {
|
|
if (X509_add_ext(cert, ex, -1) != 1)
|
|
rc = -EIO;
|
|
}
|
|
X509_EXTENSION_free(ex);
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (sk_ext != NULL)
|
|
sk_X509_EXTENSION_pop_free(sk_ext, X509_EXTENSION_free);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Generates a serial number of a specified bit size by random and sets it
|
|
* as serial number into the certificate.
|
|
*
|
|
* @param cert the certificate to set the serial number for
|
|
* @param sn_bit_size the size of the serial number in bits
|
|
*
|
|
* @returns zero for success, a negative errno in case of an error:
|
|
* -EINVAL: invalid parameter
|
|
* -EIO: error during serial number generation
|
|
*/
|
|
int SK_UTIL_generate_x509_serial_number(X509 *cert, size_t sn_bit_size)
|
|
{
|
|
ASN1_INTEGER *ai = NULL;
|
|
BIGNUM *bn = NULL;
|
|
int rc;
|
|
|
|
if (cert == NULL)
|
|
return -EINVAL;
|
|
|
|
bn = BN_new();
|
|
if (bn == NULL)
|
|
return -ENOMEM;
|
|
|
|
rc = BN_rand(bn, sn_bit_size, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY);
|
|
if (rc != 1) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
ai = X509_get_serialNumber(cert);
|
|
if (ai == NULL) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
if (BN_to_ASN1_INTEGER(bn, ai) == NULL) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
if (bn != NULL)
|
|
BN_free(bn);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Builds an DER encoded signature from a raw signature.
|
|
*
|
|
* @param raw_sig the raw signature to encode
|
|
* @param raw_sig_len the size of the raw signature (2 times prime len)
|
|
* @param sig a buffer for storing he encoded signature. If
|
|
* NULL, then required size is returend in sig_len.
|
|
* @param sig_len On entry: the size of the buffer in sig.
|
|
* On exit: the size of the encoded sigature.
|
|
*
|
|
* @returns zero for success, a negative errno in case of an error:
|
|
* -ERANGE: signature buffer is too small
|
|
* -EIO: error during signature encoding
|
|
*/
|
|
int SK_UTIL_build_ecdsa_signature(const unsigned char *raw_sig,
|
|
size_t raw_sig_len,
|
|
unsigned char *sig, size_t *sig_len)
|
|
{
|
|
unsigned char *der = NULL;
|
|
ECDSA_SIG *ec_sig = NULL;
|
|
BIGNUM *bn_r = NULL;
|
|
BIGNUM *bn_s = NULL;
|
|
int rc = 0, der_len;
|
|
|
|
ec_sig = ECDSA_SIG_new();
|
|
if (ec_sig == NULL) {
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
bn_r = BN_bin2bn(raw_sig, raw_sig_len / 2, NULL);
|
|
bn_s = BN_bin2bn(raw_sig + raw_sig_len / 2, raw_sig_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, NULL);
|
|
if (der_len <= 0) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
if (sig == NULL) {
|
|
*sig_len = der_len;
|
|
goto out;
|
|
}
|
|
if (der_len > (int)*sig_len) {
|
|
rc = -ERANGE;
|
|
goto out;
|
|
}
|
|
|
|
memset(sig, 0, *sig_len);
|
|
der = sig;
|
|
der_len = i2d_ECDSA_SIG(ec_sig, &der);
|
|
if (der_len <= 0) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
*sig_len = der_len;
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/**
|
|
* Reads a X.509 certificate from the specified PEM file.
|
|
*
|
|
* @param pem_filename the name of the PEM file to read
|
|
* @param cert on Return: the X.509 certificate 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 SK_UTIL_read_x509_certificate(const char *pem_filename, X509 **cert)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (pem_filename == NULL || cert == NULL)
|
|
return -EINVAL;
|
|
|
|
fp = fopen(pem_filename, "r");
|
|
if (fp == NULL)
|
|
return -errno;
|
|
|
|
*cert = PEM_read_X509(fp, NULL, NULL, NULL);
|
|
|
|
fclose(fp);
|
|
|
|
if (*cert == NULL)
|
|
return -EIO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Writes a X.509 certificate to the specified PEM file.
|
|
*
|
|
* @param pem_filename the name of the PEM file to write to
|
|
* @param cert the X.509 certificate 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 SK_UTIL_write_x509_certificate(const char *pem_filename, X509 *cert)
|
|
{
|
|
FILE *fp;
|
|
int rc;
|
|
|
|
if (pem_filename == NULL || cert == NULL)
|
|
return -EINVAL;
|
|
|
|
fp = fopen(pem_filename, "w");
|
|
if (fp == NULL)
|
|
return -errno;
|
|
|
|
rc = PEM_write_X509(fp, cert);
|
|
|
|
fclose(fp);
|
|
|
|
if (rc != 1)
|
|
return -EIO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Writes a X.509 certificate signing request to the specified PEM file.
|
|
*
|
|
* @param pem_filename the name of the PEM file to write to
|
|
* @param req the X.509 request object to write
|
|
* @param new_hdr if true, output "NEW" in the PEM header lines
|
|
*
|
|
* @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 SK_UTIL_write_x509_request(const char *pem_filename, X509_REQ *req,
|
|
bool new_hdr)
|
|
{
|
|
FILE *fp;
|
|
int rc;
|
|
|
|
if (pem_filename == NULL || req == NULL)
|
|
return -EINVAL;
|
|
|
|
fp = fopen(pem_filename, "w");
|
|
if (fp == NULL)
|
|
return -errno;
|
|
|
|
if (new_hdr)
|
|
rc = PEM_write_X509_REQ_NEW(fp, req);
|
|
else
|
|
rc = PEM_write_X509_REQ(fp, req);
|
|
|
|
fclose(fp);
|
|
|
|
if (rc != 1)
|
|
return -EIO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Reads a secure key from the specified file.
|
|
*
|
|
* @param filename the name of the file to read
|
|
* @param key_blob on Return: the key blob
|
|
* @param key_blob_len on Entry: the size of the buffer,
|
|
* on Return: the size of the key blob read
|
|
*
|
|
* @returns zero for success, a negative errno in case of an error
|
|
*/
|
|
int SK_UTIL_read_key_blob(const char *filename, unsigned char *key_blob,
|
|
size_t *key_blob_len)
|
|
{
|
|
size_t count, size;
|
|
struct stat sb;
|
|
FILE *fp;
|
|
|
|
if (filename == NULL || key_blob_len == NULL)
|
|
return -EINVAL;
|
|
|
|
if (stat(filename, &sb))
|
|
return -errno;
|
|
size = sb.st_size;
|
|
|
|
if (key_blob == NULL) {
|
|
*key_blob_len = size;
|
|
return 0;
|
|
}
|
|
|
|
if (size > *key_blob_len) {
|
|
*key_blob_len = size;
|
|
return -ERANGE;
|
|
}
|
|
|
|
fp = fopen(filename, "r");
|
|
if (fp == NULL)
|
|
return -errno;
|
|
|
|
count = fread(key_blob, 1, size, fp);
|
|
if (count != size) {
|
|
fclose(fp);
|
|
return -EIO;
|
|
}
|
|
|
|
*key_blob_len = size;
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Writes a secure key to the specified file.
|
|
*
|
|
* @param filename the name of the file to write
|
|
* @param key_blob the key blob
|
|
* @param key_blob_len the size of the key blob
|
|
*
|
|
* @returns zero for success, a negative errno in case of an error
|
|
*/
|
|
int SK_UTIL_write_key_blob(const char *filename, unsigned char *key_blob,
|
|
size_t key_blob_len)
|
|
{
|
|
size_t count;
|
|
FILE *fp;
|
|
|
|
if (filename == NULL || key_blob == NULL || key_blob_len == 0)
|
|
return -EINVAL;
|
|
|
|
fp = fopen(filename, "w");
|
|
if (fp == NULL)
|
|
return -errno;
|
|
|
|
count = fwrite(key_blob, 1, key_blob_len, fp);
|
|
if (count != key_blob_len) {
|
|
fclose(fp);
|
|
return -EIO;
|
|
}
|
|
|
|
fclose(fp);
|
|
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 SK_UTIL_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 a public 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 SK_UTIL_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;
|
|
}
|
|
|