| /* |
| * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <openssl/asn1.h> |
| #include <openssl/asn1t.h> |
| #include <openssl/err.h> |
| #include <openssl/x509v3.h> |
| |
| #include "cmd_opt.h" |
| #include "ext.h" |
| |
| DECLARE_ASN1_ITEM(ASN1_INTEGER) |
| DECLARE_ASN1_ITEM(X509_ALGOR) |
| DECLARE_ASN1_ITEM(ASN1_OCTET_STRING) |
| |
| typedef struct { |
| X509_ALGOR *hashAlgorithm; |
| ASN1_OCTET_STRING *dataHash; |
| } HASH; |
| |
| ASN1_SEQUENCE(HASH) = { |
| ASN1_SIMPLE(HASH, hashAlgorithm, X509_ALGOR), |
| ASN1_SIMPLE(HASH, dataHash, ASN1_OCTET_STRING), |
| } ASN1_SEQUENCE_END(HASH) |
| |
| DECLARE_ASN1_FUNCTIONS(HASH) |
| IMPLEMENT_ASN1_FUNCTIONS(HASH) |
| |
| /* |
| * This function adds the TBB extensions to the internal extension list |
| * maintained by OpenSSL so they can be used later. |
| * |
| * It also initializes the methods to print the contents of the extension. If an |
| * alias is specified in the TBB extension, we reuse the methods of the alias. |
| * Otherwise, only methods for V_ASN1_INTEGER and V_ASN1_OCTET_STRING are |
| * provided. Any other type will be printed as a raw ascii string. |
| * |
| * Return: 0 = success, Otherwise: error |
| */ |
| int ext_init(void) |
| { |
| cmd_opt_t cmd_opt; |
| ext_t *ext; |
| X509V3_EXT_METHOD *m; |
| int nid, ret; |
| unsigned int i; |
| |
| for (i = 0; i < num_extensions; i++) { |
| ext = &extensions[i]; |
| /* Register command line option */ |
| if (ext->opt) { |
| cmd_opt.long_opt.name = ext->opt; |
| cmd_opt.long_opt.has_arg = required_argument; |
| cmd_opt.long_opt.flag = NULL; |
| cmd_opt.long_opt.val = CMD_OPT_EXT; |
| cmd_opt.help_msg = ext->help_msg; |
| cmd_opt_add(&cmd_opt); |
| } |
| /* Register the extension OID in OpenSSL */ |
| if (ext->oid == NULL) { |
| continue; |
| } |
| nid = OBJ_create(ext->oid, ext->sn, ext->ln); |
| if (ext->alias) { |
| X509V3_EXT_add_alias(nid, ext->alias); |
| } else { |
| m = &ext->method; |
| memset(m, 0x0, sizeof(X509V3_EXT_METHOD)); |
| switch (ext->asn1_type) { |
| case V_ASN1_INTEGER: |
| m->it = ASN1_ITEM_ref(ASN1_INTEGER); |
| m->i2s = (X509V3_EXT_I2S)i2s_ASN1_INTEGER; |
| m->s2i = (X509V3_EXT_S2I)s2i_ASN1_INTEGER; |
| break; |
| case V_ASN1_OCTET_STRING: |
| m->it = ASN1_ITEM_ref(ASN1_OCTET_STRING); |
| m->i2s = (X509V3_EXT_I2S)i2s_ASN1_OCTET_STRING; |
| m->s2i = (X509V3_EXT_S2I)s2i_ASN1_OCTET_STRING; |
| break; |
| default: |
| continue; |
| } |
| m->ext_nid = nid; |
| ret = X509V3_EXT_add(m); |
| if (!ret) { |
| ERR_print_errors_fp(stdout); |
| return 1; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| * Create a new extension |
| * |
| * Extension ::= SEQUENCE { |
| * id OBJECT IDENTIFIER, |
| * critical BOOLEAN DEFAULT FALSE, |
| * value OCTET STRING } |
| * |
| * Parameters: |
| * pex: OpenSSL extension pointer (output parameter) |
| * nid: extension identifier |
| * crit: extension critical (EXT_NON_CRIT, EXT_CRIT) |
| * data: extension data. This data will be encapsulated in an Octet String |
| * |
| * Return: Extension address, NULL if error |
| */ |
| static |
| X509_EXTENSION *ext_new(int nid, int crit, unsigned char *data, int len) |
| { |
| X509_EXTENSION *ex; |
| ASN1_OCTET_STRING *ext_data; |
| |
| /* Octet string containing the extension data */ |
| ext_data = ASN1_OCTET_STRING_new(); |
| ASN1_OCTET_STRING_set(ext_data, data, len); |
| |
| /* Create the extension */ |
| ex = X509_EXTENSION_create_by_NID(NULL, nid, crit, ext_data); |
| |
| /* The extension makes a copy of the data, so we can free this object */ |
| ASN1_OCTET_STRING_free(ext_data); |
| |
| return ex; |
| } |
| |
| /* |
| * Creates a x509v3 extension containing a hash |
| * |
| * DigestInfo ::= SEQUENCE { |
| * digestAlgorithm AlgorithmIdentifier, |
| * digest OCTET STRING |
| * } |
| * |
| * AlgorithmIdentifier ::= SEQUENCE { |
| * algorithm OBJECT IDENTIFIER, |
| * parameters ANY DEFINED BY algorithm OPTIONAL |
| * } |
| * |
| * Parameters: |
| * nid: extension identifier |
| * crit: extension critical (EXT_NON_CRIT, EXT_CRIT) |
| * md: hash algorithm |
| * buf: pointer to the buffer that contains the hash |
| * len: size of the hash in bytes |
| * |
| * Return: Extension address, NULL if error |
| */ |
| X509_EXTENSION *ext_new_hash(int nid, int crit, const EVP_MD *md, |
| unsigned char *buf, size_t len) |
| { |
| X509_EXTENSION *ex; |
| ASN1_OCTET_STRING *octet; |
| HASH *hash; |
| ASN1_OBJECT *algorithm; |
| X509_ALGOR *x509_algor; |
| unsigned char *p = NULL; |
| int sz; |
| |
| /* OBJECT_IDENTIFIER with hash algorithm */ |
| algorithm = OBJ_nid2obj(EVP_MD_type(md)); |
| if (algorithm == NULL) { |
| return NULL; |
| } |
| |
| /* Create X509_ALGOR */ |
| x509_algor = X509_ALGOR_new(); |
| if (x509_algor == NULL) { |
| return NULL; |
| } |
| x509_algor->algorithm = algorithm; |
| x509_algor->parameter = ASN1_TYPE_new(); |
| ASN1_TYPE_set(x509_algor->parameter, V_ASN1_NULL, NULL); |
| |
| /* OCTET_STRING with the actual hash */ |
| octet = ASN1_OCTET_STRING_new(); |
| if (octet == NULL) { |
| X509_ALGOR_free(x509_algor); |
| return NULL; |
| } |
| ASN1_OCTET_STRING_set(octet, buf, len); |
| |
| /* HASH structure containing algorithm + hash */ |
| hash = HASH_new(); |
| if (hash == NULL) { |
| ASN1_OCTET_STRING_free(octet); |
| X509_ALGOR_free(x509_algor); |
| return NULL; |
| } |
| hash->hashAlgorithm = x509_algor; |
| hash->dataHash = octet; |
| |
| /* DER encoded HASH */ |
| sz = i2d_HASH(hash, &p); |
| if ((sz <= 0) || (p == NULL)) { |
| HASH_free(hash); |
| X509_ALGOR_free(x509_algor); |
| return NULL; |
| } |
| |
| /* Create the extension */ |
| ex = ext_new(nid, crit, p, sz); |
| |
| /* Clean up */ |
| OPENSSL_free(p); |
| HASH_free(hash); |
| |
| return ex; |
| } |
| |
| /* |
| * Creates a x509v3 extension containing a nvcounter encapsulated in an ASN1 |
| * Integer |
| * |
| * Parameters: |
| * pex: OpenSSL extension pointer (output parameter) |
| * nid: extension identifier |
| * crit: extension critical (EXT_NON_CRIT, EXT_CRIT) |
| * value: nvcounter value |
| * |
| * Return: Extension address, NULL if error |
| */ |
| X509_EXTENSION *ext_new_nvcounter(int nid, int crit, int value) |
| { |
| X509_EXTENSION *ex; |
| ASN1_INTEGER *counter; |
| unsigned char *p = NULL; |
| int sz; |
| |
| /* Encode counter */ |
| counter = ASN1_INTEGER_new(); |
| ASN1_INTEGER_set(counter, value); |
| sz = i2d_ASN1_INTEGER(counter, &p); |
| |
| /* Create the extension */ |
| ex = ext_new(nid, crit, p, sz); |
| |
| /* Free objects */ |
| OPENSSL_free(p); |
| ASN1_INTEGER_free(counter); |
| |
| return ex; |
| } |
| |
| /* |
| * Creates a x509v3 extension containing a public key in DER format: |
| * |
| * SubjectPublicKeyInfo ::= SEQUENCE { |
| * algorithm AlgorithmIdentifier, |
| * subjectPublicKey BIT STRING } |
| * |
| * Parameters: |
| * pex: OpenSSL extension pointer (output parameter) |
| * nid: extension identifier |
| * crit: extension critical (EXT_NON_CRIT, EXT_CRIT) |
| * k: key |
| * |
| * Return: Extension address, NULL if error |
| */ |
| X509_EXTENSION *ext_new_key(int nid, int crit, EVP_PKEY *k) |
| { |
| X509_EXTENSION *ex; |
| unsigned char *p; |
| int sz; |
| |
| /* Encode key */ |
| BIO *mem = BIO_new(BIO_s_mem()); |
| if (i2d_PUBKEY_bio(mem, k) <= 0) { |
| ERR_print_errors_fp(stderr); |
| return NULL; |
| } |
| p = (unsigned char *)OPENSSL_malloc(4096); |
| sz = BIO_read(mem, p, 4096); |
| |
| /* Create the extension */ |
| ex = ext_new(nid, crit, p, sz); |
| |
| /* Clean up */ |
| OPENSSL_free(p); |
| |
| return ex; |
| } |
| |
| ext_t *ext_get_by_opt(const char *opt) |
| { |
| ext_t *ext; |
| unsigned int i; |
| |
| /* Sequential search. This is not a performance concern since the number |
| * of extensions is bounded and the code runs on a host machine */ |
| for (i = 0; i < num_extensions; i++) { |
| ext = &extensions[i]; |
| if (ext->opt && !strcmp(ext->opt, opt)) { |
| return ext; |
| } |
| } |
| |
| return NULL; |
| } |