/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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) { 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; }