[openssl] master update
beldmit at gmail.com
beldmit at gmail.com
Wed Aug 26 11:06:43 UTC 2020
The branch master has been updated
via a149f7502458dc022501c2347629cc847f2e1298 (commit)
via 26930bd3c28db93323af566d537012477de2f820 (commit)
via 8a302080c3fedfa02ad4b17e3f2d44dd006c08a0 (commit)
via a0188e284e4a34d4e03eeaa4f09a97ed787a848b (commit)
via 0bf093be31f4a485104068e8f52dd99b242480cd (commit)
via 90c9319d47e9f8bdeea2a60362528e2061109544 (commit)
via 69d9245996088c485072aa4d4e86baec49ccaa80 (commit)
via 4650f2b5908947a318ff28b0970b7d451fac32ae (commit)
from eb800ef5533947b8583d42a8f767f6ff385d2c17 (commit)
- Log -----------------------------------------------------------------
commit a149f7502458dc022501c2347629cc847f2e1298
Author: Dmitry Belyavskiy <beldmit at gmail.com>
Date: Wed Jul 1 10:24:51 2020 +0300
Replace hierogliphs with stub to pass tests
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
commit 26930bd3c28db93323af566d537012477de2f820
Author: Dmitry Belyavskiy <beldmit at gmail.com>
Date: Mon Oct 14 16:35:42 2019 +0300
Documentation for internal PUNYCODE-related functions
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
commit 8a302080c3fedfa02ad4b17e3f2d44dd006c08a0
Author: Dmitry Belyavskiy <beldmit at gmail.com>
Date: Wed Sep 18 21:27:17 2019 +0300
EAI test script and data
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
commit a0188e284e4a34d4e03eeaa4f09a97ed787a848b
Author: Dmitry Belyavskiy <beldmit at gmail.com>
Date: Wed Aug 21 18:36:10 2019 +0300
RFC 8398: documentation
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
commit 0bf093be31f4a485104068e8f52dd99b242480cd
Author: Dmitry Belyavskiy <beldmit at gmail.com>
Date: Wed Aug 21 18:35:45 2019 +0300
Add NID_id_on_SmtpUTF8Mailbox to table of X.509 attributes
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
commit 90c9319d47e9f8bdeea2a60362528e2061109544
Author: Dmitry Belyavskiy <beldmit at gmail.com>
Date: Wed Aug 21 18:34:27 2019 +0300
RFC 8398: EAI comparison
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
commit 69d9245996088c485072aa4d4e86baec49ccaa80
Author: Dmitry Belyavskiy <beldmit at gmail.com>
Date: Wed Aug 21 18:33:14 2019 +0300
RFC 8398: Name constraints validation
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
commit 4650f2b5908947a318ff28b0970b7d451fac32ae
Author: Dmitry Belyavskiy <beldmit at gmail.com>
Date: Wed Aug 21 18:31:43 2019 +0300
Punycode decoding implementation
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9654)
-----------------------------------------------------------------------
Summary of changes:
crypto/asn1/tbl_standard.h | 3 +-
crypto/build.info | 1 +
crypto/punycode.c | 338 ++++++++++++++++++++++++++
crypto/x509/v3_ncons.c | 73 +++++-
crypto/x509/v3_utl.c | 18 +-
doc/internal/man3/ossl_punycode_decode.pod | 60 +++++
doc/man3/X509_check_host.pod | 8 +-
doc/man5/x509v3_config.pod | 9 +
include/crypto/punycode.h | 24 ++
test/recipes/25-test_eai_data.t | 53 ++++
test/recipes/25-test_eai_data/ascii_chain.pem | 53 ++++
test/recipes/25-test_eai_data/ascii_leaf.pem | 28 +++
test/recipes/25-test_eai_data/san.ascii | 2 +
test/recipes/25-test_eai_data/san.utf8 | 2 +
test/recipes/25-test_eai_data/utf8_chain.pem | 53 ++++
test/recipes/25-test_eai_data/utf8_leaf.pem | 28 +++
16 files changed, 743 insertions(+), 10 deletions(-)
create mode 100644 crypto/punycode.c
create mode 100644 doc/internal/man3/ossl_punycode_decode.pod
create mode 100644 include/crypto/punycode.h
create mode 100644 test/recipes/25-test_eai_data.t
create mode 100644 test/recipes/25-test_eai_data/ascii_chain.pem
create mode 100644 test/recipes/25-test_eai_data/ascii_leaf.pem
create mode 100644 test/recipes/25-test_eai_data/san.ascii
create mode 100644 test/recipes/25-test_eai_data/san.utf8
create mode 100644 test/recipes/25-test_eai_data/utf8_chain.pem
create mode 100644 test/recipes/25-test_eai_data/utf8_leaf.pem
diff --git a/crypto/asn1/tbl_standard.h b/crypto/asn1/tbl_standard.h
index f1750fe78f..ea6ad2847a 100644
--- a/crypto/asn1/tbl_standard.h
+++ b/crypto/asn1/tbl_standard.h
@@ -56,6 +56,7 @@ static const ASN1_STRING_TABLE tbl_standard[] = {
{NID_SNILS, 1, 11, B_ASN1_NUMERICSTRING, STABLE_NO_MASK},
{NID_countryCode3c, 3, 3, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
{NID_countryCode3n, 3, 3, B_ASN1_NUMERICSTRING, STABLE_NO_MASK},
- {NID_dnsName, 0, -1, B_ASN1_UTF8STRING, STABLE_NO_MASK}
+ {NID_dnsName, 0, -1, B_ASN1_UTF8STRING, STABLE_NO_MASK},
+ {NID_id_on_SmtpUTF8Mailbox, 1, ub_email_address, B_ASN1_UTF8STRING, STABLE_NO_MASK}
};
diff --git a/crypto/build.info b/crypto/build.info
index 9e10145d3c..814d8dcab7 100644
--- a/crypto/build.info
+++ b/crypto/build.info
@@ -78,6 +78,7 @@ SOURCE[../libcrypto]=$UTIL_COMMON \
mem.c mem_sec.c \
cversion.c info.c cpt_err.c ebcdic.c uid.c o_time.c o_dir.c \
o_fopen.c getenv.c o_init.c init.c trace.c provider.c \
+ punycode.c \
$UPLINKSRC
SOURCE[../providers/libfips.a]=$UTIL_COMMON
SOURCE[../providers/liblegacy.a]=$UTIL_COMMON
diff --git a/crypto/punycode.c b/crypto/punycode.c
new file mode 100644
index 0000000000..2607d6157a
--- /dev/null
+++ b/crypto/punycode.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <openssl/e_os2.h>
+#include "crypto/punycode.h"
+
+static const unsigned int base = 36;
+static const unsigned int tmin = 1;
+static const unsigned int tmax = 26;
+static const unsigned int skew = 38;
+static const unsigned int damp = 700;
+static const unsigned int initial_bias = 72;
+static const unsigned int initial_n = 0x80;
+static const unsigned int maxint = 0xFFFFFFFF;
+static const char delimiter = '-';
+
+#define LABEL_BUF_SIZE 512
+
+/*-
+ * Pseudocode:
+ *
+ * function adapt(delta,numpoints,firsttime):
+ * if firsttime then let delta = delta div damp
+ * else let delta = delta div 2
+ * let delta = delta + (delta div numpoints)
+ * let k = 0
+ * while delta > ((base - tmin) * tmax) div 2 do begin
+ * let delta = delta div (base - tmin)
+ * let k = k + base
+ * end
+ * return k + (((base - tmin + 1) * delta) div (delta + skew))
+ */
+
+static int adapt(unsigned int delta, unsigned int numpoints,
+ unsigned int firsttime)
+{
+ unsigned int k = 0;
+
+ delta = (firsttime) ? delta / damp : delta / 2;
+ delta = delta + delta / numpoints;
+
+ while (delta > ((base - tmin) * tmax) / 2) {
+ delta = delta / (base - tmin);
+ k = k + base;
+ }
+
+ return k + (((base - tmin + 1) * delta) / (delta + skew));
+}
+
+static ossl_inline int is_basic(unsigned int a)
+{
+ return (a < 0x80) ? 1 : 0;
+}
+
+/*-
+ * code points digit-values
+ * ------------ ----------------------
+ * 41..5A (A-Z) = 0 to 25, respectively
+ * 61..7A (a-z) = 0 to 25, respectively
+ * 30..39 (0-9) = 26 to 35, respectively
+ */
+static ossl_inline int digit_decoded(const unsigned char a)
+{
+ if (a >= 0x41 && a <= 0x5A)
+ return a - 0x41;
+
+ if (a >= 0x61 && a <= 0x7A)
+ return a - 0x61;
+
+ if (a >= 0x30 && a <= 0x39)
+ return a - 0x30 + 26;
+
+ return -1;
+}
+
+/*-
+ * Pseudocode:
+ *
+ * function ossl_punycode_decode
+ * let n = initial_n
+ * let i = 0
+ * let bias = initial_bias
+ * let output = an empty string indexed from 0
+ * consume all code points before the last delimiter (if there is one)
+ * and copy them to output, fail on any non-basic code point
+ * if more than zero code points were consumed then consume one more
+ * (which will be the last delimiter)
+ * while the input is not exhausted do begin
+ * let oldi = i
+ * let w = 1
+ * for k = base to infinity in steps of base do begin
+ * consume a code point, or fail if there was none to consume
+ * let digit = the code point's digit-value, fail if it has none
+ * let i = i + digit * w, fail on overflow
+ * let t = tmin if k <= bias {+ tmin}, or
+ * tmax if k >= bias + tmax, or k - bias otherwise
+ * if digit < t then break
+ * let w = w * (base - t), fail on overflow
+ * end
+ * let bias = adapt(i - oldi, length(output) + 1, test oldi is 0?)
+ * let n = n + i div (length(output) + 1), fail on overflow
+ * let i = i mod (length(output) + 1)
+ * {if n is a basic code point then fail}
+ * insert n into output at position i
+ * increment i
+ * end
+ */
+
+int ossl_punycode_decode(const char *pEncoded, const size_t enc_len,
+ unsigned int *pDecoded, unsigned int *pout_length)
+{
+ unsigned int n = initial_n;
+ unsigned int i = 0;
+ unsigned int bias = initial_bias;
+ size_t processed_in = 0, written_out = 0;
+ unsigned int max_out = *pout_length;
+
+ unsigned int basic_count = 0;
+ unsigned int loop;
+
+ for (loop = 0; loop < enc_len; loop++) {
+ if (pEncoded[loop] == delimiter)
+ basic_count = loop;
+ }
+
+ if (basic_count > 0) {
+ if (basic_count > max_out)
+ return 0;
+
+ for (loop = 0; loop < basic_count; loop++) {
+ if (is_basic(pEncoded[loop]) == 0)
+ return 0;
+
+ pDecoded[loop] = pEncoded[loop];
+ written_out++;
+ }
+ processed_in = basic_count + 1;
+ }
+
+ for (loop = processed_in; loop < enc_len;) {
+ unsigned int oldi = i;
+ unsigned int w = 1;
+ unsigned int k, t;
+ int digit;
+
+ for (k = base;; k += base) {
+ if (loop >= enc_len)
+ return 0;
+
+ digit = digit_decoded(pEncoded[loop]);
+ loop++;
+
+ if (digit < 0)
+ return 0;
+ if ((unsigned int)digit > (maxint - i) / w)
+ return 0;
+
+ i = i + digit * w;
+ t = (k <= bias) ? tmin : (k >= bias + tmax) ? tmax : k - bias;
+
+ if ((unsigned int)digit < t)
+ break;
+
+ if (w > maxint / (base - t))
+ return 0;
+ w = w * (base - t);
+ }
+
+ bias = adapt(i - oldi, written_out + 1, (oldi == 0));
+ if (i / (written_out + 1) > maxint - n)
+ return 0;
+ n = n + i / (written_out + 1);
+ i %= (written_out + 1);
+
+ if (written_out > max_out)
+ return 0;
+
+ memmove(pDecoded + i + 1, pDecoded + i,
+ (written_out - i) * sizeof *pDecoded);
+ pDecoded[i] = n;
+ i++;
+ written_out++;
+ }
+
+ *pout_length = written_out;
+ return 1;
+}
+
+/*
+ * Encode a code point using UTF-8
+ * return number of bytes on success, 0 on failure
+ * (also produces U+FFFD, which uses 3 bytes on failure)
+ */
+static int codepoint2utf8(unsigned char *out, unsigned long utf)
+{
+ if (utf <= 0x7F) {
+ /* Plain ASCII */
+ out[0] = (unsigned char)utf;
+ out[1] = 0;
+ return 1;
+ } else if (utf <= 0x07FF) {
+ /* 2-byte unicode */
+ out[0] = (unsigned char)(((utf >> 6) & 0x1F) | 0xC0);
+ out[1] = (unsigned char)(((utf >> 0) & 0x3F) | 0x80);
+ out[2] = 0;
+ return 2;
+ } else if (utf <= 0xFFFF) {
+ /* 3-byte unicode */
+ out[0] = (unsigned char)(((utf >> 12) & 0x0F) | 0xE0);
+ out[1] = (unsigned char)(((utf >> 6) & 0x3F) | 0x80);
+ out[2] = (unsigned char)(((utf >> 0) & 0x3F) | 0x80);
+ out[3] = 0;
+ return 3;
+ } else if (utf <= 0x10FFFF) {
+ /* 4-byte unicode */
+ out[0] = (unsigned char)(((utf >> 18) & 0x07) | 0xF0);
+ out[1] = (unsigned char)(((utf >> 12) & 0x3F) | 0x80);
+ out[2] = (unsigned char)(((utf >> 6) & 0x3F) | 0x80);
+ out[3] = (unsigned char)(((utf >> 0) & 0x3F) | 0x80);
+ out[4] = 0;
+ return 4;
+ } else {
+ /* error - use replacement character */
+ out[0] = (unsigned char)0xEF;
+ out[1] = (unsigned char)0xBF;
+ out[2] = (unsigned char)0xBD;
+ out[3] = 0;
+ return 0;
+ }
+}
+
+/*-
+ * Return values:
+ * 1 - ok, *outlen contains valid buf length
+ * 0 - ok but buf was too short, *outlen contains valid buf length
+ * -1 - bad string passed
+ */
+
+int ossl_a2ulabel(const char *in, char *out, size_t *outlen)
+{
+ /*-
+ * Domain name has some parts consisting of ASCII chars joined with dot.
+ * If a part is shorter than 5 chars, it becomes U-label as is.
+ * If it does not start with xn--, it becomes U-label as is.
+ * Otherwise we try to decode it.
+ */
+ char *outptr = out;
+ const char *inptr = in;
+ size_t size = 0;
+ int result = 1;
+
+ unsigned int buf[LABEL_BUF_SIZE]; /* It's a hostname */
+ if (out == NULL)
+ result = 0;
+
+ while (1) {
+ char *tmpptr = strchr(inptr, '.');
+ size_t delta = (tmpptr) ? (size_t)(tmpptr - inptr) : strlen(inptr);
+
+ if (strncmp(inptr, "xn--", 4) != 0) {
+ size += delta + 1;
+
+ if (size >= *outlen - 1)
+ result = 0;
+
+ if (result > 0) {
+ memcpy(outptr, inptr, delta + 1);
+ outptr += delta + 1;
+ }
+ } else {
+ unsigned int bufsize = LABEL_BUF_SIZE;
+ unsigned int i;
+
+ if (ossl_punycode_decode(inptr + 4, delta - 4, buf, &bufsize) <= 0)
+ return -1;
+
+ for (i = 0; i < bufsize; i++) {
+ unsigned char seed[6];
+ size_t utfsize = codepoint2utf8(seed, buf[i]);
+ if (utfsize == 0)
+ return -1;
+
+ size += utfsize;
+ if (size >= *outlen - 1)
+ result = 0;
+
+ if (result > 0) {
+ memcpy(outptr, seed, utfsize);
+ outptr += utfsize;
+ }
+ }
+
+ if (tmpptr != NULL) {
+ *outptr = '.';
+ outptr++;
+ size++;
+ if (size >= *outlen - 1)
+ result = 0;
+ }
+ }
+
+ if (tmpptr == NULL)
+ break;
+
+ inptr = tmpptr + 1;
+ }
+
+ return result;
+}
+
+/*-
+ * a MUST be A-label
+ * u MUST be U-label
+ * Returns 0 if compared values are equal
+ * 1 if not
+ * -1 in case of errors
+ */
+
+int ossl_a2ucompare(const char *a, const char *u)
+{
+ char a_ulabel[LABEL_BUF_SIZE];
+ size_t a_size = sizeof(a_ulabel);
+
+ if (ossl_a2ulabel(a, a_ulabel, &a_size) <= 0) {
+ return -1;
+ }
+
+ return (strcmp(a_ulabel, u) == 0) ? 0 : 1;
+}
diff --git a/crypto/x509/v3_ncons.c b/crypto/x509/v3_ncons.c
index 4543ec2e11..8da9cca24d 100644
--- a/crypto/x509/v3_ncons.c
+++ b/crypto/x509/v3_ncons.c
@@ -17,6 +17,7 @@
#include <openssl/bn.h>
#include "crypto/x509.h"
+#include "crypto/punycode.h"
#include "ext_dat.h"
DEFINE_STACK_OF(CONF_VALUE)
@@ -38,6 +39,7 @@ static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen);
static int nc_dn(const X509_NAME *sub, const X509_NAME *nm);
static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns);
static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml);
+static int nc_email_eai(ASN1_UTF8STRING *sub, ASN1_IA5STRING *eml);
static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base);
static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base);
@@ -459,6 +461,14 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
{
GENERAL_SUBTREE *sub;
int i, r, match = 0;
+ /*
+ * We need to compare not gen->type field but an "effective" type because
+ * the otherName field may contain EAI email address treated specially
+ * according to RFC 8398, section 6
+ */
+ int effective_type = ((gen->type == GEN_OTHERNAME) &&
+ (OBJ_obj2nid(gen->d.otherName->type_id) ==
+ NID_id_on_SmtpUTF8Mailbox)) ? GEN_EMAIL : gen->type;
/*
* Permitted subtrees: if any subtrees exist of matching the type at
@@ -467,7 +477,7 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) {
sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
- if (gen->type != sub->base->type)
+ if (effective_type != sub->base->type)
continue;
if (!nc_minmax_valid(sub))
return X509_V_ERR_SUBTREE_MINMAX;
@@ -490,7 +500,7 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) {
sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
- if (gen->type != sub->base->type)
+ if (effective_type != sub->base->type)
continue;
if (!nc_minmax_valid(sub))
return X509_V_ERR_SUBTREE_MINMAX;
@@ -509,7 +519,14 @@ static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base)
{
- switch (base->type) {
+ switch (gen->type) {
+ case GEN_OTHERNAME:
+ /*
+ * We are here only when we have SmtpUTF8 name,
+ * so we match the value of othername with base->d.rfc822Name
+ */
+ return nc_email_eai(gen->d.otherName->value->value.utf8string,
+ base->d.rfc822Name);
case GEN_DIRNAME:
return nc_dn(gen->d.directoryName, base->d.directoryName);
@@ -577,13 +594,59 @@ static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
}
+/*
+ * This function implements comparison between ASCII/U-label in eml
+ * and A-label in base according to RFC 8398, section 6.
+ * Convert base to U-label and ASCII-parts of domain names, for base
+ * Octet-to-octet comparison of `eml` and `base` hostname parts
+ * (ASCII-parts should be compared in case-insensitive manner)
+ */
+static int nc_email_eai(ASN1_UTF8STRING *eml, ASN1_IA5STRING *base)
+{
+ const char *baseptr = (char *)base->data;
+ const char *emlptr = (char *)eml->data;
+ const char *emlat = strrchr(emlptr, '@');
+
+ char ulabel[256];
+ size_t size = sizeof(ulabel) - 1;
+
+ if (emlat == NULL)
+ return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
+
+ memset(ulabel, 0, sizeof(ulabel));
+ /* Special case: initial '.' is RHS match */
+ if (*baseptr == '.') {
+ ulabel[0] = '.';
+ size -= 1;
+ if (ossl_a2ulabel(baseptr, ulabel + 1, &size) <= 0)
+ return X509_V_ERR_UNSPECIFIED;
+
+ if ((size_t)eml->length > size + 1) {
+ emlptr += eml->length - (size + 1);
+ if (ia5casecmp(ulabel, emlptr) == 0)
+ return X509_V_OK;
+ }
+ return X509_V_ERR_PERMITTED_VIOLATION;
+ }
+
+ emlptr = emlat + 1;
+ if (ossl_a2ulabel(baseptr, ulabel, &size) <= 0)
+ return X509_V_ERR_UNSPECIFIED;
+ /* Just have hostname left to match: case insensitive */
+ if (ia5casecmp(ulabel, emlptr))
+ return X509_V_ERR_PERMITTED_VIOLATION;
+
+ return X509_V_OK;
+
+}
+
static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
{
const char *baseptr = (char *)base->data;
const char *emlptr = (char *)eml->data;
- const char *baseat = strchr(baseptr, '@');
- const char *emlat = strchr(emlptr, '@');
+ const char *baseat = strrchr(baseptr, '@');
+ const char *emlat = strrchr(emlptr, '@');
if (!emlat)
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
/* Special case: initial '.' is RHS match */
diff --git a/crypto/x509/v3_utl.c b/crypto/x509/v3_utl.c
index 9083ed8686..e31be45e03 100644
--- a/crypto/x509/v3_utl.c
+++ b/crypto/x509/v3_utl.c
@@ -878,8 +878,22 @@ static int do_x509_check(X509 *x, const char *chk, size_t chklen,
ASN1_STRING *cstr;
gen = sk_GENERAL_NAME_value(gens, i);
- if (gen->type != check_type)
- continue;
+ if ((gen->type == GEN_OTHERNAME) && (check_type == GEN_EMAIL)) {
+ if (OBJ_obj2nid(gen->d.otherName->type_id) ==
+ NID_id_on_SmtpUTF8Mailbox) {
+ san_present = 1;
+ cstr = gen->d.otherName->value->value.utf8string;
+
+ /* Positive on success, negative on error! */
+ if ((rv = do_check_string(cstr, 0, equal, flags,
+ chk, chklen, peername)) != 0)
+ break;
+ } else
+ continue;
+ } else {
+ if ((gen->type != check_type) && (gen->type != GEN_OTHERNAME))
+ continue;
+ }
san_present = 1;
if (check_type == GEN_EMAIL)
cstr = gen->d.rfc822Name;
diff --git a/doc/internal/man3/ossl_punycode_decode.pod b/doc/internal/man3/ossl_punycode_decode.pod
new file mode 100644
index 0000000000..b0ff76653b
--- /dev/null
+++ b/doc/internal/man3/ossl_punycode_decode.pod
@@ -0,0 +1,60 @@
+=pod
+
+=head1 NAME
+
+ossl_punycode_decode, ossl_a2ulabel, ossl_a2ucompare
+- internal punycode-related functions
+
+=head1 SYNOPSIS
+
+ #include "crypto/punycode.h"
+
+ int ossl_punycode_decode(const char *pEncoded, const size_t enc_len,
+ unsigned int *pDecoded, unsigned int *pout_length);
+
+ int ossl_a2ulabel(const char *in, char *out, size_t *outlen);
+
+ int ossl_a2ucompare(const char *a, const char *u);
+
+=head1 DESCRIPTION
+
+PUNYCODE encoding introduced in RFCs 3490-3492 is widely used for
+representation of host names in ASCII-only format. Some specifications,
+such as RFC 8398, require comparison of host names encoded in UTF-8 charset.
+
+ossl_a2ulabel() decodes NULL-terminated hostname from PUNYCODE to UTF-8,
+using a provided buffer for output.
+
+ossl_a2ucompare() accepts two NULL-terminated hostnames, decodes the 1st
+from PUNYCODE to UTF-8 and compares it with the 2nd one as is.
+
+ossl_punycode_decode() decodes one label (one dot-separated part) from
+a hostname, with stripped PUNYCODE marker I<xn-->.
+
+=head1 RETURN VALUES
+
+ossl_a2ulabel() returns 1 on success, 0 on not enough buf passed,
+-1 on invalid PUNYCODE string passed. When valid string is provided, it sets the
+I<*outlen> to the length of required buffer to perform correct decoding.
+
+ossl_a2ucompare() returns 1 on non-equal strings, 0 on equal strings,
+-1 when invalid PUNYCODE string passed.
+
+ossl_punycode_decode() returns 1 on success, 0 on error. On success,
+*pout_length contains the number of codepoints decoded.
+
+=head1 HISTORY
+
+The functions described here were all added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License"). You may not use
+this file except in compliance with the License. You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
+
diff --git a/doc/man3/X509_check_host.pod b/doc/man3/X509_check_host.pod
index b541901c00..23476a81df 100644
--- a/doc/man3/X509_check_host.pod
+++ b/doc/man3/X509_check_host.pod
@@ -48,9 +48,13 @@ is responsible for freeing the peername via OPENSSL_free() when it
is no longer needed.
X509_check_email() checks if the certificate matches the specified
-email B<address>. Only the mailbox syntax of RFC 822 is supported,
+email B<address>. The mailbox syntax of RFC 822 is supported,
comments are not allowed, and no attempt is made to normalize quoted
-characters. The B<addresslen> argument must be the number of
+characters. The mailbox syntax of RFC 6531 is supported for
+SmtpUTF8Mailbox address in subjectAltName according to RFC 8398,
+with similar limitations as for RFC 822 syntax, and no attempt
+is made to convert from A-label to U-label before comparison.
+The B<addresslen> argument must be the number of
characters in the address string or zero in which case the length
is calculated with strlen(B<address>).
diff --git a/doc/man5/x509v3_config.pod b/doc/man5/x509v3_config.pod
index a16f862bae..953b0268cd 100644
--- a/doc/man5/x509v3_config.pod
+++ b/doc/man5/x509v3_config.pod
@@ -241,6 +241,15 @@ Examples:
OU = My Unit
CN = My Name
+Non-ASCII Email Address conforming the syntax defined in Section 3.3 of RFC 6531
+are provided as otherName.SmtpUTF8Mailbox. According to RFC 8398, the email
+address should be provided as UTF8String. To enforce the valid representation in
+the certificate, the SmtpUTF8Mailbox should be provided as follows
+
+ subjectAltName=@alts
+ [alts]
+ otherName = 1.3.6.1.5.5.7.8.9;FORMAT:UTF8,UTF8String:nonasciiname.example.com
+
=head2 Issuer Alternative Name
This extension supports most of the options of subject alternative name;
diff --git a/include/crypto/punycode.h b/include/crypto/punycode.h
new file mode 100644
index 0000000000..ec97c17cab
--- /dev/null
+++ b/include/crypto/punycode.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_CRYPTO_PUNYCODE_H
+# define OSSL_CRYPTO_PUNYCODE_H
+
+
+int ossl_punycode_decode (
+ const char *pEncoded,
+ const size_t enc_len,
+ unsigned int *pDecoded,
+ unsigned int *pout_length
+);
+
+int ossl_a2ulabel(const char *in, char *out, size_t *outlen);
+
+int ossl_a2ucompare(const char *a, const char *u);
+#endif
diff --git a/test/recipes/25-test_eai_data.t b/test/recipes/25-test_eai_data.t
new file mode 100644
index 0000000000..ccdb09dd9c
--- /dev/null
+++ b/test/recipes/25-test_eai_data.t
@@ -0,0 +1,53 @@
+#! /usr/bin/env perl
+# Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+use strict;
+use warnings;
+
+use File::Spec;
+use OpenSSL::Test::Utils;
+use OpenSSL::Test qw/:DEFAULT srctop_file/;
+
+setup("test_eai_data");
+
+#./util/wrap.pl apps/openssl verify -nameopt utf8 -no_check_time -CAfile test/recipes/25-test_eai_data/ascii_chain.pem test/recipes/25-test_eai_data/ascii_leaf.pem
+#./util/wrap.pl apps/openssl verify -nameopt utf8 -no_check_time -CAfile test/recipes/25-test_eai_data/utf8_chain.pem test/recipes/25-test_eai_data/utf8_leaf.pem
+#./util/wrap.pl apps/openssl verify -nameopt utf8 -no_check_time -CAfile test/recipes/25-test_eai_data/utf8_chain.pem test/recipes/25-test_eai_data/ascii_leaf.pem
+#./util/wrap.pl apps/openssl verify -nameopt utf8 -no_check_time -CAfile test/recipes/25-test_eai_data/ascii_chain.pem test/recipes/25-test_eai_data/utf8_leaf.pem
+
+plan tests => 11;
+
+require_ok(srctop_file('test','recipes','tconversion.pl'));
+my $folder = "test/recipes/25-test_eai_data";
+
+my $ascii_pem = srctop_file($folder, "ascii_leaf.pem");
+my $utf8_pem = srctop_file($folder, "utf8_leaf.pem");
+
+my $ascii_chain_pem = srctop_file($folder, "ascii_chain.pem");
+my $utf8_chain_pem = srctop_file($folder, "utf8_chain.pem");
+
+my $out = "san.tmp";
+
+ok(run(app(["openssl", "x509", "-ext", "subjectAltName", "-in", $ascii_pem, "-noout", "-out", $out])));
+is(cmp_text($out, srctop_file($folder, "san.ascii")), 0, 'Comparing othername for ASCII domain');
+
+ok(run(app(["openssl", "x509", "-ext", "subjectAltName", "-in", $utf8_pem, "-noout", "-out", $out])));
+is(cmp_text($out, srctop_file($folder, "san.utf8")), 0, 'Comparing othername for IDN domain');
+
+unlink $out;
+
+ok(run(app(["openssl", "verify", "-nameopt", "utf8", "-no_check_time", "-verify_email", "学生\@elementary.school.example.com", "-CAfile", $ascii_chain_pem, $ascii_pem])));
+ok(run(app(["openssl", "verify", "-nameopt", "utf8", "-no_check_time", "-verify_email", "医生\@大学.example.com", "-CAfile", $utf8_chain_pem, $utf8_pem])));
+
+ok(run(app(["openssl", "verify", "-nameopt", "utf8", "-no_check_time", "-CAfile", $ascii_chain_pem, $ascii_pem])));
+ok(run(app(["openssl", "verify", "-nameopt", "utf8", "-no_check_time", "-CAfile", $utf8_chain_pem, $utf8_pem])));
+
+ok(!run(app(["openssl", "verify", "-nameopt", "utf8", "-no_check_time", "-CAfile", $ascii_chain_pem, $utf8_pem])));
+ok(!run(app(["openssl", "verify", "-nameopt", "utf8", "-no_check_time", "-CAfile", $utf8_chain_pem, $ascii_pem])));
+
diff --git a/test/recipes/25-test_eai_data/ascii_chain.pem b/test/recipes/25-test_eai_data/ascii_chain.pem
new file mode 100644
index 0000000000..ea258a3885
--- /dev/null
+++ b/test/recipes/25-test_eai_data/ascii_chain.pem
@@ -0,0 +1,53 @@
+-----BEGIN CERTIFICATE-----
+MIIEjDCCA3SgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwgbsxCzAJBgNVBAYTAlVT
+MQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEYMBYGA1UECgwP
+RXhhbXBsZSBDb21wYW55MS4wLAYDVQQLDCVFeGFtcGxlIENvbXBhbnkgQ2VydGlm
+aWNhdGUgQXV0aG9yaXR5MRswGQYDVQQDDBJFeGFtcGxlIENvbXBhbnkgQ0ExIDAe
+BgkqhkiG9w0BCQEWEWFsaWNlQGV4YW1wbGUuY29tMB4XDTE5MDMyNzA5MzkwMloX
+DTI5MDMyNDA5MzkwMlowgb0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEYMBYG
+A1UECgwPRXhhbXBsZSBDb21wYW55MTswOQYDVQQLDDJFeGFtcGxlIENvbXBhbnkg
+SW50ZXJtZWRpYXRlIENlcnRpZmljYXRlIEF1dGhvcml0eTEoMCYGA1UEAwwfRXhh
+bXBsZSBDb21wYW55IEludGVybWVkaWF0ZSBDQTEgMB4GCSqGSIb3DQEJARYRYWxp
+Y2VAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9
+G/Vjs763ft6Jz6F63pNY2cc0jzCBxgw6c7UY3QC9Szmi7F/YxW+mCXwUFy4kT16X
+gujoV1prCJC7ywF2zmapIqRg08oUMdyYAfqi+OH1sbelih+J9ptvoLlQ4HULkXqP
+r9EAjI90shcVhFM/+C1C+7AC+fM74Q89U5D4i6GWFvGjVT88/fsQYwRGoB7e13gQ
+DZTFDpun8ayCI2cuIDEmpnplNpYYIbCdvBNNTp1D82g5eEz1VJVEwnr6JyVUFg5U
+aHApPzaqAXoIDjjW9G0RdX61zNAdbtjqZmP98Dt1MNGyRVHNUiafY2Zr9CtR02Fe
+AzVq8HGKCQ+FXy5VQXB7AgMBAAGjgZUwgZIwHQYDVR0OBBYEFAw/3qi2nZHj2xms
+cUd3PxN+/lxzMB8GA1UdIwQYMBaAFE7KwQ4mMV6AoFSttAbzi7rsIMY2MBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMCwGA1UdHgQlMCOgITAfgR1l
+bGVtZW50YXJ5LnNjaG9vbC5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEA
+DwUJJlG487NnzXCZjhztXGwjDmPb5ZDybIsOQH7CVSoajLLFaAVAIUYy9kFSENfZ
+gBT2LfJbW4i4ziRAIgHwWpjZAItirFwZtyl8BWJF4xdUmd+m0gj0ReEgPWKJdgLc
+O9quzLsGZ2n//oFG97gWqctIEw1ugOhwdeTcaC2WZMf62sE0yqjOOf+fynfCFjUV
++enle/EyAFghVBbuF/Yk6Y7/x+7pjncTHKl9zTBLWp5yH2JvQ5dCloho9jD16TGs
+TRL/b1I154INi/XwZMtFLxNxosB1HC86sr4l4GVO8nZxzgJ+8cppp1goGzEUDY1F
+lMWa0rQbcHhk98IaP+OAWg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEXjCCA0agAwIBAgIJAMujkjMG9iZjMA0GCSqGSIb3DQEBCwUAMIG7MQswCQYD
+VQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGDAW
+BgNVBAoMD0V4YW1wbGUgQ29tcGFueTEuMCwGA1UECwwlRXhhbXBsZSBDb21wYW55
+IENlcnRpZmljYXRlIEF1dGhvcml0eTEbMBkGA1UEAwwSRXhhbXBsZSBDb21wYW55
+IENBMSAwHgYJKoZIhvcNAQkBFhFhbGljZUBleGFtcGxlLmNvbTAeFw0xOTAzMjcw
+NjIzNDBaFw0zOTAzMjIwNjIzNDBaMIG7MQswCQYDVQQGEwJVUzELMAkGA1UECAwC
+Q0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGDAWBgNVBAoMD0V4YW1wbGUgQ29t
+cGFueTEuMCwGA1UECwwlRXhhbXBsZSBDb21wYW55IENlcnRpZmljYXRlIEF1dGhv
+cml0eTEbMBkGA1UEAwwSRXhhbXBsZSBDb21wYW55IENBMSAwHgYJKoZIhvcNAQkB
+FhFhbGljZUBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKhAtiyuvE09abRB1qPBnnMkzU7mRX3xcbOPT/NzfDTkbuTVbcJ98Ei3TRv1
+fmS5Ds1t2fcc493Cx1BTPmfdZSWhvONa9PDZ84wmtkaoU0Utzzdm+62zb/0/I0wh
+MgGaC25D8IRZ5yp5eTtX/yasOVaij9UjphAoHa4eD1GGzpc/LNI9hrV15BmdfcS2
+3FbtlUjFdIjsR41bTVkEb5nwBSrixV5MAcV6Y6I7mSykSTrytY6DcJs/k9yrzOM9
+p6x3v9Npyzny1vUxHWJmsSPwlfxCigcFU7I1ixybWuJB6opPu59h46r4VOVedhcj
+chSa9UcSLIKfmEhrQnw3YIUhudkCAwEAAaNjMGEwHQYDVR0OBBYEFE7KwQ4mMV6A
+oFSttAbzi7rsIMY2MB8GA1UdIwQYMBaAFE7KwQ4mMV6AoFSttAbzi7rsIMY2MA8G
+A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB
+AQCBmOcwt/AqQjCAFRdqLFsWdGsDC2iQE4lJhsv6oypBsNNxrYV962JrrgkZThPv
+e+OkBmPrN2izT7UX5Yxbi8qmCe/eaY239GCIgRQs6wehMqlGPx5cZeruwLHDjcsv
+4/KmyDJ9u1lx83OsORxcAr4W+HEIFtLEueQkGbJTMZSKMKkbSa4Ik2bji6ctZBkO
+qWtD5s4C0P29Z8CJL1jRrYNjFg5alGSzoYi26c1o9Oz35pNiurxySE+iDHjkj1vq
+d9+F+869tkXsbuGucdv7oKGZrfdTWKp+LiRQBJzjA9nrZuUqMSMsmSXpXPHV3BdI
+4k1AgvwY9/u+RlmeGW2qRTZJ
+-----END CERTIFICATE-----
diff --git a/test/recipes/25-test_eai_data/ascii_leaf.pem b/test/recipes/25-test_eai_data/ascii_leaf.pem
new file mode 100644
index 0000000000..286d317968
--- /dev/null
+++ b/test/recipes/25-test_eai_data/ascii_leaf.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIE3DCCA8SgAwIBAgIUPTX8yrPZtf85fFr2BqMIknud10EwDQYJKoZIhvcNAQEL
+BQAwgb0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEYMBYGA1UECgwPRXhhbXBs
+ZSBDb21wYW55MTswOQYDVQQLDDJFeGFtcGxlIENvbXBhbnkgSW50ZXJtZWRpYXRl
+IENlcnRpZmljYXRlIEF1dGhvcml0eTEoMCYGA1UEAwwfRXhhbXBsZSBDb21wYW55
+IEludGVybWVkaWF0ZSBDQTEgMB4GCSqGSIb3DQEJARYRYWxpY2VAZXhhbXBsZS5j
+b20wHhcNMTkwNjA4MTA0NDA2WhcNMjAwNjE3MTA0NDA2WjCBiDELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRgwFgYDVQQK
+DA9FeGFtcGxlIENvbXBhbnkxHTAbBgNVBAsMFEV4YW1wbGUgQ29tcGFueSBVbml0
+MRswGQYDVQQDDBJBbGljZSBBc2NpaSBEb21haW4wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDfpjeb8F/YlO5NYh+5+UcdaKIG3cpORrYZddUYprsbn8ic
+Utk4OXOklvMC1PwhW/8KcwuF0pUsQ/QZUmiJWv2umopmvl05OvPbTVfE4nE9JezF
+bNagPPrAugsbAC/1K65iPuZO4ZEZV7zjHxbdQk8fPzAZUfRqJsyinSnc2r3P4OvJ
+BffRJk8ZDJvx8kT3POYScSjTrSNVOHT3mT+2S+z00vwqKWdtroVyUr7TyC0/ocbf
+AKly1TmkD9RZNJ3ASlG0ZVri/a/TuglEs0WLtZran4IuKkzNnWccDILke0lkXZ+H
+mC1eKY3fYt8tAN+nkdTE1liJJNth4SpmYHLKoqstAgMBAAGjggEFMIIBATAJBgNV
+HRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAzBglghkgBhvhCAQ0EJhYkT3BlblNT
+TCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRpZmljYXRlMB0GA1UdDgQWBBTCGcnEt0A+
+oJcD1GbkcwiWGG8f9DAfBgNVHSMEGDAWgBQMP96otp2R49sZrHFHdz8Tfv5cczAO
+BgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMD0G
+A1UdEQQ2MDSgMgYIKwYBBQUHCAmgJgwk5a2m55SfQGVsZW1lbnRhcnkuc2Nob29s
+LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQALoWleKWXxN/WXsIMvceNO
+IfAy01Yrks0+qVhD0Z9GOAliqXOcSdr9ns+8Vhh56qPAevdP6vqACE8LnQ5Uwq1d
+SV0dxEKIaJoWRI+CnbKu+TVrR9mZAyABbPqWIBP41luj32Y/tL85k8gPF02hp0Vw
+tqrJjbCyzY4Sly14v9dBdxkJPPm+uPcgICbaCthSVm2iB9Bh+FdwJA9As+texqyI
+3Yzc6GcQpRB9M5kb5Ibqw3RMEzBGkdBCBehh06hDrx6c/JmgCspLZjCgImjn7eLk
+VyPZTiTcnTFUJuL4F/z/aAUAXzE2ueQCCRQOeX/x5KC9CElpFCw7RcpcZdCAw9KN
+-----END CERTIFICATE-----
diff --git a/test/recipes/25-test_eai_data/san.ascii b/test/recipes/25-test_eai_data/san.ascii
new file mode 100644
index 0000000000..e719e2660f
--- /dev/null
+++ b/test/recipes/25-test_eai_data/san.ascii
@@ -0,0 +1,2 @@
+X509v3 Subject Alternative Name:
+ othername: SmtpUTF8Mailbox::学生@elementary.school.example.com
diff --git a/test/recipes/25-test_eai_data/san.utf8 b/test/recipes/25-test_eai_data/san.utf8
new file mode 100644
index 0000000000..cf62d9dfbe
--- /dev/null
+++ b/test/recipes/25-test_eai_data/san.utf8
@@ -0,0 +1,2 @@
+X509v3 Subject Alternative Name:
+ othername: SmtpUTF8Mailbox::医生@大学.example.com
diff --git a/test/recipes/25-test_eai_data/utf8_chain.pem b/test/recipes/25-test_eai_data/utf8_chain.pem
new file mode 100644
index 0000000000..793902569f
--- /dev/null
+++ b/test/recipes/25-test_eai_data/utf8_chain.pem
@@ -0,0 +1,53 @@
+-----BEGIN CERTIFICATE-----
+MIIEhTCCA22gAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwgbsxCzAJBgNVBAYTAlVT
+MQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEYMBYGA1UECgwP
+RXhhbXBsZSBDb21wYW55MS4wLAYDVQQLDCVFeGFtcGxlIENvbXBhbnkgQ2VydGlm
+aWNhdGUgQXV0aG9yaXR5MRswGQYDVQQDDBJFeGFtcGxlIENvbXBhbnkgQ0ExIDAe
+BgkqhkiG9w0BCQEWEWFsaWNlQGV4YW1wbGUuY29tMB4XDTE5MDMyNzA5NDEzOVoX
+DTI5MDMyNDA5NDEzOVowgb0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEYMBYG
+A1UECgwPRXhhbXBsZSBDb21wYW55MTswOQYDVQQLDDJFeGFtcGxlIENvbXBhbnkg
+SW50ZXJtZWRpYXRlIENlcnRpZmljYXRlIEF1dGhvcml0eTEoMCYGA1UEAwwfRXhh
+bXBsZSBDb21wYW55IEludGVybWVkaWF0ZSBDQTEgMB4GCSqGSIb3DQEJARYRYWxp
+Y2VAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9
+G/Vjs763ft6Jz6F63pNY2cc0jzCBxgw6c7UY3QC9Szmi7F/YxW+mCXwUFy4kT16X
+gujoV1prCJC7ywF2zmapIqRg08oUMdyYAfqi+OH1sbelih+J9ptvoLlQ4HULkXqP
+r9EAjI90shcVhFM/+C1C+7AC+fM74Q89U5D4i6GWFvGjVT88/fsQYwRGoB7e13gQ
+DZTFDpun8ayCI2cuIDEmpnplNpYYIbCdvBNNTp1D82g5eEz1VJVEwnr6JyVUFg5U
+aHApPzaqAXoIDjjW9G0RdX61zNAdbtjqZmP98Dt1MNGyRVHNUiafY2Zr9CtR02Fe
+AzVq8HGKCQ+FXy5VQXB7AgMBAAGjgY4wgYswHQYDVR0OBBYEFAw/3qi2nZHj2xms
+cUd3PxN+/lxzMB8GA1UdIwQYMBaAFE7KwQ4mMV6AoFSttAbzi7rsIMY2MBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMCUGA1UdHgQeMBygGjAYgRZ4
+bi0tcHNzMjVjLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCEpk1PVmSQ
+LoDTVDZlhbJmRP4UNq0ODaKNJHwpqVm0Q0lpg694brvLapmgAeRHPHHdTOv8VdGj
+y4/NvaWRjdBqnI6sVobMfC0JJtHQufWpQ4i85D6ljh6RQX62Tz9/EGd6jgE/Pjrw
+siU4geDY4c4EWGskJAGHaMYSTjQdutSP8NAxFXcupyniDyF75Kjfeb7kVj3zuvhS
+UYN13IeB2498t46BNqYVP1Sh51UMg4vkddHt3rI37WdilDq2j5e3qcUvYGFmiU7O
+j7P48QXoDHsWZmUvZcXlFky+9eFYd1a3QAV7AMc2iD93+GJBbwqxdN1393Y9Kk4R
+Y/RIVt183q8X
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEXjCCA0agAwIBAgIJAMujkjMG9iZjMA0GCSqGSIb3DQEBCwUAMIG7MQswCQYD
+VQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGDAW
+BgNVBAoMD0V4YW1wbGUgQ29tcGFueTEuMCwGA1UECwwlRXhhbXBsZSBDb21wYW55
+IENlcnRpZmljYXRlIEF1dGhvcml0eTEbMBkGA1UEAwwSRXhhbXBsZSBDb21wYW55
+IENBMSAwHgYJKoZIhvcNAQkBFhFhbGljZUBleGFtcGxlLmNvbTAeFw0xOTAzMjcw
+NjIzNDBaFw0zOTAzMjIwNjIzNDBaMIG7MQswCQYDVQQGEwJVUzELMAkGA1UECAwC
+Q0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGDAWBgNVBAoMD0V4YW1wbGUgQ29t
+cGFueTEuMCwGA1UECwwlRXhhbXBsZSBDb21wYW55IENlcnRpZmljYXRlIEF1dGhv
+cml0eTEbMBkGA1UEAwwSRXhhbXBsZSBDb21wYW55IENBMSAwHgYJKoZIhvcNAQkB
+FhFhbGljZUBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKhAtiyuvE09abRB1qPBnnMkzU7mRX3xcbOPT/NzfDTkbuTVbcJ98Ei3TRv1
+fmS5Ds1t2fcc493Cx1BTPmfdZSWhvONa9PDZ84wmtkaoU0Utzzdm+62zb/0/I0wh
+MgGaC25D8IRZ5yp5eTtX/yasOVaij9UjphAoHa4eD1GGzpc/LNI9hrV15BmdfcS2
+3FbtlUjFdIjsR41bTVkEb5nwBSrixV5MAcV6Y6I7mSykSTrytY6DcJs/k9yrzOM9
+p6x3v9Npyzny1vUxHWJmsSPwlfxCigcFU7I1ixybWuJB6opPu59h46r4VOVedhcj
+chSa9UcSLIKfmEhrQnw3YIUhudkCAwEAAaNjMGEwHQYDVR0OBBYEFE7KwQ4mMV6A
+oFSttAbzi7rsIMY2MB8GA1UdIwQYMBaAFE7KwQ4mMV6AoFSttAbzi7rsIMY2MA8G
+A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB
+AQCBmOcwt/AqQjCAFRdqLFsWdGsDC2iQE4lJhsv6oypBsNNxrYV962JrrgkZThPv
+e+OkBmPrN2izT7UX5Yxbi8qmCe/eaY239GCIgRQs6wehMqlGPx5cZeruwLHDjcsv
+4/KmyDJ9u1lx83OsORxcAr4W+HEIFtLEueQkGbJTMZSKMKkbSa4Ik2bji6ctZBkO
+qWtD5s4C0P29Z8CJL1jRrYNjFg5alGSzoYi26c1o9Oz35pNiurxySE+iDHjkj1vq
+d9+F+869tkXsbuGucdv7oKGZrfdTWKp+LiRQBJzjA9nrZuUqMSMsmSXpXPHV3BdI
+4k1AgvwY9/u+RlmeGW2qRTZJ
+-----END CERTIFICATE-----
diff --git a/test/recipes/25-test_eai_data/utf8_leaf.pem b/test/recipes/25-test_eai_data/utf8_leaf.pem
new file mode 100644
index 0000000000..61d5965bc8
--- /dev/null
+++ b/test/recipes/25-test_eai_data/utf8_leaf.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEzjCCA7agAwIBAgIUCf16eBeNMQe+sxEnCZnFEuEONAwwDQYJKoZIhvcNAQEL
+BQAwgb0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEYMBYGA1UECgwPRXhhbXBs
+ZSBDb21wYW55MTswOQYDVQQLDDJFeGFtcGxlIENvbXBhbnkgSW50ZXJtZWRpYXRl
+IENlcnRpZmljYXRlIEF1dGhvcml0eTEoMCYGA1UEAwwfRXhhbXBsZSBDb21wYW55
+IEludGVybWVkaWF0ZSBDQTEgMB4GCSqGSIb3DQEJARYRYWxpY2VAZXhhbXBsZS5j
+b20wHhcNMTkwNjA4MTA1MzUzWhcNMjAwNjE3MTA1MzUzWjCBhzELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRgwFgYDVQQK
+DA9FeGFtcGxlIENvbXBhbnkxHTAbBgNVBAsMFEV4YW1wbGUgQ29tcGFueSBVbml0
+MRowGAYDVQQDDBFCb2IgLSB1dGY4IERvbWFpbjCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAN+mN5vwX9iU7k1iH7n5Rx1oogbdyk5Gthl11RimuxufyJxS
+2Tg5c6SW8wLU/CFb/wpzC4XSlSxD9BlSaIla/a6aima+XTk689tNV8TicT0l7MVs
+1qA8+sC6CxsAL/UrrmI+5k7hkRlXvOMfFt1CTx8/MBlR9GomzKKdKdzavc/g68kF
+99EmTxkMm/HyRPc85hJxKNOtI1U4dPeZP7ZL7PTS/CopZ22uhXJSvtPILT+hxt8A
+qXLVOaQP1Fk0ncBKUbRlWuL9r9O6CUSzRYu1mtqfgi4qTM2dZxwMguR7SWRdn4eY
+LV4pjd9i3y0A36eR1MTWWIkk22HhKmZgcsqiqy0CAwEAAaOB+TCB9jAJBgNVHRME
+AjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBH
+ZW5lcmF0ZWQgQ2xpZW50IENlcnRpZmljYXRlMB0GA1UdDgQWBBTCGcnEt0A+oJcD
+1GbkcwiWGG8f9DAfBgNVHSMEGDAWgBQMP96otp2R49sZrHFHdz8Tfv5cczAOBgNV
+HQ8BAf8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMDIGA1Ud
+EQQrMCmgJwYIKwYBBQUHCAmgGwwZ5Yy755SfQOWkp+Wtpi5leGFtcGxlLmNvbTAN
+BgkqhkiG9w0BAQsFAAOCAQEAJMye89+KKYE0Dn2fSUCA3JcoRqWGi5rJkBKzGLQE
+sXmXJ/ECjWXugIz69vkJhh5L4cvRv/Orsq40aAsEhzQHVwFZvjgaDW3EHL10FeY9
+fiZgCThMcktaxWnc9xrUE2GUrEt9+QBkaVozNQgsnGDGPh499Tupzwp7YArHHdUP
+Z1y7jCR4Wlhl3RKP/VRYT0MZGuOKwyVsV4D6+4dQ/CigCqvD0eruI6owREvfH8Lc
+sIwyPtO87EhMWOKD0EkU9bH97D0UZyZlhEg0uq1VaWOHztsnb63Go7u5m1QWlII6
+jAEfnqwuMqNAkRkTT2pfNCq+2+GUlBZDRtuiTljc6QQegg==
+-----END CERTIFICATE-----
More information about the openssl-commits
mailing list