Files
GmSSL/src/x509_ext.c
2023-01-31 19:18:46 +08:00

2746 lines
72 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright 2014-2023 The GmSSL Project. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <gmssl/sm2.h>
#include <gmssl/oid.h>
#include <gmssl/asn1.h>
#include <gmssl/x509.h>
#include <gmssl/x509_str.h>
#include <gmssl/x509_oid.h>
#include <gmssl/x509_ext.h>
#include <gmssl/error.h>
int x509_ext_to_der_ex(int oid, int critical, const uint8_t *d, size_t dlen, uint8_t **out, size_t *outlen)
{
size_t vlen = 0;
size_t len = 0;
if (asn1_sequence_to_der(d, dlen, NULL, &vlen) != 1) {
error_print();
return -1;
}
if (x509_ext_id_to_der(oid, NULL, &len) != 1
|| asn1_boolean_to_der(critical, NULL, &len) < 0
|| asn1_tag_to_der(ASN1_TAG_OCTET_STRING, NULL, &len) != 1
|| asn1_length_to_der(vlen, NULL, &len) != 1
|| asn1_sequence_to_der(d, dlen, NULL, &len) != 1
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_ext_id_to_der(oid, out, outlen) != 1
|| asn1_boolean_to_der(critical, out, outlen) < 0
|| asn1_tag_to_der(ASN1_TAG_OCTET_STRING, out, outlen) != 1
|| asn1_length_to_der(vlen, out, outlen) != 1
|| asn1_sequence_to_der(d, dlen, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_sequence(uint8_t *exts, size_t *extslen, size_t maxlen,
int oid, int critical, const uint8_t *d, size_t dlen)
{
size_t curlen = *extslen;
exts += *extslen;
if (x509_ext_to_der_ex(oid, critical, d, dlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der_ex(oid, critical, d, dlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_authority_key_identifier(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical,
const uint8_t *keyid, size_t keyid_len,
const uint8_t *issuer, size_t issuer_len,
const uint8_t *serial, size_t serial_len)
{
int oid = OID_ce_authority_key_identifier;
size_t curlen = *extslen;
uint8_t val[512];
uint8_t *p = val;
size_t vlen = 0;
size_t len = 0;
exts += *extslen;
if (x509_authority_key_identifier_to_der(
keyid, keyid_len,
issuer, issuer_len,
serial, serial_len,
NULL, &len) != 1
|| asn1_length_le(len, sizeof(val)) != 1
|| x509_authority_key_identifier_to_der(
keyid, keyid_len,
issuer, issuer_len,
serial, serial_len,
&p, &vlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_default_authority_key_identifier(uint8_t *exts, size_t *extslen, size_t maxlen,
const SM2_KEY *public_key)
{
uint8_t buf[65];
uint8_t id[32];
int critical = -1;
sm2_point_to_uncompressed_octets(&public_key->public_key, buf);
sm3_digest(buf, sizeof(buf), id);
if (x509_exts_add_authority_key_identifier(exts, extslen, maxlen, critical,
id, sizeof(id), NULL, 0, NULL, 0) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_subject_key_identifier(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const uint8_t *d, size_t dlen)
{
int oid = OID_ce_subject_key_identifier;
size_t curlen = *extslen;
uint8_t val[32 + X509_SUBJECT_KEY_IDENTIFIER_MAX_LEN];
uint8_t *p = val;
size_t vlen = 0;
if (dlen < X509_SUBJECT_KEY_IDENTIFIER_MIN_LEN
|| dlen > X509_SUBJECT_KEY_IDENTIFIER_MAX_LEN) {
error_print();
return -1;
}
exts += *extslen;
if (asn1_octet_string_to_der(d, dlen, &p, &vlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_subject_key_identifier_ex(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const SM2_KEY *subject_key)
{
uint8_t buf[65];
uint8_t id[32];
sm2_point_to_uncompressed_octets(&subject_key->public_key, buf);
sm3_digest(buf, sizeof(buf), id);
if (x509_exts_add_subject_key_identifier(exts, extslen, maxlen, critical, id, 32) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_key_usage(uint8_t *exts, size_t *extslen, size_t maxlen, int critical, int bits)
{
int oid = OID_ce_key_usage;
size_t curlen = *extslen;
uint8_t val[16];
uint8_t *p = val;
size_t vlen = 0;
if (!bits) {
// TODO: 检查是否在合法范围内
error_print();
return -1;
}
exts += *extslen;
if (asn1_bits_to_der(bits, &p, &vlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_certificate_policies(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const uint8_t *d, size_t dlen)
{
int oid = OID_ce_certificate_policies;
return x509_exts_add_sequence(exts, extslen, maxlen, oid, critical, d, dlen);
}
int x509_exts_add_policy_mappings(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const uint8_t *d, size_t dlen)
{
int oid = OID_ce_policy_mappings;
return x509_exts_add_sequence(exts, extslen, maxlen, oid, critical, d, dlen);
}
int x509_exts_add_subject_alt_name(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const uint8_t *d, size_t dlen)
{
int oid = OID_ce_subject_alt_name;
return x509_exts_add_sequence(exts, extslen, maxlen, oid, critical, d, dlen);
}
int x509_exts_add_issuer_alt_name(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const uint8_t *d, size_t dlen)
{
int oid = OID_ce_issuer_alt_name;
return x509_exts_add_sequence(exts, extslen, maxlen, oid, critical, d, dlen);
}
int x509_exts_add_subject_directory_attributes(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const uint8_t *d, size_t dlen)
{
int oid = OID_ce_subject_directory_attributes;
return x509_exts_add_sequence(exts, extslen, maxlen, oid, critical, d, dlen);
}
int x509_exts_add_name_constraints(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical,
const uint8_t *permitted_subtrees, size_t permitted_subtrees_len,
const uint8_t *excluded_subtrees, size_t excluded_subtrees_len)
{
int oid = OID_ce_name_constraints;
size_t curlen = *extslen;
uint8_t val[512];
uint8_t *p = val;
size_t vlen = 0;
size_t len = 0;
exts += *extslen;
if (x509_name_constraints_to_der(
permitted_subtrees, permitted_subtrees_len,
excluded_subtrees, excluded_subtrees_len,
NULL, &len) != 1
|| asn1_length_le(len, sizeof(val)) != 1
|| x509_name_constraints_to_der(
permitted_subtrees, permitted_subtrees_len,
excluded_subtrees, excluded_subtrees_len,
&p, &vlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_policy_constraints(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, int require_explicit_policy, int inhibit_policy_mapping)
{
int oid = OID_ce_policy_constraints;
size_t curlen = *extslen;
uint8_t val[32];
uint8_t *p = val;
size_t vlen = 0;
exts += *extslen;
if (x509_policy_constraints_to_der(
require_explicit_policy,
inhibit_policy_mapping,
&p, &vlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_basic_constraints(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, int ca, int path_len_constraint)
{
int oid = OID_ce_basic_constraints;
size_t curlen = *extslen;
uint8_t val[32];
uint8_t *p = val;
size_t vlen = 0;
exts += *extslen;
if (x509_basic_constraints_to_der(ca, path_len_constraint, &p, &vlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_ext_key_usage(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const int *key_purposes, size_t key_purposes_cnt)
{
int oid = OID_ce_ext_key_usage;
size_t curlen = *extslen;
uint8_t val[256];
uint8_t *p = val;
size_t vlen = 0;
size_t len = 0;
exts += *extslen;
if (x509_ext_key_usage_to_der(key_purposes, key_purposes_cnt, NULL, &len) != 1
|| asn1_length_le(len, sizeof(val)) != 1
|| x509_ext_key_usage_to_der(key_purposes, key_purposes_cnt, &p, &vlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_crl_distribution_points_ex(uint8_t *exts, size_t *extslen, size_t maxlen,
int oid, int critical, const char *uri, size_t urilen, const char *ldap_uri, size_t ldap_urilen)
{
size_t curlen = *extslen;
uint8_t val[256];
uint8_t *p = val;
size_t vlen = 0;
size_t len = 0;
if (x509_uri_as_distribution_points_to_der(uri, urilen, -1, NULL, 0, NULL, &len) != 1
|| asn1_length_le(len, sizeof(val)) != 1
|| x509_uri_as_distribution_points_to_der(uri, urilen, -1, NULL, 0, &p, &vlen) != 1) {
error_print();
return -1;
}
exts += *extslen;
if (x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_crl_distribution_points(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const char *http_uri, size_t http_urilen, const char *ldap_uri, size_t ldap_urilen)
{
int oid = OID_ce_crl_distribution_points;
if (x509_exts_add_crl_distribution_points_ex(exts, extslen, maxlen,
oid, critical, http_uri, http_urilen, ldap_uri, ldap_urilen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_inhibit_any_policy(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, int skip_certs)
{
int oid = OID_ce_inhibit_any_policy;
size_t curlen = *extslen;
uint8_t val[16];
uint8_t *p = val;
size_t vlen = 0;
exts += *extslen;
if (x509_inhibit_any_policy_to_der(skip_certs, &p, &vlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_exts_add_freshest_crl(uint8_t *exts, size_t *extslen, size_t maxlen,
int critical, const uint8_t *d, size_t dlen)
{
int oid = OID_ce_freshest_crl;
return x509_exts_add_sequence(exts, extslen, maxlen, oid, critical, d, dlen);
}
int x509_other_name_to_der(
const uint32_t *type_nodes, size_t type_nodes_cnt,
const uint8_t *value, size_t value_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (asn1_object_identifier_to_der(type_nodes, type_nodes_cnt, NULL, &len) != 1
|| asn1_explicit_to_der(0, value, value_len, NULL, &len) != 1
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| asn1_object_identifier_to_der(type_nodes, type_nodes_cnt, out, outlen) != 1
|| asn1_explicit_to_der(0, value, value_len, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_other_name_from_der(
uint32_t *type_nodes, size_t *type_nodes_cnt,
const uint8_t **value, size_t *value_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *p;
size_t len;
if ((ret = asn1_sequence_from_der(&p, &len, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (asn1_object_identifier_from_der(type_nodes, type_nodes_cnt, &p, &len) != 1
|| asn1_explicit_from_der(0, value, value_len, &p, &len) != 1
|| asn1_length_is_zero(len) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_other_name_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
uint32_t nodes[32];
size_t nodes_cnt;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if (asn1_object_identifier_from_der(nodes, &nodes_cnt, &d, &dlen) != 1) goto err;
asn1_object_identifier_print(fp, fmt, ind, "type-id", NULL, nodes, nodes_cnt);
if (asn1_explicit_from_der(0, &p, &len, &d, &dlen) != 1) goto err;
format_bytes(fp, fmt, ind, "value", p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_edi_party_name_to_der(
int assigner_choice, const uint8_t *assigner, size_t assigner_len,
int party_name_choice, const uint8_t *party_name, size_t party_name_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (x509_explicit_directory_name_to_der(0, assigner_choice, assigner, assigner_len, NULL, &len) < 0
|| x509_explicit_directory_name_to_der(1, party_name_choice, party_name, party_name_len, NULL, &len) != 1
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_explicit_directory_name_to_der(0, assigner_choice, assigner, assigner_len, out, outlen) < 0
|| x509_explicit_directory_name_to_der(1, party_name_choice, party_name, party_name_len, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_edi_party_name_from_der(
int *assigner_choice, const uint8_t **assigner, size_t *assigner_len,
int *party_name_choice, const uint8_t **party_name, size_t *party_name_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *p;
size_t len;
if ((ret = asn1_sequence_from_der(&p, &len, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_explicit_directory_name_from_der(0, assigner_choice, assigner, assigner_len, &p, &len) < 0
|| x509_explicit_directory_name_from_der(1, party_name_choice, party_name, party_name_len, &p, &len) != 1
|| asn1_length_is_zero(len) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_edi_party_name_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret;
const uint8_t *p;
size_t len;
int tag;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if ((ret = x509_explicit_directory_name_from_der(0, &tag, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_directory_name_print(fp, fmt, ind, "nameAssigner", tag, p, len);
if (x509_explicit_directory_name_from_der(1, &tag, &p, &len, &d, &dlen) != 1) goto err;
x509_directory_name_print(fp, fmt, ind, "partyName", tag, p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_general_name_to_der(int choice, const uint8_t *d, size_t dlen, uint8_t **out, size_t *outlen)
{
int ret;
switch (choice) {
case X509_gn_rfc822_name:
case X509_gn_dns_name:
case X509_gn_uniform_resource_identifier:
case X509_gn_ip_address:
if ((ret = asn1_implicit_to_der(choice, d, dlen, out, outlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
break;
default:
error_print();
return -1;
}
return 1;
}
int x509_general_name_from_der(int *choice, const uint8_t **d, size_t *dlen, const uint8_t **in, size_t *inlen)
{
int ret;
int tag;
if ((ret = asn1_any_type_from_der(&tag, d, dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
switch (tag) {
case ASN1_TAG_EXPLICIT(0): *choice = 0; break;
case ASN1_TAG_IMPLICIT(1): *choice = 1; break;
case ASN1_TAG_IMPLICIT(2): *choice = 2; break;
case ASN1_TAG_EXPLICIT(3): *choice = 3; break;
case ASN1_TAG_EXPLICIT(4): *choice = 4; break;
case ASN1_TAG_EXPLICIT(5): *choice = 5; break;
case ASN1_TAG_IMPLICIT(6): *choice = 6; break;
case ASN1_TAG_IMPLICIT(7): *choice = 7; break;
case ASN1_TAG_IMPLICIT(8): *choice = 8; break;
default:
fprintf(stderr, "%s %d: tag = %x\n", __FILE__, __LINE__, tag);
error_print();
return -1;
}
return 1;
}
int x509_general_name_print(FILE *fp, int fmt, int ind, const char *label, int choice, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
switch (choice) {
case 0:
case 3:
case 4:
case 5:
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
d = p;
dlen = len;
}
switch (choice) {
case 0: return x509_other_name_print(fp, fmt, ind, "otherName", d, dlen);
case 1: return asn1_string_print(fp, fmt, ind, "rfc822Name", ASN1_TAG_IA5String, d, dlen);
case 2: return asn1_string_print(fp, fmt, ind, "DNSName", ASN1_TAG_IA5String, d, dlen);
case 3: return format_bytes(fp, fmt, ind, "x400Address", d, dlen);
case 4: return x509_name_print(fp, fmt, ind, "directoryName", d, dlen);
case 5: return x509_edi_party_name_print(fp, fmt, ind, "ediPartyName", d, dlen);
case 6: return asn1_string_print(fp, fmt, ind, "URI", ASN1_TAG_IA5String, d, dlen);
case 7: return format_bytes(fp, fmt, ind, "IPAddress", d, dlen);
case 8:
{
uint32_t nodes[32];
size_t nodes_cnt;
if (asn1_object_identifier_from_octets(nodes, &nodes_cnt, d, dlen) != 1) {
error_print();
return -1;
}
return asn1_object_identifier_print(fp, fmt, ind, "registeredID", NULL, nodes, nodes_cnt);
}
default:
error_print();
return -1;
}
return 1;
}
int x509_general_names_add_general_name(uint8_t *gns, size_t *gnslen, size_t maxlen,
int choice, const uint8_t *d, size_t dlen)
{
size_t len = 0;
uint8_t *p = gns + *gnslen;
switch (choice) {
case X509_gn_rfc822_name:
case X509_gn_dns_name:
case X509_gn_uniform_resource_identifier:
if (asn1_string_is_ia5_string((char *)d, dlen) != 1) {
error_print();
return -1;
}
break;
}
if (x509_general_name_to_der(choice, d, dlen, NULL, &len) != 1
|| asn1_length_le(*gnslen + len, maxlen) != 1
|| x509_general_name_to_der(choice, d, dlen, &p, gnslen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_general_names_add_other_name(uint8_t *gns, size_t *gnslen, size_t maxlen,
const uint32_t *nodes, size_t nodes_cnt,
const uint8_t *value, size_t value_len)
{
int choice = X509_gn_other_name;
uint8_t buf[128];
uint8_t *p = buf;
const uint8_t *cp = buf;
size_t len = 0;
const uint8_t *d;
size_t dlen;
if (x509_other_name_to_der(nodes, nodes_cnt, value, value_len, &p, &len) != 1
|| asn1_sequence_from_der(&d, &dlen, &cp, &len) != 1
|| x509_general_names_add_general_name(gns, gnslen, maxlen, choice, d, dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_general_names_add_edi_party_name(uint8_t *gns, size_t *gnslen, size_t maxlen,
int assigner_tag, const uint8_t *assigner, size_t assigner_len,
int party_name_tag, const uint8_t *party_name, size_t party_name_len)
{
int choice = X509_gn_edi_party_name;
uint8_t buf[128];
uint8_t *p = buf;
const uint8_t *cp = buf;
size_t len = 0;
const uint8_t *d;
size_t dlen;
if (x509_edi_party_name_to_der(
assigner_tag, assigner, assigner_len,
party_name_tag, party_name, party_name_len,
&p, &len) != 1
|| asn1_sequence_from_der(&d, &dlen, &cp, &len) != 1
|| x509_general_names_add_general_name(gns, gnslen, maxlen, choice, d, dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_general_names_add_registered_id(uint8_t *gns, size_t *gnslen, size_t maxlen,
const uint32_t *nodes, size_t nodes_cnt)
{
int choice = X509_gn_registered_id;
uint8_t d[128];
size_t dlen;
if (asn1_object_identifier_to_octets(nodes, nodes_cnt, d, &dlen) != 1
|| x509_general_names_add_general_name(gns, gnslen, maxlen, choice, d, dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_general_names_get_next(const uint8_t *gns, size_t gns_len, const uint8_t **ptr, int choice, const uint8_t **d, size_t *dlen)
{
if (!gns || !gns_len) {
error_print();
return -1;
}
if (!ptr || !d || !dlen) {
error_print();
return -1;
}
if (*ptr > gns + gns_len) {
error_print();
return -1;
}
gns_len -= (*ptr - gns);
while (gns_len) {
int tag;
if (x509_general_name_from_der(&tag, d, dlen, ptr, &gns_len) != 1) {
error_print();
return -1;
}
if (tag == choice) {
return 1;
}
}
return 0;
}
int x509_general_names_get_first(const uint8_t *gns, size_t gns_len, const uint8_t **ptr, int choice, const uint8_t **d, size_t *dlen)
{
int ret;
const uint8_t *p;
p = gns;
if ((ret = x509_general_names_get_next(gns, gns_len, &p, choice, d, dlen)) < 0) {
error_print();
return - 1;
}
if (ptr) {
*ptr = p;
}
return ret;
}
/*
DistributionPointName ::= CHOICE {
fullName [0] GeneralNames,
nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
本来GeneralNames是一个SEQUENCE OF本来这个类型编码的时候应该是80开头的
*/
int x509_uri_as_general_names_to_der_ex(int tag, const char *uri, size_t urilen,
uint8_t **out, size_t *outlen)
{
int choice = X509_gn_uniform_resource_identifier;
size_t len = 0;
if (!uri || !urilen) {
return 0;
}
if (x509_general_name_to_der(choice, (uint8_t *)uri, urilen, NULL, &len) != 1
|| asn1_sequence_header_to_der_ex(tag, len, out, outlen) != 1
|| x509_general_name_to_der(choice, (uint8_t *)uri, urilen, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_uri_as_general_names_from_der_ex(int tag, const uint8_t **uri, size_t *urilen,
const uint8_t **in, size_t *inlen)
{
int choice = X509_gn_uniform_resource_identifier;
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_type_from_der(tag, &d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_general_names_get_first(d, dlen, NULL, choice, uri, urilen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_general_names_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int choice;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
while (dlen) {
if (x509_general_name_from_der(&choice, &p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_general_name_print(fp, fmt, ind, "GeneralName", choice, p, len);
}
return 1;
}
int x509_authority_key_identifier_to_der(
const uint8_t *keyid, size_t keyid_len,
const uint8_t *issuer, size_t issuer_len,
const uint8_t *serial, size_t serial_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (asn1_implicit_octet_string_to_der(0, keyid, keyid_len, NULL, &len) < 0
|| asn1_implicit_sequence_to_der(1, issuer, issuer_len, NULL, &len) < 0
|| asn1_implicit_integer_to_der(2, serial, serial_len, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| asn1_implicit_octet_string_to_der(0, keyid, keyid_len, out, outlen) < 0
|| asn1_implicit_sequence_to_der(1, issuer, issuer_len, out, outlen) < 0
|| asn1_implicit_integer_to_der(2, serial, serial_len, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_authority_key_identifier_from_der(
const uint8_t **keyid, size_t *keyid_len,
const uint8_t **issuer, size_t *issuer_len,
const uint8_t **serial, size_t *serial_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (asn1_implicit_octet_string_from_der(0, keyid, keyid_len, &d, &dlen) < 0
|| asn1_implicit_sequence_from_der(1, issuer, issuer_len, &d, &dlen) < 0
|| asn1_implicit_integer_from_der(2, serial, serial_len, &d, &dlen) < 0
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_authority_key_identifier_validate(const uint8_t *a, size_t alen)
{
const uint8_t *keyid;
size_t keyid_len;
const uint8_t *issuer;
size_t issuer_len;
const uint8_t *serial;
size_t serial_len;
if (x509_authority_key_identifier_from_der(
&keyid, &keyid_len,
&issuer, &issuer_len,
&serial, &serial_len, &a, &alen) != 1
|| asn1_length_is_zero(alen) != 1) {
error_print();
return -1;
}
if (!keyid && !issuer && !serial) {
error_print();
return -1;
}
if (issuer) {
/*
if (asn1_general_names_validate(issuer, issuer_len) != 1) {
error_print();
return -1;
}
*/
}
return 1;
}
int x509_authority_key_identifier_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if ((ret = asn1_implicit_octet_string_from_der(0, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) format_bytes(fp, fmt, ind, "keyIdentifier", p, len);
if ((ret = asn1_implicit_sequence_from_der(1, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_general_names_print(fp, fmt, ind, "authorityCertIssuer", p, len);
if ((ret = asn1_implicit_integer_from_der(2, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) format_bytes(fp, fmt, ind, "authorityCertSerialNumber", p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
static const char *x509_key_usages[] = {
"digitalSignature",
"nonRepudiation",
"keyEncipherment",
"dataEncipherment",
"keyAgreement",
"keyCertSign",
"cRLSign",
"encipherOnly",
"decipherOnly",
};
static size_t x509_key_usages_count =
sizeof(x509_key_usages)/sizeof(x509_key_usages[0]);
const char *x509_key_usage_name(int flag)
{
int i;
for (i = 0; i < x509_key_usages_count; i++) {
if (flag & 1) {
if (flag >> 1) {
error_print();
return NULL;
}
return x509_key_usages[i];
}
flag >>= 1;
}
error_print();
return NULL;
}
int x509_key_usage_from_name(int *flag, const char *name)
{
int i;
for (i = 0; i < x509_key_usages_count; i++) {
if (strcmp(name, x509_key_usages[i]) == 0) {
*flag = 1 << i;
return 1;
}
}
*flag = 0;
error_print();
return -1;
}
int x509_key_usage_validate(int bits, int cert_type)
{
switch (cert_type) {
case X509_cert_server_auth:
case X509_cert_client_auth:
if (!(bits & X509_KU_DIGITAL_SIGNATURE)
//&& !(bits & X509_KU_NON_REPUDIATION) // un-comment for compatibility
) {
error_print();
return -1;
}
if ((bits & X509_KU_KEY_CERT_SIGN)
|| (bits & X509_KU_CRL_SIGN)) {
error_print();
return -1;
}
break;
case X509_cert_server_key_encipher:
case X509_cert_client_key_encipher:
if (!(bits & X509_KU_KEY_ENCIPHERMENT)
//&& !(bits & X509_KU_KEY_AGREEMENT) // un-comment for compatibility
) {
error_print();
return -1;
}
if ((bits & X509_KU_KEY_CERT_SIGN)
|| (bits & X509_KU_CRL_SIGN)) {
error_print();
return -1;
}
break;
case X509_cert_ca:
if (!(bits & X509_KU_KEY_CERT_SIGN)) {
error_print();
return -1;
}
if ((bits & X509_KU_DIGITAL_SIGNATURE)
|| (bits & X509_KU_NON_REPUDIATION)) {
error_print();
//return -1; // comment to print warning
}
break;
case X509_cert_crl_sign:
if (!(bits & X509_KU_CRL_SIGN)) {
error_print();
return -1;
}
if ((bits & X509_KU_DIGITAL_SIGNATURE)
|| (bits & X509_KU_NON_REPUDIATION)) {
error_print();
//return -1; // comment to print warning
}
break;
default:
error_print();
return -1;
}
return 1;
}
int x509_key_usage_print(FILE *fp, int fmt, int ind, const char *label, int bits)
{
return asn1_bits_print(fp, fmt, ind, label, x509_key_usages, x509_key_usages_count, bits);
}
int x509_notice_reference_to_der(
int org_tag, const uint8_t *org, size_t org_len,
const int *notice_numbers, size_t notice_numbers_cnt,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (x509_display_text_to_der(org_tag, org, org_len, NULL, &len) != 1
|| asn1_sequence_of_int_to_der(notice_numbers, notice_numbers_cnt, NULL, &len) != 1
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_display_text_to_der(org_tag, org, org_len, out, outlen) != 1
|| asn1_sequence_of_int_to_der(notice_numbers, notice_numbers_cnt, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_notice_reference_from_der(
int *org_tag, const uint8_t **org, size_t *org_len,
int notice_numbers[X509_MAX_NOTICE_NUMBERS], size_t *notice_numbers_cnt, size_t max_notice_numbers, //FIXME: max_notice_numbers 还没检查
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
else error_print();
return ret;
}
if (x509_display_text_from_der(org_tag, org, org_len, &d, &dlen) != 1
|| asn1_sequence_of_int_from_der(notice_numbers, notice_numbers_cnt, X509_MAX_NOTICE_NUMBERS, &d, &dlen) != 1
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_notice_reference_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int tag;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if (x509_display_text_from_der(&tag, &p, &len, &d, &dlen) != 1) goto err;
x509_display_text_print(fp, fmt, ind, "organization", tag, p, len);
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) goto err;
asn1_sequence_of_int_print(fp, fmt, ind, "noticeNumbers", p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_user_notice_to_der(
int notice_ref_org_tag, const uint8_t *notice_ref_org, size_t notice_ref_org_len,
const int *notice_ref_notice_numbers, size_t notice_ref_notice_numbers_cnt,
int explicit_text_tag, const uint8_t *explicit_text, size_t explicit_text_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (x509_notice_reference_to_der(
notice_ref_org_tag, notice_ref_org, notice_ref_org_len,
notice_ref_notice_numbers, notice_ref_notice_numbers_cnt,
NULL, &len) < 0
|| x509_display_text_to_der(explicit_text_tag, explicit_text, explicit_text_len, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_notice_reference_to_der(
notice_ref_org_tag, notice_ref_org, notice_ref_org_len,
notice_ref_notice_numbers, notice_ref_notice_numbers_cnt,
out, outlen) < 0
|| x509_display_text_to_der(explicit_text_tag, explicit_text, explicit_text_len, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_user_notice_from_der(
int *notice_ref_org_tag, const uint8_t **notice_ref_org, size_t *notice_ref_org_len,
int *notice_ref_notice_numbers, size_t *notice_ref_notice_numbers_cnt, size_t max_notice_ref_notice_numbers, // FIXME: max_notice_ref_notice_numbers
int *explicit_text_tag, const uint8_t **explicit_text, size_t *explicit_text_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_notice_reference_from_der(notice_ref_org_tag, notice_ref_org, notice_ref_org_len,
notice_ref_notice_numbers, notice_ref_notice_numbers_cnt, max_notice_ref_notice_numbers, &d, &dlen) < 0
|| x509_display_text_from_der(explicit_text_tag, explicit_text, explicit_text_len, &d, &dlen) < 0
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_user_notice_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret;
const uint8_t *p;
size_t len;
int tag;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if ((ret = asn1_sequence_from_der(&p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_notice_reference_print(fp, fmt, ind, "noticeRef", p, len);
if ((ret = x509_display_text_from_der(&tag, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_display_text_print(fp, fmt, ind, "explicitText", tag, p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
// 是否要针对oid = cps的IA5String做一个方便的接口呢毕竟oid 只有两个可选项
int x509_policy_qualifier_info_to_der(
int oid,
const uint8_t *qualifier, size_t qualifier_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (x509_qualifier_id_to_der(oid, NULL, &len) != 1
|| asn1_any_to_der(qualifier, qualifier_len, NULL, &len) != 1
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_qualifier_id_to_der(oid, out, outlen) != 1
|| asn1_any_to_der(qualifier, qualifier_len, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_policy_qualifier_info_from_der(int *oid, const uint8_t **qualifier, size_t *qualifier_len, const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *p;
size_t len;
if ((ret = asn1_sequence_from_der(&p, &len, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_qualifier_id_from_der(oid, &p, &len) != 1
|| asn1_any_from_der(qualifier, qualifier_len, &p, &len) != 1
|| asn1_length_is_zero(len) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_policy_qualifier_info_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int oid;
const uint8_t *p;
size_t len;
if (x509_qualifier_id_from_der(&oid, &d, &dlen) != 1) goto err;
switch (oid) {
case OID_qt_cps:
if (asn1_ia5_string_from_der((const char **)&p, &len, &d, &dlen) != 1) goto err;
format_string(fp, fmt, ind, "cPSuri", p, len);
break;
case OID_qt_unotice:
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) goto err;
x509_user_notice_print(fp, fmt, ind, "userNotice", p, len);
break;
}
return 1;
err:
error_print();
return -1;
}
int x509_policy_qualifier_infos_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
while (dlen) {
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_policy_qualifier_info_print(fp, fmt, ind, "PolicyQualifierInfo", p, len);
}
return 1;
}
int x509_policy_information_to_der(
int oid, const uint32_t *nodes, size_t nodes_cnt,
const uint8_t *qualifiers, size_t qualifiers_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (x509_cert_policy_id_to_der(oid, nodes, nodes_cnt, NULL, &len) != 1
|| asn1_sequence_to_der(qualifiers, qualifiers_len, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_cert_policy_id_to_der(oid, nodes, nodes_cnt, out, outlen) != 1
|| asn1_sequence_to_der(qualifiers, qualifiers_len, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_policy_information_from_der(
int *oid, uint32_t *nodes, size_t *nodes_cnt,
const uint8_t **qualifiers, size_t *qualifiers_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_cert_policy_id_from_der(oid, nodes, nodes_cnt, &d, &dlen) != 1
|| asn1_sequence_from_der(qualifiers, qualifiers_len, &d, &dlen) < 0
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_policy_information_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret, oid;
uint32_t nodes[32];
size_t nodes_cnt;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if (x509_cert_policy_id_from_der(&oid, nodes, &nodes_cnt, &d, &dlen) != 1) goto err;
asn1_object_identifier_print(fp, fmt, ind, "policyIdentifier", x509_cert_policy_id_name(oid), nodes, nodes_cnt);
if ((ret = asn1_sequence_from_der(&p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_policy_qualifier_infos_print(fp, fmt, ind, "policyQualifiers", p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_certificate_policies_add_policy_information(uint8_t *d, size_t *dlen, size_t maxlen,
int policy_oid, const uint32_t *policy_nodes, size_t policy_nodes_cnt,
const uint8_t *qualifiers, size_t qualifiers_len)
{
error_print();
return -1;
}
int x509_certificate_polices_validate(const uint8_t *d, size_t dlen)
{
error_print();
return -1;
}
int x509_certificate_policies_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
while (dlen) {
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_policy_information_print(fp, fmt, ind, label, p, len);
}
return 1;
}
int x509_policy_mapping_to_der(
int issuer_policy_oid, const uint32_t *issuer_policy_nodes, size_t issuer_policy_nodes_cnt,
int subject_policy_oid, const uint32_t *subject_policy_nodes, size_t subject_policy_nodes_cnt,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (x509_cert_policy_id_to_der(issuer_policy_oid,
issuer_policy_nodes, issuer_policy_nodes_cnt, NULL, &len) != 1
|| x509_cert_policy_id_to_der(subject_policy_oid,
subject_policy_nodes, subject_policy_nodes_cnt, NULL, &len) != 1
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_cert_policy_id_to_der(issuer_policy_oid,
issuer_policy_nodes, issuer_policy_nodes_cnt, out, outlen) != 1
|| x509_cert_policy_id_to_der(subject_policy_oid,
subject_policy_nodes, subject_policy_nodes_cnt, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_policy_mapping_from_der(
int *issuer_policy_oid, uint32_t *issuer_policy_nodes, size_t *issuer_policy_nodes_cnt,
int *subject_policy_oid, uint32_t *subject_policy_nodes, size_t *subject_policy_nodes_cnt,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_cert_policy_id_from_der(issuer_policy_oid,
issuer_policy_nodes, issuer_policy_nodes_cnt, &d, &dlen) != 1
|| x509_cert_policy_id_from_der(subject_policy_oid,
subject_policy_nodes, subject_policy_nodes_cnt, &d, &dlen) != 1
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_policy_mapping_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int oid;
uint32_t nodes[32];
size_t nodes_cnt;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if (x509_cert_policy_id_from_der(&oid, nodes, &nodes_cnt, &d, &dlen) != 1) goto err;
asn1_object_identifier_print(fp, fmt, ind, "issuerDomainPolicy", x509_cert_policy_id_name(oid), nodes, nodes_cnt);
if (x509_cert_policy_id_from_der(&oid, nodes, &nodes_cnt, &d, &dlen) != 1) goto err;
asn1_object_identifier_print(fp, fmt, ind, "subjectDomainPolicy", x509_cert_policy_id_name(oid), nodes, nodes_cnt);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_policy_mapping_validate(const uint8_t *a, size_t alen)
{
return -1;
}
int x509_policy_mappings_validate(const uint8_t *d, size_t dlen)
{
return -1;
}
int x509_policy_mappings_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
while (dlen) {
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_policy_mapping_print(fp, fmt, ind, label, p, len);
}
return 1;
}
int x509_attribute_to_der(
const uint32_t *nodes, size_t nodes_cnt,
const uint8_t *values, size_t values_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (asn1_object_identifier_to_der(nodes, nodes_cnt, NULL, &len) != 1
|| asn1_set_to_der(values, values_len, NULL, &len) != 1
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| asn1_object_identifier_to_der(nodes, nodes_cnt, out, outlen) != 1
|| asn1_set_to_der(values, values_len, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_attribute_from_der(
int *oid, uint32_t *nodes, size_t *nodes_cnt,
const uint8_t **values, size_t *values_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *p;
size_t len;
*oid = OID_undef;
if ((ret = asn1_sequence_from_der(&p, &len, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (asn1_object_identifier_from_der(nodes, nodes_cnt, &p, &len) != 1
|| asn1_set_from_der(values, values_len, &p, &len) != 1
|| asn1_length_is_zero(len) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_attribute_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
uint32_t nodes[32];
size_t nodes_cnt;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if (asn1_object_identifier_from_der(nodes, &nodes_cnt, &d, &dlen) != 1) goto err;
asn1_object_identifier_print(fp, fmt, ind, "type", NULL, nodes, nodes_cnt);
if (asn1_set_from_der(&p, &len, &d, &dlen) != 1) goto err;
format_bytes(fp, fmt, ind, "values", p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_attributes_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
if (label) {
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
}
while (dlen) {
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_attribute_print(fp, fmt, ind, "Attribute", p, len);
}
return 1;
}
int x509_basic_constraints_to_der(int ca, int path_len_cons, uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (asn1_boolean_to_der(ca, NULL, &len) < 0
|| asn1_int_to_der(path_len_cons, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| asn1_boolean_to_der(ca, out, outlen) < 0
|| asn1_int_to_der(path_len_cons, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_basic_constraints_from_der(int *ca, int *path_len_cons, const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (!d || !dlen) {
*ca = -1;
*path_len_cons = -1;
return 1;
}
if (asn1_boolean_from_der(ca, &d, &dlen) < 0
|| asn1_int_from_der(path_len_cons, &d, &dlen) < 0
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
if (*ca < 0 && *path_len_cons < 0) {
error_print();
return -1;
}
// from_der() MUST NOT set default value to *ca
return 1;
}
// 这个函数原型可能要改一下
int x509_basic_constraints_validate(int ca, int path_len_cons, int cert_type)
{
/*
entity_cert:
ca = -1 or 0
path_len_constraints = -1
first_ca_cert:
ca = 1
path_len_constraints = 0
middle_ca_cert:
ca = 1
path_len_constraints = -1 or > 0
root_ca_cert:
ca = 1
path_len_constraints = -1 or > 0 (=0 might be ok?)
*/
if (cert_type == X509_cert_ca) {
if (ca != 1) {
error_print();
return -1;
}
if (path_len_cons < 0 || path_len_cons > 6) {
error_print();
return -1;
}
} else {
if (ca == 1 || path_len_cons >= 0) {
error_print();
return -1; // comment to only warning
}
}
return 1;
}
int x509_basic_constraints_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret, val;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
// BasicConstraints might be an empty sequence in entity certificates
if (!d || !dlen) {
return 1;
}
if ((ret = asn1_boolean_from_der(&val, &d, &dlen)) < 0) goto err;
if (ret) format_print(fp, fmt, ind, "cA: %s\n", asn1_boolean_name(val));
else format_print(fp, fmt, ind, "cA: %s\n", asn1_boolean_name(0)); // 特殊对待无论cA值是否编码均输出结果
if ((ret = asn1_int_from_der(&val, &d, &dlen)) < 0) goto err;
if (ret) format_print(fp, fmt, ind, "pathLenConstraint: %d\n", val);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_general_subtree_to_der(
int base_choice, const uint8_t *base, size_t base_len,
int minimum,
int maximum,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (x509_general_name_to_der(base_choice, base, base_len, NULL, &len) != 1
|| asn1_implicit_int_to_der(0, minimum, NULL, &len) < 0
|| asn1_implicit_int_to_der(1, maximum, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_general_name_to_der(base_choice, base, base_len, out, outlen) != 1
|| asn1_implicit_int_to_der(0, minimum, out, outlen) < 0
|| asn1_implicit_int_to_der(1, maximum, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_general_subtree_from_der(
int *base_choice, const uint8_t **base, size_t *base_len,
int *minimum,
int *maximum,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_general_name_from_der(base_choice, base, base_len, &d, &dlen) != 1
|| asn1_implicit_int_from_der(0, minimum, &d, &dlen) < 0
|| asn1_implicit_int_from_der(1, maximum, &d, &dlen) < 0
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
if (*minimum < 0) *minimum = 0;
return 1;
}
int x509_general_subtree_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret, choice, val;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if (x509_general_name_from_der(&choice, &p, &len, &d, &dlen) != 1) goto err;
x509_general_name_print(fp, fmt, ind, "base", choice, p, len);
if ((ret = asn1_implicit_int_from_der(0, &val, &d, &dlen)) < 0) goto err;
if (ret) format_print(fp, fmt, ind, "minimum: %d\n", val);
if ((ret = asn1_implicit_int_from_der(1, &val, &d, &dlen)) < 0) goto err;
if (ret) format_print(fp, fmt, ind, "maximum: %d\n", val);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_general_subtrees_add_general_subtree(uint8_t *d, size_t *dlen, size_t maxlen,
int base_choice, const uint8_t *base, size_t base_len,
int minimum, int maximum)
{
error_print();
return -1;
}
int x509_general_subtrees_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
while (dlen) {
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_general_subtree_print(fp, fmt, ind, "GeneralSubtree", p, len);
}
return 1;
}
int x509_name_constraints_to_der(
const uint8_t *permitted_subtrees, size_t permitted_subtrees_len,
const uint8_t *excluded_subtrees, size_t excluded_subtrees_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (asn1_implicit_sequence_to_der(0, permitted_subtrees, permitted_subtrees_len, NULL, &len) < 0
|| asn1_implicit_sequence_to_der(1, excluded_subtrees, excluded_subtrees_len, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| asn1_implicit_sequence_to_der(0, permitted_subtrees, permitted_subtrees_len, out, outlen) < 0
|| asn1_implicit_sequence_to_der(1, excluded_subtrees, excluded_subtrees_len, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_name_constraints_from_der(
const uint8_t **permitted_subtrees, size_t *permitted_subtrees_len,
const uint8_t **excluded_subtrees, size_t *excluded_subtrees_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
*permitted_subtrees = NULL;
*permitted_subtrees_len = 0;
*excluded_subtrees = NULL;
*excluded_subtrees_len = 0;
if (asn1_implicit_sequence_from_der(0, permitted_subtrees, permitted_subtrees_len, &d, &dlen) < 0
|| asn1_implicit_sequence_from_der(1, excluded_subtrees, excluded_subtrees_len, &d, &dlen) < 0
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_name_constraints_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if ((ret = asn1_implicit_sequence_from_der(0, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_general_subtrees_print(fp, fmt, ind, "permittedSubtrees", p, len);
if ((ret = asn1_implicit_sequence_from_der(1, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_general_subtrees_print(fp, fmt, ind, "excludedSubtrees", p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_policy_constraints_to_der(
int require_explicit_policy,
int inhibit_policy_mapping,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (asn1_implicit_int_to_der(0, require_explicit_policy, NULL, &len) < 0
|| asn1_implicit_int_to_der(1, inhibit_policy_mapping, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| asn1_implicit_int_to_der(0, require_explicit_policy, out, outlen) < 0
|| asn1_implicit_int_to_der(1, inhibit_policy_mapping, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_policy_constraints_from_der(
int *require_explicit_policy,
int *inhibit_policy_mapping,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
*require_explicit_policy = -1;
*inhibit_policy_mapping = -1;
if (asn1_implicit_int_from_der(0, require_explicit_policy, &d, &dlen) < 0
|| asn1_implicit_int_from_der(1, inhibit_policy_mapping, &d, &dlen) < 0
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_policy_constraints_validate(const uint8_t *a, size_t alen)
{
return -1;
}
int x509_policy_constraints_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret, val;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if ((ret = asn1_implicit_int_from_der(0, &val, &d, &dlen)) < 0) goto err;
if (ret) format_print(fp, fmt, ind, "requireExplicitPolicy: %d\n", val);
if ((ret = asn1_implicit_int_from_der(1, &val, &d, &dlen)) < 0) goto err;
if (ret) format_print(fp, fmt, ind, "inhibitPolicyMapping: %d\n", val);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_ext_key_usage_to_der(const int *oids, size_t oids_cnt, uint8_t **out, size_t *outlen)
{
size_t len = 0;
size_t i;
if (oids_cnt > X509_MAX_KEY_PURPOSES) {
error_print();
return -1;
}
for (i = 0; i < oids_cnt; i++) {
if (x509_key_purpose_to_der(oids[i], NULL, &len) != 1) {
error_print();
return -1;
}
}
if (asn1_sequence_header_to_der(len, out, outlen) != 1) {
error_print();
return -1;
}
for (i = 0; i < oids_cnt; i++) {
if (x509_key_purpose_to_der(oids[i], out, outlen) != 1) {
error_print();
return -1;
}
}
return 1;
}
int x509_ext_key_usage_from_der(int *oids, size_t *oids_cnt, size_t max_cnt, const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *p;
size_t len;
*oids_cnt = 0;
if ((ret = asn1_sequence_from_der(&p, &len, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
while (len && (*oids_cnt < max_cnt)) {
if (x509_key_purpose_from_der(oids, &p, &len) != 1) {
error_print();
return -1;
}
oids++;
(*oids_cnt)++;
}
if (len) {
error_print();
return -1;
}
return 1;
}
// 这个函数原型可能也要改一下
int x509_ext_key_usage_validate(const int *oids, size_t oids_cnt, int cert_type)
{
int ret = -1;
size_t i;
for (i = 0; i < oids_cnt; i++) {
// anyExtendedKeyUsage might not acceptable for strict validation
if (oids[i] == OID_any_extended_key_usage) {
ret = 0;
}
switch (cert_type) {
case X509_cert_server_auth:
case X509_cert_server_key_encipher:
if (oids[i] == OID_kp_server_auth) {
return 1;
}
break;
case X509_cert_client_auth:
case X509_cert_client_key_encipher:
if (oids[i] == OID_kp_client_auth) {
return 1;
}
break;
default:
error_print();
return -1;
}
}
return ret;
}
int x509_ext_key_usage_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int oid;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
while (dlen) {
if (x509_key_purpose_from_der(&oid, &d, &dlen) != 1) {
error_print();
return -1;
}
format_print(fp, fmt, ind, "%s\n", x509_key_purpose_name(oid));
}
return 1;
}
/*
CRL Distribution Points
* 假设证书的cRLIssuer和issuer一致即签发证书的CA也签发了证书所属的CRL
不支持可选的cRLIssuer编码解码时忽略cRLIssuer
编解、解码时均要求DistributionPoint中包含distributionPoint
* 如果证书扩展中不包含reasons那么在CRL中必须包含完整的reasons。
证书扩展中包含reasons逻辑上意味由不同的CRL包含因不同原因注销的证书RFC不推荐这种方式
* 编码时最多支持2个DistributionPoint分别用于HTTP和LDAP的URI
即DistributionPointName CHOICE GeneralNames其中只有一个GeneralName CHOICE uri
* 解码时对每个解析成功的DistributionPoint的uri进行判断
最多返回一个http和一个ldap其他协议的uri被忽略
* RFC要求每个DistributionPoint中至少包含一个HTTP或LDAP uri
* 解码时不支持DistributionPointName为nameRelativeToCRLIssuer
解码时DistributionPointName为(GeneralNames)fullName时只返回第一个CHOICE为uri的GeneralName
* 当uri为HTTP时CRL文件为DER编码
* 当uri为LDAP时
如 ldap://ldap.example.com/cn=example%20CA,dc=example,dc=com?certificateRevocationList;binary
如 ldap:///cn=example%20CA,dc=example,dc=com?certificateRevocationList;binary
必须包含 DN, certificateRevocationList 应包含host部分
*/
static const char *x509_revoke_reason_flags[] = {
"unused",
"keyCompromise",
"cACompromise",
"affiliationChanged",
"superseded",
"cessationOfOperation",
"certificateHold",
"privilegeWithdrawn",
"aACompromise",
};
static size_t x509_revoke_reason_flags_count =
sizeof(x509_revoke_reason_flags)/sizeof(x509_revoke_reason_flags[0]);
const char *x509_revoke_reason_flag_name(int flag)
{
int i;
for (i = 0; i < x509_revoke_reason_flags_count; i++) {
if (flag & 1) {
if (flag >> 1) {
error_print();
return NULL;
}
return x509_revoke_reason_flags[i];
}
flag >>= 1;
}
return NULL;
}
int x509_revoke_reason_flag_from_name(int *flag, const char *name)
{
int i;
for (i = 0; i < x509_revoke_reason_flags_count; i++) {
if (strcmp(name, x509_revoke_reason_flags[i]) == 0) {
*flag = 1 << i;
return 1;
}
}
*flag = 0;
error_print();
return -1;
}
int x509_revoke_reason_flags_print(FILE *fp, int fmt, int ind, const char *label, int bits)
{
return asn1_bits_print(fp, fmt, ind, label, x509_revoke_reason_flags, x509_revoke_reason_flags_count, bits);
}
/*
Example CRLDistributionPoints extension
1 DistributionPoint in CRLDistributionPoints
distributionPoint choice: fullName
2 GeneralName in fullName(GeneralNames), same CRL with different URI
Extension
extnID: CRLDistributionPoints (2.5.29.31)
CRLDistributionPoints
DistributionPoint
distributionPoint
fullName
GeneralName
URI: http://mscrl.microsoft.com/pki/mscorp/crl/Microsoft%20RSA%20TLS%20CA%2002.crl
GeneralName
URI: http://crl.microsoft.com/pki/mscorp/crl/Microsoft%20RSA%20TLS%20CA%2002.crl
*/
int x509_uri_as_distribution_point_name_to_der(const char *uri, size_t urilen,
uint8_t **out, size_t *outlen)
{
int ret;
if ((ret = x509_uri_as_general_names_to_der_ex(ASN1_TAG_EXPLICIT(0), uri, urilen, out, outlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
return 1;
}
int x509_distribution_point_name_from_der(int *choice, const uint8_t **d, size_t *dlen,
const uint8_t **in, size_t *inlen)
{
int ret;
int tag;
if ((ret = asn1_any_type_from_der(&tag, d, dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
switch (tag) {
case ASN1_TAG_EXPLICIT(0):
*choice = 0;
// 此时返回的值是GeneralNames的d,dlen因此这个返回值不能用general_names_from来解析
break;
case ASN1_TAG_EXPLICIT(1):
*choice = 1;
break;
default:
error_print();
return -1;
}
return 1;
}
int x509_uri_as_distribution_point_name_from_der(const char **uri, size_t *urilen,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
int choice;
if ((ret = x509_distribution_point_name_from_der(&choice, &d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (choice == 0) {
if (x509_general_names_get_first(d, dlen, NULL, choice, (const uint8_t **)uri, urilen) < 0) {
error_print();
return -1;
}
}
return 1;
}
int x509_distribution_point_name_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *a, size_t alen)
{
int tag;
const uint8_t *d;
size_t dlen;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if (asn1_any_type_from_der(&tag, &d, &dlen, &a, &alen) != 1) {
error_print();
return -1;
}
switch (tag) {
case ASN1_TAG_EXPLICIT(0): return x509_general_names_print(fp, fmt, ind, "fullName", d, dlen);
case ASN1_TAG_IMPLICIT(1): return x509_rdn_print(fp, fmt, ind, "nameRelativeToCRLIssuer", d, dlen);
default:
error_print();
return -1;
}
return 1;
}
int x509_uri_as_explicit_distribution_point_name_to_der(int index,
const char *uri, size_t urilen, uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (!uri || !urilen) {
return 0;
}
if (x509_uri_as_distribution_point_name_to_der(uri, urilen, NULL, &len) != 1
|| asn1_explicit_header_to_der(index, len, out, outlen) != 1
|| x509_uri_as_distribution_point_name_to_der(uri, urilen, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_uri_as_explicit_distribution_point_name_from_der(int index,
const char **uri, size_t *urilen, const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *a;
size_t alen;
if ((ret = asn1_explicit_from_der(index, &a, &alen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_uri_as_distribution_point_name_from_der(uri, urilen, &a, &alen) != 1
|| asn1_length_is_zero(alen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_uri_as_distribution_point_to_der(const char *uri, size_t urilen,
int reasons, const uint8_t *crl_issuer, size_t crl_issuer_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (!uri || !urilen) {
return 0;
}
if (x509_uri_as_explicit_distribution_point_name_to_der(0, uri, urilen, NULL, &len) != 1
|| x509_revoke_reason_flags_to_der(reasons, NULL, &len) < 0
|| x509_general_names_to_der(crl_issuer, crl_issuer_len, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_uri_as_explicit_distribution_point_name_to_der(0, uri, urilen, out, outlen) != 1
|| x509_revoke_reason_flags_to_der(reasons, out, outlen) < 0
|| x509_general_names_to_der(crl_issuer, crl_issuer_len, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
int x509_uri_as_distribution_point_from_der(const char **uri, size_t *urilen,
int *reasons, const uint8_t **crl_issuer, size_t *crl_issuer_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
if (x509_uri_as_explicit_distribution_point_name_from_der(0, uri, urilen, &d, &dlen) != 1
|| x509_revoke_reason_flags_from_der(reasons, &d, &dlen) < 0
|| x509_general_names_from_der(crl_issuer, crl_issuer_len, &d, &dlen) < 0
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_distribution_point_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int ret;
const uint8_t *p;
size_t len;
int bits;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if ((ret = asn1_explicit_from_der(0, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_distribution_point_name_print(fp, fmt, ind, "distributionPoint", p, len);
if ((ret = asn1_implicit_bits_from_der(1, &bits, &d, &dlen)) < 0) goto err;
if (ret) x509_revoke_reason_flags_print(fp, fmt, ind, "reasons", bits);
if ((ret = asn1_implicit_sequence_from_der(2, &p, &len, &d, &dlen)) < 0) goto err;
if (ret) x509_general_names_print(fp, fmt, ind, "cRLIssuer", p, len);
if (asn1_length_is_zero(dlen) != 1) goto err;
return 1;
err:
error_print();
return -1;
}
int x509_uri_as_distribution_points_to_der(const char *uri, size_t urilen,
int reasons, const uint8_t *crl_issuer, size_t crl_issuer_len,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (!uri || !urilen) {
return 0;
}
if (x509_uri_as_distribution_point_to_der(uri, urilen,
reasons, crl_issuer, crl_issuer_len, NULL, &len) < 0
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_uri_as_distribution_point_to_der(uri, urilen,
reasons, crl_issuer, crl_issuer_len, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
/*
DistributionPoints :== SEQUENCE OF DistributionPoint
80 Len -- DistributionPoints header
80 Len DistributionPoint 1 header
80 EXPLICIT(0)
DistributionPointName
80 len d,dln GeneralNames
06 len URI
01 len d,dlen ReasonFlags
82 len, d,dlen GeneralNames
80 Len DistributionPoint 2 header
80 Len DistributionPoint 3 header
*/
int x509_uri_as_distribution_points_from_der(const char **uri, size_t *urilen,
int *reasons, const uint8_t **crl_issuer, size_t *crl_issuer_len,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
*uri = NULL;
*urilen = 0;
while (dlen) {
if (x509_uri_as_distribution_point_from_der(uri, urilen, reasons, crl_issuer, crl_issuer_len, &d, &dlen) != 1) {
error_print();
return -1;
}
if (*uri) {
return 1;
}
}
return 1;
}
int x509_distribution_points_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
if (label) {
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
}
while (dlen) {
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_distribution_point_print(fp, fmt, ind, "DistributionPoint", p, len);
}
return 1;
}
static const char *netscape_cert_types[] = {
"SSL Client certificate",
"SSL Server certificate",
"S/MIME certificate",
"Object-signing certificate",
"Reserved for future use",
"SSL CA certificate",
"S/MIME CA certificate",
"Object-signing CA certificate",
};
int x509_netscape_cert_type_print(FILE *fp, int fmt, int ind, const char *label, int bits)
{
return asn1_bits_print(fp, fmt, ind, label, netscape_cert_types,
sizeof(netscape_cert_types)/sizeof(netscape_cert_types[0]), bits);
}
int x509_exts_validate(const uint8_t *exts, size_t extslen, int cert_type,
int *path_len_constraints)
{
int oid;
uint32_t nodes[32];
size_t nodes_cnt;
int critical;
const uint8_t *val;
size_t vlen;
int ca = -1;
int path_len = -1;
int key_usage;
int ext_key_usages[X509_MAX_KEY_PURPOSES];
size_t ext_key_usages_cnt;
*path_len_constraints = -1;
while (extslen) {
if (x509_ext_from_der(&oid, nodes, &nodes_cnt, &critical, &val, &vlen, &exts, &extslen) != 1) {
error_print();
return -1;
}
switch (oid) {
case OID_ce_authority_key_identifier:
if (critical == X509_critical) {
error_print();
return -1;
}
/*
if (x509_authority_key_identifier(val, vlen) != 1) {
error_print();
return -1;
}
*/
break;
case OID_ce_subject_key_identifier:
if (critical == X509_critical) {
error_print();
return -1;
}
const uint8_t *p;
size_t len;
if (asn1_octet_string_from_der(&p, &len, &val, &vlen) != 1
|| asn1_length_is_zero(vlen) != 1) {
error_print();
return -1;
}
if (!p || !len) {
error_print();
return -1;
}
break;
case OID_ce_key_usage:
if (critical != X509_critical) {
error_print();
// conforming CAs SHOULD mark this extension as critical.
}
if (asn1_bits_from_der(&key_usage, &val, &vlen) != 1
|| x509_key_usage_validate(key_usage, cert_type) != 1) {
error_print();
return -1;
}
break;
case OID_ce_certificate_policies:
break;
case OID_ce_policy_mappings:
if (critical != X509_critical) {
error_print();
return -1;
}
break;
case OID_ce_subject_alt_name:
break;
case OID_ce_issuer_alt_name:
if (critical == X509_critical) {
error_print();
return -1;
}
break;
case OID_ce_subject_directory_attributes:
if (critical == X509_critical) {
error_print();
return -1;
}
break;
case OID_ce_basic_constraints:
if (x509_basic_constraints_from_der(&ca, &path_len, &val, &vlen) != 1
|| x509_basic_constraints_validate(ca, path_len, cert_type) != 1) {
error_print();
return -1;
}
break;
case OID_ce_ext_key_usage:
if (x509_ext_key_usage_from_der(ext_key_usages, &ext_key_usages_cnt,
sizeof(ext_key_usages)/sizeof(ext_key_usages[0]), &val, &vlen) != 1
|| x509_ext_key_usage_validate(ext_key_usages, ext_key_usages_cnt, cert_type) != 1) {
error_print();
return -1;
}
break;
case OID_ce_name_constraints:
case OID_ce_policy_constraints:
case OID_ce_crl_distribution_points:
case OID_ce_inhibit_any_policy:
case OID_ce_freshest_crl:
break;
default:
if (critical == X509_critical) {
error_print();
return -1;
}
}
}
switch (cert_type) {
case X509_cert_ca:
if (ca != 1 || path_len < 0) {
error_print();
return -1;
}
*path_len_constraints = path_len;
break;
}
return 1;
}
// AuthorityInfoAccess Extension
static uint32_t oid_ad_ocsp[] = { oid_ad,1 };
static uint32_t oid_ad_ca_issuers[] = { oid_ad,2 };
#define cnt(oid) (sizeof(oid)/sizeof((oid)[0]))
static const ASN1_OID_INFO access_methods[] = {
{ OID_ad_ocsp, "OCSP", oid_ad_ocsp, oid_cnt(oid_ad_ocsp) },
{ OID_ad_ca_issuers, "CAIssuers", oid_ad_ca_issuers, oid_cnt(oid_ad_ca_issuers) },
};
const char *x509_access_method_name(int oid)
{
const ASN1_OID_INFO *info;
if (!(info = asn1_oid_info_from_oid(access_methods, cnt(access_methods), oid))) {
error_print();
return NULL;
}
return info->name;
}
int x509_access_method_from_name(const char *name)
{
const ASN1_OID_INFO *info;
if (!(info = asn1_oid_info_from_name(access_methods, cnt(access_methods), name))) {
error_print();
return OID_undef;
}
return info->oid;
}
int x509_access_method_to_der(int oid, uint8_t **out, size_t *outlen)
{
const ASN1_OID_INFO *info;
if (!(info = asn1_oid_info_from_oid(access_methods, cnt(access_methods), oid))) {
error_print();
return -1;
}
if (asn1_object_identifier_to_der(info->nodes, info->nodes_cnt, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_access_method_from_der(int *oid, const uint8_t **in, size_t *inlen)
{
int ret;
const ASN1_OID_INFO *info;
uint32_t nodes[32];
size_t nodes_cnt;
if ((ret = asn1_oid_info_from_der_ex(&info, nodes, &nodes_cnt, access_methods, cnt(access_methods), in, inlen)) != 1) {
if (ret < 0) error_print();
else *oid = -1;
return ret;
}
*oid = info->oid;
return 1;
}
// currently AccessDescription not support values of SubjectInfoAccess extension
int x509_access_description_to_der(int oid, const char *uri, size_t urilen, uint8_t **out, size_t *outlen)
{
const int uri_choice = X509_gn_uniform_resource_identifier;
size_t len = 0;
if (oid != OID_ad_ocsp && oid != OID_ad_ca_issuers) {
error_print();
return -1;
}
if (!uri || !urilen) {
error_print();
return -1;
}
if (x509_access_method_to_der(oid, NULL, &len) != 1
|| x509_general_name_to_der(uri_choice, (const uint8_t *)uri, urilen, NULL, &len) != 1
|| asn1_sequence_header_to_der(len, out, outlen) != 1
|| x509_access_method_to_der(oid, out, outlen) != 1
|| x509_general_name_to_der(uri_choice, (const uint8_t *)uri, urilen, out, outlen) != 1) {
error_print();
return -1;
}
return 1;
}
int x509_access_description_from_der(int *oid, const char **uri, size_t *urilen, const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
int uri_choice;
if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
else {
*oid = -1;
*uri = NULL;
*urilen = 0;
}
return ret;
}
if (x509_access_method_from_der(oid, &d, &dlen) != 1
|| x509_general_name_from_der(&uri_choice, (const uint8_t **)uri, urilen, &d, &dlen) != 1
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
if (uri_choice != X509_gn_uniform_resource_identifier) {
error_print();
return -1;
}
if (*uri == NULL || *urilen == 0) {
error_print();
return -1;
}
return 1;
}
int x509_access_description_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
int oid;
int choice;
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
if (x509_access_method_from_der(&oid, &d, &dlen) != 1) {
error_print();
return -1;
}
format_print(fp, fmt, ind, "accessMethod: %s\n", x509_access_method_name(oid));
if (x509_general_name_from_der(&choice, &p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_general_name_print(fp, fmt, ind, "GeneralName", choice, p, len);
if (dlen) {
error_print();
return -1;
}
return 1;
}
int x509_authority_info_access_to_der(
const char *ca_issuers_uri, size_t ca_issuers_urilen,
const char *ocsp_uri, size_t ocsp_urilen,
uint8_t **out, size_t *outlen)
{
size_t len = 0;
if (ca_issuers_uri && ca_issuers_urilen) {
if (x509_access_description_to_der(OID_ad_ca_issuers, ca_issuers_uri, ca_issuers_urilen, NULL, &len) != 1) {
error_print();
return -1;
}
}
if (ocsp_uri && ocsp_urilen) {
if (x509_access_description_to_der(OID_ad_ocsp, ocsp_uri, ocsp_urilen, NULL, &len) != 1) {
error_print();
return -1;
}
}
if (!len) {
error_print();
return -1;
}
if (asn1_sequence_header_to_der(len, out, outlen) != 1) {
error_print();
return -1;
}
if (ca_issuers_uri && ca_issuers_urilen) {
if (x509_access_description_to_der(OID_ad_ca_issuers, ca_issuers_uri, ca_issuers_urilen, out, outlen) != 1) {
error_print();
return -1;
}
}
if (ocsp_uri && ocsp_urilen) {
if (x509_access_description_to_der(OID_ad_ocsp, ocsp_uri, ocsp_urilen, out, outlen) != 1) {
error_print();
return -1;
}
}
return 1;
}
int x509_authority_info_access_from_der(
const char **ca_issuers_uri, size_t *ca_issuers_urilen,
const char **ocsp_uri, size_t *ocsp_urilen,
const uint8_t **in, size_t *inlen)
{
int ret;
const uint8_t *d;
size_t dlen;
const uint8_t *ad;
size_t adlen;
if (!ca_issuers_uri || !ca_issuers_urilen || !ocsp_uri || !ocsp_urilen || !in || !(*in) || !inlen) {
error_print();
return -1;
}
*ca_issuers_uri = NULL;
*ca_issuers_urilen = 0;
*ocsp_uri = NULL;
*ocsp_urilen = 0;
if ((ret = asn1_sequence_of_from_der(&d, &dlen, in, inlen)) != 1) {
if (ret < 0) error_print();
return ret;
}
while (dlen) {
int oid;
const char *uri;
size_t urilen;
if (x509_access_description_from_der(&oid, &uri, &urilen, &d, &dlen) != 1) {
error_print();
return -1;
}
switch (oid) {
case OID_ad_ca_issuers:
if (*ca_issuers_uri) {
error_print();
return -1;
}
*ca_issuers_uri = uri;
*ca_issuers_urilen = urilen;
break;
case OID_ad_ocsp:
if (*ocsp_uri) {
error_print();
return -1;
}
*ocsp_uri = uri;
*ocsp_urilen = urilen;
break;
default:
error_print();
return -1;
}
}
return 1;
}
int x509_authority_info_access_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
format_print(fp, fmt, ind, "%s\n", label);
ind += 4;
while (dlen) {
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1) {
error_print();
return -1;
}
x509_access_description_print(fp, fmt, ind, "AccessDescription", p, len);
}
return 1;
}
int x509_exts_add_authority_info_access(uint8_t *exts, size_t *extslen, size_t maxlen, int critical,
const char *ca_issuers_uri, size_t ca_issuers_urilen, const char *ocsp_uri, size_t ocsp_urilen)
{
int oid = OID_pe_authority_info_access;
size_t curlen = *extslen;
uint8_t val[256];
uint8_t *p = val;
size_t vlen = 0;
size_t len = 0;
// Conforming CAs MUST mark this extension as non-critical.
if (critical == 1) {
error_print();
return -1;
}
if (x509_authority_info_access_to_der(ca_issuers_uri, ca_issuers_urilen, ocsp_uri, ocsp_urilen, NULL, &len) != 1
|| asn1_length_le(len, sizeof(val)) != 1
|| x509_authority_info_access_to_der(ca_issuers_uri, ca_issuers_urilen, ocsp_uri, ocsp_urilen, &p, &vlen) != 1) {
error_print();
return -1;
}
exts += *extslen;
if (x509_ext_to_der(oid, critical, val, vlen, NULL, &curlen) != 1
|| asn1_length_le(curlen, maxlen) != 1
|| x509_ext_to_der(oid, critical, val, vlen, &exts, extslen) != 1) {
error_print();
return -1;
}
return 1;
}